diff options
1956 files changed, 48118 insertions, 21399 deletions
diff --git a/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__arm_CtsShimPriv_apk.asciipb b/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__arm_CtsShimPriv_apk.asciipb index a40adac7369e..82ef3e60d7a9 100644 --- a/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__arm_CtsShimPriv_apk.asciipb +++ b/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__arm_CtsShimPriv_apk.asciipb @@ -1,6 +1,6 @@ drops { android_build_drop { - build_id: "10093150" + build_id: "11947186" target: "CtsShim" source_file: "aosp_arm64/CtsShimPriv.apk" } @@ -8,7 +8,7 @@ drops { version: "" version_group: "" git_project: "platform/frameworks/base" - git_branch: "udc-dev" + git_branch: "main" transform: TRANSFORM_NONE transform_options { } diff --git a/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__arm_CtsShim_apk.asciipb b/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__arm_CtsShim_apk.asciipb index 96444ba9b989..7d0e5d74b082 100644 --- a/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__arm_CtsShim_apk.asciipb +++ b/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__arm_CtsShim_apk.asciipb @@ -1,6 +1,6 @@ drops { android_build_drop { - build_id: "10093150" + build_id: "11947186" target: "CtsShim" source_file: "aosp_arm64/CtsShim.apk" } @@ -8,7 +8,7 @@ drops { version: "" version_group: "" git_project: "platform/frameworks/base" - git_branch: "udc-dev" + git_branch: "main" transform: TRANSFORM_NONE transform_options { } diff --git a/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__x86_CtsShimPriv_apk.asciipb b/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__x86_CtsShimPriv_apk.asciipb index 4d6f8ed5523e..be320608cc57 100644 --- a/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__x86_CtsShimPriv_apk.asciipb +++ b/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__x86_CtsShimPriv_apk.asciipb @@ -1,6 +1,6 @@ drops { android_build_drop { - build_id: "10093150" + build_id: "11947186" target: "CtsShim" source_file: "aosp_x86_64/CtsShimPriv.apk" } @@ -8,7 +8,7 @@ drops { version: "" version_group: "" git_project: "platform/frameworks/base" - git_branch: "udc-dev" + git_branch: "main" transform: TRANSFORM_NONE transform_options { } diff --git a/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__x86_CtsShim_apk.asciipb b/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__x86_CtsShim_apk.asciipb index bfd6788ec7df..1a6448a6d803 100644 --- a/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__x86_CtsShim_apk.asciipb +++ b/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__x86_CtsShim_apk.asciipb @@ -1,6 +1,6 @@ drops { android_build_drop { - build_id: "10093150" + build_id: "11947186" target: "CtsShim" source_file: "aosp_x86_64/CtsShim.apk" } @@ -8,7 +8,7 @@ drops { version: "" version_group: "" git_project: "platform/frameworks/base" - git_branch: "udc-dev" + git_branch: "main" transform: TRANSFORM_NONE transform_options { } diff --git a/AconfigFlags.bp b/AconfigFlags.bp index 2d0560635201..96d6f32fc1df 100644 --- a/AconfigFlags.bp +++ b/AconfigFlags.bp @@ -307,6 +307,11 @@ java_aconfig_library { defaults: ["framework-minus-apex-aconfig-java-defaults"], } +cc_aconfig_library { + name: "android_security_flags_aconfig_c_lib", + aconfig_declarations: "android.security.flags-aconfig", +} + // UsageStats aconfig_declarations { name: "android.app.usage.flags-aconfig", diff --git a/Android.bp b/Android.bp index af312bf833e5..fb1fa3b58f74 100644 --- a/Android.bp +++ b/Android.bp @@ -115,7 +115,7 @@ filegroup { ":android.security.legacykeystore-java-source", ":android.security.maintenance-java-source", ":android.security.metrics-java-source", - ":android.system.keystore2-V3-java-source", + ":android.system.keystore2-V4-java-source", ":android.hardware.cas-V1-java-source", ":credstore_aidl", ":dumpstate_aidl", @@ -404,6 +404,7 @@ java_defaults { "android.hardware.common.fmq-V1-java", "bouncycastle-repackaged-unbundled", "com.android.sysprop.foldlockbehavior", + "com.android.sysprop.view", "framework-internal-utils", // 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. diff --git a/DREAM_MANAGER_OWNERS b/DREAM_MANAGER_OWNERS deleted file mode 100644 index 48bde6024cba..000000000000 --- a/DREAM_MANAGER_OWNERS +++ /dev/null @@ -1 +0,0 @@ -brycelee@google.com diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg index e8571757c6f7..fea2b7b7d476 100644 --- a/PREUPLOAD.cfg +++ b/PREUPLOAD.cfg @@ -30,7 +30,7 @@ hidden_api_txt_exclude_hook = ${REPO_ROOT}/frameworks/base/tools/hiddenapi/exclu ktlint_hook = ${REPO_ROOT}/prebuilts/ktlint/ktlint.py --no-verify-format -f ${PREUPLOAD_FILES} # This flag check hook runs only for "packages/SystemUI" subdirectory. If you want to include this check for other subdirectories, please modify flag_check.py. -flag_hook = ${REPO_ROOT}/frameworks/base/packages/SystemUI/flag_check.py --msg=${PREUPLOAD_COMMIT_MESSAGE} --files=${PREUPLOAD_FILES} --project=${REPO_PATH} +flag_hook = ${REPO_ROOT}/frameworks/base/packages/SystemUI/flag_check.py --msg=${PREUPLOAD_COMMIT_MESSAGE} --files=${PREUPLOAD_FILES} --project=${REPO_PROJECT} [Tool Paths] -ktfmt = ${REPO_ROOT}/prebuilts/build-tools/common/framework/ktfmt.jar +ktfmt = ${REPO_ROOT}/external/ktfmt/ktfmt.sh diff --git a/apct-tests/perftests/OWNERS b/apct-tests/perftests/OWNERS index 8ff3f9bc6620..f4346b150ca2 100644 --- a/apct-tests/perftests/OWNERS +++ b/apct-tests/perftests/OWNERS @@ -8,4 +8,3 @@ philipcuadra@google.com shayba@google.com shombert@google.com timmurray@google.com -wessam@google.com diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/generate_java.py b/apct-tests/perftests/core/src/android/libcore/varhandles/generate_java.py index 01abdb6c4be1..cfcb1d2a02d4 100755 --- a/apct-tests/perftests/core/src/android/libcore/varhandles/generate_java.py +++ b/apct-tests/perftests/core/src/android/libcore/varhandles/generate_java.py @@ -21,7 +21,7 @@ Adapted to use CrystalBall from art/test/2239-varhandle-perf/util-src/generate_j To run use: python generate_java.py <destination_directory> And then to correct lint errors (from frameworks/base): -../../tools/repohooks/tools/google-java-format.py --fix --sort-imports --google-java-format-diff ../../external/google-java-format/scripts/google-java-format-diff.py +../../tools/repohooks/tools/google-java-format.py --fix --google-java-format-diff ../../external/google-java-format/scripts/google-java-format-diff.py """ diff --git a/apct-tests/perftests/core/src/android/permission/AppOpsPerfTest.kt b/apct-tests/perftests/core/src/android/permission/AppOpsPerfTest.kt index 2af878e60db7..daf991c313c8 100644 --- a/apct-tests/perftests/core/src/android/permission/AppOpsPerfTest.kt +++ b/apct-tests/perftests/core/src/android/permission/AppOpsPerfTest.kt @@ -17,8 +17,7 @@ package android.permission import android.app.AppOpsManager import android.content.Context -import androidx.benchmark.BenchmarkState -import androidx.benchmark.junit4.BenchmarkRule +import android.perftests.utils.PerfStatusReporter import androidx.test.core.app.ApplicationProvider import androidx.test.filters.LargeTest import org.junit.Before @@ -34,7 +33,7 @@ import org.junit.Test * these APIs should be monitored closely for performance. */ class AppOpsPerfTest { - @get:Rule val mBenchmarkRule: BenchmarkRule = BenchmarkRule() + @get:Rule val perfStatusReporter = PerfStatusReporter() private lateinit var appOpsManager: AppOpsManager private lateinit var opPackageName: String private var opPackageUid: Int = 0 @@ -49,7 +48,7 @@ class AppOpsPerfTest { @Test fun testNoteOp() { - val state: BenchmarkState = mBenchmarkRule.getState() + val state = perfStatusReporter.benchmarkState while (state.keepRunning()) { appOpsManager.noteOp( AppOpsManager.OPSTR_FINE_LOCATION, @@ -63,7 +62,7 @@ class AppOpsPerfTest { @Test fun testUnsafeCheckOp() { - val state: BenchmarkState = mBenchmarkRule.getState() + val state = perfStatusReporter.benchmarkState while (state.keepRunning()) { appOpsManager.unsafeCheckOp( AppOpsManager.OPSTR_FINE_LOCATION, diff --git a/cmds/bootanimation/Android.bp b/cmds/bootanimation/Android.bp index 98767ee733ad..3534624a58a2 100644 --- a/cmds/bootanimation/Android.bp +++ b/cmds/bootanimation/Android.bp @@ -74,7 +74,4 @@ cc_library_shared { "libGLESv2", "libgui", ], - whole_static_libs: [ - "libc++fs", - ], } diff --git a/cmds/bootanimation/BootAnimation.cpp b/cmds/bootanimation/BootAnimation.cpp index 5adcd930e341..7eb9d0f3ea91 100644 --- a/cmds/bootanimation/BootAnimation.cpp +++ b/cmds/bootanimation/BootAnimation.cpp @@ -1335,7 +1335,8 @@ bool BootAnimation::preloadZip(Animation& animation) { if (path.string() == animation.parts[j].path.c_str()) { uint16_t method; // supports only stored png files - if (zip->getEntryInfo(entry, &method, nullptr, nullptr, nullptr, nullptr, nullptr)) { + if (zip->getEntryInfo(entry, &method, nullptr, nullptr, nullptr, nullptr, + nullptr, nullptr)) { if (method == ZipFileRO::kCompressStored) { FileMap* map = zip->createEntryFileMap(entry); if (map) { diff --git a/cmds/idmap2/Android.bp b/cmds/idmap2/Android.bp index 6e51f009f76c..58763a7f9aca 100644 --- a/cmds/idmap2/Android.bp +++ b/cmds/idmap2/Android.bp @@ -313,7 +313,6 @@ cc_binary { "libziparchive", ], static_libs: [ - "libc++fs", "libidmap2_policies", "libidmap2_protos", "libidmap2daidl", diff --git a/core/api/current.txt b/core/api/current.txt index c0bc6d96f7ef..831cf0194ca3 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -5605,7 +5605,6 @@ package android.app { method @Deprecated public void onCancel(android.content.DialogInterface); method @Deprecated public android.app.Dialog onCreateDialog(android.os.Bundle); method @Deprecated public void onDismiss(android.content.DialogInterface); - method public android.view.LayoutInflater onGetLayoutInflater(android.os.Bundle); method @Deprecated public void setCancelable(boolean); method @Deprecated public void setShowsDialog(boolean); method @Deprecated public void setStyle(int, int); @@ -9801,6 +9800,7 @@ package android.companion { method @NonNull public java.util.List<android.companion.AssociationInfo> getMyAssociations(); method @Deprecated public boolean hasNotificationAccess(android.content.ComponentName); method @FlaggedApi("android.companion.perm_sync_user_consent") public boolean isPermissionTransferUserConsented(int); + method @FlaggedApi("android.companion.unpair_associated_device") @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean removeBond(int); method public void requestNotificationAccess(android.content.ComponentName); method @FlaggedApi("android.companion.association_tag") public void setAssociationTag(int, @NonNull String); method @RequiresPermission(android.Manifest.permission.REQUEST_OBSERVE_COMPANION_DEVICE_PRESENCE) public void startObservingDevicePresence(@NonNull String) throws android.companion.DeviceNotAssociatedException; @@ -15974,6 +15974,7 @@ package android.graphics { enum_constant public static final android.graphics.ColorSpace.Named LINEAR_EXTENDED_SRGB; enum_constant public static final android.graphics.ColorSpace.Named LINEAR_SRGB; enum_constant public static final android.graphics.ColorSpace.Named NTSC_1953; + enum_constant @FlaggedApi("com.android.graphics.flags.ok_lab_colorspace") public static final android.graphics.ColorSpace.Named OK_LAB; enum_constant public static final android.graphics.ColorSpace.Named PRO_PHOTO_RGB; enum_constant public static final android.graphics.ColorSpace.Named SMPTE_C; enum_constant public static final android.graphics.ColorSpace.Named SRGB; @@ -19910,7 +19911,6 @@ package android.hardware.camera2 { } @FlaggedApi("com.android.internal.camera.flags.concert_mode_api") public final class ExtensionCaptureRequest { - ctor public ExtensionCaptureRequest(); field @FlaggedApi("com.android.internal.camera.flags.concert_mode_api") @NonNull public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Boolean> EFV_AUTO_ZOOM; field @FlaggedApi("com.android.internal.camera.flags.concert_mode_api") @NonNull public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Float> EFV_MAX_PADDING_ZOOM_FACTOR; field @FlaggedApi("com.android.internal.camera.flags.concert_mode_api") @NonNull public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Float> EFV_PADDING_ZOOM_FACTOR; @@ -19923,7 +19923,6 @@ package android.hardware.camera2 { } @FlaggedApi("com.android.internal.camera.flags.concert_mode_api") public final class ExtensionCaptureResult { - ctor public ExtensionCaptureResult(); field @FlaggedApi("com.android.internal.camera.flags.concert_mode_api") @NonNull public static final android.hardware.camera2.CaptureResult.Key<java.lang.Boolean> EFV_AUTO_ZOOM; field @FlaggedApi("com.android.internal.camera.flags.concert_mode_api") @NonNull public static final android.hardware.camera2.CaptureResult.Key<int[]> EFV_AUTO_ZOOM_PADDING_REGION; field @FlaggedApi("com.android.internal.camera.flags.concert_mode_api") @NonNull public static final android.hardware.camera2.CaptureResult.Key<java.lang.Float> EFV_MAX_PADDING_ZOOM_FACTOR; @@ -34055,6 +34054,7 @@ package android.os { field public static final String DISALLOW_BLUETOOTH_SHARING = "no_bluetooth_sharing"; field public static final String DISALLOW_CAMERA_TOGGLE = "disallow_camera_toggle"; field public static final String DISALLOW_CELLULAR_2G = "no_cellular_2g"; + field @FlaggedApi("android.nfc.enable_nfc_user_restriction") public static final String DISALLOW_CHANGE_NEAR_FIELD_COMMUNICATION_RADIO = "no_change_near_field_communication_radio"; field public static final String DISALLOW_CHANGE_WIFI_STATE = "no_change_wifi_state"; field public static final String DISALLOW_CONFIG_BLUETOOTH = "no_config_bluetooth"; field public static final String DISALLOW_CONFIG_BRIGHTNESS = "no_config_brightness"; @@ -47133,6 +47133,8 @@ package android.telephony.ims { method public int describeContents(); method public int getAttributeFlags(); method @NonNull public java.util.Set<java.lang.String> getFeatureTags(); + method @FlaggedApi("com.android.internal.telephony.flags.emergency_registration_state") public boolean getFlagRegistrationTypeEmergency(); + method @FlaggedApi("com.android.internal.telephony.flags.emergency_registration_state") public boolean getFlagVirtualRegistrationForEmergencyCall(); method @Nullable public android.telephony.ims.SipDetails getSipDetails(); method public int getTransportType(); method public void writeToParcel(@NonNull android.os.Parcel, int); diff --git a/core/api/lint-baseline.txt b/core/api/lint-baseline.txt index 1b0da055038d..e6a7ca5bf54c 100644 --- a/core/api/lint-baseline.txt +++ b/core/api/lint-baseline.txt @@ -245,14 +245,6 @@ BroadcastBehavior: android.telephony.euicc.EuiccManager#ACTION_NOTIFY_CARRIER_SE Field 'ACTION_NOTIFY_CARRIER_SETUP_INCOMPLETE' is missing @BroadcastBehavior -CompileTimeConstant: android.hardware.camera2.ExtensionCaptureRequest#EFV_STABILIZATION_MODE_GIMBAL: - All constants must be defined at compile time: android.hardware.camera2.ExtensionCaptureRequest#EFV_STABILIZATION_MODE_GIMBAL -CompileTimeConstant: android.hardware.camera2.ExtensionCaptureRequest#EFV_STABILIZATION_MODE_LOCKED: - All constants must be defined at compile time: android.hardware.camera2.ExtensionCaptureRequest#EFV_STABILIZATION_MODE_LOCKED -CompileTimeConstant: android.hardware.camera2.ExtensionCaptureRequest#EFV_STABILIZATION_MODE_OFF: - All constants must be defined at compile time: android.hardware.camera2.ExtensionCaptureRequest#EFV_STABILIZATION_MODE_OFF - - DeprecationMismatch: android.accounts.AccountManager#newChooseAccountIntent(android.accounts.Account, java.util.ArrayList<android.accounts.Account>, String[], boolean, String, String, String[], android.os.Bundle): Method android.accounts.AccountManager.newChooseAccountIntent(android.accounts.Account, java.util.ArrayList<android.accounts.Account>, String[], boolean, String, String, String[], android.os.Bundle): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match DeprecationMismatch: android.app.Activity#enterPictureInPictureMode(): @@ -1095,14 +1087,6 @@ RequiresPermission: android.webkit.WebSettings#setGeolocationEnabled(boolean): Method 'setGeolocationEnabled' documentation mentions permissions without declaring @RequiresPermission -StaticUtils: ExtensionCaptureRequest: - Fully-static utility classes must not have constructor -StaticUtils: android.hardware.camera2.ExtensionCaptureRequest: - Fully-static utility classes must not have constructor -StaticUtils: android.hardware.camera2.ExtensionCaptureResult: - Fully-static utility classes must not have constructor - - Todo: android.hardware.camera2.params.StreamConfigurationMap: Documentation mentions 'TODO' Todo: android.provider.ContactsContract.RawContacts#newEntityIterator(android.database.Cursor): @@ -1462,14 +1446,6 @@ UnflaggedApi: android.graphics.text.PositionedGlyphs#getItalicOverride(int): UnflaggedApi: android.graphics.text.PositionedGlyphs#getWeightOverride(int): New API must be flagged with @FlaggedApi: method android.graphics.text.PositionedGlyphs.getWeightOverride(int) -UnflaggedApi: android.hardware.camera2.ExtensionCaptureRequest: - New API must be flagged with @FlaggedApi: class android.hardware.camera2.ExtensionCaptureRequest -UnflaggedApi: android.hardware.camera2.ExtensionCaptureRequest#ExtensionCaptureRequest(): - New API must be flagged with @FlaggedApi: constructor android.hardware.camera2.ExtensionCaptureRequest() -UnflaggedApi: android.hardware.camera2.ExtensionCaptureResult: - New API must be flagged with @FlaggedApi: class android.hardware.camera2.ExtensionCaptureResult -UnflaggedApi: android.hardware.camera2.ExtensionCaptureResult#ExtensionCaptureResult(): - New API must be flagged with @FlaggedApi: constructor android.hardware.camera2.ExtensionCaptureResult() UnflaggedApi: android.media.MediaRoute2Info#TYPE_REMOTE_CAR: New API must be flagged with @FlaggedApi: field android.media.MediaRoute2Info.TYPE_REMOTE_CAR UnflaggedApi: android.media.MediaRoute2Info#TYPE_REMOTE_COMPUTER: diff --git a/core/api/test-current.txt b/core/api/test-current.txt index 14ae3f543436..d03dd1623074 100644 --- a/core/api/test-current.txt +++ b/core/api/test-current.txt @@ -46,6 +46,7 @@ package android { field public static final String REMAP_MODIFIER_KEYS = "android.permission.REMAP_MODIFIER_KEYS"; field public static final String REMOVE_TASKS = "android.permission.REMOVE_TASKS"; field public static final String REQUEST_UNIQUE_ID_ATTESTATION = "android.permission.REQUEST_UNIQUE_ID_ATTESTATION"; + field public static final String RESERVED_FOR_TESTING_SIGNATURE = "android.permission.RESERVED_FOR_TESTING_SIGNATURE"; field public static final String RESET_APP_ERRORS = "android.permission.RESET_APP_ERRORS"; field public static final String REVOKE_POST_NOTIFICATIONS_WITHOUT_KILL = "android.permission.REVOKE_POST_NOTIFICATIONS_WITHOUT_KILL"; field public static final String SET_AND_VERIFY_LOCKSCREEN_CREDENTIALS = "android.permission.SET_AND_VERIFY_LOCKSCREEN_CREDENTIALS"; diff --git a/core/api/test-lint-baseline.txt b/core/api/test-lint-baseline.txt index 6cc71e5450ae..b4a3abc4ad22 100644 --- a/core/api/test-lint-baseline.txt +++ b/core/api/test-lint-baseline.txt @@ -1973,6 +1973,8 @@ Todo: android.window.WindowContainerTransaction#setActivityWindowingMode(android UnflaggedApi: android.Manifest.permission#MANAGE_REMOTE_AUTH: New API must be flagged with @FlaggedApi: field android.Manifest.permission.MANAGE_REMOTE_AUTH +UnflaggedApi: android.Manifest.permission#RESERVED_FOR_TESTING_SIGNATURE: + New API must be flagged with @FlaggedApi: field android.Manifest.permission.RESERVED_FOR_TESTING_SIGNATURE UnflaggedApi: android.Manifest.permission#START_ACTIVITIES_FROM_SDK_SANDBOX: New API must be flagged with @FlaggedApi: field android.Manifest.permission.START_ACTIVITIES_FROM_SDK_SANDBOX UnflaggedApi: android.Manifest.permission#USE_REMOTE_AUTH: diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java index 65628d32e583..fd9600c1f87f 100644 --- a/core/java/android/accessibilityservice/AccessibilityService.java +++ b/core/java/android/accessibilityservice/AccessibilityService.java @@ -16,7 +16,6 @@ package android.accessibilityservice; -import static android.accessibilityservice.AccessibilityServiceInfo.CAPABILITY_CAN_CONTROL_MAGNIFICATION; import static android.accessibilityservice.MagnificationConfig.MAGNIFICATION_MODE_FULLSCREEN; import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY; @@ -70,8 +69,6 @@ import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction; import android.view.accessibility.AccessibilityWindowInfo; import android.view.inputmethod.EditorInfo; -import androidx.annotation.GuardedBy; - import com.android.internal.annotations.VisibleForTesting; import com.android.internal.inputmethod.CancellationGroup; import com.android.internal.inputmethod.IAccessibilityInputMethodSession; @@ -643,8 +640,6 @@ public abstract class AccessibilityService extends Service { /** The detected gesture information for different displays */ boolean onGesture(AccessibilityGestureEvent gestureInfo); boolean onKeyEvent(KeyEvent event); - /** Magnification SystemUI connection changed callbacks */ - void onMagnificationSystemUIConnectionChanged(boolean connected); /** Magnification changed callbacks for different displays */ void onMagnificationChanged(int displayId, @NonNull Region region, MagnificationConfig config); @@ -795,6 +790,7 @@ public abstract class AccessibilityService extends Service { public static final String KEY_ACCESSIBILITY_SCREENSHOT_TIMESTAMP = "screenshot_timestamp"; + /** * Annotations for result codes of attaching accessibility overlays. * @@ -841,13 +837,6 @@ public abstract class AccessibilityService extends Service { private WindowManager mWindowManager; - @GuardedBy("mLock") - private boolean mServiceConnected; - @GuardedBy("mLock") - private boolean mMagnificationSystemUIConnected; - @GuardedBy("mLock") - private boolean mServiceConnectedNotified; - /** List of magnification controllers, mapping from displayId -> MagnificationController. */ private final SparseArray<MagnificationController> mMagnificationControllers = new SparseArray<>(0); @@ -897,14 +886,11 @@ public abstract class AccessibilityService extends Service { for (int i = 0; i < mMagnificationControllers.size(); i++) { mMagnificationControllers.valueAt(i).onServiceConnectedLocked(); } - checkIsMagnificationSystemUIConnectedAlready(); final AccessibilityServiceInfo info = getServiceInfo(); if (info != null) { updateInputMethod(info); mMotionEventSources = info.getMotionEventSources(); } - mServiceConnected = true; - mServiceConnectedNotified = false; } if (mSoftKeyboardController != null) { mSoftKeyboardController.onServiceConnected(); @@ -912,57 +898,7 @@ public abstract class AccessibilityService extends Service { // The client gets to handle service connection last, after we've set // up any state upon which their code may rely. - if (android.view.accessibility.Flags - .waitMagnificationSystemUiConnectionToNotifyServiceConnected()) { - notifyOnServiceConnectedIfReady(); - } else { - onServiceConnected(); - } - } - - private void notifyOnServiceConnectedIfReady() { - synchronized (mLock) { - if (mServiceConnectedNotified) { - return; - } - boolean canControlMagnification; - final AccessibilityServiceInfo info = getServiceInfo(); - if (info != null) { - int flagMask = CAPABILITY_CAN_CONTROL_MAGNIFICATION; - canControlMagnification = (info.getCapabilities() & flagMask) == flagMask; - } else { - canControlMagnification = false; - } - boolean ready = canControlMagnification - ? (mServiceConnected && mMagnificationSystemUIConnected) - : mServiceConnected; - if (ready) { - getMainExecutor().execute(() -> onServiceConnected()); - mServiceConnectedNotified = true; - } - } - } - - @GuardedBy("mLock") - private void checkIsMagnificationSystemUIConnectedAlready() { - if (!android.view.accessibility.Flags - .waitMagnificationSystemUiConnectionToNotifyServiceConnected()) { - return; - } - if (mMagnificationSystemUIConnected) { - return; - } - final IAccessibilityServiceConnection connection = - AccessibilityInteractionClient.getInstance(this).getConnection(mConnectionId); - if (connection != null) { - try { - boolean connected = connection.isMagnificationSystemUIConnected(); - mMagnificationSystemUIConnected = connected; - } catch (RemoteException re) { - Log.w(LOG_TAG, "Failed to check magnification system ui connection", re); - re.rethrowFromSystemServer(); - } - } + onServiceConnected(); } private void updateInputMethod(AccessibilityServiceInfo info) { @@ -1424,22 +1360,6 @@ public abstract class AccessibilityService extends Service { } } - private void onMagnificationSystemUIConnectionChanged(boolean connected) { - if (!android.view.accessibility.Flags - .waitMagnificationSystemUiConnectionToNotifyServiceConnected()) { - return; - } - - synchronized (mLock) { - boolean changed = (mMagnificationSystemUIConnected != connected); - mMagnificationSystemUIConnected = connected; - - if (changed) { - notifyOnServiceConnectedIfReady(); - } - } - } - private void onMagnificationChanged(int displayId, @NonNull Region region, MagnificationConfig config) { MagnificationController controller; @@ -2903,11 +2823,6 @@ public abstract class AccessibilityService extends Service { } @Override - public void onMagnificationSystemUIConnectionChanged(boolean connected) { - AccessibilityService.this.onMagnificationSystemUIConnectionChanged(connected); - } - - @Override public void onMagnificationChanged(int displayId, @NonNull Region region, MagnificationConfig config) { AccessibilityService.this.onMagnificationChanged(displayId, region, config); @@ -3117,16 +3032,6 @@ public abstract class AccessibilityService extends Service { }); } - @Override - public void onMagnificationSystemUIConnectionChanged(boolean connected) { - mExecutor.execute(() -> { - if (mConnectionId != AccessibilityInteractionClient.NO_ID) { - mCallback.onMagnificationSystemUIConnectionChanged(connected); - } - return; - }); - } - /** Magnification changed callbacks for different displays */ public void onMagnificationChanged(int displayId, @NonNull Region region, MagnificationConfig config) { diff --git a/core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl b/core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl index f1479ef79dd9..3bc61e560d8c 100644 --- a/core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl +++ b/core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl @@ -48,8 +48,6 @@ import com.android.internal.inputmethod.IRemoteAccessibilityInputConnection; void onKeyEvent(in KeyEvent event, int sequence); - void onMagnificationSystemUIConnectionChanged(boolean connected); - void onMagnificationChanged(int displayId, in Region region, in MagnificationConfig config); void onMotionEvent(in MotionEvent event); diff --git a/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl b/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl index 149e7194a43b..713d8e5dd12f 100644 --- a/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl +++ b/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl @@ -130,9 +130,6 @@ interface IAccessibilityServiceConnection { void setMagnificationCallbackEnabled(int displayId, boolean enabled); @RequiresNoPermission - boolean isMagnificationSystemUIConnected(); - - @RequiresNoPermission boolean setSoftKeyboardShowMode(int showMode); @RequiresNoPermission diff --git a/core/java/android/animation/OWNERS b/core/java/android/animation/OWNERS index 822a35c348ed..f3b330a02116 100644 --- a/core/java/android/animation/OWNERS +++ b/core/java/android/animation/OWNERS @@ -2,5 +2,4 @@ romainguy@google.com tianliu@google.com -alanv@google.com adamp@google.com diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index 79e2bd437c3e..807fa48117b3 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -7555,6 +7555,12 @@ public class Activity extends ContextThemeWrapper * the activity to be restarted). Otherwise, this will be used the next * time the activity is visible. * + * <aside class="note"><b>Note:</b> Device manufacturers can configure devices to override + * (ignore) calls to this method to improve the layout of orientation-restricted apps. See + * <a href="{@docRoot}guide/practices/device-compatibility-mode"> + * Device compatibility mode</a>. + * </aside> + * * @param requestedOrientation An orientation constant as used in * {@link ActivityInfo#screenOrientation ActivityInfo.screenOrientation}. */ @@ -9291,11 +9297,11 @@ public class Activity extends ContextThemeWrapper if (DEBUG_LIFECYCLE) Slog.v(TAG, "dispatchMultiWindowModeChanged " + this + ": " + isInMultiWindowMode + " " + newConfig); + mIsInMultiWindowMode = isInMultiWindowMode; mFragments.dispatchMultiWindowModeChanged(isInMultiWindowMode, newConfig); if (mWindow != null) { mWindow.onMultiWindowModeChanged(); } - mIsInMultiWindowMode = isInMultiWindowMode; onMultiWindowModeChanged(isInMultiWindowMode, newConfig); } @@ -9304,11 +9310,11 @@ public class Activity extends ContextThemeWrapper if (DEBUG_LIFECYCLE) Slog.v(TAG, "dispatchPictureInPictureModeChanged " + this + ": " + isInPictureInPictureMode + " " + newConfig); + mIsInPictureInPictureMode = isInPictureInPictureMode; mFragments.dispatchPictureInPictureModeChanged(isInPictureInPictureMode, newConfig); if (mWindow != null) { mWindow.onPictureInPictureModeChanged(isInPictureInPictureMode); } - mIsInPictureInPictureMode = isInPictureInPictureMode; onPictureInPictureModeChanged(isInPictureInPictureMode, newConfig); } diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java index 8e99e46be6ac..83c3bf6eda76 100644 --- a/core/java/android/app/ActivityManagerInternal.java +++ b/core/java/android/app/ActivityManagerInternal.java @@ -477,6 +477,11 @@ public abstract class ActivityManagerInternal { */ public static final int OOM_ADJ_REASON_COMPONENT_DISABLED = 22; + /** + * Oom Adj Reason: Follow up update for time sensitive state evaluations. + */ + public static final int OOM_ADJ_REASON_FOLLOW_UP = 23; + @IntDef(prefix = {"OOM_ADJ_REASON_"}, value = { OOM_ADJ_REASON_NONE, OOM_ADJ_REASON_ACTIVITY, @@ -501,6 +506,7 @@ public abstract class ActivityManagerInternal { OOM_ADJ_REASON_EXECUTING_SERVICE, OOM_ADJ_REASON_RESTRICTION_CHANGE, OOM_ADJ_REASON_COMPONENT_DISABLED, + OOM_ADJ_REASON_FOLLOW_UP, }) @Retention(RetentionPolicy.SOURCE) public @interface OomAdjReason {} @@ -1101,19 +1107,9 @@ public abstract class ActivityManagerInternal { public abstract ArraySet<String> getClientPackages(String servicePackageName); /** - * Retrieve an IUnsafeIntentStrictModeCallback matching the given callingUid. - * Returns null no match is found. - * @param callingPid The PID mapped with the callback. - * @return The callback, if it exists. - */ - public abstract IUnsafeIntentStrictModeCallback getRegisteredStrictModeCallback( - int callingPid); - - /** - * Unregisters an IUnsafeIntentStrictModeCallback matching the given callingUid. - * @param callingPid The PID mapped with the callback. + * Trigger an unsafe intent usage strict mode violation. */ - public abstract void unregisterStrictModeCallback(int callingPid); + public abstract void triggerUnsafeIntentStrictMode(int callingPid, int type, Intent intent); /** * Start a foreground service delegate. diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index 76c1ed619510..b384326201fc 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -7500,7 +7500,7 @@ public final class ActivityThread extends ClientTransactionHandler + data.instrumentationName + ": " + e.toString(), e); } try { - timestampApplicationOnCreateNs = SystemClock.elapsedRealtimeNanos(); + timestampApplicationOnCreateNs = SystemClock.uptimeNanos(); mInstrumentation.callApplicationOnCreate(app); } catch (Exception e) { timestampApplicationOnCreateNs = 0; diff --git a/core/java/android/app/AppOpsManagerInternal.java b/core/java/android/app/AppOpsManagerInternal.java index f8a8f5dedc4c..b21defbcc0e3 100644 --- a/core/java/android/app/AppOpsManagerInternal.java +++ b/core/java/android/app/AppOpsManagerInternal.java @@ -172,11 +172,9 @@ public abstract class AppOpsManagerInternal { * @param virtualDeviceId the device for which to finish the op * @param superImpl */ - default void finishOperation(IBinder clientId, int code, int uid, String packageName, + void finishOperation(IBinder clientId, int code, int uid, String packageName, String attributionTag, int virtualDeviceId, @NonNull HexConsumer<IBinder, Integer, - Integer, String, String, Integer> superImpl) { - superImpl.accept(clientId, code, uid, packageName, attributionTag, virtualDeviceId); - } + Integer, String, String, Integer> superImpl); /** * Allows overriding finish proxy op. diff --git a/core/java/android/app/ApplicationStartInfo.java b/core/java/android/app/ApplicationStartInfo.java index 3715c6e633dc..f77c50a271be 100644 --- a/core/java/android/app/ApplicationStartInfo.java +++ b/core/java/android/app/ApplicationStartInfo.java @@ -416,11 +416,34 @@ public final class ApplicationStartInfo implements Parcelable { /** * @see #getStartIntent + * + * <p class="note"> Note: This method will clone the provided intent and ensure that the cloned + * intent doesn't contain any large objects like bitmaps in its extras by stripping it in the + * least aggressive acceptable way for the individual intent.</p> + * * @hide */ public void setIntent(Intent startIntent) { if (startIntent != null) { - mStartIntent = startIntent.maybeStripForHistory(); + if (startIntent.canStripForHistory()) { + // If maybeStripForHistory will return a lightened version, do that. + mStartIntent = startIntent.maybeStripForHistory(); + } else if (startIntent.getExtras() != null) { + // If maybeStripForHistory would not return a lightened version and extras is + // non-null then extras contains un-parcelled data. Use cloneFilter to strip data + // more aggressively. + mStartIntent = startIntent.cloneFilter(); + } else { + // Finally, if maybeStripForHistory would not return a lightened version and extras + // is null then do a regular clone so we don't leak the intent. + mStartIntent = new Intent(startIntent); + } + + // If the newly cloned intent has an original intent, clear that as we don't need it and + // can't guarantee it doesn't need to be stripped as well. + if (mStartIntent.getOriginalIntent() != null) { + mStartIntent.setOriginalIntent(null); + } } } diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl index 15b13dc97554..ffb920b907ab 100644 --- a/core/java/android/app/IActivityManager.aidl +++ b/core/java/android/app/IActivityManager.aidl @@ -757,6 +757,15 @@ interface IActivityManager { void addStartInfoTimestamp(int key, long timestampNs, int userId); /** + * Reports view related timestamps to be added to the calling apps most + * recent {@link ApplicationStartInfo}. + * + * @param renderThreadDrawStartTimeNs Clock monotonic time in nanoseconds of RenderThread draw start + * @param framePresentedTimeNs Clock monotonic time in nanoseconds of frame presented + */ + oneway void reportStartInfoViewTimestamps(long renderThreadDrawStartTimeNs, long framePresentedTimeNs); + + /** * Return a list of {@link ApplicationExitInfo} records. * * <p class="note"> Note: System stores these historical information in a ring buffer, older diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl index cf0641651d66..5bc0ddc48c8b 100644 --- a/core/java/android/app/INotificationManager.aidl +++ b/core/java/android/app/INotificationManager.aidl @@ -42,6 +42,7 @@ import android.service.notification.StatusBarNotification; import android.service.notification.ZenPolicy; import android.app.AutomaticZenRule; import android.service.notification.ZenModeConfig; +import android.service.notification.ZenDeviceEffects; /** {@hide} */ interface INotificationManager @@ -227,6 +228,7 @@ interface INotificationManager int getRuleInstanceCount(in ComponentName owner); int getAutomaticZenRuleState(String id); void setAutomaticZenRuleState(String id, in Condition condition); + void setManualZenRuleDeviceEffects(in ZenDeviceEffects effects); byte[] getBackupPayload(int user); void applyRestore(in byte[] payload, int user); diff --git a/core/java/android/app/IUnsafeIntentStrictModeCallback.aidl b/core/java/android/app/IUnsafeIntentStrictModeCallback.aidl index e2b3bb194e02..69e99a3adde7 100644 --- a/core/java/android/app/IUnsafeIntentStrictModeCallback.aidl +++ b/core/java/android/app/IUnsafeIntentStrictModeCallback.aidl @@ -24,5 +24,5 @@ import android.content.Intent; */ oneway interface IUnsafeIntentStrictModeCallback { - void onImplicitIntentMatchedInternalComponent(in Intent intent); + void onUnsafeIntent(int type, in Intent intent); } diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index fc3bb0288d67..a1fa4047f839 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -2239,9 +2239,6 @@ public class Notification implements Parcelable private void visitUris(@NonNull Consumer<Uri> visitor) { visitIconUri(visitor, getIcon()); - if (actionIntent != null) { - actionIntent.visitUris(visitor); - } } @Override @@ -2957,21 +2954,6 @@ public class Notification implements Parcelable } } - // allPendingIntents should contain all associated intents after parcelling, but it may also - // contain intents added by the app to extras for their own purposes. We only care about - // checking the intents known and used by system_server, to avoid the confused deputy issue. - List<PendingIntent> pendingIntents = Arrays.asList(contentIntent, deleteIntent, - fullScreenIntent); - for (PendingIntent intent : pendingIntents) { - if (intent != null) { - intent.visitUris(visitor); - } - } - - if (mBubbleMetadata != null) { - mBubbleMetadata.visitUris(visitor); - } - if (extras != null) { visitIconUri(visitor, extras.getParcelable(EXTRA_LARGE_ICON_BIG, Icon.class)); visitIconUri(visitor, extras.getParcelable(EXTRA_PICTURE_ICON, Icon.class)); @@ -3043,28 +3025,15 @@ public class Notification implements Parcelable callPerson.visitUris(visitor); } visitIconUri(visitor, extras.getParcelable(EXTRA_VERIFICATION_ICON, Icon.class)); + } - // Extras for MediaStyle. - PendingIntent deviceIntent = extras.getParcelable(EXTRA_MEDIA_REMOTE_INTENT, - PendingIntent.class); - if (deviceIntent != null) { - deviceIntent.visitUris(visitor); - } - - if (extras.containsKey(WearableExtender.EXTRA_WEARABLE_EXTENSIONS)) { - WearableExtender extender = new WearableExtender(this); - extender.visitUris(visitor); - } - - if (extras.containsKey(TvExtender.EXTRA_TV_EXTENDER)) { - TvExtender extender = new TvExtender(this); - extender.visitUris(visitor); - } + if (mBubbleMetadata != null) { + visitIconUri(visitor, mBubbleMetadata.getIcon()); + } - if (extras.containsKey(CarExtender.EXTRA_CAR_EXTENDER)) { - CarExtender extender = new CarExtender(this); - extender.visitUris(visitor); - } + if (extras != null && extras.containsKey(WearableExtender.EXTRA_WEARABLE_EXTENSIONS)) { + WearableExtender extender = new WearableExtender(this); + extender.visitUris(visitor); } } @@ -11459,16 +11428,6 @@ public class Notification implements Parcelable } } - private void visitUris(@NonNull Consumer<Uri> visitor) { - visitIconUri(visitor, getIcon()); - if (mPendingIntent != null) { - mPendingIntent.visitUris(visitor); - } - if (mDeleteIntent != null) { - mDeleteIntent.visitUris(visitor); - } - } - /** * Builder to construct a {@link BubbleMetadata} object. */ @@ -12667,9 +12626,6 @@ public class Notification implements Parcelable } private void visitUris(@NonNull Consumer<Uri> visitor) { - if (mDisplayIntent != null) { - mDisplayIntent.visitUris(visitor); - } for (Action action : mActions) { action.visitUris(visitor); } @@ -12829,12 +12785,6 @@ public class Notification implements Parcelable return mUnreadConversation; } - private void visitUris(@NonNull Consumer<Uri> visitor) { - if (mUnreadConversation != null) { - mUnreadConversation.visitUris(visitor); - } - } - /** * A class which holds the unread messages from a conversation. */ @@ -12986,16 +12936,7 @@ public class Notification implements Parcelable onRead, participants, b.getLong(KEY_TIMESTAMP)); } - - private void visitUris(@NonNull Consumer<Uri> visitor) { - if (mReadPendingIntent != null) { - mReadPendingIntent.visitUris(visitor); - } - if (mReplyPendingIntent != null) { - mReplyPendingIntent.visitUris(visitor); - } - } - } + }; /** * Builder class for {@link CarExtender.UnreadConversation} objects. @@ -13318,15 +13259,6 @@ public class Notification implements Parcelable public boolean isSuppressShowOverApps() { return mSuppressShowOverApps; } - - private void visitUris(@NonNull Consumer<Uri> visitor) { - if (mContentIntent != null) { - mContentIntent.visitUris(visitor); - } - if (mDeleteIntent != null) { - mDeleteIntent.visitUris(visitor); - } - } } /** diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java index e4310c1a5361..bd80dc1e3c60 100644 --- a/core/java/android/app/NotificationManager.java +++ b/core/java/android/app/NotificationManager.java @@ -57,6 +57,7 @@ import android.provider.Settings.Global; import android.service.notification.Adjustment; import android.service.notification.Condition; import android.service.notification.StatusBarNotification; +import android.service.notification.ZenDeviceEffects; import android.service.notification.ZenModeConfig; import android.service.notification.ZenPolicy; import android.util.Log; @@ -1828,6 +1829,18 @@ public class NotificationManager { throw e.rethrowFromSystemServer(); } } + /** + * @hide + */ + @FlaggedApi(Flags.FLAG_MODES_UI) + public void setManualZenRuleDeviceEffects(@NonNull ZenDeviceEffects effects) { + INotificationManager service = getService(); + try { + service.setManualZenRuleDeviceEffects(effects); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } /** * For apps targeting {@link Build.VERSION_CODES#VANILLA_ICE_CREAM} and above, the diff --git a/core/java/android/app/OWNERS b/core/java/android/app/OWNERS index 2e38c06a7479..0fad979e27cf 100644 --- a/core/java/android/app/OWNERS +++ b/core/java/android/app/OWNERS @@ -61,7 +61,7 @@ per-file ReceiverInfo* = file:/BROADCASTS_OWNERS per-file ComponentCaller.java = file:COMPONENT_CALLER_OWNERS # DreamManager -per-file DreamManager.java = file:/DREAM_MANAGER_OWNERS +per-file DreamManager.java = file:/core/java/android/service/dreams/OWNERS # GrammaticalInflectionManager per-file *GrammaticalInflection* = file:/services/core/java/com/android/server/grammaticalinflection/OWNERS diff --git a/core/java/android/app/PendingIntent.java b/core/java/android/app/PendingIntent.java index 1ac08ac4cd24..3714e5de23ca 100644 --- a/core/java/android/app/PendingIntent.java +++ b/core/java/android/app/PendingIntent.java @@ -44,8 +44,6 @@ import android.content.IntentSender; import android.content.pm.PackageManager.ResolveInfoFlagsBits; import android.content.pm.ParceledListSlice; import android.content.pm.ResolveInfo; -import android.net.Uri; -import android.os.Binder; import android.os.Build; import android.os.Bundle; import android.os.Handler; @@ -71,7 +69,6 @@ import java.util.Collections; import java.util.List; import java.util.Objects; import java.util.concurrent.Executor; -import java.util.function.Consumer; /** * A description of an Intent and target action to perform with it. Instances @@ -1471,21 +1468,6 @@ public final class PendingIntent implements Parcelable { return sb.toString(); } - /** - * See {@link Intent#visitUris(Consumer)}. - * - * @hide - */ - public void visitUris(@NonNull Consumer<Uri> visitor) { - if (android.app.Flags.visitRiskyUris()) { - Intent intent = Binder.withCleanCallingIdentity(this::getIntent); - - if (intent != null) { - intent.visitUris(visitor); - } - } - } - /** @hide */ public void dumpDebug(ProtoOutputStream proto, long fieldId) { final long token = proto.start(fieldId); diff --git a/core/java/android/app/Person.java b/core/java/android/app/Person.java index 04186600125f..96f6f4eac372 100644 --- a/core/java/android/app/Person.java +++ b/core/java/android/app/Person.java @@ -189,7 +189,7 @@ public final class Person implements Parcelable { */ public void visitUris(@NonNull Consumer<Uri> visitor) { visitor.accept(getIconUri()); - if (Flags.visitRiskyUris()) { + if (Flags.visitPersonUri()) { if (mUri != null && !mUri.isEmpty()) { visitor.accept(Uri.parse(mUri)); } diff --git a/core/java/android/app/UiAutomation.java b/core/java/android/app/UiAutomation.java index 273a79efb591..348d4d8fd809 100644 --- a/core/java/android/app/UiAutomation.java +++ b/core/java/android/app/UiAutomation.java @@ -1969,11 +1969,6 @@ public final class UiAutomation { } @Override - public void onMagnificationSystemUIConnectionChanged(boolean connected) { - /* do nothing */ - } - - @Override public void onMagnificationChanged(int displayId, @NonNull Region region, MagnificationConfig config) { /* do nothing */ diff --git a/core/java/android/app/assist/AssistContent.java b/core/java/android/app/assist/AssistContent.java index e5316bc05749..a48868906487 100644 --- a/core/java/android/app/assist/AssistContent.java +++ b/core/java/android/app/assist/AssistContent.java @@ -34,12 +34,22 @@ public class AssistContent implements Parcelable { } /** + * Create an AssistContent with extras initialized. + * * @hide + */ + public AssistContent(@android.annotation.NonNull Bundle extras) { + mExtras = extras; + } + + /** * Called by {@link android.app.ActivityThread} to set the default Intent based on * {@link android.app.Activity#getIntent Activity.getIntent}. * * <p>Automatically populates {@link #mUri} if that Intent is an {@link Intent#ACTION_VIEW} * of a web (http or https scheme) URI.</p> + * + * @hide */ public void setDefaultIntent(Intent intent) { mIntent = intent; diff --git a/core/java/android/app/contextualsearch/flags.aconfig b/core/java/android/app/contextualsearch/flags.aconfig index 3385b2b42074..3b0c86795738 100644 --- a/core/java/android/app/contextualsearch/flags.aconfig +++ b/core/java/android/app/contextualsearch/flags.aconfig @@ -7,3 +7,9 @@ flag { description: "Flag to enable the service" bug: "309689654" } +flag { + name: "enable_token_refresh" + namespace: "machine_learning" + description: "Flag to refresh the token to the callback" + bug: "309689654" +}
\ No newline at end of file diff --git a/core/java/android/app/notification.aconfig b/core/java/android/app/notification.aconfig index 2d783177e0d7..f751a234ccac 100644 --- a/core/java/android/app/notification.aconfig +++ b/core/java/android/app/notification.aconfig @@ -46,10 +46,13 @@ flag { } flag { - name: "visit_risky_uris" + name: "visit_person_uri" namespace: "systemui" - description: "Guards the security fix that ensures all URIs in intents and Person.java are valid" + 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 @@ -199,3 +202,11 @@ flag { description: "redacts notifications on the lockscreen if they have the 'sensitiveContent' flag" bug: "343631648" } + +flag { + name: "api_rich_ongoing" + is_exported: true + namespace: "systemui" + description: "Guards new android.app.richongoingnotification api" + bug: "337261753" +}
\ No newline at end of file diff --git a/core/java/android/app/pinner/OWNERS b/core/java/android/app/pinner/OWNERS index 3e3fa66ca916..fe5da9f7f0e3 100644 --- a/core/java/android/app/pinner/OWNERS +++ b/core/java/android/app/pinner/OWNERS @@ -5,6 +5,5 @@ kevinjeon@google.com philipcuadra@google.com shombert@google.com timmurray@google.com -wessam@google.com jdduke@google.com -shayba@google.com
\ No newline at end of file +shayba@google.com diff --git a/core/java/android/app/servertransaction/ClientTransaction.java b/core/java/android/app/servertransaction/ClientTransaction.java index 48081bb04863..9a04dedb7c85 100644 --- a/core/java/android/app/servertransaction/ClientTransaction.java +++ b/core/java/android/app/servertransaction/ClientTransaction.java @@ -249,6 +249,9 @@ public class ClientTransaction implements Parcelable, ObjectPoolItem { @Override public void recycle() { + if (Flags.disableObjectPool()) { + return; + } if (mTransactionItems != null) { int size = mTransactionItems.size(); for (int i = 0; i < size; i++) { diff --git a/core/java/android/app/servertransaction/ObjectPool.java b/core/java/android/app/servertransaction/ObjectPool.java index 2fec30a0dde7..598bd8a75637 100644 --- a/core/java/android/app/servertransaction/ObjectPool.java +++ b/core/java/android/app/servertransaction/ObjectPool.java @@ -16,6 +16,8 @@ package android.app.servertransaction; +import com.android.window.flags.Flags; + import java.util.ArrayList; import java.util.HashMap; import java.util.Map; @@ -38,6 +40,9 @@ class ObjectPool { * @return An instance or null if there is none. */ public static <T extends ObjectPoolItem> T obtain(Class<T> itemClass) { + if (Flags.disableObjectPool()) { + return null; + } synchronized (sPoolSync) { @SuppressWarnings("unchecked") final ArrayList<T> itemPool = (ArrayList<T>) sPoolMap.get(itemClass); @@ -54,6 +59,9 @@ class ObjectPool { * @see ObjectPoolItem#recycle() */ public static <T extends ObjectPoolItem> void recycle(T item) { + if (Flags.disableObjectPool()) { + return; + } synchronized (sPoolSync) { @SuppressWarnings("unchecked") ArrayList<T> itemPool = (ArrayList<T>) sPoolMap.get(item.getClass()); diff --git a/core/java/android/app/wearable/WearableSensingManager.java b/core/java/android/app/wearable/WearableSensingManager.java index 4b77c74bb174..d28969d0b3c1 100644 --- a/core/java/android/app/wearable/WearableSensingManager.java +++ b/core/java/android/app/wearable/WearableSensingManager.java @@ -436,20 +436,20 @@ public class WearableSensingManager { /** * Requests the wearable to start hotword recognition. * - * <p>When this method is called, the system will attempt to provide a {@link - * android.service.wearable.WearableHotwordAudioConsumer} to {@link WearableSensingService}. - * After first-stage hotword is detected on a wearable, {@link WearableSensingService} should - * send the hotword audio to the {@link android.service.wearable.WearableHotwordAudioConsumer}, - * which will forward the data to the {@link android.service.voice.HotwordDetectionService} for + * <p>When this method is called, the system will attempt to provide a {@code + * Consumer<android.service.voice.HotwordAudioStream>} to {@link WearableSensingService}. After + * first-stage hotword is detected on a wearable, {@link WearableSensingService} should send the + * hotword audio to the {@code Consumer<android.service.voice.HotwordAudioStream>}, which will + * forward the data to the {@link android.service.voice.HotwordDetectionService} for * second-stage hotword validation. If hotword is detected there, the audio data will be * forwarded to the {@link android.service.voice.VoiceInteractionService}. * * <p>If the {@code targetVisComponentName} provided here is not null, when {@link - * WearableSensingService} sends hotword audio to the {@link - * android.service.wearable.WearableHotwordAudioConsumer}, the system will check whether the - * {@link android.service.voice.VoiceInteractionService} at that time is {@code + * WearableSensingService} sends hotword audio to the {@code + * Consumer<android.service.voice.HotwordAudioStream>}, the system will check whether the {@link + * android.service.voice.VoiceInteractionService} at that time is {@code * targetVisComponentName}. If not, the system will call {@link - * WearableSensingService#onActiveHotwordAudioStopRequested()} and will not forward the audio + * WearableSensingService#onStopHotwordAudioStream()} and will not forward the audio * data to the current {@link android.service.voice.HotwordDetectionService} nor {@link * android.service.voice.VoiceInteractionService}. The system will not send a status code to * {@code statusConsumer} regarding the {@code targetVisComponentName} check. The caller is @@ -457,16 +457,16 @@ public class WearableSensingManager { * android.service.voice.VoiceInteractionService} is the same as {@code targetVisComponentName}. * The check here is just a protection against race conditions. * - * <p>Calling this method again will send a new {@link - * android.service.wearable.WearableHotwordAudioConsumer} to {@link WearableSensingService}. For + * <p>Calling this method again will send a new {@code + * Consumer<android.service.voice.HotwordAudioStream>} to {@link WearableSensingService}. For * audio data sent to the new consumer, the system will perform the above check using the newly * provided {@code targetVisComponentName}. The {@link WearableSensingService} should not * continue to use the previous consumers after receiving a new one. * * <p>If the {@code statusConsumer} returns {@link STATUS_SUCCESS}, the caller should call - * {@link #stopListeningForHotword(Executor, Consumer)} when it wants the wearable to stop + * {@link #stopHotwordRecognition(Executor, Consumer)} when it wants the wearable to stop * listening for hotword. If the {@code statusConsumer} returns any other status code, a failure - * has occurred and calling {@link #stopListeningForHotword(Executor, Consumer)} is not + * has occurred and calling {@link #stopHotwordRecognition(Executor, Consumer)} is not * required. The system will not retry listening automatically. The caller should call this * method again if they want to retry. * diff --git a/core/java/android/companion/CompanionDeviceManager.java b/core/java/android/companion/CompanionDeviceManager.java index 8fe5ae09a36d..b4ad1c8fff12 100644 --- a/core/java/android/companion/CompanionDeviceManager.java +++ b/core/java/android/companion/CompanionDeviceManager.java @@ -1149,6 +1149,32 @@ public final class CompanionDeviceManager { } } + /** + * Remove bonding between this device and an associated companion device. + * + * <p>This is an asynchronous call, it will return immediately. Register for {@link + * BluetoothDevice#ACTION_BOND_STATE_CHANGED} intents to be notified when the bond removal + * process completes, and its result. + * + * @param associationId an already-associated companion device to remove bond from + * @return false on immediate error, true if bond removal process will begin + */ + @FlaggedApi(Flags.FLAG_UNPAIR_ASSOCIATED_DEVICE) + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) + public boolean removeBond(int associationId) { + if (mService == null) { + Log.w(TAG, "CompanionDeviceManager service is not available."); + return false; + } + + try { + return mService.removeBond(associationId, mContext.getOpPackageName(), + mContext.getUserId()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + // TODO(b/315163162) Add @Deprecated keyword after 24Q2 cut. /** * Register to receive callbacks whenever the associated device comes in and out of range. diff --git a/core/java/android/companion/ICompanionDeviceManager.aidl b/core/java/android/companion/ICompanionDeviceManager.aidl index 1b00f90e1fb3..de3ddec05d27 100644 --- a/core/java/android/companion/ICompanionDeviceManager.aidl +++ b/core/java/android/companion/ICompanionDeviceManager.aidl @@ -141,4 +141,7 @@ interface ICompanionDeviceManager { byte[] getBackupPayload(int userId); void applyRestoredPayload(in byte[] payload, int userId); + + @EnforcePermission("BLUETOOTH_CONNECT") + boolean removeBond(int associationId, in String packageName, int userId); } diff --git a/core/java/android/companion/ObservingDevicePresenceRequest.java b/core/java/android/companion/ObservingDevicePresenceRequest.java index 11ea735dff4f..3150b8735033 100644 --- a/core/java/android/companion/ObservingDevicePresenceRequest.java +++ b/core/java/android/companion/ObservingDevicePresenceRequest.java @@ -180,6 +180,9 @@ public final class ObservingDevicePresenceRequest implements Parcelable { * <p>Calling apps must use either this API or {@link #setAssociationId(int)}, * but not both.</p> * + * <p>Calling app must hold the + * {@link AssociationRequest#DEVICE_PROFILE_AUTOMOTIVE_PROJECTION} profile.</p> + * * @param uuid The ParcelUuid for observing device presence. */ @NonNull diff --git a/core/java/android/companion/flags.aconfig b/core/java/android/companion/flags.aconfig index 36d0e081af2a..fd4ba83b02e3 100644 --- a/core/java/android/companion/flags.aconfig +++ b/core/java/android/companion/flags.aconfig @@ -46,4 +46,11 @@ flag { namespace: "companion" description: "Enable ongoing perm sync" bug: "338469649" +} + +flag { + name: "unpair_associated_device" + namespace: "companion" + description: "Unpair with an associated bluetooth device" + bug: "322237619" }
\ No newline at end of file diff --git a/core/java/android/companion/virtual/IVirtualDevice.aidl b/core/java/android/companion/virtual/IVirtualDevice.aidl index 30a1135d6be4..24f18cc257f8 100644 --- a/core/java/android/companion/virtual/IVirtualDevice.aidl +++ b/core/java/android/companion/virtual/IVirtualDevice.aidl @@ -281,5 +281,5 @@ interface IVirtualDevice { * Returns the id of the virtual camera with given config. */ @EnforcePermission("CREATE_VIRTUAL_DEVICE") - int getVirtualCameraId(in VirtualCameraConfig camera); + String getVirtualCameraId(in VirtualCameraConfig camera); } diff --git a/core/java/android/companion/virtual/VirtualDeviceInternal.java b/core/java/android/companion/virtual/VirtualDeviceInternal.java index 00d534370fa1..60448bad8e69 100644 --- a/core/java/android/companion/virtual/VirtualDeviceInternal.java +++ b/core/java/android/companion/virtual/VirtualDeviceInternal.java @@ -378,8 +378,8 @@ public class VirtualDeviceInternal { VirtualCamera createVirtualCamera(@NonNull VirtualCameraConfig config) { try { mVirtualDevice.registerVirtualCamera(config); - return new VirtualCamera(mVirtualDevice, - Integer.toString(mVirtualDevice.getVirtualCameraId(config)), config); + return new VirtualCamera(mVirtualDevice, mVirtualDevice.getVirtualCameraId(config), + config); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index bad73fc68f3a..4fcf6b66895c 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -7188,9 +7188,10 @@ public abstract class Context { * as the package remains in the foreground, or has any active manifest components (e.g. when * another app is accessing a content provider in the package). * <p> - * If you want to revoke the permissions right away, you could call {@code System.exit()}, but - * this could affect other apps that are accessing your app at the moment. For example, apps - * accessing a content provider in your app will all crash. + * If you want to revoke the permissions right away, you could call {@code System.exit()} in + * {@code Handler.postDelayed} with a delay to allow completion of async IPC, But + * {@code System.exit()} could affect other apps that are accessing your app at the moment. + * For example, apps accessing a content provider in your app will all crash. * <p> * Note that the settings UI shows a permission group as granted as long as at least one * permission in the group is granted. If you want the user to observe the revocation in the diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index 02d62a2a402b..2e60cb03d18b 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -106,7 +106,6 @@ 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 @@ -8330,27 +8329,6 @@ public class Intent implements Parcelable, Cloneable { } } - /** - * Note all {@link Uri} that are referenced internally, with the expectation that Uri permission - * grants will need to be issued to ensure the recipient of this object is able to render its - * contents. - * See b/281044385 for more context and examples about what happens when this isn't done - * correctly. - * - * @hide - */ - public void visitUris(@NonNull Consumer<Uri> visitor) { - if (android.app.Flags.visitRiskyUris()) { - visitor.accept(mData); - if (mSelector != null) { - mSelector.visitUris(visitor); - } - if (mOriginalIntent != null) { - mOriginalIntent.visitUris(visitor); - } - } - } - public static Intent getIntentOld(String uri) throws URISyntaxException { Intent intent = getIntentOld(uri, 0); intent.mLocalFlags |= LOCAL_FLAG_FROM_URI; diff --git a/core/java/android/content/pm/LauncherApps.java b/core/java/android/content/pm/LauncherApps.java index 6168b6800adc..df27f9d4e457 100644 --- a/core/java/android/content/pm/LauncherApps.java +++ b/core/java/android/content/pm/LauncherApps.java @@ -695,15 +695,12 @@ public class LauncherApps { * <p>If the caller is running on a managed profile, it'll return only the current profile. * Otherwise it'll return the same list as {@link UserManager#getUserProfiles()} would. * - * <p>To get hidden profile {@link UserManager.USER_TYPE_PROFILE_PRIVATE}, - * caller should have either:</p> - * <ul> - * <li>the privileged {@link android.Manifest.permission.ACCESS_HIDDEN_PROFILES_FULL} - * permission</li> - * <li>the normal {@link android.Manifest.permission.ACCESS_HIDDEN_PROFILES} permission and the - * {@link android.app.role.RoleManager.ROLE_HOME} role. </li> - * </ul> + * <p>To get hidden profile {@link UserManager#USER_TYPE_PROFILE_PRIVATE}, + * caller should have normal {@link android.Manifest.permission#ACCESS_HIDDEN_PROFILES} + * permission and the {@link android.app.role.RoleManager#ROLE_HOME} role. */ + // Alternatively, a system app can access this api for private profile if they've been granted + // with the {@code android.Manifest.permission#ACCESS_HIDDEN_PROFILES_FULL} permission. @SuppressLint("RequiresPermission") @RequiresPermission(conditional = true, anyOf = {ACCESS_HIDDEN_PROFILES_FULL, ACCESS_HIDDEN_PROFILES}) @@ -764,20 +761,17 @@ public class LauncherApps { * list.</li> * </ul> * - * <p>If the user in question is a hidden profile {@link UserManager.USER_TYPE_PROFILE_PRIVATE}, - * caller should have either:</p> - * <ul> - * <li>the privileged {@link android.Manifest.permission.ACCESS_HIDDEN_PROFILES_FULL} - * permission</li> - * <li>the normal {@link android.Manifest.permission.ACCESS_HIDDEN_PROFILES} permission and the - * {@link android.app.role.RoleManager.ROLE_HOME} role. </li> - * </ul> + * <p>If the user in question is a hidden profile {@link UserManager#USER_TYPE_PROFILE_PRIVATE}, + * caller should have normal {@link android.Manifest.permission#ACCESS_HIDDEN_PROFILES} + * permission and the {@link android.app.role.RoleManager#ROLE_HOME} role. * * @param packageName The specific package to query. If null, it checks all installed packages * in the profile. * @param user The UserHandle of the profile. * @return List of launchable activities. Can be an empty list but will not be null. */ + // Alternatively, a system app can access this api for private profile if they've been granted + // with the {@code android.Manifest.permission#ACCESS_HIDDEN_PROFILES_FULL} permission. @SuppressLint("RequiresPermission") @RequiresPermission(conditional = true, anyOf = {ACCESS_HIDDEN_PROFILES_FULL, ACCESS_HIDDEN_PROFILES}) @@ -820,19 +814,16 @@ public class LauncherApps { * Returns information related to a user which is useful for displaying UI elements * to distinguish it from other users (eg, badges). * - * <p>If the user in question is a hidden profile {@link UserManager.USER_TYPE_PROFILE_PRIVATE}, - * caller should have either:</p> - * <ul> - * <li>the privileged {@link android.Manifest.permission.ACCESS_HIDDEN_PROFILES_FULL} - * permission</li> - * <li>the normal {@link android.Manifest.permission.ACCESS_HIDDEN_PROFILES} permission and the - * {@link android.app.role.RoleManager.ROLE_HOME} role. </li> - * </ul> + * <p>If the user in question is a hidden profile {@link UserManager#USER_TYPE_PROFILE_PRIVATE}, + * caller should have normal {@link android.Manifest.permission#ACCESS_HIDDEN_PROFILES} + * permission and the {@link android.app.role.RoleManager#ROLE_HOME} role. * * @param userHandle user handle of the user for which LauncherUserInfo is requested. * @return the {@link LauncherUserInfo} object related to the user specified, null in case * the user is inaccessible. */ + // Alternatively, a system app can access this api for private profile if they've been granted + // with the {@code android.Manifest.permission#ACCESS_HIDDEN_PROFILES_FULL} permission. @Nullable @SuppressLint("RequiresPermission") @FlaggedApi(Flags.FLAG_ALLOW_PRIVATE_PROFILE) @@ -873,14 +864,9 @@ public class LauncherApps { * </ul> * </p> * - * <p>If the user in question is a hidden profile {@link UserManager.USER_TYPE_PROFILE_PRIVATE}, - * caller should have either:</p> - * <ul> - * <li>the privileged {@link android.Manifest.permission.ACCESS_HIDDEN_PROFILES_FULL} - * permission</li> - * <li>the normal {@link android.Manifest.permission.ACCESS_HIDDEN_PROFILES} permission and the - * {@link android.app.role.RoleManager.ROLE_HOME} role. </li> - * </ul> + * <p>If the user in question is a hidden profile {@link UserManager#USER_TYPE_PROFILE_PRIVATE}, + * caller should have normal {@link android.Manifest.permission#ACCESS_HIDDEN_PROFILES} + * permission and the {@link android.app.role.RoleManager#ROLE_HOME} role. * * @param packageName the package for which intent sender to launch App Market Activity is * required. @@ -888,6 +874,8 @@ public class LauncherApps { * @return {@link IntentSender} object which launches the App Market Activity, null in case * there is no such activity. */ + // Alternatively, a system app can access this api for private profile if they've been granted + // with the {@code android.Manifest.permission#ACCESS_HIDDEN_PROFILES_FULL} permission. @Nullable @SuppressLint("RequiresPermission") @FlaggedApi(Flags.FLAG_ALLOW_PRIVATE_PROFILE) @@ -913,19 +901,16 @@ public class LauncherApps { * <p>An empty list denotes that all system packages should be treated as pre-installed for that * user at creation. * - * <p>If the user in question is a hidden profile {@link UserManager.USER_TYPE_PROFILE_PRIVATE}, - * caller should have either:</p> - * <ul> - * <li>the privileged {@link android.Manifest.permission.ACCESS_HIDDEN_PROFILES_FULL} - * permission</li> - * <li>the normal {@link android.Manifest.permission.ACCESS_HIDDEN_PROFILES} permission and the - * {@link android.app.role.RoleManager.ROLE_HOME} role. </li> - * </ul> + * <p>If the user in question is a hidden profile {@link UserManager#USER_TYPE_PROFILE_PRIVATE}, + * caller should have normal {@link android.Manifest.permission#ACCESS_HIDDEN_PROFILES} + * permission and the {@link android.app.role.RoleManager#ROLE_HOME} role. * * @param userHandle the user for which installed system packages are required. * @return {@link List} of {@link String}, representing the package name of the installed * package. Can be empty but not null. */ + // Alternatively, a system app can access this api for private profile if they've been granted + // with the {@code android.Manifest.permission#ACCESS_HIDDEN_PROFILES_FULL} permission. @FlaggedApi(Flags.FLAG_ALLOW_PRIVATE_PROFILE) @NonNull @SuppressLint("RequiresPermission") @@ -945,13 +930,15 @@ public class LauncherApps { /** * Returns {@link IntentSender} which can be used to start the Private Space Settings Activity. * - * <p> Caller should have {@link android.app.role.RoleManager.ROLE_HOME} and either of the + * <p> Caller should have {@link android.app.role.RoleManager#ROLE_HOME} and either of the * permissions required.</p> * * @return {@link IntentSender} object which launches the Private Space Settings Activity, if * successful, null otherwise. * @hide */ + // Alternatively, a system app can access this api for private profile if they've been granted + // with the {@code android.Manifest.permission#ACCESS_HIDDEN_PROFILES_FULL} permission. @Nullable @FlaggedApi(Flags.FLAG_ALLOW_PRIVATE_PROFILE) @RequiresPermission(conditional = true, @@ -968,19 +955,16 @@ public class LauncherApps { * Returns the activity info for a given intent and user handle, if it resolves. Otherwise it * returns null. * - * <p>If the user in question is a hidden profile {@link UserManager.USER_TYPE_PROFILE_PRIVATE}, - * caller should have either:</p> - * <ul> - * <li>the privileged {@link android.Manifest.permission.ACCESS_HIDDEN_PROFILES_FULL} - * permission</li> - * <li>the normal {@link android.Manifest.permission.ACCESS_HIDDEN_PROFILES} permission and the - * {@link android.app.role.RoleManager.ROLE_HOME} role. </li> - * </ul> + * <p>If the user in question is a hidden profile {@link UserManager#USER_TYPE_PROFILE_PRIVATE}, + * caller should have normal {@link android.Manifest.permission#ACCESS_HIDDEN_PROFILES} + * permission and the {@link android.app.role.RoleManager#ROLE_HOME} role. * * @param intent The intent to find a match for. * @param user The profile to look in for a match. * @return An activity info object if there is a match. */ + // Alternatively, a system app can access this api for private profile if they've been granted + // with the {@code android.Manifest.permission#ACCESS_HIDDEN_PROFILES_FULL} permission. @SuppressLint("RequiresPermission") @RequiresPermission(conditional = true, anyOf = {ACCESS_HIDDEN_PROFILES_FULL, ACCESS_HIDDEN_PROFILES}) @@ -1033,20 +1017,17 @@ public class LauncherApps { /** * Starts a Main activity in the specified profile. * - * <p>If the user in question is a hidden profile {@link UserManager.USER_TYPE_PROFILE_PRIVATE}, - * caller should have either:</p> - * <ul> - * <li>the privileged {@link android.Manifest.permission.ACCESS_HIDDEN_PROFILES_FULL} - * permission</li> - * <li>the normal {@link android.Manifest.permission.ACCESS_HIDDEN_PROFILES} permission and the - * {@link android.app.role.RoleManager.ROLE_HOME} role. </li> - * </ul> + * <p>If the user in question is a hidden profile {@link UserManager#USER_TYPE_PROFILE_PRIVATE}, + * caller should have normal {@link android.Manifest.permission#ACCESS_HIDDEN_PROFILES} + * permission and the {@link android.app.role.RoleManager#ROLE_HOME} role. * * @param component The ComponentName of the activity to launch * @param user The UserHandle of the profile * @param sourceBounds The Rect containing the source bounds of the clicked icon * @param opts Options to pass to startActivity */ + // Alternatively, a system app can access this api for private profile if they've been granted + // with the {@code android.Manifest.permission#ACCESS_HIDDEN_PROFILES_FULL} permission. @SuppressLint("RequiresPermission") @RequiresPermission(conditional = true, anyOf = {ACCESS_HIDDEN_PROFILES_FULL, ACCESS_HIDDEN_PROFILES}) @@ -1087,20 +1068,17 @@ public class LauncherApps { * Starts the settings activity to show the application details for a * package in the specified profile. * - * <p>If the user in question is a hidden profile {@link UserManager.USER_TYPE_PROFILE_PRIVATE}, - * caller should have either:</p> - * <ul> - * <li>the privileged {@link android.Manifest.permission.ACCESS_HIDDEN_PROFILES_FULL} - * permission</li> - * <li>the normal {@link android.Manifest.permission.ACCESS_HIDDEN_PROFILES} permission and the - * {@link android.app.role.RoleManager.ROLE_HOME} role. </li> - * </ul> + * <p>If the user in question is a hidden profile {@link UserManager#USER_TYPE_PROFILE_PRIVATE}, + * caller should have normal {@link android.Manifest.permission#ACCESS_HIDDEN_PROFILES} + * permission and the {@link android.app.role.RoleManager#ROLE_HOME} role. * * @param component The ComponentName of the package to launch settings for. * @param user The UserHandle of the profile * @param sourceBounds The Rect containing the source bounds of the clicked icon * @param opts Options to pass to startActivity */ + // Alternatively, a system app can access this api for private profile if they've been granted + // with the {@code android.Manifest.permission#ACCESS_HIDDEN_PROFILES_FULL} permission. @SuppressLint("RequiresPermission") @RequiresPermission(conditional = true, anyOf = {ACCESS_HIDDEN_PROFILES_FULL, ACCESS_HIDDEN_PROFILES}) @@ -1215,20 +1193,17 @@ public class LauncherApps { /** * Checks if the package is installed and enabled for a profile. * - * <p>If the user in question is a hidden profile {@link UserManager.USER_TYPE_PROFILE_PRIVATE}, - * caller should have either:</p> - * <ul> - * <li>the privileged {@link android.Manifest.permission.ACCESS_HIDDEN_PROFILES_FULL} - * permission</li> - * <li>the normal {@link android.Manifest.permission.ACCESS_HIDDEN_PROFILES} permission and the - * {@link android.app.role.RoleManager.ROLE_HOME} role. </li> - * </ul> + * <p>If the user in question is a hidden profile {@link UserManager#USER_TYPE_PROFILE_PRIVATE}, + * caller should have normal {@link android.Manifest.permission#ACCESS_HIDDEN_PROFILES} + * permission and the {@link android.app.role.RoleManager#ROLE_HOME} role. * * @param packageName The package to check. * @param user The UserHandle of the profile. * * @return true if the package exists and is enabled. */ + // Alternatively, a system app can access this api for private profile if they've been granted + // with the {@code android.Manifest.permission#ACCESS_HIDDEN_PROFILES_FULL} permission. @SuppressLint("RequiresPermission") @RequiresPermission(conditional = true, anyOf = {ACCESS_HIDDEN_PROFILES_FULL, ACCESS_HIDDEN_PROFILES}) @@ -1249,14 +1224,9 @@ public class LauncherApps { * <p>The contents of this {@link Bundle} are supposed to be a contract between the suspending * app and the launcher. * - * <p>If the user in question is a hidden profile {@link UserManager.USER_TYPE_PROFILE_PRIVATE}, - * caller should have either:</p> - * <ul> - * <li>the privileged {@link android.Manifest.permission.ACCESS_HIDDEN_PROFILES_FULL} - * permission</li> - * <li>the normal {@link android.Manifest.permission.ACCESS_HIDDEN_PROFILES} permission and the - * {@link android.app.role.RoleManager.ROLE_HOME} role. </li> - * </ul> + * <p>If the user in question is a hidden profile {@link UserManager#USER_TYPE_PROFILE_PRIVATE}, + * caller should have normal {@link android.Manifest.permission#ACCESS_HIDDEN_PROFILES} + * permission and the {@link android.app.role.RoleManager#ROLE_HOME} role. * * <p>Note: This just returns whatever extras were provided to the system, <em>which might * even be {@code null}.</em> @@ -1269,6 +1239,8 @@ public class LauncherApps { * @see Callback#onPackagesSuspended(String[], UserHandle, Bundle) * @see PackageManager#isPackageSuspended() */ + // Alternatively, a system app can access this api for private profile if they've been granted + // with the {@code android.Manifest.permission#ACCESS_HIDDEN_PROFILES_FULL} permission. @SuppressLint("RequiresPermission") @RequiresPermission(conditional = true, anyOf = {ACCESS_HIDDEN_PROFILES_FULL, ACCESS_HIDDEN_PROFILES}) @@ -1286,19 +1258,16 @@ public class LauncherApps { * could be done because the package was marked as distracting to the user via * {@code PackageManager.setDistractingPackageRestrictions(String[], int)}. * - * <p>If the user in question is a hidden profile {@link UserManager.USER_TYPE_PROFILE_PRIVATE}, - * caller should have either:</p> - * <ul> - * <li>the privileged {@link android.Manifest.permission.ACCESS_HIDDEN_PROFILES_FULL} - * permission</li> - * <li>the normal {@link android.Manifest.permission.ACCESS_HIDDEN_PROFILES} permission and the - * {@link android.app.role.RoleManager.ROLE_HOME} role. </li> - * </ul> + * <p>If the user in question is a hidden profile {@link UserManager#USER_TYPE_PROFILE_PRIVATE}, + * caller should have normal {@link android.Manifest.permission#ACCESS_HIDDEN_PROFILES} + * permission and the {@link android.app.role.RoleManager#ROLE_HOME} role. * * @param packageName The package for which to check. * @param user the {@link UserHandle} of the profile. * @return */ + // Alternatively, a system app can access this api for private profile if they've been granted + // with the {@code android.Manifest.permission#ACCESS_HIDDEN_PROFILES_FULL} permission. @SuppressLint("RequiresPermission") @RequiresPermission(conditional = true, anyOf = {ACCESS_HIDDEN_PROFILES_FULL, ACCESS_HIDDEN_PROFILES}) @@ -1316,14 +1285,9 @@ public class LauncherApps { /** * Returns {@link ApplicationInfo} about an application installed for a specific user profile. * - * <p>If the user in question is a hidden profile {@link UserManager.USER_TYPE_PROFILE_PRIVATE}, - * caller should have either:</p> - * <ul> - * <li>the privileged {@link android.Manifest.permission.ACCESS_HIDDEN_PROFILES_FULL} - * permission</li> - * <li>the normal {@link android.Manifest.permission.ACCESS_HIDDEN_PROFILES} permission and the - * {@link android.app.role.RoleManager.ROLE_HOME} role. </li> - * </ul> + * <p>If the user in question is a hidden profile {@link UserManager#USER_TYPE_PROFILE_PRIVATE}, + * caller should have normal {@link android.Manifest.permission#ACCESS_HIDDEN_PROFILES} + * permission and the {@link android.app.role.RoleManager#ROLE_HOME} role. * * @param packageName The package name of the application * @param flags Additional option flags {@link PackageManager#getApplicationInfo} @@ -1333,6 +1297,8 @@ public class LauncherApps { * {@code null} if the package isn't installed for the given profile, or the profile * isn't enabled. */ + // Alternatively, a system app can access this api for private profile if they've been granted + // with the {@code android.Manifest.permission#ACCESS_HIDDEN_PROFILES_FULL} permission. @SuppressLint("RequiresPermission") @RequiresPermission(conditional = true, anyOf = {ACCESS_HIDDEN_PROFILES_FULL, ACCESS_HIDDEN_PROFILES}) @@ -1385,20 +1351,17 @@ public class LauncherApps { * <p>The activity may still not be exported, in which case {@link #startMainActivity} will * throw a {@link SecurityException} unless the caller has the same UID as the target app's. * - * <p>If the user in question is a hidden profile {@link UserManager.USER_TYPE_PROFILE_PRIVATE}, - * caller should have either:</p> - * <ul> - * <li>the privileged {@link android.Manifest.permission.ACCESS_HIDDEN_PROFILES_FULL} - * permission</li> - * <li>the normal {@link android.Manifest.permission.ACCESS_HIDDEN_PROFILES} permission and the - * {@link android.app.role.RoleManager.ROLE_HOME} role. </li> - * </ul> + * <p>If the user in question is a hidden profile {@link UserManager#USER_TYPE_PROFILE_PRIVATE}, + * caller should have normal {@link android.Manifest.permission#ACCESS_HIDDEN_PROFILES} + * permission and the {@link android.app.role.RoleManager#ROLE_HOME} role. * * @param component The activity to check. * @param user The UserHandle of the profile. * * @return true if the activity exists and is enabled. */ + // Alternatively, a system app can access this api for private profile if they've been granted + // with the {@code android.Manifest.permission#ACCESS_HIDDEN_PROFILES_FULL} permission. @SuppressLint("RequiresPermission") @RequiresPermission(conditional = true, anyOf = {ACCESS_HIDDEN_PROFILES_FULL, ACCESS_HIDDEN_PROFILES}) @@ -1960,17 +1923,14 @@ public class LauncherApps { /** * Registers a callback for changes to packages in this user and managed profiles. * - * <p>To receive callbacks for hidden profile {@link UserManager.USER_TYPE_PROFILE_PRIVATE}, - * caller should have either:</p> - * <ul> - * <li>the privileged {@link android.Manifest.permission.ACCESS_HIDDEN_PROFILES_FULL} - * permission</li> - * <li>the normal {@link android.Manifest.permission.ACCESS_HIDDEN_PROFILES} permission and the - * {@link android.app.role.RoleManager.ROLE_HOME} role. </li> - * </ul> + * <p>To receive callbacks for hidden profile {@link UserManager#USER_TYPE_PROFILE_PRIVATE}, + * caller should have normal {@link android.Manifest.permission#ACCESS_HIDDEN_PROFILES} + * permission and the {@link android.app.role.RoleManager#ROLE_HOME} role. * * @param callback The callback to register. */ + // Alternatively, a system app can access this api for private profile if they've been granted + // with the {@code android.Manifest.permission#ACCESS_HIDDEN_PROFILES_FULL} permission. @SuppressLint("RequiresPermission") @RequiresPermission(conditional = true, anyOf = {ACCESS_HIDDEN_PROFILES_FULL, ACCESS_HIDDEN_PROFILES}) @@ -1981,18 +1941,15 @@ public class LauncherApps { /** * Registers a callback for changes to packages in this user and managed profiles. * - * <p>To receive callbacks for hidden profile {@link UserManager.USER_TYPE_PROFILE_PRIVATE}, - * caller should have either:</p> - * <ul> - * <li>the privileged {@link android.Manifest.permission.ACCESS_HIDDEN_PROFILES_FULL} - * permission</li> - * <li>the normal {@link android.Manifest.permission.ACCESS_HIDDEN_PROFILES} permission and the - * {@link android.app.role.RoleManager.ROLE_HOME} role. </li> - * </ul> + * <p>To receive callbacks for hidden profile {@link UserManager#USER_TYPE_PROFILE_PRIVATE}, + * caller should have normal {@link android.Manifest.permission#ACCESS_HIDDEN_PROFILES} + * permission and the {@link android.app.role.RoleManager#ROLE_HOME} role. * * @param callback The callback to register. * @param handler that should be used to post callbacks on, may be null. */ + // Alternatively, a system app can access this api for private profile if they've been granted + // with the {@code android.Manifest.permission#ACCESS_HIDDEN_PROFILES_FULL} permission. @SuppressLint("RequiresPermission") @RequiresPermission(conditional = true, anyOf = {ACCESS_HIDDEN_PROFILES_FULL, ACCESS_HIDDEN_PROFILES}) @@ -2446,14 +2403,9 @@ public class LauncherApps { * package name in the app's manifest, have the android.permission.QUERY_ALL_PACKAGES, or be * the session owner to retrieve these details. * - * <p>To receive callbacks for hidden profile {@link UserManager.USER_TYPE_PROFILE_PRIVATE}, - * caller should have either:</p> - * <ul> - * <li>the privileged {@link android.Manifest.permission.ACCESS_HIDDEN_PROFILES} - * permission</li> - * <li>the normal {@link android.Manifest.permission.ACCESS_HIDDEN_PROFILES} permission and the - * {@link android.app.role.RoleManager.ROLE_HOME} role. </li> - * </ul> + * <p>To receive callbacks for hidden profile {@link UserManager#USER_TYPE_PROFILE_PRIVATE}, + * caller should have normal {@link android.Manifest.permission#ACCESS_HIDDEN_PROFILES} + * permission and the {@link android.app.role.RoleManager#ROLE_HOME} role. * * @see PackageInstaller#getAllSessions() */ diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java index 821034aaf204..c673d5846d5d 100644 --- a/core/java/android/content/pm/PackageInstaller.java +++ b/core/java/android/content/pm/PackageInstaller.java @@ -2797,6 +2797,8 @@ public class PackageInstaller { public int developmentInstallFlags = 0; /** {@hide} */ public int unarchiveId = -1; + /** {@hide} */ + public @Nullable String dexoptCompilerFilter = null; private final ArrayMap<String, Integer> mPermissionStates; @@ -2850,6 +2852,7 @@ public class PackageInstaller { applicationEnabledSettingPersistent = source.readBoolean(); developmentInstallFlags = source.readInt(); unarchiveId = source.readInt(); + dexoptCompilerFilter = source.readString(); } /** {@hide} */ @@ -2885,6 +2888,7 @@ public class PackageInstaller { ret.applicationEnabledSettingPersistent = applicationEnabledSettingPersistent; ret.developmentInstallFlags = developmentInstallFlags; ret.unarchiveId = unarchiveId; + ret.dexoptCompilerFilter = dexoptCompilerFilter; return ret; } @@ -3564,6 +3568,11 @@ public class PackageInstaller { } /** @hide */ + public void setDexoptCompilerFilter(@Nullable String dexoptCompilerFilter) { + this.dexoptCompilerFilter = dexoptCompilerFilter; + } + + /** @hide */ @NonNull public ArrayMap<String, Integer> getPermissionStates() { return mPermissionStates; @@ -3622,6 +3631,7 @@ public class PackageInstaller { applicationEnabledSettingPersistent); pw.printHexPair("developmentInstallFlags", developmentInstallFlags); pw.printPair("unarchiveId", unarchiveId); + pw.printPair("dexoptCompilerFilter", dexoptCompilerFilter); pw.println(); } @@ -3667,6 +3677,7 @@ public class PackageInstaller { dest.writeBoolean(applicationEnabledSettingPersistent); dest.writeInt(developmentInstallFlags); dest.writeInt(unarchiveId); + dest.writeString(dexoptCompilerFilter); } public static final Parcelable.Creator<SessionParams> diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index 1f6730b9e3f9..4b579e7db9f8 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -2628,15 +2628,6 @@ public class PackageParser { return Build.VERSION_CODES.CUR_DEVELOPMENT; } - // STOPSHIP: hack for the pre-release SDK - if (platformSdkCodenames.length == 0 - && Build.VERSION.KNOWN_CODENAMES.stream().max(String::compareTo).orElse("").equals( - targetCode)) { - Slog.w(TAG, "Package requires development platform " + targetCode - + ", returning current version " + Build.VERSION.SDK_INT); - return Build.VERSION.SDK_INT; - } - // Otherwise, we're looking at an incompatible pre-release SDK. if (platformSdkCodenames.length > 0) { outError[0] = "Requires development platform " + targetCode @@ -2708,15 +2699,6 @@ public class PackageParser { return Build.VERSION_CODES.CUR_DEVELOPMENT; } - // STOPSHIP: hack for the pre-release SDK - if (platformSdkCodenames.length == 0 - && Build.VERSION.KNOWN_CODENAMES.stream().max(String::compareTo).orElse("").equals( - minCode)) { - Slog.w(TAG, "Package requires min development platform " + minCode - + ", returning current version " + Build.VERSION.SDK_INT); - return Build.VERSION.SDK_INT; - } - // Otherwise, we're looking at an incompatible pre-release SDK. if (platformSdkCodenames.length > 0) { outError[0] = "Requires development platform " + minCode diff --git a/core/java/android/content/pm/multiuser.aconfig b/core/java/android/content/pm/multiuser.aconfig index 5668c540f5bc..a9c07d174767 100644 --- a/core/java/android/content/pm/multiuser.aconfig +++ b/core/java/android/content/pm/multiuser.aconfig @@ -267,3 +267,13 @@ flag { purpose: PURPOSE_BUGFIX } } + +flag { + name: "use_private_space_icon_in_biometric_prompt" + namespace: "profile_experiences" + description: "Update the biometric prompt from generic Settings icon to private space icon" + bug: "333528540" + metadata { + purpose: PURPOSE_BUGFIX + } +} diff --git a/core/java/android/content/pm/parsing/FrameworkParsingPackageUtils.java b/core/java/android/content/pm/parsing/FrameworkParsingPackageUtils.java index c7403c0ea98c..153dd9a93490 100644 --- a/core/java/android/content/pm/parsing/FrameworkParsingPackageUtils.java +++ b/core/java/android/content/pm/parsing/FrameworkParsingPackageUtils.java @@ -316,15 +316,6 @@ public class FrameworkParsingPackageUtils { return input.success(Build.VERSION_CODES.CUR_DEVELOPMENT); } - // STOPSHIP: hack for the pre-release SDK - if (platformSdkCodenames.length == 0 - && Build.VERSION.KNOWN_CODENAMES.stream().max(String::compareTo).orElse("").equals( - minCode)) { - Slog.w(TAG, "Parsed package requires min development platform " + minCode - + ", returning current version " + Build.VERSION.SDK_INT); - return input.success(Build.VERSION.SDK_INT); - } - // Otherwise, we're looking at an incompatible pre-release SDK. if (platformSdkCodenames.length > 0) { return input.error(PackageManager.INSTALL_FAILED_OLDER_SDK, @@ -377,27 +368,19 @@ public class FrameworkParsingPackageUtils { return input.success(targetVers); } - // If it's a pre-release SDK and the codename matches this platform, it - // definitely targets this SDK. - if (matchTargetCode(platformSdkCodenames, targetCode)) { - return input.success(Build.VERSION_CODES.CUR_DEVELOPMENT); - } - - // STOPSHIP: hack for the pre-release SDK - if (platformSdkCodenames.length == 0 - && Build.VERSION.KNOWN_CODENAMES.stream().max(String::compareTo).orElse("").equals( - targetCode)) { - Slog.w(TAG, "Parsed package requires development platform " + targetCode - + ", returning current version " + Build.VERSION.SDK_INT); - return input.success(Build.VERSION.SDK_INT); - } - try { if (allowUnknownCodenames && UnboundedSdkLevel.isAtMost(targetCode)) { return input.success(Build.VERSION_CODES.CUR_DEVELOPMENT); } } catch (IllegalArgumentException e) { - return input.error(PackageManager.INSTALL_FAILED_OLDER_SDK, "Bad package SDK"); + // isAtMost() throws it when encountering an older SDK codename + return input.error(PackageManager.INSTALL_FAILED_OLDER_SDK, e.getMessage()); + } + + // If it's a pre-release SDK and the codename matches this platform, it + // definitely targets this SDK. + if (matchTargetCode(platformSdkCodenames, targetCode)) { + return input.success(Build.VERSION_CODES.CUR_DEVELOPMENT); } // Otherwise, we're looking at an incompatible pre-release SDK. diff --git a/core/java/android/credentials/CredentialManager.java b/core/java/android/credentials/CredentialManager.java index 481ff2e5d3b7..f0f691d339ee 100644 --- a/core/java/android/credentials/CredentialManager.java +++ b/core/java/android/credentials/CredentialManager.java @@ -470,8 +470,8 @@ public final class CredentialManager { * Returns {@code true} if the calling application provides a CredentialProviderService that is * enabled for the current user, or {@code false} otherwise. CredentialProviderServices are * enabled on a per-service basis so the individual component name of the service should be - * passed in here. <strong>Usage of this API is discouraged as it is not fully functional, and - * may throw a NullPointerException on certain devices and/or API versions.</strong> + * passed in here. <strong>Usage of this API is encouraged in API level 35 and above. It + * may throw a NullPointerException on certain devices running other API versions.</strong> * * @throws IllegalArgumentException if the componentName package does not match the calling * package name this call will throw an exception diff --git a/core/java/android/database/DefaultDatabaseErrorHandler.java b/core/java/android/database/DefaultDatabaseErrorHandler.java index cf019e134bc2..cf019e134bc2 100755..100644 --- a/core/java/android/database/DefaultDatabaseErrorHandler.java +++ b/core/java/android/database/DefaultDatabaseErrorHandler.java diff --git a/core/java/android/gesture/OWNERS b/core/java/android/gesture/OWNERS index b3b8775dc036..168630af6da4 100644 --- a/core/java/android/gesture/OWNERS +++ b/core/java/android/gesture/OWNERS @@ -1,7 +1,6 @@ # Bug component: 25700 romainguy@google.com -alanv@google.com adamp@google.com aurimas@google.com nduca@google.com diff --git a/core/java/android/hardware/biometrics/BiometricPrompt.java b/core/java/android/hardware/biometrics/BiometricPrompt.java index 61f1ee11e059..37a2df83cc00 100644 --- a/core/java/android/hardware/biometrics/BiometricPrompt.java +++ b/core/java/android/hardware/biometrics/BiometricPrompt.java @@ -38,6 +38,9 @@ import android.content.ComponentName; import android.content.Context; import android.content.DialogInterface; import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.Drawable; import android.hardware.face.FaceManager; import android.hardware.fingerprint.FingerprintManager; import android.os.Binder; @@ -193,7 +196,14 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan @RequiresPermission(SET_BIOMETRIC_DIALOG_ADVANCED) @NonNull public BiometricPrompt.Builder setLogoRes(@DrawableRes int logoRes) { - mPromptInfo.setLogoRes(logoRes); + if (mPromptInfo.getLogoBitmap() != null) { + throw new IllegalStateException( + "Exclusively one of logo resource or logo bitmap can be set"); + } + if (logoRes != 0) { + mPromptInfo.setLogo(logoRes, + convertDrawableToBitmap(mContext.getDrawable(logoRes))); + } return this; } @@ -212,7 +222,11 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan @RequiresPermission(SET_BIOMETRIC_DIALOG_ADVANCED) @NonNull public BiometricPrompt.Builder setLogoBitmap(@NonNull Bitmap logoBitmap) { - mPromptInfo.setLogoBitmap(logoBitmap); + if (mPromptInfo.getLogoRes() != 0) { + throw new IllegalStateException( + "Exclusively one of logo resource or logo bitmap can be set"); + } + mPromptInfo.setLogo(0, logoBitmap); return this; } @@ -821,7 +835,7 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan * Gets the drawable resource of the logo for the prompt, as set by * {@link Builder#setLogoRes(int)}. Currently for system applications use only. * - * @return The drawable resource of the logo, or -1 if the prompt has no logo resource set. + * @return The drawable resource of the logo, or 0 if the prompt has no logo resource set. */ @FlaggedApi(FLAG_CUSTOM_BIOMETRIC_PROMPT) @RequiresPermission(SET_BIOMETRIC_DIALOG_ADVANCED) @@ -1516,4 +1530,29 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan private static boolean isCredentialAllowed(@Authenticators.Types int allowedAuthenticators) { return (allowedAuthenticators & Authenticators.DEVICE_CREDENTIAL) != 0; } + + /** Converts {@code drawable} to a {@link Bitmap}. */ + private static Bitmap convertDrawableToBitmap(Drawable drawable) { + if (drawable == null) { + return null; + } + + if (drawable instanceof BitmapDrawable) { + return ((BitmapDrawable) drawable).getBitmap(); + } + + Bitmap bitmap; + if (drawable.getIntrinsicWidth() <= 0 || drawable.getIntrinsicHeight() <= 0) { + bitmap = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888); + // Single color bitmap will be created of 1x1 pixel + } else { + bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), + drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888); + } + + final Canvas canvas = new Canvas(bitmap); + drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight()); + drawable.draw(canvas); + return bitmap; + } } diff --git a/core/java/android/hardware/biometrics/PromptInfo.java b/core/java/android/hardware/biometrics/PromptInfo.java index bb07b9b881f8..ba9f30dfcf4b 100644 --- a/core/java/android/hardware/biometrics/PromptInfo.java +++ b/core/java/android/hardware/biometrics/PromptInfo.java @@ -33,7 +33,7 @@ import java.util.List; */ public class PromptInfo implements Parcelable { - @DrawableRes private int mLogoRes = -1; + @DrawableRes private int mLogoRes; @Nullable private Bitmap mLogoBitmap; @Nullable private String mLogoDescription; @NonNull private CharSequence mTitle; @@ -187,7 +187,7 @@ public class PromptInfo implements Parcelable { * this permission. */ public boolean requiresAdvancedPermission() { - if (mLogoRes != -1) { + if (mLogoRes != 0) { return true; } else if (mLogoBitmap != null) { return true; @@ -217,14 +217,17 @@ public class PromptInfo implements Parcelable { // LINT.ThenChange(frameworks/base/core/java/android/hardware/biometrics/BiometricPrompt.java) // Setters - public void setLogoRes(@DrawableRes int logoRes) { - mLogoRes = logoRes; - checkOnlyOneLogoSet(); - } - public void setLogoBitmap(@NonNull Bitmap logoBitmap) { + /** + * Sets logo res and bitmap + * + * @param logoRes The logo res set by the app; Or 0 if the app sets bitmap directly. + * @param logoBitmap The bitmap from logoRes if the app sets logoRes; Or the bitmap set by the + * app directly. + */ + public void setLogo(@DrawableRes int logoRes, @NonNull Bitmap logoBitmap) { + mLogoRes = logoRes; mLogoBitmap = logoBitmap; - checkOnlyOneLogoSet(); } public void setLogoDescription(@NonNull String logoDescription) { @@ -326,13 +329,29 @@ public class PromptInfo implements Parcelable { } // Getters + + /** + * Returns the logo bitmap either from logo resource or bitmap passed in from the app. + */ + public Bitmap getLogo() { + return mLogoBitmap; + } + + /** + * Returns the logo res set by the app. + */ @DrawableRes public int getLogoRes() { return mLogoRes; } + /** + * Returns the logo bitmap set by the app. + */ public Bitmap getLogoBitmap() { - return mLogoBitmap; + // If mLogoRes has been set, return null since mLogoBitmap is from the res, but not from + // the app directly. + return mLogoRes == 0 ? mLogoBitmap : null; } public String getLogoDescription() { @@ -436,10 +455,4 @@ public class PromptInfo implements Parcelable { return mComponentNameForConfirmDeviceCredentialActivity; } - private void checkOnlyOneLogoSet() { - if (mLogoRes != -1 && mLogoBitmap != null) { - throw new IllegalStateException( - "Exclusively one of logo resource or logo bitmap can be set"); - } - } } diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java index 3cc87ea9d359..6fffb822c9ca 100644 --- a/core/java/android/hardware/camera2/CameraCharacteristics.java +++ b/core/java/android/hardware/camera2/CameraCharacteristics.java @@ -1472,9 +1472,9 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri new Key<Integer>("android.flash.info.strengthDefaultLevel", int.class); /** - * <p>Maximum flash brightness level for manual flash control in SINGLE mode.</p> + * <p>Maximum flash brightness level for manual flash control in <code>SINGLE</code> mode.</p> * <p>Maximum flash brightness level in camera capture mode and - * {@link CaptureRequest#FLASH_MODE android.flash.mode} set to SINGLE. + * {@link CaptureRequest#FLASH_MODE android.flash.mode} set to <code>SINGLE</code>. * Value will be > 1 if the manual flash strength control feature is supported, * otherwise the value will be equal to 1. * Note that this level is just a number of supported levels (the granularity of control). @@ -1490,7 +1490,7 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri new Key<Integer>("android.flash.singleStrengthMaxLevel", int.class); /** - * <p>Default flash brightness level for manual flash control in SINGLE mode.</p> + * <p>Default flash brightness level for manual flash control in <code>SINGLE</code> mode.</p> * <p>If flash unit is available this will be greater than or equal to 1 and less * or equal to {@link CameraCharacteristics#FLASH_SINGLE_STRENGTH_MAX_LEVEL android.flash.singleStrengthMaxLevel}. * Note for devices that do not support the manual flash strength control @@ -1506,9 +1506,9 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri new Key<Integer>("android.flash.singleStrengthDefaultLevel", int.class); /** - * <p>Maximum flash brightness level for manual flash control in TORCH mode</p> + * <p>Maximum flash brightness level for manual flash control in <code>TORCH</code> mode</p> * <p>Maximum flash brightness level in camera capture mode and - * {@link CaptureRequest#FLASH_MODE android.flash.mode} set to TORCH. + * {@link CaptureRequest#FLASH_MODE android.flash.mode} set to <code>TORCH</code>. * Value will be > 1 if the manual flash strength control feature is supported, * otherwise the value will be equal to 1.</p> * <p>Note that this level is just a number of supported levels(the granularity of control). @@ -1530,7 +1530,7 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri new Key<Integer>("android.flash.torchStrengthMaxLevel", int.class); /** - * <p>Default flash brightness level for manual flash control in TORCH mode</p> + * <p>Default flash brightness level for manual flash control in <code>TORCH</code> mode</p> * <p>If flash unit is available this will be greater than or equal to 1 and less * or equal to {@link CameraCharacteristics#FLASH_TORCH_STRENGTH_MAX_LEVEL android.flash.torchStrengthMaxLevel}. * Note for the devices that do not support the manual flash strength control feature, @@ -4152,10 +4152,16 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri /** * <p>Whether the RAW images output from this camera device are subject to * lens shading correction.</p> - * <p>If TRUE, all images produced by the camera device in the RAW image formats will - * have lens shading correction already applied to it. If FALSE, the images will - * not be adjusted for lens shading correction. - * See {@link CameraCharacteristics#REQUEST_MAX_NUM_OUTPUT_RAW android.request.maxNumOutputRaw} for a list of RAW image formats.</p> + * <p>If <code>true</code>, all images produced by the camera device in the <code>RAW</code> image formats will have + * at least some lens shading correction already applied to it. If <code>false</code>, the images will + * not be adjusted for lens shading correction. See {@link CameraCharacteristics#REQUEST_MAX_NUM_OUTPUT_RAW android.request.maxNumOutputRaw} for a + * list of RAW image formats.</p> + * <p>When <code>true</code>, the <code>lensShadingCorrectionMap</code> key may still have values greater than 1.0, + * and those will need to be applied to any captured RAW frames for them to match the shading + * correction of processed buffers such as <code>YUV</code> or <code>JPEG</code> images. This may occur, for + * example, when some basic fixed lens shading correction is applied by hardware to RAW data, + * and additional correction is done dynamically in the camera processing pipeline after + * demosaicing.</p> * <p>This key will be <code>null</code> for all devices do not report this information. * Devices with RAW capability will always report this information in this key.</p> * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p> diff --git a/core/java/android/hardware/camera2/CameraMetadata.java b/core/java/android/hardware/camera2/CameraMetadata.java index de26384211a4..4819f67cb566 100644 --- a/core/java/android/hardware/camera2/CameraMetadata.java +++ b/core/java/android/hardware/camera2/CameraMetadata.java @@ -2360,11 +2360,8 @@ public abstract class CameraMetadata<TKey> { * <p>If the session configuration is not supported, the AE mode reported in the * CaptureResult will be 'ON' instead of 'ON_LOW_LIGHT_BOOST_BRIGHTNESS_PRIORITY'.</p> * <p>When this AE mode is enabled, the CaptureResult field - * {@link CaptureResult#CONTROL_LOW_LIGHT_BOOST_STATE android.control.lowLightBoostState} will be present and not null. Otherwise, the - * {@link CaptureResult#CONTROL_LOW_LIGHT_BOOST_STATE android.control.lowLightBoostState} field will not be present in the CaptureResult.</p> - * <p>The application can observe the CaptureResult field - * {@link CaptureResult#CONTROL_LOW_LIGHT_BOOST_STATE android.control.lowLightBoostState} to determine when low light boost is 'ACTIVE' or - * 'INACTIVE'.</p> + * {@link CaptureResult#CONTROL_LOW_LIGHT_BOOST_STATE android.control.lowLightBoostState} will indicate when low light boost is 'ACTIVE' + * or 'INACTIVE'. By default {@link CaptureResult#CONTROL_LOW_LIGHT_BOOST_STATE android.control.lowLightBoostState} will be 'INACTIVE'.</p> * <p>The low light boost is 'ACTIVE' once the scene lighting condition is less than the * upper bound lux value defined by {@link CameraCharacteristics#CONTROL_LOW_LIGHT_BOOST_INFO_LUMINANCE_RANGE android.control.lowLightBoostInfoLuminanceRange}. * This mode will be 'INACTIVE' once the scene lighting condition is greater than the diff --git a/core/java/android/hardware/camera2/CaptureRequest.java b/core/java/android/hardware/camera2/CaptureRequest.java index 938636f7e8e5..6968f279dbc0 100644 --- a/core/java/android/hardware/camera2/CaptureRequest.java +++ b/core/java/android/hardware/camera2/CaptureRequest.java @@ -2683,27 +2683,27 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>> /** * <p>Flash strength level to be used when manual flash control is active.</p> * <p>Flash strength level to use in capture mode i.e. when the applications control - * flash with either SINGLE or TORCH mode.</p> + * flash with either <code>SINGLE</code> or <code>TORCH</code> mode.</p> * <p>Use {@link CameraCharacteristics#FLASH_SINGLE_STRENGTH_MAX_LEVEL android.flash.singleStrengthMaxLevel} and * {@link CameraCharacteristics#FLASH_TORCH_STRENGTH_MAX_LEVEL android.flash.torchStrengthMaxLevel} to check whether the device supports * flash strength control or not. - * If the values of android.flash.info.singleStrengthMaxLevel and + * If the values of {@link CameraCharacteristics#FLASH_SINGLE_STRENGTH_MAX_LEVEL android.flash.singleStrengthMaxLevel} and * {@link CameraCharacteristics#FLASH_TORCH_STRENGTH_MAX_LEVEL android.flash.torchStrengthMaxLevel} are greater than 1, * then the device supports manual flash strength control.</p> - * <p>If the {@link CaptureRequest#FLASH_MODE android.flash.mode} <code>==</code> TORCH the value must be >= 1 + * <p>If the {@link CaptureRequest#FLASH_MODE android.flash.mode} <code>==</code> <code>TORCH</code> the value must be >= 1 * and <= {@link CameraCharacteristics#FLASH_TORCH_STRENGTH_MAX_LEVEL android.flash.torchStrengthMaxLevel}. * If the application doesn't set the key and * {@link CameraCharacteristics#FLASH_TORCH_STRENGTH_MAX_LEVEL android.flash.torchStrengthMaxLevel} > 1, * then the flash will be fired at the default level set by HAL in * {@link CameraCharacteristics#FLASH_TORCH_STRENGTH_DEFAULT_LEVEL android.flash.torchStrengthDefaultLevel}. - * If the {@link CaptureRequest#FLASH_MODE android.flash.mode} <code>==</code> SINGLE, then the value must be >= 1 + * If the {@link CaptureRequest#FLASH_MODE android.flash.mode} <code>==</code> <code>SINGLE</code>, then the value must be >= 1 * and <= {@link CameraCharacteristics#FLASH_SINGLE_STRENGTH_MAX_LEVEL android.flash.singleStrengthMaxLevel}. * If the application does not set this key and * {@link CameraCharacteristics#FLASH_SINGLE_STRENGTH_MAX_LEVEL android.flash.singleStrengthMaxLevel} > 1, * then the flash will be fired at the default level set by HAL * in {@link CameraCharacteristics#FLASH_SINGLE_STRENGTH_DEFAULT_LEVEL android.flash.singleStrengthDefaultLevel}. - * If {@link CaptureRequest#CONTROL_AE_MODE android.control.aeMode} is set to any of ON_AUTO_FLASH, ON_ALWAYS_FLASH, - * ON_AUTO_FLASH_REDEYE, ON_EXTERNAL_FLASH values, then the strengthLevel will be ignored.</p> + * If {@link CaptureRequest#CONTROL_AE_MODE android.control.aeMode} is set to any of <code>ON_AUTO_FLASH</code>, <code>ON_ALWAYS_FLASH</code>, + * <code>ON_AUTO_FLASH_REDEYE</code>, <code>ON_EXTERNAL_FLASH</code> values, then the strengthLevel will be ignored.</p> * <p><b>Range of valid values:</b><br> * <code>[1-{@link CameraCharacteristics#FLASH_TORCH_STRENGTH_MAX_LEVEL android.flash.torchStrengthMaxLevel}]</code> when the {@link CaptureRequest#FLASH_MODE android.flash.mode} is * set to TORCH; diff --git a/core/java/android/hardware/camera2/CaptureResult.java b/core/java/android/hardware/camera2/CaptureResult.java index 4406a419c317..d652b4cc42cd 100644 --- a/core/java/android/hardware/camera2/CaptureResult.java +++ b/core/java/android/hardware/camera2/CaptureResult.java @@ -2819,12 +2819,11 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> { * <p>When low light boost is enabled by setting the AE mode to * 'ON_LOW_LIGHT_BOOST_BRIGHTNESS_PRIORITY', it can dynamically apply a low light * boost when the light level threshold is exceeded.</p> - * <p>This field is present in the CaptureResult when the AE mode is set to - * 'ON_LOW_LIGHT_BOOST_BRIGHTNESS_PRIORITY'. Otherwise, the field is not present.</p> * <p>This state indicates when low light boost is 'ACTIVE' and applied. Similarly, it can * indicate when it is not being applied by returning 'INACTIVE'.</p> * <p>This key will be absent from the CaptureResult if AE mode is not set to * 'ON_LOW_LIGHT_BOOST_BRIGHTNESS_PRIORITY.</p> + * <p>The default value will always be 'INACTIVE'.</p> * <p><b>Possible values:</b></p> * <ul> * <li>{@link #CONTROL_LOW_LIGHT_BOOST_STATE_INACTIVE INACTIVE}</li> @@ -2976,27 +2975,27 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> { /** * <p>Flash strength level to be used when manual flash control is active.</p> * <p>Flash strength level to use in capture mode i.e. when the applications control - * flash with either SINGLE or TORCH mode.</p> + * flash with either <code>SINGLE</code> or <code>TORCH</code> mode.</p> * <p>Use {@link CameraCharacteristics#FLASH_SINGLE_STRENGTH_MAX_LEVEL android.flash.singleStrengthMaxLevel} and * {@link CameraCharacteristics#FLASH_TORCH_STRENGTH_MAX_LEVEL android.flash.torchStrengthMaxLevel} to check whether the device supports * flash strength control or not. - * If the values of android.flash.info.singleStrengthMaxLevel and + * If the values of {@link CameraCharacteristics#FLASH_SINGLE_STRENGTH_MAX_LEVEL android.flash.singleStrengthMaxLevel} and * {@link CameraCharacteristics#FLASH_TORCH_STRENGTH_MAX_LEVEL android.flash.torchStrengthMaxLevel} are greater than 1, * then the device supports manual flash strength control.</p> - * <p>If the {@link CaptureRequest#FLASH_MODE android.flash.mode} <code>==</code> TORCH the value must be >= 1 + * <p>If the {@link CaptureRequest#FLASH_MODE android.flash.mode} <code>==</code> <code>TORCH</code> the value must be >= 1 * and <= {@link CameraCharacteristics#FLASH_TORCH_STRENGTH_MAX_LEVEL android.flash.torchStrengthMaxLevel}. * If the application doesn't set the key and * {@link CameraCharacteristics#FLASH_TORCH_STRENGTH_MAX_LEVEL android.flash.torchStrengthMaxLevel} > 1, * then the flash will be fired at the default level set by HAL in * {@link CameraCharacteristics#FLASH_TORCH_STRENGTH_DEFAULT_LEVEL android.flash.torchStrengthDefaultLevel}. - * If the {@link CaptureRequest#FLASH_MODE android.flash.mode} <code>==</code> SINGLE, then the value must be >= 1 + * If the {@link CaptureRequest#FLASH_MODE android.flash.mode} <code>==</code> <code>SINGLE</code>, then the value must be >= 1 * and <= {@link CameraCharacteristics#FLASH_SINGLE_STRENGTH_MAX_LEVEL android.flash.singleStrengthMaxLevel}. * If the application does not set this key and * {@link CameraCharacteristics#FLASH_SINGLE_STRENGTH_MAX_LEVEL android.flash.singleStrengthMaxLevel} > 1, * then the flash will be fired at the default level set by HAL * in {@link CameraCharacteristics#FLASH_SINGLE_STRENGTH_DEFAULT_LEVEL android.flash.singleStrengthDefaultLevel}. - * If {@link CaptureRequest#CONTROL_AE_MODE android.control.aeMode} is set to any of ON_AUTO_FLASH, ON_ALWAYS_FLASH, - * ON_AUTO_FLASH_REDEYE, ON_EXTERNAL_FLASH values, then the strengthLevel will be ignored.</p> + * If {@link CaptureRequest#CONTROL_AE_MODE android.control.aeMode} is set to any of <code>ON_AUTO_FLASH</code>, <code>ON_ALWAYS_FLASH</code>, + * <code>ON_AUTO_FLASH_REDEYE</code>, <code>ON_EXTERNAL_FLASH</code> values, then the strengthLevel will be ignored.</p> * <p><b>Range of valid values:</b><br> * <code>[1-{@link CameraCharacteristics#FLASH_TORCH_STRENGTH_MAX_LEVEL android.flash.torchStrengthMaxLevel}]</code> when the {@link CaptureRequest#FLASH_MODE android.flash.mode} is * set to TORCH; @@ -4846,6 +4845,9 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> { * correction map that needs to be applied to get shading * corrected images that match the camera device's output for * non-RAW formats.</p> + * <p>Therefore, whatever the value of lensShadingApplied is, the lens + * shading map should always be applied to RAW images if the goal is to + * match the shading appearance of processed (non-RAW) images.</p> * <p>For a complete shading correction map, the least shaded * section of the image will have a gain factor of 1; all * other sections will have gains above 1.</p> diff --git a/core/java/android/hardware/camera2/ExtensionCaptureRequest.java b/core/java/android/hardware/camera2/ExtensionCaptureRequest.java index c33956b59f2f..b681ce40dfd9 100644 --- a/core/java/android/hardware/camera2/ExtensionCaptureRequest.java +++ b/core/java/android/hardware/camera2/ExtensionCaptureRequest.java @@ -18,7 +18,6 @@ package android.hardware.camera2; import android.annotation.FlaggedApi; import android.annotation.NonNull; -import android.hardware.camera2.CameraCharacteristics; import android.hardware.camera2.CaptureRequest; import android.hardware.camera2.CaptureRequest.Key; import android.hardware.camera2.impl.ExtensionKey; @@ -43,6 +42,9 @@ import com.android.internal.camera.flags.Flags; @FlaggedApi(Flags.FLAG_CONCERT_MODE_API) public final class ExtensionCaptureRequest { + /** To avoid exposing constructor */ + private ExtensionCaptureRequest() {} + /** * <p>Used to apply an additional digital zoom factor for the * {@link android.hardware.camera2.CameraExtensionCharacteristics#EXTENSION_EYES_FREE_VIDEOGRAPHY } diff --git a/core/java/android/hardware/camera2/ExtensionCaptureResult.java b/core/java/android/hardware/camera2/ExtensionCaptureResult.java index 95feb2fd268a..b7ba78cb34b5 100644 --- a/core/java/android/hardware/camera2/ExtensionCaptureResult.java +++ b/core/java/android/hardware/camera2/ExtensionCaptureResult.java @@ -18,7 +18,6 @@ package android.hardware.camera2; import android.annotation.FlaggedApi; import android.annotation.NonNull; -import android.hardware.camera2.CameraCharacteristics; import android.hardware.camera2.CameraExtensionCharacteristics; import android.hardware.camera2.CaptureResult; import android.hardware.camera2.CaptureResult.Key; @@ -45,6 +44,9 @@ import com.android.internal.camera.flags.Flags; @FlaggedApi(Flags.FLAG_CONCERT_MODE_API) public final class ExtensionCaptureResult { + /** To avoid exposing constructor */ + private ExtensionCaptureResult() {} + /** * <p>The padding region for the * {@link android.hardware.camera2.CameraExtensionCharacteristics#EXTENSION_EYES_FREE_VIDEOGRAPHY } diff --git a/core/java/android/hardware/camera2/impl/CameraDeviceSetupImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceSetupImpl.java index 8898a4c37446..df057a1489f0 100644 --- a/core/java/android/hardware/camera2/impl/CameraDeviceSetupImpl.java +++ b/core/java/android/hardware/camera2/impl/CameraDeviceSetupImpl.java @@ -105,7 +105,8 @@ public class CameraDeviceSetupImpl extends CameraDevice.CameraDeviceSetup { try { return cameraService.isSessionConfigurationWithParametersSupported(mCameraId, - mTargetSdkVersion, config, mContext.getDeviceId(), + mTargetSdkVersion, config, + mContext.getDeviceId(), mCameraManager.getDevicePolicyFromContext(mContext)); } catch (ServiceSpecificException e) { throw ExceptionUtils.throwAsPublicException(e); diff --git a/core/java/android/hardware/camera2/params/OutputConfiguration.java b/core/java/android/hardware/camera2/params/OutputConfiguration.java index dda52dd5e8cc..ebcc37113d1a 100644 --- a/core/java/android/hardware/camera2/params/OutputConfiguration.java +++ b/core/java/android/hardware/camera2/params/OutputConfiguration.java @@ -638,13 +638,15 @@ public final class OutputConfiguration implements Parcelable { /** * Create a list of {@link OutputConfiguration} instances for a - * {@link android.hardware.camera2.params.MultiResolutionImageReader}. + * {@link MultiResolutionImageReader}. * * <p>This method can be used to create query OutputConfigurations for a * MultiResolutionImageReader that can be included in a SessionConfiguration passed into - * {@link CameraDeviceSetup#isSessionConfigurationSupported} before opening and setting up - * a camera device in full, at which point {@link #setSurfacesForMultiResolutionOutput} - * can be used to link to the actual MultiResolutionImageReader.</p> + * {@link + * android.hardware.camera2.CameraDevice.CameraDeviceSetup#isSessionConfigurationSupported} + * before opening and setting up a camera device in full, at which point {@link + * #setSurfacesForMultiResolutionOutput} can be used to link to the actual + * MultiResolutionImageReader.</p> * * <p>This constructor takes same arguments used to create a {@link * MultiResolutionImageReader}: a collection of {@link MultiResolutionStreamInfo} @@ -655,12 +657,12 @@ public final class OutputConfiguration implements Parcelable { * @param format The format of the MultiResolutionImageReader. This must be one of the {@link * android.graphics.ImageFormat} or {@link android.graphics.PixelFormat} constants * supported by the camera device. Note that not all formats are supported, like - * {@link ImageFormat.NV21}. The supported multi-resolution reader format can be + * {@link ImageFormat#NV21}. The supported multi-resolution reader format can be * queried by {@link MultiResolutionStreamConfigurationMap#getOutputFormats}. * * @return The list of {@link OutputConfiguration} objects for a MultiResolutionImageReader. * - * @throws IllegaArgumentException If the {@code streams} is null or doesn't contain + * @throws IllegalArgumentException If the {@code streams} is null or doesn't contain * at least 2 items, or if {@code format} isn't a valid camera * format. * @@ -710,7 +712,7 @@ public final class OutputConfiguration implements Parcelable { * instances.</p> * * @param outputConfigurations The OutputConfiguration objects created by {@link - * #createInstancesFromMultiResolutionOutput} + * #createInstancesForMultiResolutionOutput} * @param multiResolutionImageReader The MultiResolutionImageReader object created from the same * MultiResolutionStreamInfo parameters as * {@code outputConfigurations}. @@ -759,31 +761,33 @@ public final class OutputConfiguration implements Parcelable { * the deferred Surface can be obtained: (1) from {@link android.view.SurfaceView} * by calling {@link android.view.SurfaceHolder#getSurface}, (2) from * {@link android.graphics.SurfaceTexture} via - * {@link android.view.Surface#Surface(android.graphics.SurfaceTexture)}, (3) from {@link - * android.media.MediaRecorder} via {@link android.media.MediaRecorder.getSurface} or {@link - * android.media.MediaCodec#createPersistentInputSurface}, or (4) from {@link - * android.media.MediaCodce} via {@link android.media.MediaCodec#createInputSurface} or {@link - * android.media.MediaCodec#createPersistentInputSource}.</p> + * {@link android.view.Surface#Surface(android.graphics.SurfaceTexture)}, (3) from + * {@link android.media.MediaRecorder} via {@link android.media.MediaRecorder#getSurface} or + * {@link android.media.MediaCodec#createPersistentInputSurface}, or (4) from + * {@link android.media.MediaCodec} via {@link android.media.MediaCodec#createInputSurface} or + * {@link android.media.MediaCodec#createPersistentInputSurface}.</p> * * <ul> * <li>Surfaces for {@link android.view.SurfaceView} and {@link android.graphics.SurfaceTexture} * can be deferred until after {@link CameraDevice#createCaptureSession}. In that case, the * output Surface must be set via {@link #addSurface}, and the Surface configuration must be - * finalized via {@link CameraCaptureSession#finalizeOutputConfiguration} before submitting + * finalized via {@link CameraCaptureSession#finalizeOutputConfigurations} before submitting * a request with the Surface target.</li> * <li>For all other target types, the output Surface must be set by {@link #addSurface}, - * and {@link CameraCaptureSession#finalizeOutputConfiguration} is not needed because the + * and {@link CameraCaptureSession#finalizeOutputConfigurations} is not needed because the * OutputConfiguration used to create the session will contain the actual Surface.</li> * </ul> * * <p>Before {@link android.os.Build.VERSION_CODES#VANILLA_ICE_CREAM Android V}, only {@link * android.view.SurfaceView} and {@link android.graphics.SurfaceTexture} are supported. Both * kind of outputs can be deferred until after {@link - * CameraDevice#createCaptureSessionByOutputConfiguration}.</p> + * CameraDevice#createCaptureSessionByOutputConfigurations}.</p> * * <p>An OutputConfiguration object created by this constructor can be used for {@link - * CameraDeviceSetup.isSessionConfigurationSupported} and {@link - * CameraDeviceSetup.getSessionCharacteristics} without having called {@link #addSurface}.</p> + * android.hardware.camera2.CameraDevice.CameraDeviceSetup#isSessionConfigurationSupported} + * and {@link + * android.hardware.camera2.CameraDevice.CameraDeviceSetup#getSessionCharacteristics} without + * having called {@link #addSurface}.</p> * * @param surfaceSize Size for the deferred surface. * @param klass a non-{@code null} {@link Class} object reference that indicates the source of @@ -849,8 +853,10 @@ public final class OutputConfiguration implements Parcelable { * before creating the capture session.</p> * * <p>An OutputConfiguration object created by this constructor can be used for {@link - * CameraDeviceSetup.isSessionConfigurationSupported} and {@link - * CameraDeviceSetup.getSessionCharacteristics} without having called {@link #addSurface}.</p> + * android.hardware.camera2.CameraDevice.CameraDeviceSetup#isSessionConfigurationSupported} + * and {@link + * android.hardware.camera2.CameraDevice.CameraDeviceSetup#getSessionCharacteristics} without + * having called {@link #addSurface}.</p> * * @param format The format of the ImageReader output. This must be one of the * {@link android.graphics.ImageFormat} or {@link android.graphics.PixelFormat} @@ -873,8 +879,10 @@ public final class OutputConfiguration implements Parcelable { * before creating the capture session.</p> * * <p>An OutputConfiguration object created by this constructor can be used for {@link - * CameraDeviceSetup.isSessionConfigurationSupported} and {@link - * CameraDeviceSetup.getSessionCharacteristics} without having called {@link #addSurface}.</p> + * android.hardware.camera2.CameraDevice.CameraDeviceSetup#isSessionConfigurationSupported} + * and {@link + * android.hardware.camera2.CameraDevice.CameraDeviceSetup#getSessionCharacteristics} without + * having called {@link #addSurface}.</p> * * @param surfaceGroupId A group ID for this output, used for sharing memory between multiple * outputs. @@ -899,8 +907,10 @@ public final class OutputConfiguration implements Parcelable { * before creating the capture session.</p> * * <p>An OutputConfiguration object created by this constructor can be used for {@link - * CameraDeviceSetup.isSessionConfigurationSupported} and {@link - * CameraDeviceSetup.getSessionCharacteristics} without having called {@link #addSurface}.</p> + * android.hardware.camera2.CameraDevice.CameraDeviceSetup#isSessionConfigurationSupported} + * and {@link + * android.hardware.camera2.CameraDevice.CameraDeviceSetup#getSessionCharacteristics} without + * having called {@link #addSurface}.</p> * * @param format The format of the ImageReader output. This must be one of the * {@link android.graphics.ImageFormat} or {@link android.graphics.PixelFormat} @@ -923,8 +933,10 @@ public final class OutputConfiguration implements Parcelable { * before creating the capture session.</p> * * <p>An OutputConfiguration object created by this constructor can be used for {@link - * CameraDeviceSetup.isSessionConfigurationSupported} and {@link - * CameraDeviceSetup.getSessionCharacteristics} without having called {@link #addSurface}.</p> + * android.hardware.camera2.CameraDevice.CameraDeviceSetup#isSessionConfigurationSupported} + * and {@link + * android.hardware.camera2.CameraDevice.CameraDeviceSetup#getSessionCharacteristics} without + * having called {@link #addSurface}.</p> * * @param surfaceGroupId A group ID for this output, used for sharing memory between multiple * outputs. @@ -1171,9 +1183,9 @@ public final class OutputConfiguration implements Parcelable { * <li>from {@link android.media.MediaRecorder} by calling * {@link android.media.MediaRecorder#getSurface} or {@link * android.media.MediaCodec#createPersistentInputSurface}</li> - * <li>from {@link android.media.MediaCodce} by calling - * {@link android.media.MediaCodec#createInputSurface} or {@link - * android.media.MediaCodec#createPersistentInputSource}</li> + * <li>from {@link android.media.MediaCodec} by calling + * {@link android.media.MediaCodec#createInputSurface} or + * {@link android.media.MediaCodec#createPersistentInputSurface()}</li> * </ul> * * <p> If the OutputConfiguration was constructed by {@link #OutputConfiguration(int, Size)} diff --git a/core/java/android/hardware/display/DisplayManagerGlobal.java b/core/java/android/hardware/display/DisplayManagerGlobal.java index 3d7b714a2f5b..85197221e651 100644 --- a/core/java/android/hardware/display/DisplayManagerGlobal.java +++ b/core/java/android/hardware/display/DisplayManagerGlobal.java @@ -548,6 +548,20 @@ public final class DisplayManagerGlobal { } } + /** + * Request to power a display ON or OFF. + * @hide + */ + @RequiresPermission("android.permission.MANAGE_DISPLAYS") + public boolean requestDisplayPower(int displayId, boolean on) { + try { + return mDm.requestDisplayPower(displayId, on); + } catch (RemoteException ex) { + Log.e(TAG, "Error trying to request display power " + on, ex); + return false; + } + } + public void startWifiDisplayScan() { synchronized (mLock) { if (mWifiDisplayScanNestCount++ == 0) { diff --git a/core/java/android/hardware/display/IDisplayManager.aidl b/core/java/android/hardware/display/IDisplayManager.aidl index 70efc6f2e33f..b7c02b0d0720 100644 --- a/core/java/android/hardware/display/IDisplayManager.aidl +++ b/core/java/android/hardware/display/IDisplayManager.aidl @@ -236,6 +236,10 @@ interface IDisplayManager { @EnforcePermission("MANAGE_DISPLAYS") void disableConnectedDisplay(int displayId); + // Request to power display ON or OFF. + @EnforcePermission("MANAGE_DISPLAYS") + boolean requestDisplayPower(int displayId, boolean on); + // Restricts display modes to specified modeIds. @EnforcePermission("RESTRICT_DISPLAY_MODES") void requestDisplayModes(in IBinder token, int displayId, in @nullable int[] modeIds); diff --git a/core/java/android/hardware/location/ISignificantPlaceProvider.aidl b/core/java/android/hardware/location/ISignificantPlaceProvider.aidl index e02169ef2c4d..992dbff4e3ca 100644 --- a/core/java/android/hardware/location/ISignificantPlaceProvider.aidl +++ b/core/java/android/hardware/location/ISignificantPlaceProvider.aidl @@ -7,4 +7,5 @@ import android.hardware.location.ISignificantPlaceProviderManager; */ oneway interface ISignificantPlaceProvider { void setSignificantPlaceProviderManager(in ISignificantPlaceProviderManager manager); + void onSignificantPlaceCheck(); } diff --git a/core/java/android/hardware/usb/flags/system_sw_usb_flags.aconfig b/core/java/android/hardware/usb/flags/system_sw_usb_flags.aconfig index 967fc42e8783..0f944cfb7f67 100644 --- a/core/java/android/hardware/usb/flags/system_sw_usb_flags.aconfig +++ b/core/java/android/hardware/usb/flags/system_sw_usb_flags.aconfig @@ -28,4 +28,11 @@ flag { namespace: "preload_safety" description: "Enables signal API with staking" bug: "287498482" -}
\ No newline at end of file +} + +flag { + name: "enable_usb_sysfs_midi_identification" + namespace: "system_sw_usb" + description: "Enable identifying midi device using USB sysfs" + bug: "333778731" +} diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java index 278e8631622c..943b04f517f3 100644 --- a/core/java/android/inputmethodservice/InputMethodService.java +++ b/core/java/android/inputmethodservice/InputMethodService.java @@ -401,10 +401,7 @@ public class InputMethodService extends AbstractInputMethodService { private long mStylusHwSessionsTimeout = STYLUS_HANDWRITING_IDLE_TIMEOUT_MS; private Runnable mStylusWindowIdleTimeoutRunnable; private long mStylusWindowIdleTimeoutForTest; - /** - * Tracks last {@link MotionEvent#getToolType(int)} used for {@link MotionEvent#ACTION_DOWN}. - **/ - private int mLastUsedToolType; + /** * Tracks the ctrl+shift shortcut **/ @@ -720,6 +717,7 @@ public class InputMethodService extends AbstractInputMethodService { final ViewTreeObserver.OnComputeInternalInsetsListener mInsetsComputer = info -> { onComputeInsets(mTmpInsets); + mNavigationBarController.updateInsets(mTmpInsets); if (!mViewsCreated) { // The IME views are not ready, keep visible insets untouched. mTmpInsets.visibleTopInsets = 0; @@ -1367,7 +1365,6 @@ public class InputMethodService extends AbstractInputMethodService { private void updateEditorToolTypeInternal(int toolType) { if (Flags.useHandwritingListenerForTooltype()) { - mLastUsedToolType = toolType; if (mInputEditorInfo != null) { mInputEditorInfo.setInitialToolType(toolType); } @@ -3384,9 +3381,6 @@ public class InputMethodService extends AbstractInputMethodService { null /* icProto */); mInputStarted = true; mStartedInputConnection = ic; - if (Flags.useHandwritingListenerForTooltype()) { - editorInfo.setInitialToolType(mLastUsedToolType); - } mInputEditorInfo = editorInfo; initialize(); mInlineSuggestionSessionController.notifyOnStartInput( diff --git a/core/java/android/inputmethodservice/NavigationBarController.java b/core/java/android/inputmethodservice/NavigationBarController.java index 9c55b0ee0623..de67e06ddda7 100644 --- a/core/java/android/inputmethodservice/NavigationBarController.java +++ b/core/java/android/inputmethodservice/NavigationBarController.java @@ -58,6 +58,10 @@ import java.util.Objects; final class NavigationBarController { private interface Callback { + + default void updateInsets(@NonNull InputMethodService.Insets originalInsets) { + } + default void updateTouchableInsets(@NonNull InputMethodService.Insets originalInsets, @NonNull ViewTreeObserver.InternalInsetsInfo dest) { } @@ -96,6 +100,15 @@ final class NavigationBarController { ? new Impl(inputMethodService) : Callback.NOOP; } + /** + * Update the given insets to be at least as big as the IME navigation bar, when visible. + * + * @param originalInsets the insets to check and modify to include the IME navigation bar. + */ + void updateInsets(@NonNull InputMethodService.Insets originalInsets) { + mImpl.updateInsets(originalInsets); + } + void updateTouchableInsets(@NonNull InputMethodService.Insets originalInsets, @NonNull ViewTreeObserver.InternalInsetsInfo dest) { mImpl.updateTouchableInsets(originalInsets, dest); @@ -270,6 +283,24 @@ final class NavigationBarController { } @Override + public void updateInsets(@NonNull InputMethodService.Insets originalInsets) { + if (!mImeDrawsImeNavBar || mNavigationBarFrame == null + || mNavigationBarFrame.getVisibility() != View.VISIBLE + || mService.isFullscreenMode()) { + return; + } + + final int[] loc = new int[2]; + mNavigationBarFrame.getLocationInWindow(loc); + if (originalInsets.contentTopInsets > loc[1]) { + originalInsets.contentTopInsets = loc[1]; + } + if (originalInsets.visibleTopInsets > loc[1]) { + originalInsets.visibleTopInsets = loc[1]; + } + } + + @Override public void updateTouchableInsets(@NonNull InputMethodService.Insets originalInsets, @NonNull ViewTreeObserver.InternalInsetsInfo dest) { if (!mImeDrawsImeNavBar || mNavigationBarFrame == null) { diff --git a/core/java/android/os/BatteryConsumer.java b/core/java/android/os/BatteryConsumer.java index b41753413baf..02f3a256aae3 100644 --- a/core/java/android/os/BatteryConsumer.java +++ b/core/java/android/os/BatteryConsumer.java @@ -197,6 +197,11 @@ public abstract class BatteryConsumer { POWER_COMPONENT_MOBILE_RADIO, POWER_COMPONENT_WIFI, POWER_COMPONENT_BLUETOOTH, + POWER_COMPONENT_AUDIO, + POWER_COMPONENT_VIDEO, + POWER_COMPONENT_FLASHLIGHT, + POWER_COMPONENT_CAMERA, + POWER_COMPONENT_GNSS, }; static final int COLUMN_INDEX_BATTERY_CONSUMER_TYPE = 0; diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java index 3e6223ab9400..065b3d6c7a8a 100644 --- a/core/java/android/os/BatteryStats.java +++ b/core/java/android/os/BatteryStats.java @@ -1994,7 +1994,8 @@ public abstract class BatteryStats { // STATES2 bits that are used for Power Stats tracking public static final int IMPORTANT_FOR_POWER_STATS_STATES2 = - STATE2_VIDEO_ON_FLAG | STATE2_FLASHLIGHT_FLAG | STATE2_CAMERA_FLAG; + STATE2_VIDEO_ON_FLAG | STATE2_FLASHLIGHT_FLAG | STATE2_CAMERA_FLAG + | STATE2_GPS_SIGNAL_QUALITY_MASK; @UnsupportedAppUsage public int states2; diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java index 8aa2c35b49e4..30d2dec8b4c4 100755..100644 --- a/core/java/android/os/Build.java +++ b/core/java/android/os/Build.java @@ -1238,6 +1238,18 @@ public class Build { public static final int VANILLA_ICE_CREAM = 35; } + /** + * The vendor API for 2024 Q2 + * + * <p>For Android 14-QPR3 and later, the vendor API level is completely decoupled from the SDK + * API level and the format has switched to YYYYMM (year and month) + * + * @see <a href="https://preview.source.android.com/docs/core/architecture/api-flags">Vendor API + * level</a> + * @hide + */ + public static final int VENDOR_API_2024_Q2 = 202404; + /** The type of build, like "user" or "eng". */ public static final String TYPE = getString("ro.build.type"); diff --git a/core/java/android/os/FileUtils.java b/core/java/android/os/FileUtils.java index 61b52c67dfe2..e6b1c07846f9 100644 --- a/core/java/android/os/FileUtils.java +++ b/core/java/android/os/FileUtils.java @@ -580,6 +580,8 @@ public final class FileUtils { ", copied:" + progress + ", read:" + (count - countToRead) + ", in pipe: " + countInPipe); + Os.close(pipes[0]); + Os.close(pipes[1]); throw new ErrnoException("splice, pipe --> fdOut", EIO); } else { progress += t; @@ -607,6 +609,8 @@ public final class FileUtils { listener.onProgress(progressSnapshot); }); } + Os.close(pipes[0]); + Os.close(pipes[1]); return progress; } diff --git a/core/java/android/os/StrictMode.java b/core/java/android/os/StrictMode.java index 222c69ceb612..292e6bdba539 100644 --- a/core/java/android/os/StrictMode.java +++ b/core/java/android/os/StrictMode.java @@ -17,6 +17,10 @@ package android.os; import static android.annotation.SystemApi.Client.MODULE_LIBRARIES; +import static com.android.internal.util.FrameworkStatsLog.UNSAFE_INTENT_EVENT_REPORTED__EVENT_TYPE__EXPLICIT_INTENT_FILTER_UNMATCH; +import static com.android.internal.util.FrameworkStatsLog.UNSAFE_INTENT_EVENT_REPORTED__EVENT_TYPE__INTERNAL_NON_EXPORTED_COMPONENT_MATCH; +import static com.android.internal.util.FrameworkStatsLog.UNSAFE_INTENT_EVENT_REPORTED__EVENT_TYPE__NULL_ACTION_MATCH; + import android.animation.ValueAnimator; import android.annotation.IntDef; import android.annotation.NonNull; @@ -2135,27 +2139,26 @@ public final class StrictMode { } } - private static void registerIntentMatchingRestrictionCallback() { - try { - ActivityManager.getService().registerStrictModeCallback( - new UnsafeIntentStrictModeCallback()); - } catch (RemoteException e) { - /* - If exception is DeadObjectException it means system process is dead, so we can ignore - */ - if (!(e instanceof DeadObjectException)) { - Log.e(TAG, "RemoteException handling StrictMode violation", e); - } - } - } - private static final class UnsafeIntentStrictModeCallback extends IUnsafeIntentStrictModeCallback.Stub { @Override - public void onImplicitIntentMatchedInternalComponent(Intent intent) { + public void onUnsafeIntent(int type, Intent intent) { if (StrictMode.vmUnsafeIntentLaunchEnabled()) { - StrictMode.onUnsafeIntentLaunch(intent, - "Launch of unsafe implicit intent: " + intent); + StrictMode.onUnsafeIntentLaunch(type, intent); + } + } + } + + /** Each process should only have one singleton callback */ + private static volatile UnsafeIntentStrictModeCallback sUnsafeIntentCallback; + + private static void registerIntentMatchingRestrictionCallback() { + if (sUnsafeIntentCallback == null) { + sUnsafeIntentCallback = new UnsafeIntentStrictModeCallback(); + try { + ActivityManager.getService().registerStrictModeCallback(sUnsafeIntentCallback); + } catch (RemoteException e) { + // system_server should not throw } } } @@ -2383,9 +2386,22 @@ public final class StrictMode { onVmPolicyViolation(new UnsafeIntentLaunchViolation(intent)); } - /** @hide */ - public static void onUnsafeIntentLaunch(Intent intent, String message) { - onVmPolicyViolation(new UnsafeIntentLaunchViolation(intent, message)); + private static void onUnsafeIntentLaunch(int type, Intent intent) { + String msg; + switch (type) { + case UNSAFE_INTENT_EVENT_REPORTED__EVENT_TYPE__NULL_ACTION_MATCH: + msg = "Launch of intent with null action: "; + break; + case UNSAFE_INTENT_EVENT_REPORTED__EVENT_TYPE__INTERNAL_NON_EXPORTED_COMPONENT_MATCH: + msg = "Implicit intent matching internal non-exported component: "; + break; + case UNSAFE_INTENT_EVENT_REPORTED__EVENT_TYPE__EXPLICIT_INTENT_FILTER_UNMATCH: + msg = "Intent mismatch target component intent filter: "; + break; + default: + return; + } + onVmPolicyViolation(new UnsafeIntentLaunchViolation(intent, msg + intent)); } /** Assume locked until we hear otherwise */ diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java index fdce476388d3..20522fad6652 100644 --- a/core/java/android/os/UserManager.java +++ b/core/java/android/os/UserManager.java @@ -1907,6 +1907,31 @@ public class UserManager { "no_near_field_communication_radio"; /** + * This user restriction specifies if Near-field communication is disallowed to change + * on the device. If Near-field communication is disallowed it cannot be changed via Settings. + * + * <p>This restriction can only be set by a device owner or a profile owner of an + * organization-owned managed profile on the parent profile. + * In both cases, the restriction applies globally on the device and will not allow Near-field + * communication state being changed. + * + * <p> + * Near-field communication (NFC) is a radio technology that allows two devices (like your phone + * and a payments terminal) to communicate with each other when they're close together. + * + * <p>Default is <code>false</code>. + * + * <p>Key for user restrictions. + * <p>Type: Boolean + * @see DevicePolicyManager#addUserRestriction(ComponentName, String) + * @see DevicePolicyManager#clearUserRestriction(ComponentName, String) + * @see #getUserRestrictions() + */ + @FlaggedApi(Flags.FLAG_ENABLE_NFC_USER_RESTRICTION) + public static final String DISALLOW_CHANGE_NEAR_FIELD_COMMUNICATION_RADIO = + "no_change_near_field_communication_radio"; + + /** * This user restriction specifies if Thread network is disallowed on the device. If Thread * network is disallowed it cannot be turned on via Settings. * @@ -2007,6 +2032,7 @@ public class UserManager { DISALLOW_CAMERA, DISALLOW_CAMERA_TOGGLE, DISALLOW_CELLULAR_2G, + DISALLOW_CHANGE_NEAR_FIELD_COMMUNICATION_RADIO, DISALLOW_CHANGE_WIFI_STATE, DISALLOW_CONFIG_BLUETOOTH, DISALLOW_CONFIG_BRIGHTNESS, diff --git a/core/java/android/os/health/SystemHealthManager.java b/core/java/android/os/health/SystemHealthManager.java index 322a8e62dbb3..deabfed365a6 100644 --- a/core/java/android/os/health/SystemHealthManager.java +++ b/core/java/android/os/health/SystemHealthManager.java @@ -33,13 +33,16 @@ import android.os.Process; import android.os.RemoteException; import android.os.ResultReceiver; import android.os.ServiceManager; +import android.os.SynchronousResultReceiver; import com.android.internal.app.IBatteryStats; +import com.android.server.power.optimization.Flags; import java.util.Arrays; import java.util.Comparator; import java.util.List; import java.util.concurrent.Executor; +import java.util.concurrent.TimeoutException; import java.util.function.Consumer; /** @@ -67,6 +70,14 @@ public class SystemHealthManager { private final IPowerStatsService mPowerStats; private List<PowerMonitor> mPowerMonitorsInfo; private final Object mPowerMonitorsLock = new Object(); + private static final long TAKE_UID_SNAPSHOT_TIMEOUT_MILLIS = 10_000; + + private static class PendingUidSnapshots { + public int[] uids; + public SynchronousResultReceiver resultReceiver; + } + + private final PendingUidSnapshots mPendingUidSnapshots = new PendingUidSnapshots(); /** * Construct a new SystemHealthManager object. @@ -111,12 +122,19 @@ public class SystemHealthManager { * @see Process#myUid() Process.myUid() */ public HealthStats takeUidSnapshot(int uid) { - try { - final HealthStatsParceler parceler = mBatteryStats.takeUidSnapshot(uid); - return parceler.getHealthStats(); - } catch (RemoteException ex) { - throw new RuntimeException(ex); + if (!Flags.onewayBatteryStatsService()) { + try { + final HealthStatsParceler parceler = mBatteryStats.takeUidSnapshot(uid); + return parceler.getHealthStats(); + } catch (RemoteException ex) { + throw ex.rethrowFromSystemServer(); + } } + final HealthStats[] result = takeUidSnapshots(new int[]{uid}); + if (result != null && result.length >= 1) { + return result[0]; + } + return null; } /** @@ -144,17 +162,61 @@ public class SystemHealthManager { * other than its own. */ public HealthStats[] takeUidSnapshots(int[] uids) { + if (!Flags.onewayBatteryStatsService()) { + try { + final HealthStatsParceler[] parcelers = mBatteryStats.takeUidSnapshots(uids); + final int count = uids.length; + final HealthStats[] results = new HealthStats[count]; + for (int i = 0; i < count; i++) { + results[i] = parcelers[i].getHealthStats(); + } + return results; + } catch (RemoteException ex) { + throw ex.rethrowFromSystemServer(); + } + } + + SynchronousResultReceiver resultReceiver; + synchronized (mPendingUidSnapshots) { + if (Arrays.equals(mPendingUidSnapshots.uids, uids)) { + resultReceiver = mPendingUidSnapshots.resultReceiver; + } else { + mPendingUidSnapshots.uids = Arrays.copyOf(uids, uids.length); + mPendingUidSnapshots.resultReceiver = resultReceiver = + new SynchronousResultReceiver("takeUidSnapshots"); + try { + mBatteryStats.takeUidSnapshotsAsync(uids, resultReceiver); + } catch (RemoteException ex) { + throw ex.rethrowFromSystemServer(); + } + } + } + + SynchronousResultReceiver.Result result; try { - final HealthStatsParceler[] parcelers = mBatteryStats.takeUidSnapshots(uids); - final HealthStats[] results = new HealthStats[uids.length]; - final int N = uids.length; - for (int i = 0; i < N; i++) { - results[i] = parcelers[i].getHealthStats(); + result = resultReceiver.awaitResult(TAKE_UID_SNAPSHOT_TIMEOUT_MILLIS); + } catch (TimeoutException e) { + throw new RuntimeException(e); + } finally { + synchronized (mPendingUidSnapshots) { + if (mPendingUidSnapshots.resultReceiver == resultReceiver) { + mPendingUidSnapshots.uids = null; + mPendingUidSnapshots.resultReceiver = null; + } + } + } + + final HealthStats[] results = new HealthStats[uids.length]; + if (result.bundle != null) { + HealthStatsParceler[] parcelers = result.bundle.getParcelableArray( + IBatteryStats.KEY_UID_SNAPSHOTS, HealthStatsParceler.class); + if (parcelers != null && parcelers.length == uids.length) { + for (int i = 0; i < parcelers.length; i++) { + results[i] = parcelers[i].getHealthStats(); + } } - return results; - } catch (RemoteException ex) { - throw new RuntimeException(ex); } + return results; } /** diff --git a/core/java/android/permission/flags.aconfig b/core/java/android/permission/flags.aconfig index 34fb963924ae..e029e520f1b1 100644 --- a/core/java/android/permission/flags.aconfig +++ b/core/java/android/permission/flags.aconfig @@ -193,7 +193,7 @@ flag { namespace: "permissions" description: "Enable getDeviceId API in OpEventProxyInfo" bug: "337340961" - } +} flag { name: "device_aware_app_op_new_schema_enabled" @@ -201,4 +201,15 @@ flag { namespace: "permissions" description: "Persist device attributed AppOp accesses on the disk" bug: "308201969" -}
\ No newline at end of file +} + +flag { + name: "apex_signature_permission_allowlist_enabled" + is_fixed_read_only: true + namespace: "permissions" + description: "Enable reading signature permission allowlist from APEXes" + bug: "308573169" + metadata { + purpose: PURPOSE_BUGFIX + } +} diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 793321254a09..63f0b9ebca36 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -1736,6 +1736,24 @@ public final class Settings { "android.settings.NETWORK_OPERATOR_SETTINGS"; /** + * Activity Action: Show settings for selecting the network provider. + * <p> + * In some cases, a matching Activity may not be provided, so ensure you + * safeguard against this. + * <p> + * Access to this preference can be customized via Settings' app. + * <p> + * Input: Nothing. + * <p> + * Output: Nothing. + * + * @hide + */ + @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) + public static final String ACTION_NETWORK_PROVIDER_SETTINGS = + "android.settings.NETWORK_PROVIDER_SETTINGS"; + + /** * Activity Action: Show settings for selection of 2G/3G. * <p> * In some cases, a matching Activity may not exist, so ensure you @@ -6083,6 +6101,15 @@ public final class Settings { public static final String POINTER_SPEED = "pointer_speed"; /** + * Pointer scale setting. + * + * <p>This float value represents the scale by which the size of the pointer increases. + * @hide + */ + @Readable + public static final String POINTER_SCALE = "pointer_scale"; + + /** * Touchpad pointer speed setting. * This is an integer value in a range between -7 and +7, so there are 15 possible values. * -7 = slowest @@ -6129,6 +6156,15 @@ public final class Settings { public static final String TOUCHPAD_RIGHT_CLICK_ZONE = "touchpad_right_click_zone"; /** + * Pointer fill style, specified by + * {@link android.view.PointerIcon.PointerIconVectorStyleFill} constants. + * + * @hide + */ + @Readable + public static final String POINTER_FILL_STYLE = "pointer_fill_style"; + + /** * Whether lock-to-app will be triggered by long-press on recents. * @hide */ @@ -6330,6 +6366,8 @@ public final class Settings { PRIVATE_SETTINGS.add(SIP_ADDRESS_ONLY); PRIVATE_SETTINGS.add(SIP_ASK_ME_EACH_TIME); PRIVATE_SETTINGS.add(POINTER_SPEED); + PRIVATE_SETTINGS.add(POINTER_FILL_STYLE); + PRIVATE_SETTINGS.add(POINTER_SCALE); PRIVATE_SETTINGS.add(LOCK_TO_APP_ENABLED); PRIVATE_SETTINGS.add(EGG_MODE); PRIVATE_SETTINGS.add(SHOW_BATTERY_PERCENT); diff --git a/core/java/android/service/contentcapture/ContentCaptureService.java b/core/java/android/service/contentcapture/ContentCaptureService.java index e1965ef25562..405fe26a20b8 100644 --- a/core/java/android/service/contentcapture/ContentCaptureService.java +++ b/core/java/android/service/contentcapture/ContentCaptureService.java @@ -29,6 +29,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; import android.app.Service; +import android.app.assist.AssistContent; import android.content.ComponentName; import android.content.ContentCaptureOptions; import android.content.Intent; @@ -133,6 +134,16 @@ public abstract class ContentCaptureService extends Service { */ public static final String SERVICE_META_DATA = "android.content_capture"; + + /** + * Extras key to flag that the passed in {@link AssistContent} is sent only during Activity + * start. + * + * @hide + */ + public static final String ASSIST_CONTENT_ACTIVITY_START_KEY = "activity_start_assist_content"; + + private final LocalDataShareAdapterResourceManager mDataShareAdapterResourceManager = new LocalDataShareAdapterResourceManager(); diff --git a/core/java/android/service/dreams/OWNERS b/core/java/android/service/dreams/OWNERS index 77bcee832250..119ca55f8e49 100644 --- a/core/java/android/service/dreams/OWNERS +++ b/core/java/android/service/dreams/OWNERS @@ -1,10 +1,11 @@ -# Bug component: 78010 +# Bug component: 66910 brycelee@google.com -dsandler@google.com -galinap@google.com -jjaggi@google.com lusilva@google.com -michaelwr@google.com -santoscordon@google.com wxyz@google.com +justinkoh@google.com + +rgl@google.com +santoscordon@google.com + +dsandler@google.com diff --git a/core/java/android/service/notification/ZenAdapters.java b/core/java/android/service/notification/ZenAdapters.java index b249815ce8db..a122b7155b18 100644 --- a/core/java/android/service/notification/ZenAdapters.java +++ b/core/java/android/service/notification/ZenAdapters.java @@ -33,7 +33,7 @@ public class ZenAdapters { .allowAlarms(policy.allowAlarms()) .allowCalls( policy.allowCalls() - ? notificationPolicySendersToZenPolicyPeopleType( + ? prioritySendersToPeopleType( policy.allowCallsFrom()) : ZenPolicy.PEOPLE_TYPE_NONE) .allowConversations( @@ -45,7 +45,7 @@ public class ZenAdapters { .allowMedia(policy.allowMedia()) .allowMessages( policy.allowMessages() - ? notificationPolicySendersToZenPolicyPeopleType( + ? prioritySendersToPeopleType( policy.allowMessagesFrom()) : ZenPolicy.PEOPLE_TYPE_NONE) .allowReminders(policy.allowReminders()) @@ -71,7 +71,7 @@ public class ZenAdapters { /** Maps {@link ZenPolicy.PeopleType} enum to {@link Policy.PrioritySenders}. */ @Policy.PrioritySenders - public static int zenPolicyPeopleTypeToNotificationPolicySenders( + public static int peopleTypeToPrioritySenders( @ZenPolicy.PeopleType int zpPeopleType, @Policy.PrioritySenders int defaultResult) { switch (zpPeopleType) { case ZenPolicy.PEOPLE_TYPE_ANYONE: @@ -87,7 +87,7 @@ public class ZenAdapters { /** Maps {@link Policy.PrioritySenders} enum to {@link ZenPolicy.PeopleType}. */ @ZenPolicy.PeopleType - public static int notificationPolicySendersToZenPolicyPeopleType( + public static int prioritySendersToPeopleType( @Policy.PrioritySenders int npPrioritySenders) { switch (npPrioritySenders) { case Policy.PRIORITY_SENDERS_ANY: diff --git a/core/java/android/service/notification/ZenDeviceEffects.aidl b/core/java/android/service/notification/ZenDeviceEffects.aidl new file mode 100644 index 000000000000..e635493d0e06 --- /dev/null +++ b/core/java/android/service/notification/ZenDeviceEffects.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.service.notification; + +parcelable ZenDeviceEffects; + diff --git a/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java index 610a317db6d2..7a0c016fa0c6 100644 --- a/core/java/android/service/notification/ZenModeConfig.java +++ b/core/java/android/service/notification/ZenModeConfig.java @@ -24,9 +24,26 @@ import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_FULL_SCRE import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_LIGHTS; import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_PEEK; import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_SCREEN_OFF; -import static android.service.notification.ZenAdapters.notificationPolicySendersToZenPolicyPeopleType; +import static android.provider.Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS; +import static android.service.notification.ZenAdapters.peopleTypeToPrioritySenders; +import static android.service.notification.ZenAdapters.prioritySendersToPeopleType; import static android.service.notification.ZenAdapters.zenPolicyConversationSendersToNotificationPolicy; -import static android.service.notification.ZenAdapters.zenPolicyPeopleTypeToNotificationPolicySenders; +import static android.service.notification.ZenModeConfig.EventInfo.REPLY_YES_OR_MAYBE; +import static android.service.notification.ZenPolicy.PEOPLE_TYPE_STARRED; +import static android.service.notification.ZenPolicy.PRIORITY_CATEGORY_ALARMS; +import static android.service.notification.ZenPolicy.PRIORITY_CATEGORY_CALLS; +import static android.service.notification.ZenPolicy.PRIORITY_CATEGORY_EVENTS; +import static android.service.notification.ZenPolicy.PRIORITY_CATEGORY_MEDIA; +import static android.service.notification.ZenPolicy.PRIORITY_CATEGORY_MESSAGES; +import static android.service.notification.ZenPolicy.PRIORITY_CATEGORY_REMINDERS; +import static android.service.notification.ZenPolicy.PRIORITY_CATEGORY_REPEAT_CALLERS; +import static android.service.notification.ZenPolicy.PRIORITY_CATEGORY_SYSTEM; +import static android.service.notification.ZenPolicy.STATE_ALLOW; +import static android.service.notification.ZenPolicy.STATE_DISALLOW; +import static android.service.notification.ZenPolicy.VISUAL_EFFECT_AMBIENT; +import static android.service.notification.ZenPolicy.VISUAL_EFFECT_FULL_SCREEN_INTENT; +import static android.service.notification.ZenPolicy.VISUAL_EFFECT_LIGHTS; +import static android.service.notification.ZenPolicy.VISUAL_EFFECT_PEEK; import android.annotation.FlaggedApi; import android.annotation.IntDef; @@ -58,6 +75,7 @@ import android.util.Slog; import android.util.proto.ProtoOutputStream; import com.android.internal.R; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.XmlUtils; import com.android.modules.utils.TypedXmlPullParser; import com.android.modules.utils.TypedXmlSerializer; @@ -320,31 +338,107 @@ public class ZenModeConfig implements Parcelable { @UnsupportedAppUsage public ZenModeConfig() { + if (Flags.modesUi()) { + ensureManualZenRule(); + } } public ZenModeConfig(Parcel source) { - allowCalls = source.readInt() == 1; - allowRepeatCallers = source.readInt() == 1; - allowMessages = source.readInt() == 1; - allowReminders = source.readInt() == 1; - allowEvents = source.readInt() == 1; - allowCallsFrom = source.readInt(); - allowMessagesFrom = source.readInt(); + if (!Flags.modesUi()) { + allowCalls = source.readInt() == 1; + allowRepeatCallers = source.readInt() == 1; + allowMessages = source.readInt() == 1; + allowReminders = source.readInt() == 1; + allowEvents = source.readInt() == 1; + allowCallsFrom = source.readInt(); + allowMessagesFrom = source.readInt(); + } user = source.readInt(); manualRule = source.readParcelable(null, ZenRule.class); readRulesFromParcel(automaticRules, source); if (Flags.modesApi()) { readRulesFromParcel(deletedRules, source); } - allowAlarms = source.readInt() == 1; - allowMedia = source.readInt() == 1; - allowSystem = source.readInt() == 1; - suppressedVisualEffects = source.readInt(); + if (!Flags.modesUi()) { + allowAlarms = source.readInt() == 1; + allowMedia = source.readInt() == 1; + allowSystem = source.readInt() == 1; + suppressedVisualEffects = source.readInt(); + } areChannelsBypassingDnd = source.readInt() == 1; - allowConversations = source.readBoolean(); - allowConversationsFrom = source.readInt(); - if (Flags.modesApi()) { - allowPriorityChannels = source.readBoolean(); + if (!Flags.modesUi()) { + allowConversations = source.readBoolean(); + allowConversationsFrom = source.readInt(); + if (Flags.modesApi()) { + allowPriorityChannels = source.readBoolean(); + } + } + } + + public static ZenPolicy getDefaultZenPolicy() { + ZenPolicy policy = new ZenPolicy.Builder() + .allowAlarms(true) + .allowMedia(true) + .allowSystem(false) + .allowCalls(PEOPLE_TYPE_STARRED) + .allowMessages(PEOPLE_TYPE_STARRED) + .allowReminders(false) + .allowEvents(false) + .allowRepeatCallers(true) + .allowConversations(CONVERSATION_SENDERS_IMPORTANT) + .showAllVisualEffects() + .showVisualEffect(VISUAL_EFFECT_FULL_SCREEN_INTENT, false) + .showVisualEffect(VISUAL_EFFECT_LIGHTS, false) + .showVisualEffect(VISUAL_EFFECT_PEEK, false) + .showVisualEffect(VISUAL_EFFECT_AMBIENT, false) + .allowPriorityChannels(true) + .build(); + return policy; + } + + public static ZenModeConfig getDefaultConfig() { + ZenModeConfig config = new ZenModeConfig(); + + EventInfo eventInfo = new EventInfo(); + eventInfo.reply = REPLY_YES_OR_MAYBE; + ZenRule events = new ZenRule(); + events.id = EVENTS_DEFAULT_RULE_ID; + events.conditionId = toEventConditionId(eventInfo); + events.component = ComponentName.unflattenFromString( + "android/com.android.server.notification.EventConditionProvider"); + events.enabled = false; + events.zenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS; + events.pkg = "android"; + config.automaticRules.put(EVENTS_DEFAULT_RULE_ID, events); + + ScheduleInfo scheduleInfo = new ScheduleInfo(); + scheduleInfo.days = new int[] {1, 2, 3, 4, 5, 6, 7}; + scheduleInfo.startHour = 22; + scheduleInfo.endHour = 7; + scheduleInfo.exitAtAlarm = true; + ZenRule sleeping = new ZenRule(); + sleeping.id = EVERY_NIGHT_DEFAULT_RULE_ID; + sleeping.conditionId = toScheduleConditionId(scheduleInfo); + sleeping.component = ComponentName.unflattenFromString( + "android/com.android.server.notification.ScheduleConditionProvider"); + sleeping.enabled = false; + sleeping.zenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS; + sleeping.pkg = "android"; + config.automaticRules.put(EVERY_NIGHT_DEFAULT_RULE_ID, sleeping); + + return config; + } + + void ensureManualZenRule() { + if (manualRule == null) { + final ZenRule newRule = new ZenRule(); + newRule.type = AutomaticZenRule.TYPE_OTHER; + newRule.enabled = true; + newRule.conditionId = Uri.EMPTY; + newRule.allowManualInvocation = true; + newRule.zenPolicy = getDefaultZenPolicy(); + newRule.pkg = "android"; + manualRule = newRule; } } @@ -363,28 +457,34 @@ public class ZenModeConfig implements Parcelable { @Override public void writeToParcel(Parcel dest, int flags) { - dest.writeInt(allowCalls ? 1 : 0); - dest.writeInt(allowRepeatCallers ? 1 : 0); - dest.writeInt(allowMessages ? 1 : 0); - dest.writeInt(allowReminders ? 1 : 0); - dest.writeInt(allowEvents ? 1 : 0); - dest.writeInt(allowCallsFrom); - dest.writeInt(allowMessagesFrom); + if (!Flags.modesUi()) { + dest.writeInt(allowCalls ? 1 : 0); + dest.writeInt(allowRepeatCallers ? 1 : 0); + dest.writeInt(allowMessages ? 1 : 0); + dest.writeInt(allowReminders ? 1 : 0); + dest.writeInt(allowEvents ? 1 : 0); + dest.writeInt(allowCallsFrom); + dest.writeInt(allowMessagesFrom); + } dest.writeInt(user); dest.writeParcelable(manualRule, 0); writeRulesToParcel(automaticRules, dest); if (Flags.modesApi()) { writeRulesToParcel(deletedRules, dest); } - dest.writeInt(allowAlarms ? 1 : 0); - dest.writeInt(allowMedia ? 1 : 0); - dest.writeInt(allowSystem ? 1 : 0); - dest.writeInt(suppressedVisualEffects); + if (!Flags.modesUi()) { + dest.writeInt(allowAlarms ? 1 : 0); + dest.writeInt(allowMedia ? 1 : 0); + dest.writeInt(allowSystem ? 1 : 0); + dest.writeInt(suppressedVisualEffects); + } dest.writeInt(areChannelsBypassingDnd ? 1 : 0); - dest.writeBoolean(allowConversations); - dest.writeInt(allowConversationsFrom); - if (Flags.modesApi()) { - dest.writeBoolean(allowPriorityChannels); + if (!Flags.modesUi()) { + dest.writeBoolean(allowConversations); + dest.writeInt(allowConversationsFrom); + if (Flags.modesApi()) { + dest.writeBoolean(allowPriorityChannels); + } } } @@ -408,35 +508,251 @@ public class ZenModeConfig implements Parcelable { @Override public String toString() { StringBuilder sb = new StringBuilder(ZenModeConfig.class.getSimpleName()).append('[') - .append("user=").append(user) - .append(",allowAlarms=").append(allowAlarms) - .append(",allowMedia=").append(allowMedia) - .append(",allowSystem=").append(allowSystem) - .append(",allowReminders=").append(allowReminders) - .append(",allowEvents=").append(allowEvents) - .append(",allowCalls=").append(allowCalls) - .append(",allowRepeatCallers=").append(allowRepeatCallers) - .append(",allowMessages=").append(allowMessages) - .append(",allowConversations=").append(allowConversations) - .append(",allowCallsFrom=").append(sourceToString(allowCallsFrom)) - .append(",allowMessagesFrom=").append(sourceToString(allowMessagesFrom)) - .append(",allowConvFrom=").append(ZenPolicy.conversationTypeToString - (allowConversationsFrom)) - .append(",suppressedVisualEffects=").append(suppressedVisualEffects); + .append("user=").append(user); + if (!Flags.modesUi()) { + sb.append(",allowAlarms=").append(allowAlarms) + .append(",allowMedia=").append(allowMedia) + .append(",allowSystem=").append(allowSystem) + .append(",allowReminders=").append(allowReminders) + .append(",allowEvents=").append(allowEvents) + .append(",allowCalls=").append(allowCalls) + .append(",allowRepeatCallers=").append(allowRepeatCallers) + .append(",allowMessages=").append(allowMessages) + .append(",allowConversations=").append(allowConversations) + .append(",allowCallsFrom=").append(sourceToString(allowCallsFrom)) + .append(",allowMessagesFrom=").append(sourceToString(allowMessagesFrom)) + .append(",allowConvFrom=").append(ZenPolicy.conversationTypeToString + (allowConversationsFrom)) + .append("\nsuppressedVisualEffects=").append(suppressedVisualEffects); + } if (Flags.modesApi()) { - sb.append(",hasPriorityChannels=").append(areChannelsBypassingDnd); + sb.append("\nhasPriorityChannels=").append(areChannelsBypassingDnd); sb.append(",allowPriorityChannels=").append(allowPriorityChannels); } else { - sb.append(",areChannelsBypassingDnd=").append(areChannelsBypassingDnd); + sb.append("\nareChannelsBypassingDnd=").append(areChannelsBypassingDnd); } - sb.append(",\nautomaticRules=").append(rulesToString(automaticRules)) - .append(",\nmanualRule=").append(manualRule); + sb.append(",\nautomaticRules=").append(rulesToString(automaticRules)); + sb.append(",\nmanualRule=").append(manualRule); if (Flags.modesApi()) { sb.append(",\ndeletedRules=").append(rulesToString(deletedRules)); } return sb.append(']').toString(); } + public boolean isAllowPriorityChannels() { + if (Flags.modesUi()) { + throw new IllegalStateException("can't be used with modesUI flag"); + } + return allowPriorityChannels; + } + + public void setAllowPriorityChannels(boolean allowPriorityChannels) { + if (Flags.modesUi()) { + throw new IllegalStateException("can't be used with modesUI flag"); + } else { + this.allowPriorityChannels = allowPriorityChannels; + } + } + + public int getSuppressedVisualEffects() { + if (Flags.modesUi()) { + throw new IllegalStateException("can't be used with modesUI flag"); + } else { + return this.suppressedVisualEffects; + } + } + + public void setSuppressedVisualEffects(int suppressedVisualEffects) { + if (Flags.modesUi()) { + throw new IllegalStateException("can't be used with modesUI flag"); + } else { + this.suppressedVisualEffects = suppressedVisualEffects; + } + } + + public @ZenPolicy.ConversationSenders int getAllowConversationsFrom() { + if (Flags.modesUi()) { + return manualRule.zenPolicy.getPriorityConversationSenders(); + } + return allowConversationsFrom; + } + + public void setAllowConversationsFrom( + @ZenPolicy.ConversationSenders int allowConversationsFrom) { + if (Flags.modesUi()) { + throw new IllegalStateException("can't be used with modesUI flag"); + } else { + this.allowConversationsFrom = allowConversationsFrom; + } + } + + public void setAllowConversations(boolean allowConversations) { + if (Flags.modesUi()) { + throw new IllegalStateException("can't be used with modesUI flag"); + } else { + this.allowConversations = allowConversations; + } + } + + public boolean isAllowConversations() { + if (Flags.modesUi()) { + throw new IllegalStateException("can't be used with modesUI flag"); + } + return allowConversations; + } + + public @Policy.PrioritySenders int getAllowMessagesFrom() { + if (Flags.modesUi()) { + throw new IllegalStateException("can't be used with modesUI flag"); + } + return allowMessagesFrom; + } + + public void setAllowMessagesFrom(@Policy.PrioritySenders int allowMessagesFrom) { + if (Flags.modesUi()) { + throw new IllegalStateException("can't be used with modesUI flag"); + } else { + this.allowMessagesFrom = allowMessagesFrom; + } + } + + public void setAllowMessages(boolean allowMessages) { + if (Flags.modesUi()) { + throw new IllegalStateException("can't be used with modesUI flag"); + } + this.allowMessages = allowMessages; + } + + public @Policy.PrioritySenders int getAllowCallsFrom() { + if (Flags.modesUi()) { + return peopleTypeToPrioritySenders( + manualRule.zenPolicy.getPriorityCallSenders(), DEFAULT_CALLS_SOURCE); + } + return allowCallsFrom; + } + + public void setAllowCallsFrom(@Policy.PrioritySenders int allowCallsFrom) { + if (Flags.modesUi()) { + manualRule.zenPolicy = new ZenPolicy.Builder(manualRule.zenPolicy) + .allowCalls(prioritySendersToPeopleType(allowCallsFrom)) + .build(); + } else { + this.allowCallsFrom = allowCallsFrom; + } + } + + public void setAllowCalls(boolean allowCalls) { + if (Flags.modesUi()) { + throw new IllegalStateException("can't be used with modesUI flag"); + } + this.allowCalls = allowCalls; + } + + public boolean isAllowEvents() { + if (Flags.modesUi()) { + return manualRule.zenPolicy.isCategoryAllowed( + ZenPolicy.PRIORITY_CATEGORY_EVENTS, false); + } + return allowEvents; + } + + public void setAllowEvents(boolean allowEvents) { + if (Flags.modesUi()) { + throw new IllegalStateException("can't be used with modesUI flag"); + } else { + this.allowEvents = allowEvents; + } + } + + public boolean isAllowReminders() { + if (Flags.modesUi()) { + throw new IllegalStateException("can't be used with modesUI flag"); + } + return allowReminders; + } + + public void setAllowReminders(boolean allowReminders) { + if (Flags.modesUi()) { + throw new IllegalStateException("can't be used with modesUI flag"); + } else { + this.allowReminders = allowReminders; + } + } + + public boolean isAllowMessages() { + if (Flags.modesUi()) { + throw new IllegalStateException("can't be used with modesUI flag"); + } + return allowMessages; + } + + public boolean isAllowRepeatCallers() { + if (Flags.modesUi()) { + throw new IllegalStateException("can't be used with modesUI flag"); + } + return allowRepeatCallers; + } + + public void setAllowRepeatCallers(boolean allowRepeatCallers) { + if (Flags.modesUi()) { + throw new IllegalStateException("can't be used with modesUI flag"); + } else { + this.allowRepeatCallers = allowRepeatCallers; + } + } + + public boolean isAllowSystem() { + if (Flags.modesUi()) { + throw new IllegalStateException("can't be used with modesUI flag"); + } + return allowSystem; + } + + public void setAllowSystem(boolean allowSystem) { + if (Flags.modesUi()) { + throw new IllegalStateException("can't be used with modesUI flag"); + } else { + this.allowSystem = allowSystem; + } + } + + public boolean isAllowMedia() { + if (Flags.modesUi()) { + throw new IllegalStateException("can't be used with modesUI flag"); + } + return allowMedia; + } + + public void setAllowMedia(boolean allowMedia) { + if (Flags.modesUi()) { + throw new IllegalStateException("can't be used with modesUI flag"); + } else { + this.allowMedia = allowMedia; + } + } + + public boolean isAllowAlarms() { + if (Flags.modesUi()) { + throw new IllegalStateException("can't be used with modesUI flag"); + } + return allowAlarms; + } + + public void setAllowAlarms(boolean allowAlarms) { + if (Flags.modesUi()) { + throw new IllegalStateException("can't be used with modesUI flag"); + } else { + this.allowAlarms = allowAlarms; + } + } + + public boolean isAllowCalls() { + if (Flags.modesUi()) { + throw new IllegalStateException("can't be used with modesUI flag"); + } + return allowCalls; + } + private static String rulesToString(ArrayMap<String, ZenRule> ruleList) { if (ruleList.isEmpty()) { return "{}"; @@ -512,6 +828,8 @@ public class ZenModeConfig implements Parcelable { if (!(o instanceof ZenModeConfig)) return false; if (o == this) return true; final ZenModeConfig other = (ZenModeConfig) o; + // The policy fields that live on config are compared directly because the fields will + // contain data until MODES_UI is rolled out/cleaned up. boolean eq = other.allowAlarms == allowAlarms && other.allowMedia == allowMedia && other.allowSystem == allowSystem @@ -539,6 +857,8 @@ public class ZenModeConfig implements Parcelable { @Override public int hashCode() { + // The policy fields that live on config are compared directly because the fields will + // contain data until MODES_UI is rolled out/cleaned up. if (Flags.modesApi()) { return Objects.hash(allowAlarms, allowMedia, allowSystem, allowCalls, allowRepeatCallers, allowMessages, @@ -619,9 +939,14 @@ public class ZenModeConfig implements Parcelable { rt.version = safeInt(parser, ZEN_ATT_VERSION, getCurrentXmlVersion()); rt.user = safeInt(parser, ZEN_ATT_USER, rt.user); boolean readSuppressedEffects = false; + boolean readManualRule = false; while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) { tag = parser.getName(); if (type == XmlPullParser.END_TAG && ZEN_TAG.equals(tag)) { + if (Flags.modesUi() && !readManualRule) { + // migrate from fields on config into manual rule + rt.manualRule.zenPolicy = rt.toZenPolicy(); + } return rt; } if (type == XmlPullParser.START_TAG) { @@ -693,6 +1018,9 @@ public class ZenModeConfig implements Parcelable { DEFAULT_SUPPRESSED_VISUAL_EFFECTS); } else if (MANUAL_TAG.equals(tag)) { rt.manualRule = readRuleXml(parser); + if (rt.manualRule != null) { + readManualRule = true; + } } else if (AUTOMATIC_TAG.equals(tag) || (Flags.modesApi() && AUTOMATIC_DELETED_TAG.equals(tag))) { final String id = parser.getAttributeValue(null, RULE_ATT_ID); @@ -742,6 +1070,9 @@ public class ZenModeConfig implements Parcelable { ? Integer.toString(xmlVersion) : Integer.toString(version)); out.attributeInt(null, ZEN_ATT_USER, user); out.startTag(null, ALLOW_TAG); + // From MODES_UI these fields are only read if the flag has transitioned from off to on + // However, we will continue to write these fields until the flag is cleaned up so it's + // possible to turn the flag off without losing user data out.attributeBoolean(null, ALLOW_ATT_CALLS, allowCalls); out.attributeBoolean(null, ALLOW_ATT_REPEAT_CALLERS, allowRepeatCallers); out.attributeBoolean(null, ALLOW_ATT_MESSAGES, allowMessages); @@ -816,11 +1147,11 @@ public class ZenModeConfig implements Parcelable { rt.enabler = parser.getAttributeValue(null, RULE_ATT_ENABLER); rt.condition = readConditionXml(parser); - if (!Flags.modesApi() && rt.zenMode != Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS + if (!Flags.modesApi() && rt.zenMode != ZEN_MODE_IMPORTANT_INTERRUPTIONS && Condition.isValidId(rt.conditionId, SYSTEM_AUTHORITY)) { // all default rules and user created rules updated to zenMode important interruptions Slog.i(TAG, "Updating zenMode of automatic rule " + rt.name); - rt.zenMode = Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS; + rt.zenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS; } rt.modified = safeBoolean(parser, RULE_ATT_MODIFIED, false); rt.zenPolicy = readZenPolicyXml(parser); @@ -952,7 +1283,7 @@ public class ZenModeConfig implements Parcelable { if (Flags.modesApi()) { final int channels = safeInt(parser, ALLOW_ATT_CHANNELS, ZenPolicy.STATE_UNSET); if (channels != ZenPolicy.STATE_UNSET) { - builder.allowPriorityChannels(channels == ZenPolicy.STATE_ALLOW); + builder.allowPriorityChannels(channels == STATE_ALLOW); policySet = true; } } @@ -966,7 +1297,7 @@ public class ZenModeConfig implements Parcelable { policySet = true; } if (repeatCallers != ZenPolicy.STATE_UNSET) { - builder.allowRepeatCallers(repeatCallers == ZenPolicy.STATE_ALLOW); + builder.allowRepeatCallers(repeatCallers == STATE_ALLOW); policySet = true; } if (conversations != ZenPolicy.CONVERSATION_SENDERS_UNSET) { @@ -974,23 +1305,23 @@ public class ZenModeConfig implements Parcelable { policySet = true; } if (alarms != ZenPolicy.STATE_UNSET) { - builder.allowAlarms(alarms == ZenPolicy.STATE_ALLOW); + builder.allowAlarms(alarms == STATE_ALLOW); policySet = true; } if (media != ZenPolicy.STATE_UNSET) { - builder.allowMedia(media == ZenPolicy.STATE_ALLOW); + builder.allowMedia(media == STATE_ALLOW); policySet = true; } if (system != ZenPolicy.STATE_UNSET) { - builder.allowSystem(system == ZenPolicy.STATE_ALLOW); + builder.allowSystem(system == STATE_ALLOW); policySet = true; } if (events != ZenPolicy.STATE_UNSET) { - builder.allowEvents(events == ZenPolicy.STATE_ALLOW); + builder.allowEvents(events == STATE_ALLOW); policySet = true; } if (reminders != ZenPolicy.STATE_UNSET) { - builder.allowReminders(reminders == ZenPolicy.STATE_ALLOW); + builder.allowReminders(reminders == STATE_ALLOW); policySet = true; } @@ -1005,31 +1336,31 @@ public class ZenModeConfig implements Parcelable { ZenPolicy.STATE_UNSET); if (fullScreenIntent != ZenPolicy.STATE_UNSET) { - builder.showFullScreenIntent(fullScreenIntent == ZenPolicy.STATE_ALLOW); + builder.showFullScreenIntent(fullScreenIntent == STATE_ALLOW); policySet = true; } if (lights != ZenPolicy.STATE_UNSET) { - builder.showLights(lights == ZenPolicy.STATE_ALLOW); + builder.showLights(lights == STATE_ALLOW); policySet = true; } if (peek != ZenPolicy.STATE_UNSET) { - builder.showPeeking(peek == ZenPolicy.STATE_ALLOW); + builder.showPeeking(peek == STATE_ALLOW); policySet = true; } if (statusBar != ZenPolicy.STATE_UNSET) { - builder.showStatusBarIcons(statusBar == ZenPolicy.STATE_ALLOW); + builder.showStatusBarIcons(statusBar == STATE_ALLOW); policySet = true; } if (badges != ZenPolicy.STATE_UNSET) { - builder.showBadges(badges == ZenPolicy.STATE_ALLOW); + builder.showBadges(badges == STATE_ALLOW); policySet = true; } if (ambient != ZenPolicy.STATE_UNSET) { - builder.showInAmbientDisplay(ambient == ZenPolicy.STATE_ALLOW); + builder.showInAmbientDisplay(ambient == STATE_ALLOW); policySet = true; } if (notificationList != ZenPolicy.STATE_UNSET) { - builder.showInNotificationList(notificationList == ZenPolicy.STATE_ALLOW); + builder.showInNotificationList(notificationList == STATE_ALLOW); policySet = true; } @@ -1266,17 +1597,22 @@ public class ZenModeConfig implements Parcelable { } }; + public ZenPolicy getZenPolicy() { + return Flags.modesUi() ? manualRule.zenPolicy : toZenPolicy(); + } + /** * Converts a ZenModeConfig to a ZenPolicy */ - public ZenPolicy toZenPolicy() { + @VisibleForTesting + ZenPolicy toZenPolicy() { ZenPolicy.Builder builder = new ZenPolicy.Builder() .allowCalls(allowCalls - ? notificationPolicySendersToZenPolicyPeopleType(allowCallsFrom) + ? prioritySendersToPeopleType(allowCallsFrom) : ZenPolicy.PEOPLE_TYPE_NONE) .allowRepeatCallers(allowRepeatCallers) .allowMessages(allowMessages - ? notificationPolicySendersToZenPolicyPeopleType(allowMessagesFrom) + ? prioritySendersToPeopleType(allowMessagesFrom) : ZenPolicy.PEOPLE_TYPE_NONE) .allowReminders(allowReminders) .allowEvents(allowEvents) @@ -1336,7 +1672,7 @@ public class ZenModeConfig implements Parcelable { if (zenPolicy.isCategoryAllowed(ZenPolicy.PRIORITY_CATEGORY_MESSAGES, isPriorityCategoryEnabled(Policy.PRIORITY_CATEGORY_MESSAGES, defaultPolicy))) { priorityCategories |= Policy.PRIORITY_CATEGORY_MESSAGES; - messageSenders = zenPolicyPeopleTypeToNotificationPolicySenders( + messageSenders = peopleTypeToPrioritySenders( zenPolicy.getPriorityMessageSenders(), messageSenders); } @@ -1352,7 +1688,7 @@ public class ZenModeConfig implements Parcelable { if (zenPolicy.isCategoryAllowed(ZenPolicy.PRIORITY_CATEGORY_CALLS, isPriorityCategoryEnabled(Policy.PRIORITY_CATEGORY_CALLS, defaultPolicy))) { priorityCategories |= Policy.PRIORITY_CATEGORY_CALLS; - callSenders = zenPolicyPeopleTypeToNotificationPolicySenders( + callSenders = peopleTypeToPrioritySenders( zenPolicy.getPriorityCallSenders(), callSenders); } @@ -1452,46 +1788,155 @@ public class ZenModeConfig implements Parcelable { return (policy.suppressedVisualEffects & visualEffect) == 0; } + private boolean isVisualEffectAllowed(int suppressedVisualEffects, int visualEffect) { + return (suppressedVisualEffects & visualEffect) == 0; + } + public Policy toNotificationPolicy() { int priorityCategories = 0; int priorityCallSenders = Policy.PRIORITY_SENDERS_CONTACTS; int priorityMessageSenders = Policy.PRIORITY_SENDERS_CONTACTS; int priorityConversationSenders = Policy.CONVERSATION_SENDERS_IMPORTANT; - if (allowConversations) { - priorityCategories |= Policy.PRIORITY_CATEGORY_CONVERSATIONS; - } - if (allowCalls) { - priorityCategories |= Policy.PRIORITY_CATEGORY_CALLS; - } - if (allowMessages) { - priorityCategories |= Policy.PRIORITY_CATEGORY_MESSAGES; - } - if (allowEvents) { - priorityCategories |= Policy.PRIORITY_CATEGORY_EVENTS; - } - if (allowReminders) { - priorityCategories |= Policy.PRIORITY_CATEGORY_REMINDERS; - } - if (allowRepeatCallers) { - priorityCategories |= Policy.PRIORITY_CATEGORY_REPEAT_CALLERS; - } - if (allowAlarms) { - priorityCategories |= Policy.PRIORITY_CATEGORY_ALARMS; - } - if (allowMedia) { - priorityCategories |= Policy.PRIORITY_CATEGORY_MEDIA; - } - if (allowSystem) { - priorityCategories |= Policy.PRIORITY_CATEGORY_SYSTEM; - } - priorityCallSenders = sourceToPrioritySenders(allowCallsFrom, priorityCallSenders); - priorityMessageSenders = sourceToPrioritySenders(allowMessagesFrom, priorityMessageSenders); - priorityConversationSenders = zenPolicyConversationSendersToNotificationPolicy( - allowConversationsFrom, priorityConversationSenders); + int state = 0; + int suppressedVisualEffects = 0; - int state = areChannelsBypassingDnd ? Policy.STATE_CHANNELS_BYPASSING_DND : 0; - if (Flags.modesApi()) { - state = Policy.policyState(areChannelsBypassingDnd, allowPriorityChannels); + if (Flags.modesUi()) { + if (manualRule.zenPolicy.isCategoryAllowed(ZenPolicy.PRIORITY_CATEGORY_EVENTS, false)) { + priorityCategories |= Policy.PRIORITY_CATEGORY_EVENTS; + } + if (manualRule.zenPolicy.isCategoryAllowed( + ZenPolicy.PRIORITY_CATEGORY_REMINDERS, false)) { + priorityCategories |= Policy.PRIORITY_CATEGORY_REMINDERS; + } + if (manualRule.zenPolicy.isCategoryAllowed( + ZenPolicy.PRIORITY_CATEGORY_REPEAT_CALLERS, false)) { + priorityCategories |= Policy.PRIORITY_CATEGORY_REPEAT_CALLERS; + } + if (manualRule.zenPolicy.isCategoryAllowed(ZenPolicy.PRIORITY_CATEGORY_ALARMS, false)) { + priorityCategories |= Policy.PRIORITY_CATEGORY_ALARMS; + } + if (manualRule.zenPolicy.isCategoryAllowed(ZenPolicy.PRIORITY_CATEGORY_MEDIA, false)) { + priorityCategories |= Policy.PRIORITY_CATEGORY_MEDIA; + } + if (manualRule.zenPolicy.isCategoryAllowed(ZenPolicy.PRIORITY_CATEGORY_SYSTEM, false)) { + priorityCategories |= Policy.PRIORITY_CATEGORY_SYSTEM; + } + + if (manualRule.zenPolicy.getPriorityCategoryConversations() == STATE_ALLOW) { + priorityCategories |= Policy.PRIORITY_CATEGORY_CONVERSATIONS; + } + priorityConversationSenders = zenPolicyConversationSendersToNotificationPolicy( + manualRule.zenPolicy.getPriorityConversationSenders(), + CONVERSATION_SENDERS_NONE); + if (manualRule.zenPolicy.getPriorityCategoryCalls() == STATE_ALLOW) { + priorityCategories |= Policy.PRIORITY_CATEGORY_CALLS; + } + priorityCallSenders = peopleTypeToPrioritySenders( + manualRule.zenPolicy.getPriorityCallSenders(), DEFAULT_CALLS_SOURCE); + if (manualRule.zenPolicy.getPriorityCategoryMessages() == STATE_ALLOW) { + priorityCategories |= Policy.PRIORITY_CATEGORY_MESSAGES; + } + priorityMessageSenders = peopleTypeToPrioritySenders( + manualRule.zenPolicy.getPriorityMessageSenders(), DEFAULT_SOURCE); + + state = Policy.policyState(areChannelsBypassingDnd, + manualRule.zenPolicy.getPriorityChannelsAllowed() != STATE_DISALLOW); + + boolean suppressFullScreenIntent = !manualRule.zenPolicy.isVisualEffectAllowed( + ZenPolicy.VISUAL_EFFECT_FULL_SCREEN_INTENT, + isVisualEffectAllowed(DEFAULT_SUPPRESSED_VISUAL_EFFECTS, + ZenPolicy.VISUAL_EFFECT_FULL_SCREEN_INTENT)); + + boolean suppressLights = !manualRule.zenPolicy.isVisualEffectAllowed( + ZenPolicy.VISUAL_EFFECT_LIGHTS, + isVisualEffectAllowed(DEFAULT_SUPPRESSED_VISUAL_EFFECTS, + ZenPolicy.VISUAL_EFFECT_LIGHTS)); + + boolean suppressAmbient = !manualRule.zenPolicy.isVisualEffectAllowed( + ZenPolicy.VISUAL_EFFECT_AMBIENT, + isVisualEffectAllowed(DEFAULT_SUPPRESSED_VISUAL_EFFECTS, + ZenPolicy.VISUAL_EFFECT_AMBIENT)); + + if (suppressFullScreenIntent && suppressLights && suppressAmbient) { + suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_SCREEN_OFF; + } + + if (suppressFullScreenIntent) { + suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_FULL_SCREEN_INTENT; + } + + if (suppressLights) { + suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_LIGHTS; + } + + if (!manualRule.zenPolicy.isVisualEffectAllowed(ZenPolicy.VISUAL_EFFECT_PEEK, + isVisualEffectAllowed(DEFAULT_SUPPRESSED_VISUAL_EFFECTS, + ZenPolicy.VISUAL_EFFECT_PEEK))) { + suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_PEEK; + suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_SCREEN_ON; + } + + if (!manualRule.zenPolicy.isVisualEffectAllowed(ZenPolicy.VISUAL_EFFECT_STATUS_BAR, + isVisualEffectAllowed(DEFAULT_SUPPRESSED_VISUAL_EFFECTS, + ZenPolicy.VISUAL_EFFECT_STATUS_BAR))) { + suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_STATUS_BAR; + } + + if (!manualRule.zenPolicy.isVisualEffectAllowed(ZenPolicy.VISUAL_EFFECT_BADGE, + isVisualEffectAllowed(DEFAULT_SUPPRESSED_VISUAL_EFFECTS, + ZenPolicy.VISUAL_EFFECT_BADGE))) { + suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_BADGE; + } + + if (suppressAmbient) { + suppressedVisualEffects |= SUPPRESSED_EFFECT_AMBIENT; + } + + if (!manualRule.zenPolicy.isVisualEffectAllowed( + ZenPolicy.VISUAL_EFFECT_NOTIFICATION_LIST, + isVisualEffectAllowed(DEFAULT_SUPPRESSED_VISUAL_EFFECTS, + ZenPolicy.VISUAL_EFFECT_NOTIFICATION_LIST))) { + suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_NOTIFICATION_LIST; + } + } else { + if (isAllowConversations()) { + priorityCategories |= Policy.PRIORITY_CATEGORY_CONVERSATIONS; + } + if (isAllowCalls()) { + priorityCategories |= Policy.PRIORITY_CATEGORY_CALLS; + } + if (isAllowMessages()) { + priorityCategories |= Policy.PRIORITY_CATEGORY_MESSAGES; + } + if (isAllowEvents()) { + priorityCategories |= Policy.PRIORITY_CATEGORY_EVENTS; + } + if (isAllowReminders()) { + priorityCategories |= Policy.PRIORITY_CATEGORY_REMINDERS; + } + if (isAllowRepeatCallers()) { + priorityCategories |= Policy.PRIORITY_CATEGORY_REPEAT_CALLERS; + } + if (isAllowAlarms()) { + priorityCategories |= Policy.PRIORITY_CATEGORY_ALARMS; + } + if (isAllowMedia()) { + priorityCategories |= Policy.PRIORITY_CATEGORY_MEDIA; + } + if (isAllowSystem()) { + priorityCategories |= Policy.PRIORITY_CATEGORY_SYSTEM; + } + priorityCallSenders = sourceToPrioritySenders(getAllowCallsFrom(), priorityCallSenders); + priorityMessageSenders = sourceToPrioritySenders( + getAllowMessagesFrom(), priorityMessageSenders); + priorityConversationSenders = zenPolicyConversationSendersToNotificationPolicy( + getAllowConversationsFrom(), priorityConversationSenders); + + state = areChannelsBypassingDnd ? Policy.STATE_CHANNELS_BYPASSING_DND : 0; + if (Flags.modesApi()) { + state = Policy.policyState(areChannelsBypassingDnd, allowPriorityChannels); + } + suppressedVisualEffects = getSuppressedVisualEffects(); } return new Policy(priorityCategories, priorityCallSenders, priorityMessageSenders, @@ -1544,31 +1989,38 @@ public class ZenModeConfig implements Parcelable { public void applyNotificationPolicy(Policy policy) { if (policy == null) return; - allowAlarms = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_ALARMS) != 0; - allowMedia = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_MEDIA) != 0; - allowSystem = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_SYSTEM) != 0; - allowEvents = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_EVENTS) != 0; - allowReminders = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_REMINDERS) != 0; - allowCalls = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_CALLS) != 0; - allowMessages = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_MESSAGES) != 0; - allowRepeatCallers = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_REPEAT_CALLERS) - != 0; - allowCallsFrom = normalizePrioritySenders(policy.priorityCallSenders, allowCallsFrom); - allowMessagesFrom = normalizePrioritySenders(policy.priorityMessageSenders, - allowMessagesFrom); - if (policy.suppressedVisualEffects != Policy.SUPPRESSED_EFFECTS_UNSET) { - suppressedVisualEffects = policy.suppressedVisualEffects; - } - allowConversations = (policy.priorityCategories - & Policy.PRIORITY_CATEGORY_CONVERSATIONS) != 0; - allowConversationsFrom = normalizeConversationSenders(allowConversations, - policy.priorityConversationSenders, - allowConversationsFrom); + if (Flags.modesUi()) { + manualRule.zenPolicy = ZenAdapters.notificationPolicyToZenPolicy(policy); + } else { + setAllowAlarms((policy.priorityCategories & Policy.PRIORITY_CATEGORY_ALARMS) != 0); + allowMedia = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_MEDIA) != 0; + allowSystem = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_SYSTEM) != 0; + allowEvents = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_EVENTS) != 0; + allowReminders = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_REMINDERS) != 0; + allowCalls = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_CALLS) != 0; + allowMessages = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_MESSAGES) != 0; + allowRepeatCallers = + (policy.priorityCategories & Policy.PRIORITY_CATEGORY_REPEAT_CALLERS) + != 0; + allowCallsFrom = normalizePrioritySenders(policy.priorityCallSenders, allowCallsFrom); + allowMessagesFrom = normalizePrioritySenders(policy.priorityMessageSenders, + allowMessagesFrom); + if (policy.suppressedVisualEffects != Policy.SUPPRESSED_EFFECTS_UNSET) { + suppressedVisualEffects = policy.suppressedVisualEffects; + } + allowConversations = (policy.priorityCategories + & Policy.PRIORITY_CATEGORY_CONVERSATIONS) != 0; + allowConversationsFrom = normalizeConversationSenders(allowConversations, + policy.priorityConversationSenders, + allowConversationsFrom); + if (policy.state != Policy.STATE_UNSET) { + if (Flags.modesApi()) { + setAllowPriorityChannels(policy.allowPriorityChannels()); + } + } + } if (policy.state != Policy.STATE_UNSET) { areChannelsBypassingDnd = (policy.state & Policy.STATE_CHANNELS_BYPASSING_DND) != 0; - if (Flags.modesApi()) { - allowPriorityChannels = policy.allowPriorityChannels(); - } } } @@ -1995,49 +2447,11 @@ public class ZenModeConfig implements Parcelable { return ""; } - public static String getConditionSummary(Context context, ZenModeConfig config, - int userHandle, boolean shortVersion) { - return getConditionLine(context, config, userHandle, false /*useLine1*/, shortVersion); - } - - private static String getConditionLine(Context context, ZenModeConfig config, - int userHandle, boolean useLine1, boolean shortVersion) { - if (config == null) return ""; - String summary = ""; - if (config.manualRule != null) { - final Uri id = config.manualRule.conditionId; - if (config.manualRule.enabler != null) { - summary = getOwnerCaption(context, config.manualRule.enabler); - } else { - if (id == null) { - summary = context.getString(com.android.internal.R.string.zen_mode_forever); - } else { - final long time = tryParseCountdownConditionId(id); - Condition c = config.manualRule.condition; - if (time > 0) { - final long now = System.currentTimeMillis(); - final long span = time - now; - c = toTimeCondition(context, time, Math.round(span / (float) MINUTES_MS), - userHandle, shortVersion); - } - final String rt = c == null ? "" : useLine1 ? c.line1 : c.summary; - summary = TextUtils.isEmpty(rt) ? "" : rt; - } - } + public boolean isManualActive() { + if (!Flags.modesUi()) { + return manualRule != null; } - for (ZenRule automaticRule : config.automaticRules.values()) { - if (automaticRule.isAutomaticActive()) { - if (summary.isEmpty()) { - summary = automaticRule.name; - } else { - summary = context.getResources() - .getString(R.string.zen_mode_rule_name_combination, summary, - automaticRule.name); - } - - } - } - return summary; + return manualRule != null && manualRule.isAutomaticActive(); } public static class ZenRule implements Parcelable { @@ -2401,7 +2815,7 @@ public class ZenModeConfig implements Parcelable { public static boolean isZenOverridingRinger(int zen, Policy consolidatedPolicy) { return zen == Global.ZEN_MODE_NO_INTERRUPTIONS || zen == Global.ZEN_MODE_ALARMS - || (zen == Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS + || (zen == ZEN_MODE_IMPORTANT_INTERRUPTIONS && ZenModeConfig.areAllPriorityOnlyRingerSoundsMuted(consolidatedPolicy)); } @@ -2410,21 +2824,40 @@ public class ZenModeConfig implements Parcelable { * This includes notification, ringer and system sounds */ public static boolean areAllPriorityOnlyRingerSoundsMuted(ZenModeConfig config) { - boolean areChannelsBypassingDnd = config.areChannelsBypassingDnd; - if (Flags.modesApi()) { - areChannelsBypassingDnd = config.areChannelsBypassingDnd - && config.allowPriorityChannels; + if (Flags.modesUi()) { + final ZenPolicy policy = config.manualRule.zenPolicy; + return !policy.isCategoryAllowed(PRIORITY_CATEGORY_REMINDERS, false) + && !policy.isCategoryAllowed(PRIORITY_CATEGORY_CALLS, false) + && !policy.isCategoryAllowed(PRIORITY_CATEGORY_MESSAGES, false) + && !policy.isCategoryAllowed(PRIORITY_CATEGORY_EVENTS, false) + && !policy.isCategoryAllowed(PRIORITY_CATEGORY_REPEAT_CALLERS, false) + && !policy.isCategoryAllowed(PRIORITY_CATEGORY_SYSTEM, false) + && !(config.areChannelsBypassingDnd && policy.getPriorityChannelsAllowed() + == STATE_ALLOW); + + } else { + boolean areChannelsBypassingDnd = config.areChannelsBypassingDnd; + if (Flags.modesApi()) { + areChannelsBypassingDnd = config.areChannelsBypassingDnd + && config.isAllowPriorityChannels(); + } + return !config.isAllowReminders() && !config.isAllowCalls() && !config.isAllowMessages() + && !config.isAllowEvents() && !config.isAllowRepeatCallers() + && !areChannelsBypassingDnd && !config.isAllowSystem(); } - return !config.allowReminders && !config.allowCalls && !config.allowMessages - && !config.allowEvents && !config.allowRepeatCallers - && !areChannelsBypassingDnd && !config.allowSystem; } /** * Determines whether dnd mutes all sounds */ public static boolean areAllZenBehaviorSoundsMuted(ZenModeConfig config) { - return !config.allowAlarms && !config.allowMedia + if (Flags.modesUi()) { + final ZenPolicy policy = config.manualRule.zenPolicy; + return !policy.isCategoryAllowed(PRIORITY_CATEGORY_ALARMS, false) + && !policy.isCategoryAllowed(PRIORITY_CATEGORY_MEDIA, false) + && areAllPriorityOnlyRingerSoundsMuted(config); + } + return !config.isAllowAlarms() && !config.isAllowMedia() && areAllPriorityOnlyRingerSoundsMuted(config); } diff --git a/core/java/android/service/ondeviceintelligence/IOnDeviceSandboxedInferenceService.aidl b/core/java/android/service/ondeviceintelligence/IOnDeviceSandboxedInferenceService.aidl index 2aa17c4681bd..1af3b0f374f1 100644 --- a/core/java/android/service/ondeviceintelligence/IOnDeviceSandboxedInferenceService.aidl +++ b/core/java/android/service/ondeviceintelligence/IOnDeviceSandboxedInferenceService.aidl @@ -21,6 +21,7 @@ import android.app.ondeviceintelligence.IResponseCallback; import android.app.ondeviceintelligence.ITokenInfoCallback; import android.app.ondeviceintelligence.IProcessingSignal; import android.app.ondeviceintelligence.Feature; +import android.os.IRemoteCallback; import android.os.ICancellationSignal; import android.os.PersistableBundle; import android.os.Bundle; @@ -34,18 +35,19 @@ import android.service.ondeviceintelligence.IProcessingUpdateStatusCallback; * @hide */ oneway interface IOnDeviceSandboxedInferenceService { - void registerRemoteStorageService(in IRemoteStorageService storageService); + void registerRemoteStorageService(in IRemoteStorageService storageService, + in IRemoteCallback remoteCallback) = 0; void requestTokenInfo(int callerUid, in Feature feature, in Bundle request, in AndroidFuture cancellationSignal, - in ITokenInfoCallback tokenInfoCallback); + in ITokenInfoCallback tokenInfoCallback) = 1; void processRequest(int callerUid, in Feature feature, in Bundle request, in int requestType, in AndroidFuture cancellationSignal, in AndroidFuture processingSignal, - in IResponseCallback callback); + in IResponseCallback callback) = 2; void processRequestStreaming(int callerUid, in Feature feature, in Bundle request, in int requestType, in AndroidFuture cancellationSignal, in AndroidFuture processingSignal, - in IStreamingResponseCallback callback); + in IStreamingResponseCallback callback) = 3; void updateProcessingState(in Bundle processingState, - in IProcessingUpdateStatusCallback callback); + in IProcessingUpdateStatusCallback callback) = 4; }
\ No newline at end of file diff --git a/core/java/android/service/ondeviceintelligence/OnDeviceSandboxedInferenceService.java b/core/java/android/service/ondeviceintelligence/OnDeviceSandboxedInferenceService.java index d00485cb1ca5..a77e07662d23 100644 --- a/core/java/android/service/ondeviceintelligence/OnDeviceSandboxedInferenceService.java +++ b/core/java/android/service/ondeviceintelligence/OnDeviceSandboxedInferenceService.java @@ -51,6 +51,7 @@ import android.os.Handler; import android.os.HandlerExecutor; import android.os.IBinder; import android.os.ICancellationSignal; +import android.os.IRemoteCallback; import android.os.Looper; import android.os.OutcomeReceiver; import android.os.ParcelFileDescriptor; @@ -148,9 +149,12 @@ public abstract class OnDeviceSandboxedInferenceService extends Service { if (SERVICE_INTERFACE.equals(intent.getAction())) { return new IOnDeviceSandboxedInferenceService.Stub() { @Override - public void registerRemoteStorageService(IRemoteStorageService storageService) { + public void registerRemoteStorageService(IRemoteStorageService storageService, + IRemoteCallback remoteCallback) throws RemoteException { Objects.requireNonNull(storageService); mRemoteStorageService = storageService; + remoteCallback.sendResult( + Bundle.EMPTY); //to notify caller uid to system-server. } @Override diff --git a/core/java/android/service/wearable/WearableSensingService.java b/core/java/android/service/wearable/WearableSensingService.java index ac22e7067622..3735c4383031 100644 --- a/core/java/android/service/wearable/WearableSensingService.java +++ b/core/java/android/service/wearable/WearableSensingService.java @@ -398,8 +398,8 @@ public abstract class WearableSensingService extends Service { /** * Called when a data request observer is registered. Each request must not be larger than * {@link WearableSensingDataRequest#getMaxRequestSize()}. In addition, at most {@link - * WearableSensingDataRequester#getRateLimit()} requests can be sent every rolling {@link - * WearableSensingDataRequester#getRateLimitWindowSize()}. Requests that are too large or too + * WearableSensingDataRequest#getRateLimit()} requests can be sent every rolling {@link + * WearableSensingDataRequest#getRateLimitWindowSize()}. Requests that are too large or too * frequent will be dropped by the system. See {@link * WearableSensingDataRequester#requestData(WearableSensingDataRequest, Consumer)} for details * about the status code returned for each request. @@ -442,7 +442,7 @@ public abstract class WearableSensingService extends Service { * @param packageName The package name of the app that will receive the requests sent to the * dataRequester. * @param dataRequester A handle to the observer to be unregistered. It is the exact same - * instance provided in a previous {@link #onDataRequestConsumerRegistered(int, String, + * instance provided in a previous {@link #onDataRequestObserverRegistered(int, String, * WearableSensingDataRequester, Consumer)} invocation. * @param statusConsumer the consumer for the status of the data request observer * unregistration. This is different from the status for each data request. @@ -469,7 +469,7 @@ public abstract class WearableSensingService extends Service { * in which case it should return the corresponding status code. * * <p>The implementation should also store the {@code statusConsumer}. If the wearable stops - * listening for hotword for any reason other than {@link #onStopListeningForHotword(Consumer)} + * listening for hotword for any reason other than {@link #onStopHotwordRecognition(Consumer)} * being invoked, it should send an appropriate status code listed in {@link * WearableSensingManager} to {@code statusConsumer}. If the error condition cannot be described * by any of those status codes, it should send a {@link WearableSensingManager#STATUS_UNKNOWN}. @@ -514,11 +514,11 @@ public abstract class WearableSensingService extends Service { /** * Called when hotword audio data sent to the {@code hotwordAudioConsumer} in {@link - * #onStartListeningForHotword(Consumer, Consumer)} is accepted by the + * #onStartHotwordRecognition(Consumer, Consumer)} is accepted by the * {@link android.service.voice.HotwordDetectionService} as valid hotword. * * <p>After the implementation of this class sends the hotword audio data to the {@code - * hotwordAudioConsumer} in {@link #onStartListeningForHotword(Consumer, + * hotwordAudioConsumer} in {@link #onStartHotwordRecognition(Consumer, * Consumer)}, the system will forward the data into {@link * android.service.voice.HotwordDetectionService} (which runs in an isolated process) for * second-stage hotword detection. If accepted as valid hotword there, this method will be @@ -545,7 +545,7 @@ public abstract class WearableSensingService extends Service { * * <p>This method is expected to be overridden by a derived class. The implementation should * stop sending hotword audio data to the {@code hotwordAudioConsumer} in {@link - * #onStartListeningForHotword(Consumer, Consumer)} + * #onStartHotwordRecognition(Consumer, Consumer)} */ @FlaggedApi(Flags.FLAG_ENABLE_HOTWORD_WEARABLE_SENSING_API) @BinderThread diff --git a/core/java/android/telephony/DropBoxManagerLoggerBackend.java b/core/java/android/telephony/DropBoxManagerLoggerBackend.java new file mode 100644 index 000000000000..25a3b9f13d3b --- /dev/null +++ b/core/java/android/telephony/DropBoxManagerLoggerBackend.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.telephony; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.content.Context; +import android.os.DropBoxManager; +import android.os.Handler; +import android.os.HandlerThread; +import android.util.Log; + +import com.android.internal.R; +import com.android.internal.annotations.GuardedBy; + +import java.time.Instant; +import java.time.ZoneId; +import java.time.format.DateTimeFormatter; +import java.util.Optional; + +/** + * A persistent logger backend that stores logs in Android DropBoxManager + * + * @hide + */ +public class DropBoxManagerLoggerBackend implements PersistentLoggerBackend { + + private static final String TAG = "DropBoxManagerLoggerBackend"; + // Separate tag reference to be explicitly used for dropboxmanager instead of logcat logging + private static final String DROPBOX_TAG = "DropBoxManagerLoggerBackend"; + private static final DateTimeFormatter LOG_TIMESTAMP_FORMATTER = + DateTimeFormatter.ofPattern("MM-dd HH:mm:ss.SSS"); + private static final ZoneId LOCAL_ZONE_ID = ZoneId.systemDefault(); + private static final int BUFFER_SIZE_BYTES = 500 * 1024; // 500 KB + private static final int MIN_BUFFER_BYTES_FOR_FLUSH = 5 * 1024; // 5 KB + + private static DropBoxManagerLoggerBackend sInstance; + + private final DropBoxManager mDropBoxManager; + private final Object mBufferLock = new Object(); + @GuardedBy("mBufferLock") + private final StringBuilder mLogBuffer = new StringBuilder(); + private long mBufferStartTime = -1L; + private final HandlerThread mHandlerThread = new HandlerThread(DROPBOX_TAG); + private final Handler mHandler; + // Flag for determining if logging is enabled as a general feature + private final boolean mDropBoxManagerLoggingEnabled; + // Flag for controlling if logging is enabled at runtime + private boolean mIsLoggingEnabled = false; + + /** + * Returns a singleton instance of {@code DropBoxManagerLoggerBackend} that will log to + * DropBoxManager if the config_dropboxmanager_persistent_logging_enabled resource config is + * enabled. + * @param context Android context + */ + @Nullable + public static synchronized DropBoxManagerLoggerBackend getInstance(@NonNull Context context) { + if (sInstance == null) { + sInstance = new DropBoxManagerLoggerBackend(context); + } + return sInstance; + } + + private DropBoxManagerLoggerBackend(@NonNull Context context) { + mDropBoxManager = context.getSystemService(DropBoxManager.class); + mHandlerThread.start(); + mHandler = new Handler(mHandlerThread.getLooper()); + mDropBoxManagerLoggingEnabled = persistentLoggingEnabled(context); + } + + private boolean persistentLoggingEnabled(@NonNull Context context) { + try { + return context.getResources().getBoolean( + R.bool.config_dropboxmanager_persistent_logging_enabled); + } catch (RuntimeException e) { + Log.w(TAG, "Persistent logging config not found"); + return false; + } + } + + /** + * Enable or disable logging to DropBoxManager + * @param isLoggingEnabled Whether logging should be enabled + */ + public void setLoggingEnabled(boolean isLoggingEnabled) { + Log.i(DROPBOX_TAG, "toggle logging: " + isLoggingEnabled); + mIsLoggingEnabled = isLoggingEnabled; + } + + /** + * Persist a DEBUG log message. + * @param tag Used to identify the source of a log message. + * @param msg The message you would like logged. + */ + public void debug(@NonNull String tag, @NonNull String msg) { + if (!mDropBoxManagerLoggingEnabled) { + return; + } + bufferLog("D", tag, msg, Optional.empty()); + } + + /** + * Persist a INFO log message. + * @param tag Used to identify the source of a log message. + * @param msg The message you would like logged. + */ + public void info(@NonNull String tag, @NonNull String msg) { + if (!mDropBoxManagerLoggingEnabled) { + return; + } + bufferLog("I", tag, msg, Optional.empty()); + } + + /** + * Persist a WARN log message. + * @param tag Used to identify the source of a log message. + * @param msg The message you would like logged. + */ + public void warn(@NonNull String tag, @NonNull String msg) { + if (!mDropBoxManagerLoggingEnabled) { + return; + } + bufferLog("W", tag, msg, Optional.empty()); + } + + /** + * Persist a WARN log message. + * @param tag Used to identify the source of a log message. + * @param msg The message you would like logged. + * @param t An exception to log. + */ + public void warn(@NonNull String tag, @NonNull String msg, @NonNull Throwable t) { + if (!mDropBoxManagerLoggingEnabled) { + return; + } + bufferLog("W", tag, msg, Optional.of(t)); + } + + /** + * Persist a ERROR log message. + * @param tag Used to identify the source of a log message. + * @param msg The message you would like logged. + */ + public void error(@NonNull String tag, @NonNull String msg) { + if (!mDropBoxManagerLoggingEnabled) { + return; + } + bufferLog("E", tag, msg, Optional.empty()); + } + + /** + * Persist a ERROR log message. + * @param tag Used to identify the source of a log message. + * @param msg The message you would like logged. + * @param t An exception to log. + */ + public void error(@NonNull String tag, @NonNull String msg, @NonNull Throwable t) { + if (!mDropBoxManagerLoggingEnabled) { + return; + } + bufferLog("E", tag, msg, Optional.of(t)); + } + + private synchronized void bufferLog( + @NonNull String level, + @NonNull String tag, + @NonNull String msg, + Optional<Throwable> t) { + if (!mIsLoggingEnabled) { + return; + } + + if (mBufferStartTime == -1L) { + mBufferStartTime = System.currentTimeMillis(); + } + + synchronized (mBufferLock) { + mLogBuffer + .append(formatLog(level, tag, msg, t)) + .append("\n"); + + if (mLogBuffer.length() >= BUFFER_SIZE_BYTES) { + flushAsync(); + } + } + } + + private String formatLog( + @NonNull String level, + @NonNull String tag, + @NonNull String msg, + Optional<Throwable> t) { + // Expected format = "$Timestamp $Level $Tag: $Message" + return formatTimestamp(System.currentTimeMillis()) + " " + level + " " + tag + ": " + + t.map(throwable -> msg + ": " + Log.getStackTraceString(throwable)).orElse(msg); + } + + private String formatTimestamp(long currentTimeMillis) { + return Instant.ofEpochMilli(currentTimeMillis) + .atZone(LOCAL_ZONE_ID) + .format(LOG_TIMESTAMP_FORMATTER); + } + + /** + * Flushes all buffered logs into DropBoxManager as a single log record with a tag of + * {@link #DROPBOX_TAG} asynchronously. Should be invoked sparingly as DropBoxManager has + * device-level limitations on the number files that can be stored. + */ + public void flushAsync() { + if (!mDropBoxManagerLoggingEnabled) { + return; + } + + mHandler.post(this::flush); + }; + + /** + * Flushes all buffered logs into DropBoxManager as a single log record with a tag of + * {@link #DROPBOX_TAG}. Should be invoked sparingly as DropBoxManager has device-level + * limitations on the number files that can be stored. + */ + public void flush() { + if (!mDropBoxManagerLoggingEnabled) { + return; + } + + synchronized (mBufferLock) { + if (mLogBuffer.length() < MIN_BUFFER_BYTES_FOR_FLUSH) { + return; + } + + Log.d(DROPBOX_TAG, "Flushing logs from " + + formatTimestamp(mBufferStartTime) + " to " + + formatTimestamp(System.currentTimeMillis())); + + try { + mDropBoxManager.addText(DROPBOX_TAG, mLogBuffer.toString()); + } catch (Exception e) { + Log.w(DROPBOX_TAG, "Failed to flush logs of length " + + mLogBuffer.length() + " to DropBoxManager", e); + } + mLogBuffer.setLength(0); + } + mBufferStartTime = -1L; + } +} diff --git a/core/java/android/telephony/PersistentLogger.java b/core/java/android/telephony/PersistentLogger.java new file mode 100644 index 000000000000..8b12a1cb997e --- /dev/null +++ b/core/java/android/telephony/PersistentLogger.java @@ -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 android.telephony; + +import android.annotation.NonNull; + +/** + * A persistent logging client. Intended for persisting critical debug logs in situations where + * standard Android logcat logs may not be retained long enough. + * + * @hide + */ +public class PersistentLogger { + private final PersistentLoggerBackend mPersistentLoggerBackend; + + public PersistentLogger(@NonNull PersistentLoggerBackend persistentLoggerBackend) { + mPersistentLoggerBackend = persistentLoggerBackend; + } + + /** + * Persist a DEBUG log message. + * @param tag Used to identify the source of a log message. + * @param msg The message you would like logged. + */ + public void debug(@NonNull String tag, @NonNull String msg) { + mPersistentLoggerBackend.debug(tag, msg); + } + + /** + * Persist a INFO log message. + * @param tag Used to identify the source of a log message. + * @param msg The message you would like logged. + */ + public void info(@NonNull String tag, @NonNull String msg) { + mPersistentLoggerBackend.info(tag, msg); + } + + /** + * Persist a WARN log message. + * @param tag Used to identify the source of a log message. + * @param msg The message you would like logged. + */ + public void warn(@NonNull String tag, @NonNull String msg) { + mPersistentLoggerBackend.warn(tag, msg); + } + + /** + * Persist a WARN log message. + * @param tag Used to identify the source of a log message. + * @param msg The message you would like logged. + * @param t An exception to log. + */ + public void warn(@NonNull String tag, @NonNull String msg, @NonNull Throwable t) { + mPersistentLoggerBackend.warn(tag, msg, t); + } + + /** + * Persist a ERROR log message. + * @param tag Used to identify the source of a log message. + * @param msg The message you would like logged. + */ + public void error(@NonNull String tag, @NonNull String msg) { + mPersistentLoggerBackend.error(tag, msg); + } + + /** + * Persist a ERROR log message. + * @param tag Used to identify the source of a log message. + * @param msg The message you would like logged. + * @param t An exception to log. + */ + public void error(@NonNull String tag, @NonNull String msg, @NonNull Throwable t) { + mPersistentLoggerBackend.error(tag, msg, t); + } +} diff --git a/core/java/android/telephony/PersistentLoggerBackend.java b/core/java/android/telephony/PersistentLoggerBackend.java new file mode 100644 index 000000000000..e3e72e19e418 --- /dev/null +++ b/core/java/android/telephony/PersistentLoggerBackend.java @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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; + +import android.annotation.NonNull; + +/** + * Interface for logging backends to provide persistent log storage. + * + * @hide + */ +public interface PersistentLoggerBackend { + + /** + * Persist a DEBUG log message. + * @param tag Used to identify the source of a log message. + * @param msg The message you would like logged. + */ + void debug(@NonNull String tag, @NonNull String msg); + + /** + * Persist a INFO log message. + * @param tag Used to identify the source of a log message. + * @param msg The message you would like logged. + */ + void info(@NonNull String tag, @NonNull String msg); + + /** + * Persist a WARN log message. + * @param tag Used to identify the source of a log message. + * @param msg The message you would like logged. + */ + void warn(@NonNull String tag, @NonNull String msg); + + /** + * Persist a WARN log message. + * @param tag Used to identify the source of a log message. + * @param msg The message you would like logged. + * @param t An exception to log. + */ + void warn(@NonNull String tag, @NonNull String msg, @NonNull Throwable t); + + /** + * Persist a ERROR log message. + * @param tag Used to identify the source of a log message. + * @param msg The message you would like logged. + */ + void error(@NonNull String tag, @NonNull String msg); + + /** + * Persist a ERROR log message. + * @param tag Used to identify the source of a log message. + * @param msg The message you would like logged. + * @param t An exception to log. + */ + void error(@NonNull String tag, @NonNull String msg, @NonNull Throwable t); +} diff --git a/core/java/android/telephony/SubscriptionPlan.aidl b/core/java/android/telephony/SubscriptionPlan.aidl index 655df3a71b3d..655df3a71b3d 100755..100644 --- a/core/java/android/telephony/SubscriptionPlan.aidl +++ b/core/java/android/telephony/SubscriptionPlan.aidl diff --git a/core/java/android/text/flags/flags.aconfig b/core/java/android/text/flags/flags.aconfig index 70dc300e59d0..7023ef7afd2f 100644 --- a/core/java/android/text/flags/flags.aconfig +++ b/core/java/android/text/flags/flags.aconfig @@ -191,3 +191,23 @@ flag { purpose: PURPOSE_BUGFIX } } + +flag { + name: "disable_handwriting_initiator_for_ime" + namespace: "text" + description: "Don't initiate handwriting for IME views." + bug: "343304685" + metadata { + purpose: PURPOSE_BUGFIX + } +} + +flag { + name: "fix_null_typeface_bolding" + namespace: "text" + description: "Use a bold typeface when bolding is enabled and the original typeface is null" + bug: "314811487" + metadata { + purpose: PURPOSE_BUGFIX + } +} diff --git a/core/java/android/text/format/DateFormat.java b/core/java/android/text/format/DateFormat.java index e6dad27d595b..e6dad27d595b 100755..100644 --- a/core/java/android/text/format/DateFormat.java +++ b/core/java/android/text/format/DateFormat.java diff --git a/core/java/android/transition/OWNERS b/core/java/android/transition/OWNERS index eb5a58115a8f..2a8d9404dbbd 100644 --- a/core/java/android/transition/OWNERS +++ b/core/java/android/transition/OWNERS @@ -2,5 +2,4 @@ romainguy@google.com mount@google.com -alanv@google.com adamp@google.com diff --git a/core/java/android/util/DisplayMetrics.java b/core/java/android/util/DisplayMetrics.java index f14485b09424..f14485b09424 100755..100644 --- a/core/java/android/util/DisplayMetrics.java +++ b/core/java/android/util/DisplayMetrics.java diff --git a/core/java/android/view/HandwritingInitiator.java b/core/java/android/view/HandwritingInitiator.java index 57d1b8d18fe1..ccec89bb6262 100644 --- a/core/java/android/view/HandwritingInitiator.java +++ b/core/java/android/view/HandwritingInitiator.java @@ -496,9 +496,10 @@ public class HandwritingInitiator { if (delegatorPackageName == null) { delegatorPackageName = view.getContext().getOpPackageName(); } + WeakReference<View> viewRef = new WeakReference<>(view); Consumer<Boolean> consumer = delegationAccepted -> { if (delegationAccepted) { - onDelegationAccepted(view); + onDelegationAccepted(viewRef.get()); } }; mImm.acceptStylusHandwritingDelegation(view, delegatorPackageName, view::post, consumer); @@ -509,6 +510,10 @@ public class HandwritingInitiator { mState.mHandled = true; mState.mShouldInitHandwriting = false; } + if (view == null) { + // can be null if view was detached and was GCed. + return; + } if (view instanceof TextView) { ((TextView) view).hideHint(); } diff --git a/core/java/android/view/HdrRenderState.java b/core/java/android/view/HdrRenderState.java index eadc507116de..c6b39375a0ce 100644 --- a/core/java/android/view/HdrRenderState.java +++ b/core/java/android/view/HdrRenderState.java @@ -65,6 +65,7 @@ class HdrRenderState implements Consumer<Display> { void startListening() { if (isHdrEnabled() && !mIsListenerRegistered && mViewRoot.mDisplay != null) { mViewRoot.mDisplay.registerHdrSdrRatioChangedListener(mViewRoot.mExecutor, this); + mIsListenerRegistered = true; } } diff --git a/core/java/android/view/IDisplayWindowInsetsController.aidl b/core/java/android/view/IDisplayWindowInsetsController.aidl index 91270d4160f5..45dbe43bbdd5 100644 --- a/core/java/android/view/IDisplayWindowInsetsController.aidl +++ b/core/java/android/view/IDisplayWindowInsetsController.aidl @@ -55,4 +55,10 @@ oneway interface IDisplayWindowInsetsController { * @see IWindow#hideInsets */ void hideInsets(int types, boolean fromIme, in @nullable ImeTracker.Token statsToken); + + /** + * Reports the requested IME visibility of the IME input target to + * the IDisplayWindowInsetsController + */ + void setImeInputTargetRequestedVisibility(boolean visible); } diff --git a/core/java/android/view/ImeInsetsSourceConsumer.java b/core/java/android/view/ImeInsetsSourceConsumer.java index 62dbc395f070..1d950dc44e46 100644 --- a/core/java/android/view/ImeInsetsSourceConsumer.java +++ b/core/java/android/view/ImeInsetsSourceConsumer.java @@ -27,6 +27,7 @@ import android.os.IBinder; import android.os.Trace; import android.util.proto.ProtoOutputStream; import android.view.SurfaceControl.Transaction; +import android.view.inputmethod.Flags; import android.view.inputmethod.ImeTracker; import android.view.inputmethod.InputMethodManager; @@ -61,51 +62,59 @@ public final class ImeInsetsSourceConsumer extends InsetsSourceConsumer { @Override public boolean onAnimationStateChanged(boolean running) { - if (!running) { - ImeTracing.getInstance().triggerClientDump( - "ImeInsetsSourceConsumer#onAnimationFinished", - mController.getHost().getInputMethodManager(), null /* icProto */); - } - boolean insetsChanged = super.onAnimationStateChanged(running); - if (running && !isShowRequested() && mController.isPredictiveBackImeHideAnimInProgress()) { - // IME predictive back animation switched from pre-commit to post-commit. - insetsChanged |= applyLocalVisibilityOverride(); - } - - if (!isShowRequested()) { - mIsRequestedVisibleAwaitingLeash = false; - if (!running && !mHasPendingRequest) { - final var statsToken = ImeTracker.forLogging().onStart(ImeTracker.TYPE_HIDE, - ImeTracker.ORIGIN_CLIENT, - SoftInputShowHideReason.HIDE_SOFT_INPUT_ON_ANIMATION_STATE_CHANGED, - mController.getHost().isHandlingPointerEvent() /* fromUser */); - notifyHidden(statsToken); - removeSurface(); + if (Flags.refactorInsetsController()) { + return super.onAnimationStateChanged(running); + } else { + if (!running) { + ImeTracing.getInstance().triggerClientDump( + "ImeInsetsSourceConsumer#onAnimationFinished", + mController.getHost().getInputMethodManager(), null /* icProto */); + } + boolean insetsChanged = super.onAnimationStateChanged(running); + if (running && !isShowRequested() + && mController.isPredictiveBackImeHideAnimInProgress()) { + // IME predictive back animation switched from pre-commit to post-commit. + insetsChanged |= applyLocalVisibilityOverride(); + } + if (!isShowRequested()) { + mIsRequestedVisibleAwaitingLeash = false; + if (!running && !mHasPendingRequest) { + final var statsToken = ImeTracker.forLogging().onStart(ImeTracker.TYPE_HIDE, + ImeTracker.ORIGIN_CLIENT, + SoftInputShowHideReason.HIDE_SOFT_INPUT_ON_ANIMATION_STATE_CHANGED, + mController.getHost().isHandlingPointerEvent() /* fromUser */); + notifyHidden(statsToken); + removeSurface(); + } } + // This method is called + // (1) after the animation starts. + // (2) after the animation ends (including the case of cancel). + // (3) if the IME is not controllable (running == false in this case). + // We should reset mHasPendingRequest in all cases. + mHasPendingRequest = false; + return insetsChanged; } - // This method is called - // (1) after the animation starts. - // (2) after the animation ends (including the case of cancel). - // (3) if the IME is not controllable (running == false in this case). - // We should reset mHasPendingRequest in all cases. - mHasPendingRequest = false; - return insetsChanged; } @Override public void onWindowFocusGained(boolean hasViewFocus) { super.onWindowFocusGained(hasViewFocus); - getImm().registerImeConsumer(this); - if ((mController.getRequestedVisibleTypes() & getType()) != 0 && !hasLeash()) { - mIsRequestedVisibleAwaitingLeash = true; + if (!Flags.refactorInsetsController()) { + getImm().registerImeConsumer(this); + if ((mController.getRequestedVisibleTypes() & getType()) != 0 && !hasLeash()) { + mIsRequestedVisibleAwaitingLeash = true; + } } } @Override public void onWindowFocusLost() { super.onWindowFocusLost(); - getImm().unregisterImeConsumer(this); - mIsRequestedVisibleAwaitingLeash = false; + if (!Flags.refactorInsetsController()) { + getImm().unregisterImeConsumer(this); + mIsRequestedVisibleAwaitingLeash = false; + } } @Override @@ -123,50 +132,57 @@ public final class ImeInsetsSourceConsumer extends InsetsSourceConsumer { @Override @ShowResult public int requestShow(boolean fromIme, @Nullable ImeTracker.Token statsToken) { - if (fromIme) { - ImeTracing.getInstance().triggerClientDump( - "ImeInsetsSourceConsumer#requestShow", - mController.getHost().getInputMethodManager(), null /* icProto */); - } - onShowRequested(); + if (!Flags.refactorInsetsController()) { + if (fromIme) { + ImeTracing.getInstance().triggerClientDump( + "ImeInsetsSourceConsumer#requestShow", + mController.getHost().getInputMethodManager(), null /* icProto */); + } + onShowRequested(); - // TODO: ResultReceiver for IME. - // TODO: Set mShowOnNextImeRender to automatically show IME and guard it with a flag. - ImeTracker.forLogging().onProgress(statsToken, - ImeTracker.PHASE_CLIENT_INSETS_CONSUMER_REQUEST_SHOW); + // TODO: ResultReceiver for IME. + // TODO: Set mShowOnNextImeRender to automatically show IME and guard it with a flag. + ImeTracker.forLogging().onProgress(statsToken, + ImeTracker.PHASE_CLIENT_INSETS_CONSUMER_REQUEST_SHOW); - if (!hasLeash()) { - // If control or leash is null, schedule to show IME when both available. - mIsRequestedVisibleAwaitingLeash = true; - } - // If we had a request before to show from IME (tracked with mImeRequestedShow), reaching - // this code here means that we now got control, so we can start the animation immediately. - // If client window is trying to control IME and IME is already visible, it is immediate. - if (fromIme - || (mState.isSourceOrDefaultVisible(getId(), getType()) && hasLeash())) { - return ShowResult.SHOW_IMMEDIATELY; - } + if (!hasLeash()) { + // If control or leash is null, schedule to show IME when both available. + mIsRequestedVisibleAwaitingLeash = true; + } + // If we had a request before to show from IME (tracked with mImeRequestedShow), + // reaching this code here means that we now got control, so we can start the + // animation immediately. If client window is trying to control IME and IME is + // already visible, it is immediate. + if (fromIme || (mState.isSourceOrDefaultVisible(getId(), getType()) + && hasLeash())) { + return ShowResult.SHOW_IMMEDIATELY; + } - return getImm().requestImeShow(mController.getHost().getWindowToken(), statsToken) - ? ShowResult.IME_SHOW_DELAYED : ShowResult.IME_SHOW_FAILED; + return getImm().requestImeShow(mController.getHost().getWindowToken(), statsToken) + ? ShowResult.IME_SHOW_DELAYED : ShowResult.IME_SHOW_FAILED; + } else { + return ShowResult.IME_SHOW_FAILED; + } } void requestHide(boolean fromIme, @Nullable ImeTracker.Token statsToken) { - if (!fromIme) { - // Create a new token to track the hide request when we have control and leash, - // as we use the passed in token for the insets animation already. - final var notifyStatsToken = hasLeash() - ? ImeTracker.forLogging().onStart(ImeTracker.TYPE_HIDE, - ImeTracker.ORIGIN_CLIENT, - SoftInputShowHideReason.HIDE_SOFT_INPUT_REQUEST_HIDE_WITH_CONTROL, - mController.getHost().isHandlingPointerEvent() /* fromUser */) - : statsToken; - // The insets might be controlled by a remote target. Let the server know we are - // requested to hide. - notifyHidden(notifyStatsToken); - } - if (mAnimationState == ANIMATION_STATE_SHOW) { - mHasPendingRequest = true; + if (!Flags.refactorInsetsController()) { + if (!fromIme) { + // Create a new token to track the hide request when we have control and leash, + // as we use the passed in token for the insets animation already. + final var notifyStatsToken = hasLeash() + ? ImeTracker.forLogging().onStart(ImeTracker.TYPE_HIDE, + ImeTracker.ORIGIN_CLIENT, + SoftInputShowHideReason.HIDE_SOFT_INPUT_REQUEST_HIDE_WITH_CONTROL, + mController.getHost().isHandlingPointerEvent() /* fromUser */) + : statsToken; + // The insets might be controlled by a remote target. Let the server know we are + // requested to hide. + notifyHidden(notifyStatsToken); + } + if (mAnimationState == ANIMATION_STATE_SHOW) { + mHasPendingRequest = true; + } } } @@ -177,12 +193,14 @@ public final class ImeInsetsSourceConsumer extends InsetsSourceConsumer { * @param statsToken the token tracking the current IME request or {@code null} otherwise. */ private void notifyHidden(@NonNull ImeTracker.Token statsToken) { - ImeTracker.forLogging().onProgress(statsToken, - ImeTracker.PHASE_CLIENT_INSETS_CONSUMER_NOTIFY_HIDDEN); + if (!Flags.refactorInsetsController()) { + ImeTracker.forLogging().onProgress(statsToken, + ImeTracker.PHASE_CLIENT_INSETS_CONSUMER_NOTIFY_HIDDEN); - getImm().notifyImeHidden(mController.getHost().getWindowToken(), statsToken); - mIsRequestedVisibleAwaitingLeash = false; - Trace.asyncTraceEnd(TRACE_TAG_VIEW, "IC.hideRequestFromApi", 0); + getImm().notifyImeHidden(mController.getHost().getWindowToken(), statsToken); + mIsRequestedVisibleAwaitingLeash = false; + Trace.asyncTraceEnd(TRACE_TAG_VIEW, "IC.hideRequestFromApi", 0); + } } @Override @@ -196,20 +214,24 @@ public final class ImeInsetsSourceConsumer extends InsetsSourceConsumer { @Override public boolean setControl(@Nullable InsetsSourceControl control, int[] showTypes, int[] hideTypes) { - ImeTracing.getInstance().triggerClientDump("ImeInsetsSourceConsumer#setControl", - mController.getHost().getInputMethodManager(), null /* icProto */); - if (!super.setControl(control, showTypes, hideTypes)) { - return false; - } - if (control == null && !mIsRequestedVisibleAwaitingLeash) { - mController.setRequestedVisibleTypes(0 /* visibleTypes */, getType()); - removeSurface(); - } - final boolean hasLeash = control != null && control.getLeash() != null; - if (hasLeash) { - mIsRequestedVisibleAwaitingLeash = false; + if (Flags.refactorInsetsController()) { + return super.setControl(control, showTypes, hideTypes); + } else { + ImeTracing.getInstance().triggerClientDump("ImeInsetsSourceConsumer#setControl", + mController.getHost().getInputMethodManager(), null /* icProto */); + if (!super.setControl(control, showTypes, hideTypes)) { + return false; + } + if (control == null && !mIsRequestedVisibleAwaitingLeash) { + mController.setRequestedVisibleTypes(0 /* visibleTypes */, getType()); + removeSurface(); + } + final boolean hasLeash = control != null && control.getLeash() != null; + if (hasLeash) { + mIsRequestedVisibleAwaitingLeash = false; + } + return true; } - return true; } @Override @@ -228,9 +250,11 @@ public final class ImeInsetsSourceConsumer extends InsetsSourceConsumer { @Override public void onPerceptible(boolean perceptible) { super.onPerceptible(perceptible); - final IBinder window = mController.getHost().getWindowToken(); - if (window != null) { - getImm().reportPerceptible(window, perceptible); + if (!Flags.refactorInsetsController()) { + final IBinder window = mController.getHost().getWindowToken(); + if (window != null) { + getImm().reportPerceptible(window, perceptible); + } } } diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java index eec805b74b07..8d884f2d4b11 100644 --- a/core/java/android/view/InsetsController.java +++ b/core/java/android/view/InsetsController.java @@ -62,6 +62,7 @@ import android.view.WindowManager.LayoutParams.SoftInputModeFlags; import android.view.animation.Interpolator; import android.view.animation.LinearInterpolator; import android.view.animation.PathInterpolator; +import android.view.inputmethod.Flags; import android.view.inputmethod.ImeTracker; import android.view.inputmethod.ImeTracker.InputMethodJankContext; import android.view.inputmethod.InputMethodManager; @@ -675,6 +676,12 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation /** Set of inset types that we have controls of */ private @InsetsType int mControllableTypes; + /** + * Set of inset types that are about to be cancelled. + * Used in {@link InsetsSourceConsumer#onAnimationStateChanged} + */ + private @InsetsType int mCancelledForNewAnimationTypes; + private final Runnable mInvokeControllableInsetsChangedListeners = this::invokeControllableInsetsChangedListeners; @@ -706,11 +713,13 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation new InsetsState.OnTraverseCallbacks() { private @InsetsType int mTypes; + private InsetsState mFromState; private InsetsState mToState; @Override public void onStart(InsetsState state1, InsetsState state2) { mTypes = 0; + mFromState = null; mToState = null; } @@ -727,9 +736,13 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation return; } mTypes |= source1.getType(); + if (mFromState == null) { + mFromState = new InsetsState(); + } if (mToState == null) { mToState = new InsetsState(); } + mFromState.addSource(new InsetsSource(source1)); mToState.addSource(new InsetsSource(source2)); } @@ -740,7 +753,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation } cancelExistingControllers(mTypes); final InsetsAnimationControlRunner runner = new InsetsResizeAnimationRunner( - mFrame, state1, mToState, RESIZE_INTERPOLATOR, + mFrame, mFromState, mToState, RESIZE_INTERPOLATOR, ANIMATION_DURATION_RESIZE, mTypes, InsetsController.this); if (mRunningAnimations.isEmpty()) { mHost.notifyAnimationRunningStateChanged(true); @@ -1002,19 +1015,38 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation showTypes[0] &= ~animatingTypes; hideTypes[0] &= ~animatingTypes; - if (showTypes[0] != 0) { - final var statsToken = (showTypes[0] & ime()) == 0 ? null - : ImeTracker.forLogging().onStart(ImeTracker.TYPE_SHOW, - ImeTracker.ORIGIN_CLIENT, SoftInputShowHideReason.CONTROLS_CHANGED, - mHost.isHandlingPointerEvent() /* fromUser */); - applyAnimation(showTypes[0], true /* show */, false /* fromIme */, statsToken); - } - if (hideTypes[0] != 0) { - final var statsToken = (hideTypes[0] & ime()) == 0 ? null - : ImeTracker.forLogging().onStart(ImeTracker.TYPE_HIDE, - ImeTracker.ORIGIN_CLIENT, SoftInputShowHideReason.CONTROLS_CHANGED, - mHost.isHandlingPointerEvent() /* fromUser */); - applyAnimation(hideTypes[0], false /* show */, false /* fromIme */, statsToken); + if (Flags.refactorInsetsController()) { + if (mPendingImeControlRequest != null && getImeSourceConsumer().getControl() != null + && getImeSourceConsumer().getControl().getLeash() != null) { + // TODO we need to pass the statsToken + handlePendingControlRequest(null); + } else { + if (showTypes[0] != 0) { + applyAnimation(showTypes[0], true /* show */, false /* fromIme */, + null /* statsToken */); + } + if (hideTypes[0] != 0) { + applyAnimation(hideTypes[0], false /* show */, false /* fromIme */, + null /* statsToken */); + } + } + } else { + if (showTypes[0] != 0) { + final var statsToken = + (showTypes[0] & ime()) == 0 ? null : ImeTracker.forLogging().onStart( + ImeTracker.TYPE_SHOW, ImeTracker.ORIGIN_CLIENT, + SoftInputShowHideReason.CONTROLS_CHANGED, + mHost.isHandlingPointerEvent() /* fromUser */); + applyAnimation(showTypes[0], true /* show */, false /* fromIme */, statsToken); + } + if (hideTypes[0] != 0) { + final var statsToken = + (hideTypes[0] & ime()) == 0 ? null : ImeTracker.forLogging().onStart( + ImeTracker.TYPE_HIDE, ImeTracker.ORIGIN_CLIENT, + SoftInputShowHideReason.CONTROLS_CHANGED, + mHost.isHandlingPointerEvent() /* fromUser */); + applyAnimation(hideTypes[0], false /* show */, false /* fromIme */, statsToken); + } } if (mControllableTypes != controllableTypes) { @@ -1024,6 +1056,11 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation mControllableTypes = controllableTypes; } + if (Flags.refactorInsetsController()) { + // The local visibility override takes into account whether we have control. + applyLocalVisibilityOverride(); + } + // InsetsSourceConsumer#setControl might change the requested visibility. reportRequestedVisibleTypes(); } @@ -1069,6 +1106,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation mHost.isHandlingPointerEvent() /* fromUser */); } } + if (fromIme) { ImeTracing.getInstance().triggerClientDump("InsetsController#show", mHost.getInputMethodManager(), null /* icProto */); @@ -1078,21 +1116,23 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation Trace.asyncTraceBegin(TRACE_TAG_VIEW, "IC.showRequestFromApi", 0); Trace.asyncTraceBegin(TRACE_TAG_VIEW, "IC.showRequestFromApiToImeReady", 0); } - // Handle pending request ready in case there was one set. - if (fromIme && mPendingImeControlRequest != null) { - if ((types & Type.ime()) != 0) { - ImeTracker.forLatency().onShown(statsToken, ActivityThread::currentApplication); + if (!Flags.refactorInsetsController()) { + // Handle pending request ready in case there was one set. + if (fromIme && mPendingImeControlRequest != null) { + if ((types & Type.ime()) != 0) { + ImeTracker.forLatency().onShown(statsToken, ActivityThread::currentApplication); + } + handlePendingControlRequest(statsToken); + return; } - handlePendingControlRequest(statsToken); - return; } // TODO: Support a ResultReceiver for IME. // TODO(b/123718661): Make show() work for multi-session IME. - int typesReady = 0; + @InsetsType int typesReady = 0; final boolean imeVisible = mState.isSourceOrDefaultVisible( mImeSourceConsumer.getId(), ime()); - for (int type = FIRST; type <= LAST; type = type << 1) { + for (@InsetsType int type = FIRST; type <= LAST; type = type << 1) { if ((types & type) == 0) { continue; } @@ -1114,14 +1154,16 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation } continue; } - if (fromIme && animationType == ANIMATION_TYPE_USER - && !mIsPredictiveBackImeHideAnimInProgress) { - // App is already controlling the IME, don't cancel it. - if (isIme) { - ImeTracker.forLogging().onFailed( - statsToken, ImeTracker.PHASE_CLIENT_APPLY_ANIMATION); + if (!Flags.refactorInsetsController()) { + if (fromIme && animationType == ANIMATION_TYPE_USER + && !mIsPredictiveBackImeHideAnimInProgress) { + // App is already controlling the IME, don't cancel it. + if (isIme) { + ImeTracker.forLogging().onFailed( + statsToken, ImeTracker.PHASE_CLIENT_APPLY_ANIMATION); + } + continue; } - continue; } if (isIme) { ImeTracker.forLogging().onProgress( @@ -1130,7 +1172,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation typesReady |= type; } if (DEBUG) Log.d(TAG, "show typesReady: " + typesReady); - if (fromIme && (typesReady & Type.ime()) != 0) { + if ((Flags.refactorInsetsController() || fromIme) && (typesReady & Type.ime()) != 0) { ImeTracker.forLatency().onShown(statsToken, ActivityThread::currentApplication); } applyAnimation(typesReady, true /* show */, fromIme, statsToken); @@ -1184,16 +1226,26 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation } else { Trace.asyncTraceBegin(TRACE_TAG_VIEW, "IC.hideRequestFromApi", 0); } - int typesReady = 0; + @InsetsType int typesReady = 0; boolean hasImeRequestedHidden = false; final boolean hadPendingImeControlRequest = mPendingImeControlRequest != null; - for (int type = FIRST; type <= LAST; type = type << 1) { + for (@InsetsType int type = FIRST; type <= LAST; type = type << 1) { if ((types & type) == 0) { continue; } + final boolean isImeAnimation = type == ime(); + if (Flags.refactorInsetsController()) { + if (isImeAnimation) { + // When the IME is requested to be hidden, but already hidden, we don't show + // an animation again (mRequestedVisibleTypes are reported at the end of the IME + // hide animation but set at the beginning) + if ((mRequestedVisibleTypes & ime()) == 0) { + continue; + } + } + } @AnimationType final int animationType = getAnimationType(type); final boolean requestedVisible = (type & mRequestedVisibleTypes) != 0; - final boolean isImeAnimation = type == ime(); if (mPendingImeControlRequest != null && !requestedVisible) { // Remove the hide insets type from the pending show request. mPendingImeControlRequest.types &= ~type; @@ -1201,13 +1253,15 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation abortPendingImeControlRequest(); } } - if (isImeAnimation && !requestedVisible && animationType == ANIMATION_TYPE_NONE) { - hasImeRequestedHidden = true; - // Ensure to request hide IME in case there is any pending requested visible - // being applied from setControl when receiving the insets control. - if (hadPendingImeControlRequest - || getImeSourceConsumer().isRequestedVisibleAwaitingControl()) { - getImeSourceConsumer().requestHide(fromIme, statsToken); + if (!Flags.refactorInsetsController()) { + if (isImeAnimation && !requestedVisible && animationType == ANIMATION_TYPE_NONE) { + hasImeRequestedHidden = true; + // Ensure to request hide IME in case there is any pending requested visible + // being applied from setControl when receiving the insets control. + if (hadPendingImeControlRequest + || getImeSourceConsumer().isRequestedVisibleAwaitingControl()) { + getImeSourceConsumer().requestHide(fromIme, statsToken); + } } } if (!requestedVisible && animationType == ANIMATION_TYPE_NONE @@ -1228,8 +1282,8 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation typesReady |= type; } if (hasImeRequestedHidden && mPendingImeControlRequest != null) { - // Handle the pending show request for other insets types since the IME insets has being - // requested hidden. + // Handle the pending show request for other insets types since the IME insets + // has being requested hidden. handlePendingControlRequest(statsToken); getImeSourceConsumer().removeSurface(); } @@ -1287,8 +1341,8 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation durationMs, interpolator, animationType, layoutInsetsDuringAnimation, useInsetsAnimationThread, statsToken); - // We are finishing setting the requested visible types. Report them to the server and/or - // the app. + // We are finishing setting the requested visible types. Report them to the server + // and/or the app. reportRequestedVisibleTypes(); } @@ -1333,58 +1387,120 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation mLastStartedAnimTypes |= types; final SparseArray<InsetsSourceControl> controls = new SparseArray<>(); - - Pair<Integer, Boolean> typesReadyPair = collectSourceControls( - fromIme, types, controls, animationType, statsToken); - int typesReady = typesReadyPair.first; - boolean imeReady = typesReadyPair.second; - if (DEBUG) Log.d(TAG, String.format( - "controlAnimationUnchecked, typesReady: %s imeReady: %s", typesReady, imeReady)); - if (!imeReady) { - // IME isn't ready, all requested types will be animated once IME is ready - abortPendingImeControlRequest(); - final PendingControlRequest request = new PendingControlRequest(types, - listener, durationMs, - interpolator, animationType, layoutInsetsDuringAnimation, cancellationSignal, - useInsetsAnimationThread); - mPendingImeControlRequest = request; - mHandler.postDelayed(mPendingControlTimeout, PENDING_CONTROL_TIMEOUT_MS); - if (DEBUG) Log.d(TAG, "Ime not ready. Create pending request"); - if (cancellationSignal != null) { - cancellationSignal.setOnCancelListener(() -> { - if (mPendingImeControlRequest == request) { - if (DEBUG) Log.d(TAG, - "Cancellation signal abortPendingImeControlRequest"); - abortPendingImeControlRequest(); + @InsetsType int typesReady; + + if (Flags.refactorInsetsController()) { + // Ime will not be contained in typesReady nor in controls, if we don't have a leash + Pair<Integer, Integer> typesReadyPair = collectSourceControlsV2(types, controls); + typesReady = typesReadyPair.first; + @InsetsType int typesWithoutLeash = typesReadyPair.second; + if (animationType == ANIMATION_TYPE_USER) { + // When using an app-driven animation, the IME won't have a leash (because the + // window isn't created yet). If we have a control, but no leash, defers the + // request until the leash gets created. + // The mRequestedVisibleTypes were set just before, so we check the currently + // visible types + if ((types & ime()) != 0 && (types & typesWithoutLeash) != 0) { + // If we have control but no leash for any of the controlling sources, we + // wait until the leashes are ready. Thus, creating a PendingControlRequest + // is always for showing, not hiding. + // TODO (b/323319146) remove layoutInsetsDuringAnimation from + // PendingControlRequest, as it is now only used for showing + final PendingControlRequest request = new PendingControlRequest(types, + listener, durationMs, + interpolator, animationType, LAYOUT_INSETS_DURING_ANIMATION_SHOWN, + cancellationSignal, false /* useInsetsAnimationThread */); + mPendingImeControlRequest = request; + // only add a timeout when the control is not currently showing + mHandler.postDelayed(mPendingControlTimeout, PENDING_CONTROL_TIMEOUT_MS); + + if (DEBUG) Log.d(TAG, "Ime not ready. Create pending request"); + if (cancellationSignal != null) { + cancellationSignal.setOnCancelListener(() -> { + if (mPendingImeControlRequest == request) { + if (DEBUG) { + Log.d(TAG, "Cancellation signal abortPendingImeControlRequest"); + } + abortPendingImeControlRequest(); + } + }); } - }); + } + // We need to wait until all types are ready + if (typesReady != types) { + return; + } } + } else { + Pair<Integer, Boolean> typesReadyPair = collectSourceControls( + fromIme, types, controls, animationType, statsToken); + typesReady = typesReadyPair.first; + boolean imeReady = typesReadyPair.second; + if (DEBUG) { + Log.d(TAG, TextUtils.formatSimple( + "controlAnimationUnchecked, typesReady: %s imeReady: %s", typesReady, + imeReady)); + } + if (!imeReady) { + // IME isn't ready, all requested types will be animated once IME is ready + abortPendingImeControlRequest(); + final PendingControlRequest request = new PendingControlRequest(types, + listener, durationMs, + interpolator, animationType, layoutInsetsDuringAnimation, + cancellationSignal, + useInsetsAnimationThread); + mPendingImeControlRequest = request; + mHandler.postDelayed(mPendingControlTimeout, PENDING_CONTROL_TIMEOUT_MS); + if (DEBUG) Log.d(TAG, "Ime not ready. Create pending request"); + if (cancellationSignal != null) { + cancellationSignal.setOnCancelListener(() -> { + if (mPendingImeControlRequest == request) { + if (DEBUG) { + Log.d(TAG, "Cancellation signal abortPendingImeControlRequest"); + } + abortPendingImeControlRequest(); + } + }); + } - // The leashes are copied, but they won't be used. - releaseControls(controls); + // The leashes are copied, but they won't be used. + releaseControls(controls); - // The requested visibilities should be delayed as well. Otherwise, we might override - // the insets visibility before playing animation. - setRequestedVisibleTypes(mReportedRequestedVisibleTypes, types); + // The requested visibilities should be delayed as well. Otherwise, we might + // override the insets visibility before playing animation. + setRequestedVisibleTypes(mReportedRequestedVisibleTypes, types); - Trace.asyncTraceEnd(TRACE_TAG_VIEW, "IC.showRequestFromApi", 0); - if (!fromIme) { - Trace.asyncTraceEnd(TRACE_TAG_VIEW, "IC.showRequestFromApiToImeReady", 0); + Trace.asyncTraceEnd(TRACE_TAG_VIEW, "IC.showRequestFromApi", 0); + if (!fromIme) { + Trace.asyncTraceEnd(TRACE_TAG_VIEW, "IC.showRequestFromApiToImeReady", 0); + } + return; } - return; } if (typesReady == 0) { - if (DEBUG) Log.d(TAG, "No types ready. onCancelled()"); - listener.onCancelled(null); - Trace.asyncTraceEnd(TRACE_TAG_VIEW, "IC.showRequestFromApi", 0); - if (!fromIme) { - Trace.asyncTraceEnd(TRACE_TAG_VIEW, "IC.showRequestFromApiToImeReady", 0); + if (Flags.refactorInsetsController()) { + // if no types are ready, we need to wait for receiving new controls + Trace.asyncTraceEnd(TRACE_TAG_VIEW, "IC.showRequestFromApi", 0); + listener.onCancelled(null); + } else { + if (DEBUG) Log.d(TAG, "No types ready. onCancelled()"); + listener.onCancelled(null); + Trace.asyncTraceEnd(TRACE_TAG_VIEW, "IC.showRequestFromApi", 0); + if (!fromIme) { + Trace.asyncTraceEnd(TRACE_TAG_VIEW, "IC.showRequestFromApiToImeReady", 0); + } } return; } - cancelExistingControllers(typesReady); + if (Flags.refactorInsetsController()) { + mCancelledForNewAnimationTypes = typesReady; + cancelExistingControllers(typesReady); + mCancelledForNewAnimationTypes = 0; + } else { + cancelExistingControllers(typesReady); + } final InsetsAnimationControlRunner runner = useInsetsAnimationThread ? new InsetsAnimationThreadControlRunner(controls, @@ -1416,7 +1532,12 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation } else { Trace.asyncTraceBegin(TRACE_TAG_VIEW, "IC.pendingAnim", 0); } - onAnimationStateChanged(types, true /* running */); + + if (Flags.refactorInsetsController()) { + onAnimationStateChanged(typesReady, true /* running */); + } else { + onAnimationStateChanged(types, true /* running */); + } if (fromIme) { switch (animationType) { @@ -1454,7 +1575,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation ImeTracker.forLogging().onProgress(statsToken, ImeTracker.PHASE_CLIENT_COLLECT_SOURCE_CONTROLS); - int typesReady = 0; + @InsetsType int typesReady = 0; boolean imeReady = true; for (int i = mSourceConsumers.size() - 1; i >= 0; i--) { final InsetsSourceConsumer consumer = mSourceConsumers.valueAt(i); @@ -1511,6 +1632,33 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation return new Pair<>(typesReady, imeReady); } + /** + * @return Pair of (types ready to animate, types that we have control for, but no leash). + */ + private Pair<Integer, Integer> collectSourceControlsV2(@InsetsType int types, + SparseArray<InsetsSourceControl> controls) { + @InsetsType int typesReady = 0; + int typesWithoutLeash = 0; + + for (int i = mSourceConsumers.size() - 1; i >= 0; i--) { + final InsetsSourceConsumer consumer = mSourceConsumers.valueAt(i); + if ((consumer.getType() & types) == 0) { + continue; + } + + final InsetsSourceControl control = consumer.getControl(); + if (control != null) { + if (control.getLeash() != null || control.getId() == ID_IME_CAPTION_BAR) { + controls.put(control.getId(), new InsetsSourceControl(control)); + typesReady |= consumer.getType(); + } else { + typesWithoutLeash |= consumer.getType(); + } + } + } + return new Pair<>(typesReady, typesWithoutLeash); + } + private @LayoutInsetsDuringAnimation int getLayoutInsetsDuringAnimationMode( @InsetsType int types, boolean fromPredictiveBack) { if (fromPredictiveBack) { @@ -1629,6 +1777,19 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation removedTypes = control.getTypes(); if (invokeCallback) { dispatchAnimationEnd(runningAnimation.runner.getAnimation()); + } else { + if (Flags.refactorInsetsController()) { + if (removedTypes == ime() + && control.getAnimationType() == ANIMATION_TYPE_HIDE) { + if (mHost != null) { + // if the (hide) animation is cancelled, the + // requestedVisibleTypes should be reported at this point. + reportRequestedVisibleTypes(); + mHost.getInputMethodManager().removeImeSurface( + mHost.getWindowToken()); + } + } + } } break; } @@ -1659,6 +1820,10 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation } } + @InsetsType int getCancelledForNewAnimationTypes() { + return mCancelledForNewAnimationTypes; + } + @VisibleForTesting public @NonNull InsetsSourceConsumer getSourceConsumer(int id, int type) { InsetsSourceConsumer consumer = mSourceConsumers.get(id); @@ -1733,11 +1898,43 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation } /** + * @return Types of currently running animations that are controlled by the user. + */ + public @InsetsType int computeUserAnimatingTypes() { + int animatingTypes = 0; + for (int i = 0; i < mRunningAnimations.size(); i++) { + if (mRunningAnimations.get(i).runner.getAnimationType() == ANIMATION_TYPE_USER) { + animatingTypes |= mRunningAnimations.get(i).runner.getTypes(); + } + } + return animatingTypes; + } + + private @InsetsType int computeAnimatingTypes() { + int animatingTypes = 0; + for (int i = 0; i < mRunningAnimations.size(); i++) { + animatingTypes |= mRunningAnimations.get(i).runner.getTypes(); + } + return animatingTypes; + } + + /** * Called when finishing setting requested visible types or finishing setting controls. */ private void reportRequestedVisibleTypes() { - if (mReportedRequestedVisibleTypes != mRequestedVisibleTypes) { - final @InsetsType int diff = mRequestedVisibleTypes ^ mReportedRequestedVisibleTypes; + final @InsetsType int typesToReport; + if (Flags.refactorInsetsController()) { + // If the IME is currently animating out, it is still visible, therefore we only + // report its requested visibility at the end of the animation, otherwise we would + // lose the leash, and it would disappear during the animation + // TODO(b/326377046) revisit this part and see if we can make it more general + typesToReport = mRequestedVisibleTypes | (computeAnimatingTypes() & ime()); + } else { + typesToReport = mRequestedVisibleTypes; + } + + if (typesToReport != mReportedRequestedVisibleTypes) { + final @InsetsType int diff = typesToReport ^ mReportedRequestedVisibleTypes; if (WindowInsets.Type.hasCompatSystemBars(diff)) { mCompatSysUiVisibilityStaled = true; } @@ -1769,10 +1966,12 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation boolean skipAnim, @Nullable ImeTracker.Token statsToken) { if (types == 0) { // nothing to animate. - if (DEBUG) Log.d(TAG, "applyAnimation, nothing to animate"); + if (DEBUG) Log.d(TAG, "applyAnimation, nothing to animate. Stopping here"); Trace.asyncTraceEnd(TRACE_TAG_VIEW, "IC.showRequestFromApi", 0); - if (!fromIme) { - Trace.asyncTraceEnd(TRACE_TAG_VIEW, "IC.showRequestFromApiToImeReady", 0); + if (!Flags.refactorInsetsController()) { + if (!fromIme) { + Trace.asyncTraceEnd(TRACE_TAG_VIEW, "IC.showRequestFromApiToImeReady", 0); + } } return; } diff --git a/core/java/android/view/InsetsSourceConsumer.java b/core/java/android/view/InsetsSourceConsumer.java index fdb2a6ee1791..6a92fd922ca9 100644 --- a/core/java/android/view/InsetsSourceConsumer.java +++ b/core/java/android/view/InsetsSourceConsumer.java @@ -32,10 +32,13 @@ import static com.android.internal.annotations.VisibleForTesting.Visibility.PACK import android.annotation.IntDef; import android.annotation.Nullable; import android.graphics.Rect; +import android.os.IBinder; +import android.text.TextUtils; import android.util.Log; import android.util.proto.ProtoOutputStream; import android.view.SurfaceControl.Transaction; import android.view.WindowInsets.Type.InsetsType; +import android.view.inputmethod.Flags; import android.view.inputmethod.ImeTracker; import com.android.internal.annotations.VisibleForTesting; @@ -241,9 +244,15 @@ public class InsetsSourceConsumer { } final boolean showRequested = isShowRequested(); - final boolean cancelledForNewAnimation = !running && showRequested - ? mAnimationState == ANIMATION_STATE_HIDE - : mAnimationState == ANIMATION_STATE_SHOW; + final boolean cancelledForNewAnimation; + if (Flags.refactorInsetsController()) { + cancelledForNewAnimation = + (mController.getCancelledForNewAnimationTypes() & mType) != 0; + } else { + cancelledForNewAnimation = (!running && showRequested) + ? mAnimationState == ANIMATION_STATE_HIDE + : mAnimationState == ANIMATION_STATE_SHOW; + } mAnimationState = running ? (showRequested ? ANIMATION_STATE_SHOW : ANIMATION_STATE_HIDE) @@ -292,12 +301,44 @@ public class InsetsSourceConsumer { } final boolean requestedVisible = (mController.getRequestedVisibleTypes() & mType) != 0; - // If we don't have control, we are not able to change the visibility. - if (mSourceControl == null) { - if (DEBUG) Log.d(TAG, "applyLocalVisibilityOverride: No control in " - + mController.getHost().getRootViewTitle() - + " requestedVisible=" + requestedVisible); - return false; + if (Flags.refactorInsetsController()) { + // If we don't have control or the leash (in case of the IME), we enforce the + // visibility to be hidden, as otherwise we would let the app know too early. + if (mSourceControl == null) { + if (DEBUG) { + Log.d(TAG, TextUtils.formatSimple( + "applyLocalVisibilityOverride: No control in %s for type %s, " + + "requestedVisible=%s", + mController.getHost().getRootViewTitle(), + WindowInsets.Type.toString(mType), requestedVisible)); + } + return false; + // TODO(b/323136120) add a flag to the control, to define whether a leash is needed + } else if (mId != InsetsSource.ID_IME_CAPTION_BAR + && mSourceControl.getLeash() == null) { + if (DEBUG) { + Log.d(TAG, TextUtils.formatSimple( + "applyLocalVisibilityOverride: Set the source visibility to false, as" + + " there is no leash yet for type %s in %s", + WindowInsets.Type.toString(mType), + mController.getHost().getRootViewTitle())); + } + boolean wasVisible = source.isVisible(); + source.setVisible(false); + // only if it was visible before and is now hidden, we want to notify about the + // changed state + return wasVisible; + } + } else { + // If we don't have control, we are not able to change the visibility. + if (mSourceControl == null) { + if (DEBUG) { + Log.d(TAG, "applyLocalVisibilityOverride: No control in " + + mController.getHost().getRootViewTitle() + + " requestedVisible=" + requestedVisible); + } + return false; + } } if (source.isVisible() == requestedVisible) { return false; @@ -338,6 +379,15 @@ public class InsetsSourceConsumer { * @see InsetsAnimationControlCallbacks#reportPerceptible */ public void onPerceptible(boolean perceptible) { + if (Flags.refactorInsetsController()) { + if (mType == WindowInsets.Type.ime()) { + final IBinder window = mController.getHost().getWindowToken(); + if (window != null) { + mController.getHost().getInputMethodManager().reportPerceptible(window, + perceptible); + } + } + } } /** diff --git a/core/java/android/view/OWNERS b/core/java/android/view/OWNERS index 07d05a4ff1ea..31a8dfaaf86b 100644 --- a/core/java/android/view/OWNERS +++ b/core/java/android/view/OWNERS @@ -1,7 +1,6 @@ # Bug component: 25700 romainguy@google.com -alanv@google.com adamp@google.com aurimas@google.com nduca@google.com diff --git a/core/java/android/view/PointerIcon.java b/core/java/android/view/PointerIcon.java index 71199e9c3619..c30212657c57 100644 --- a/core/java/android/view/PointerIcon.java +++ b/core/java/android/view/PointerIcon.java @@ -17,6 +17,7 @@ package android.view; import android.annotation.FlaggedApi; +import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.TestApi; @@ -43,8 +44,13 @@ import android.util.Log; import android.util.SparseArray; import android.view.flags.Flags; +import androidx.annotation.VisibleForTesting; + import com.android.internal.util.XmlUtils; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + /** * Represents an icon that can be used as a mouse pointer. * <p> @@ -164,6 +170,32 @@ public final class PointerIcon implements Parcelable { // every time we need to resolve the icon (i.e. on each input event). private static final SparseArray<PointerIcon> SYSTEM_ICONS = new SparseArray<>(); + /** @hide */ + @IntDef(prefix = {"POINTER_ICON_VECTOR_STYLE_FILL_"}, value = { + POINTER_ICON_VECTOR_STYLE_FILL_BLACK, + POINTER_ICON_VECTOR_STYLE_FILL_GREEN, + POINTER_ICON_VECTOR_STYLE_FILL_YELLOW, + POINTER_ICON_VECTOR_STYLE_FILL_PINK, + POINTER_ICON_VECTOR_STYLE_FILL_BLUE + }) + @Retention(RetentionPolicy.SOURCE) + public @interface PointerIconVectorStyleFill {} + + /** @hide */ public static final int POINTER_ICON_VECTOR_STYLE_FILL_BLACK = 0; + /** @hide */ public static final int POINTER_ICON_VECTOR_STYLE_FILL_GREEN = 1; + /** @hide */ public static final int POINTER_ICON_VECTOR_STYLE_FILL_YELLOW = 2; + /** @hide */ public static final int POINTER_ICON_VECTOR_STYLE_FILL_PINK = 3; + /** @hide */ public static final int POINTER_ICON_VECTOR_STYLE_FILL_BLUE = 4; + + // If adding a PointerIconVectorStyleFill, update END value for {@link SystemSettingsValidators} + /** @hide */ public static final int POINTER_ICON_VECTOR_STYLE_FILL_BEGIN = + POINTER_ICON_VECTOR_STYLE_FILL_BLACK; + /** @hide */ public static final int POINTER_ICON_VECTOR_STYLE_FILL_END = + POINTER_ICON_VECTOR_STYLE_FILL_BLUE; + + /** @hide */ public static final float DEFAULT_POINTER_SCALE = 1f; + /** @hide */ public static final float LARGE_POINTER_SCALE = 2.5f; + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) private final int mType; @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) @@ -224,7 +256,7 @@ public final class PointerIcon implements Parcelable { * @hide */ public static @NonNull PointerIcon getLoadedSystemIcon(@NonNull Context context, int type, - boolean useLargeIcons) { + boolean useLargeIcons, float pointerScale) { if (type == TYPE_NOT_SPECIFIED) { throw new IllegalStateException("Cannot load icon for type TYPE_NOT_SPECIFIED"); } @@ -239,13 +271,18 @@ public final class PointerIcon implements Parcelable { } final int defStyle; - // TODO(b/305193969): Use scaled vectors when large icons are requested. - if (useLargeIcons) { - defStyle = com.android.internal.R.style.LargePointer; - } else if (android.view.flags.Flags.enableVectorCursors()) { + if (android.view.flags.Flags.enableVectorCursorA11ySettings()) { defStyle = com.android.internal.R.style.VectorPointer; } else { - defStyle = com.android.internal.R.style.Pointer; + // TODO(b/346358375): Remove useLargeIcons and the legacy pointer styles when + // enableVectorCursorA11ySetting is rolled out. + if (useLargeIcons) { + defStyle = com.android.internal.R.style.LargePointer; + } else if (android.view.flags.Flags.enableVectorCursors()) { + defStyle = com.android.internal.R.style.VectorPointer; + } else { + defStyle = com.android.internal.R.style.Pointer; + } } TypedArray a = context.obtainStyledAttributes(null, com.android.internal.R.styleable.Pointer, @@ -257,11 +294,11 @@ public final class PointerIcon implements Parcelable { Log.w(TAG, "Missing theme resources for pointer icon type " + type); return type == TYPE_DEFAULT ? getSystemIcon(TYPE_NULL) - : getLoadedSystemIcon(context, TYPE_DEFAULT, useLargeIcons); + : getLoadedSystemIcon(context, TYPE_DEFAULT, useLargeIcons, pointerScale); } final PointerIcon icon = new PointerIcon(type); - icon.loadResource(context.getResources(), resourceId); + icon.loadResource(context.getResources(), resourceId, context.getTheme(), pointerScale); return icon; } @@ -324,7 +361,7 @@ public final class PointerIcon implements Parcelable { } PointerIcon icon = new PointerIcon(TYPE_CUSTOM); - icon.loadResource(resources, resourceId); + icon.loadResource(resources, resourceId, null, DEFAULT_POINTER_SCALE); return icon; } @@ -431,19 +468,21 @@ public final class PointerIcon implements Parcelable { } private BitmapDrawable getBitmapDrawableFromVectorDrawable(Resources resources, - VectorDrawable vectorDrawable) { + VectorDrawable vectorDrawable, float pointerScale) { // Ensure we pass the display metrics into the Bitmap constructor so that it is initialized // with the correct density. Bitmap bitmap = Bitmap.createBitmap(resources.getDisplayMetrics(), - vectorDrawable.getIntrinsicWidth(), - vectorDrawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888, true /* hasAlpha */); + (int) (vectorDrawable.getIntrinsicWidth() * pointerScale), + (int) (vectorDrawable.getIntrinsicHeight() * pointerScale), + Bitmap.Config.ARGB_8888, true /* hasAlpha */); Canvas canvas = new Canvas(bitmap); vectorDrawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight()); vectorDrawable.draw(canvas); return new BitmapDrawable(resources, bitmap); } - private void loadResource(@NonNull Resources resources, @XmlRes int resourceId) { + private void loadResource(@NonNull Resources resources, @XmlRes int resourceId, + @Nullable Resources.Theme theme, float pointerScale) { final XmlResourceParser parser = resources.getXml(resourceId); final int bitmapRes; final float hotSpotX; @@ -454,8 +493,10 @@ public final class PointerIcon implements Parcelable { final TypedArray a = resources.obtainAttributes( parser, com.android.internal.R.styleable.PointerIcon); bitmapRes = a.getResourceId(com.android.internal.R.styleable.PointerIcon_bitmap, 0); - hotSpotX = a.getDimension(com.android.internal.R.styleable.PointerIcon_hotSpotX, 0); - hotSpotY = a.getDimension(com.android.internal.R.styleable.PointerIcon_hotSpotY, 0); + hotSpotX = a.getDimension(com.android.internal.R.styleable.PointerIcon_hotSpotX, 0) + * pointerScale; + hotSpotY = a.getDimension(com.android.internal.R.styleable.PointerIcon_hotSpotY, 0) + * pointerScale; a.recycle(); } catch (Exception ex) { throw new IllegalArgumentException("Exception parsing pointer icon resource.", ex); @@ -467,7 +508,7 @@ public final class PointerIcon implements Parcelable { throw new IllegalArgumentException("<pointer-icon> is missing bitmap attribute."); } - Drawable drawable = resources.getDrawable(bitmapRes); + Drawable drawable = resources.getDrawable(bitmapRes, theme); if (drawable instanceof AnimationDrawable) { // Extract animation frame bitmaps. final AnimationDrawable animationDrawable = (AnimationDrawable) drawable; @@ -504,7 +545,7 @@ public final class PointerIcon implements Parcelable { } if (isVectorAnimation) { drawableFrame = getBitmapDrawableFromVectorDrawable(resources, - (VectorDrawable) drawableFrame); + (VectorDrawable) drawableFrame, pointerScale); } mBitmapFrames[i - 1] = getBitmapFromDrawable((BitmapDrawable) drawableFrame); } @@ -512,7 +553,8 @@ public final class PointerIcon implements Parcelable { } if (drawable instanceof VectorDrawable) { mDrawNativeDropShadow = true; - drawable = getBitmapDrawableFromVectorDrawable(resources, (VectorDrawable) drawable); + drawable = getBitmapDrawableFromVectorDrawable(resources, (VectorDrawable) drawable, + pointerScale); } if (!(drawable instanceof BitmapDrawable)) { throw new IllegalArgumentException("<pointer-icon> bitmap attribute must " @@ -649,6 +691,27 @@ public final class PointerIcon implements Parcelable { } /** + * Convert fill style constant to resource ID. + * + * @hide + */ + public static int vectorFillStyleToResource(@PointerIconVectorStyleFill int fillStyle) { + return switch (fillStyle) { + case POINTER_ICON_VECTOR_STYLE_FILL_BLACK -> + com.android.internal.R.style.PointerIconVectorStyleFillBlack; + case POINTER_ICON_VECTOR_STYLE_FILL_GREEN -> + com.android.internal.R.style.PointerIconVectorStyleFillGreen; + case POINTER_ICON_VECTOR_STYLE_FILL_YELLOW -> + com.android.internal.R.style.PointerIconVectorStyleFillYellow; + case POINTER_ICON_VECTOR_STYLE_FILL_PINK -> + com.android.internal.R.style.PointerIconVectorStyleFillPink; + case POINTER_ICON_VECTOR_STYLE_FILL_BLUE -> + com.android.internal.R.style.PointerIconVectorStyleFillBlue; + default -> com.android.internal.R.style.PointerIconVectorStyleFillBlack; + }; + } + + /** * Sets whether drop shadow will draw in the native code. * * @hide @@ -658,4 +721,14 @@ public final class PointerIcon implements Parcelable { public void setDrawNativeDropShadow(boolean drawNativeDropShadow) { mDrawNativeDropShadow = drawNativeDropShadow; } + + /** + * Gets the PointerIcon's bitmap. + * + * @hide + */ + @VisibleForTesting + public Bitmap getBitmap() { + return mBitmap; + } } diff --git a/core/java/android/view/SurfaceControlRegistry.java b/core/java/android/view/SurfaceControlRegistry.java index 127d4a70a564..aa3654dd77ac 100644 --- a/core/java/android/view/SurfaceControlRegistry.java +++ b/core/java/android/view/SurfaceControlRegistry.java @@ -342,12 +342,14 @@ public class SurfaceControlRegistry { return false; } final boolean matchName = !sCallStackDebuggingMatchName.isEmpty(); - if (matchName && (name == null - || !sCallStackDebuggingMatchName.contains(name.toLowerCase()))) { - // Skip if target surface doesn't match requested surface + if (!matchName) { + return true; + } + if (name == null) { return false; } - return true; + return sCallStackDebuggingMatchName.contains(name.toLowerCase()) || + name.toLowerCase().contains(sCallStackDebuggingMatchName); } /** diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 5e3f09ac895c..9bc15112debc 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -204,7 +204,6 @@ import android.view.translation.ViewTranslationCallback; import android.view.translation.ViewTranslationRequest; import android.view.translation.ViewTranslationResponse; import android.widget.Checkable; -import android.widget.FrameLayout; import android.widget.ScrollBarDrawable; import android.window.OnBackInvokedDispatcher; @@ -962,21 +961,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback, public HapticScrollFeedbackProvider mScrollFeedbackProvider = null; /** - * Use the old (broken) way of building MeasureSpecs. - */ - private static boolean sUseBrokenMakeMeasureSpec = false; - - /** - * Always return a size of 0 for MeasureSpec values with a mode of UNSPECIFIED - */ - static boolean sUseZeroUnspecifiedMeasureSpec = false; - - /** - * Ignore any optimizations using the measure cache. - */ - private static boolean sIgnoreMeasureCache = false; - - /** * Ignore an optimization that skips unnecessary EXACTLY layout passes. */ private static boolean sAlwaysRemeasureExactly = false; @@ -5845,20 +5829,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback, if (!sCompatibilityDone && context != null) { final int targetSdkVersion = context.getApplicationInfo().targetSdkVersion; - // Older apps may need this compatibility hack for measurement. - sUseBrokenMakeMeasureSpec = targetSdkVersion <= Build.VERSION_CODES.JELLY_BEAN_MR1; - - // Older apps expect onMeasure() to always be called on a layout pass, regardless - // of whether a layout was requested on that View. - sIgnoreMeasureCache = targetSdkVersion < Build.VERSION_CODES.KITKAT; - - // In M and newer, our widgets can pass a "hint" value in the size - // for UNSPECIFIED MeasureSpecs. This lets child views of scrolling containers - // know what the expected parent size is going to be, so e.g. list items can size - // themselves at 1/3 the size of their container. It breaks older apps though, - // specifically apps that use some popular open source libraries. - sUseZeroUnspecifiedMeasureSpec = targetSdkVersion < Build.VERSION_CODES.M; - // Old versions of the platform would give different results from // LinearLayout measurement passes using EXACTLY and non-EXACTLY // modes, so we always need to run an additional EXACTLY pass. @@ -6035,8 +6005,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback, boolean leftPaddingDefined = false; boolean rightPaddingDefined = false; - final int targetSdkVersion = context.getApplicationInfo().targetSdkVersion; - // Set default values. viewFlagValues |= FOCUSABLE_AUTO; viewFlagMasks |= FOCUSABLE_AUTO; @@ -6257,11 +6225,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, break; //noinspection deprecation case R.styleable.View_fadingEdge: - if (targetSdkVersion >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) { - // Ignore the attribute starting with ICS - break; - } - // With builds < ICS, fall through and apply fading edges + break; case R.styleable.View_requiresFadingEdge: final int fadingEdge = a.getInt(attr, FADING_EDGE_NONE); if (fadingEdge != FADING_EDGE_NONE) { @@ -6399,35 +6363,25 @@ public class View implements Drawable.Callback, KeyEvent.Callback, PROVIDER_BACKGROUND)); break; case R.styleable.View_foreground: - if (targetSdkVersion >= Build.VERSION_CODES.M || this instanceof FrameLayout) { - setForeground(a.getDrawable(attr)); - } + setForeground(a.getDrawable(attr)); break; case R.styleable.View_foregroundGravity: - if (targetSdkVersion >= Build.VERSION_CODES.M || this instanceof FrameLayout) { - setForegroundGravity(a.getInt(attr, Gravity.NO_GRAVITY)); - } + setForegroundGravity(a.getInt(attr, Gravity.NO_GRAVITY)); break; case R.styleable.View_foregroundTintMode: - if (targetSdkVersion >= Build.VERSION_CODES.M || this instanceof FrameLayout) { - setForegroundTintBlendMode( - Drawable.parseBlendMode(a.getInt(attr, -1), - null)); - } + setForegroundTintBlendMode( + Drawable.parseBlendMode(a.getInt(attr, -1), + null)); break; case R.styleable.View_foregroundTint: - if (targetSdkVersion >= Build.VERSION_CODES.M || this instanceof FrameLayout) { - setForegroundTintList(a.getColorStateList(attr)); - } + setForegroundTintList(a.getColorStateList(attr)); break; case R.styleable.View_foregroundInsidePadding: - if (targetSdkVersion >= Build.VERSION_CODES.M || this instanceof FrameLayout) { - if (mForegroundInfo == null) { - mForegroundInfo = new ForegroundInfo(); - } - mForegroundInfo.mInsidePadding = a.getBoolean(attr, - mForegroundInfo.mInsidePadding); + if (mForegroundInfo == null) { + mForegroundInfo = new ForegroundInfo(); } + mForegroundInfo.mInsidePadding = a.getBoolean(attr, + mForegroundInfo.mInsidePadding); break; case R.styleable.View_scrollIndicators: final int scrollIndicators = @@ -7198,7 +7152,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, public void setPendingCredentialRequest(@NonNull GetCredentialRequest request, @NonNull OutcomeReceiver<GetCredentialResponse, GetCredentialException> callback) { Preconditions.checkNotNull(request, "request must not be null"); - Preconditions.checkNotNull(callback, "request must not be null"); + Preconditions.checkNotNull(callback, "callback must not be null"); for (CredentialOption option : request.getCredentialOptions()) { ArrayList<AutofillId> ids = option.getCandidateQueryData() @@ -13905,11 +13859,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback, }) @ResolvedLayoutDir public int getLayoutDirection() { - final int targetSdkVersion = getContext().getApplicationInfo().targetSdkVersion; - if (targetSdkVersion < Build.VERSION_CODES.JELLY_BEAN_MR1) { - mPrivateFlags2 |= PFLAG2_LAYOUT_DIRECTION_RESOLVED; - return LAYOUT_DIRECTION_RESOLVED_DEFAULT; - } return ((mPrivateFlags2 & PFLAG2_LAYOUT_DIRECTION_RESOLVED_RTL) == PFLAG2_LAYOUT_DIRECTION_RESOLVED_RTL) ? LAYOUT_DIRECTION_RTL : LAYOUT_DIRECTION_LTR; } @@ -22480,8 +22429,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * RTL not supported) */ private boolean isRtlCompatibilityMode() { - final int targetSdkVersion = getContext().getApplicationInfo().targetSdkVersion; - return targetSdkVersion < Build.VERSION_CODES.JELLY_BEAN_MR1 || !hasRtlSupport(); + return !hasRtlSupport(); } /** @@ -28149,7 +28097,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, cacheIndex = forceLayout ? -1 : mMeasureCache.indexOfKey(key); } - if (cacheIndex < 0 || sIgnoreMeasureCache) { + if (cacheIndex < 0) { if (isTraversalTracingEnabled()) { Trace.beginSection(mTracingStrings.onMeasure); } @@ -31135,11 +31083,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, */ public static int makeMeasureSpec(@IntRange(from = 0, to = (1 << MeasureSpec.MODE_SHIFT) - 1) int size, @MeasureSpecMode int mode) { - if (sUseBrokenMakeMeasureSpec) { - return size + mode; - } else { - return (size & ~MODE_MASK) | (mode & MODE_MASK); - } + return (size & ~MODE_MASK) | (mode & MODE_MASK); } /** @@ -31150,9 +31094,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback, */ @UnsupportedAppUsage public static int makeSafeMeasureSpec(int size, int mode) { - if (sUseZeroUnspecifiedMeasureSpec && mode == UNSPECIFIED) { - return 0; - } return makeMeasureSpec(size, mode); } @@ -33958,8 +33899,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, int category; switch (getViewRootImpl().intermittentUpdateState()) { case ViewRootImpl.INTERMITTENT_STATE_INTERMITTENT -> category = - (sToolkitFrameRateBySizeReadOnlyFlagValue ? FRAME_RATE_CATEGORY_LOW - : FRAME_RATE_CATEGORY_NORMAL) | FRAME_RATE_CATEGORY_REASON_INTERMITTENT; + FRAME_RATE_CATEGORY_NORMAL | FRAME_RATE_CATEGORY_REASON_INTERMITTENT; case ViewRootImpl.INTERMITTENT_STATE_NOT_INTERMITTENT -> category = mSizeBasedFrameRateCategoryAndReason; default -> category = mLastFrameRateCategory; diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java index ab529e6fe60a..b2c39b13525f 100644 --- a/core/java/android/view/ViewGroup.java +++ b/core/java/android/view/ViewGroup.java @@ -16,7 +16,6 @@ package android.view; -import static android.os.Build.VERSION_CODES.JELLY_BEAN_MR1; import static android.view.WindowInsetsAnimation.Callback.DISPATCH_MODE_CONTINUE_ON_SUBTREE; import static android.view.WindowInsetsAnimation.Callback.DISPATCH_MODE_STOP; @@ -55,7 +54,6 @@ import android.util.Log; import android.util.Pools; import android.util.Pools.SynchronizedPool; import android.util.SparseArray; -import android.util.SparseBooleanArray; import android.view.WindowInsetsAnimation.Bounds; import android.view.WindowInsetsAnimation.Callback.DispatchMode; import android.view.accessibility.AccessibilityEvent; @@ -718,10 +716,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager mGroupFlags |= FLAG_ANIMATION_DONE; mGroupFlags |= FLAG_ANIMATION_CACHE; mGroupFlags |= FLAG_ALWAYS_DRAWN_WITH_CACHE; - - if (mContext.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.HONEYCOMB) { - mGroupFlags |= FLAG_SPLIT_MOTION_EVENTS; - } + mGroupFlags |= FLAG_SPLIT_MOTION_EVENTS; setDescendantFocusability(FOCUS_BEFORE_DESCENDANTS); @@ -3599,48 +3594,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager childIndex = getAndVerifyPreorderedIndex(childrenCount, i, customOrder); } catch (IndexOutOfBoundsException e) { childIndex = i; - if (mContext.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.M) { - Log.w(TAG, "Bad getChildDrawingOrder while collecting assist @ " - + i + " of " + childrenCount, e); - // At least one app is failing when we call getChildDrawingOrder - // at this point, so deal semi-gracefully with it by falling back - // on the basic order. - customOrder = false; - if (i > 0) { - // If we failed at the first index, there really isn't - // anything to do -- we will just proceed with the simple - // sequence order. - // Otherwise, we failed in the middle, so need to come up - // with an order for the remaining indices and use that. - // Failed at the first one, easy peasy. - int[] permutation = new int[childrenCount]; - SparseBooleanArray usedIndices = new SparseBooleanArray(); - // Go back and collected the indices we have done so far. - for (int j = 0; j < i; j++) { - permutation[j] = getChildDrawingOrder(childrenCount, j); - usedIndices.put(permutation[j], true); - } - // Fill in the remaining indices with indices that have not - // yet been used. - int nextIndex = 0; - for (int j = i; j < childrenCount; j++) { - while (usedIndices.get(nextIndex, false)) { - nextIndex++; - } - permutation[j] = nextIndex; - nextIndex++; - } - // Build the final view list. - preorderedList = new ArrayList<>(childrenCount); - for (int j = 0; j < childrenCount; j++) { - final int index = permutation[j]; - final View child = mChildren[index]; - preorderedList.add(child); - } - } - } else { - throw e; - } + throw e; } final View child = getAndVerifyPreorderedView(preorderedList, mChildren, childIndex); @@ -7109,12 +7063,12 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager } else if (childDimension == LayoutParams.MATCH_PARENT) { // Child wants to be our size... find out how big it should // be - resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size; + resultSize = size; resultMode = MeasureSpec.UNSPECIFIED; } else if (childDimension == LayoutParams.WRAP_CONTENT) { // Child wants to determine its own size.... find out how // big it should be - resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size; + resultSize = size; resultMode = MeasureSpec.UNSPECIFIED; } break; @@ -8662,8 +8616,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager } final boolean hasRtlSupport = c.getApplicationInfo().hasRtlSupport(); - final int targetSdkVersion = c.getApplicationInfo().targetSdkVersion; - if (targetSdkVersion < JELLY_BEAN_MR1 || !hasRtlSupport) { + if (!hasRtlSupport) { mMarginFlags |= RTL_COMPATIBILITY_MODE_MASK; } diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 2afa605c7ae3..a11bb788bbf0 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -127,6 +127,7 @@ import static com.android.window.flags.Flags.activityWindowInfoFlag; import static com.android.window.flags.Flags.enableBufferTransformHintFromDisplay; import static com.android.window.flags.Flags.setScPropertiesInClient; import static com.android.window.flags.Flags.windowSessionRelayoutInfo; +import static com.android.text.flags.Flags.disableHandwritingInitiatorForIme; import android.Manifest; import android.accessibilityservice.AccessibilityService; @@ -176,6 +177,7 @@ import android.graphics.Region; 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; @@ -201,6 +203,7 @@ import android.os.Trace; import android.os.UserHandle; import android.provider.Settings; import android.sysprop.DisplayProperties; +import android.sysprop.ViewProperties; import android.text.TextUtils; import android.util.AndroidRuntimeException; import android.util.DisplayMetrics; @@ -217,6 +220,7 @@ import android.util.proto.ProtoOutputStream; import android.view.InputDevice.InputSourceClass; import android.view.Surface.OutOfResourcesException; import android.view.SurfaceControl.Transaction; +import android.view.SurfaceControl.TransactionStats; import android.view.View.AttachInfo; import android.view.View.FocusDirection; import android.view.View.MeasureSpec; @@ -291,6 +295,7 @@ import java.util.OptionalInt; import java.util.Queue; import java.util.concurrent.CountDownLatch; import java.util.concurrent.Executor; +import java.util.function.Consumer; import java.util.function.Predicate; /** * The top of a view hierarchy, implementing the needed protocol between View @@ -1110,6 +1115,8 @@ public final class ViewRootImpl implements ViewParent, FRAME_RATE_COMPATIBILITY_FIXED_SOURCE; // time for touch boost period. private static final int FRAME_RATE_TOUCH_BOOST_TIME = 3000; + // Timeout for the other frame rate boosts other than touch boost. + private static final int FRAME_RATE_BOOST_TIME = 3000; // time for evaluating the interval between current time and // the time when frame rate was set previously. private static final int FRAME_RATE_SETTING_REEVALUATE_TIME = 100; @@ -1185,6 +1192,13 @@ public final class ViewRootImpl implements ViewParent, private String mFpsTraceName; private String mLargestViewTraceName; + private final boolean mAppStartInfoTimestampsFlagValue; + @GuardedBy("this") + private boolean mAppStartTimestampsSent = false; + private boolean mAppStartTrackingStarted = false; + private long mRenderThreadDrawStartTimeNs = -1; + private long mFirstFramePresentedTimeNs = -1; + private static boolean sToolkitSetFrameRateReadOnlyFlagValue; private static boolean sToolkitFrameRateFunctionEnablingReadOnlyFlagValue; private static boolean sToolkitMetricsForFrameRateDecisionFlagValue; @@ -1196,6 +1210,7 @@ public final class ViewRootImpl implements ViewParent, Flags.enableInvalidateCheckThread(); private static boolean sSurfaceFlingerBugfixFlagValue = com.android.graphics.surfaceflinger.flags.Flags.vrrBugfix24q4(); + private static final boolean sEnableVrr = ViewProperties.vrr_enabled().orElse(true); static { sToolkitSetFrameRateReadOnlyFlagValue = toolkitSetFrameRateReadOnly(); @@ -1301,6 +1316,8 @@ public final class ViewRootImpl implements ViewParent, } else { mSensitiveContentProtectionService = null; } + + mAppStartInfoTimestampsFlagValue = android.app.Flags.appStartInfoTimestamps(); } public static void addFirstDrawHandler(Runnable callback) { @@ -2573,6 +2590,12 @@ public final class ViewRootImpl implements ViewParent, notifySurfaceDestroyed(); } destroySurface(); + + // Reset so they can be sent again for warm starts. + mAppStartTimestampsSent = false; + mAppStartTrackingStarted = false; + mRenderThreadDrawStartTimeNs = -1; + mFirstFramePresentedTimeNs = -1; } } } @@ -3288,7 +3311,6 @@ public final class ViewRootImpl implements ViewParent, == LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES; } - @VisibleForTesting(visibility = PACKAGE) public InsetsController getInsetsController() { return mInsetsController; } @@ -3454,7 +3476,7 @@ public final class ViewRootImpl implements ViewParent, if (shouldEnableDvrr() && viewVisibility == View.VISIBLE) { // Boost frame rate when the viewVisibility becomes true. // This is mainly for lanuchers that lanuch new windows. - boostFrameRate(FRAME_RATE_TOUCH_BOOST_TIME); + boostFrameRate(FRAME_RATE_BOOST_TIME); } } @@ -4186,7 +4208,7 @@ public final class ViewRootImpl implements ViewParent, if (shouldEnableDvrr()) { // Boost the frame rate when the ViewRootImpl first becomes available. - boostFrameRate(FRAME_RATE_TOUCH_BOOST_TIME); + boostFrameRate(FRAME_RATE_BOOST_TIME); } } @@ -4372,6 +4394,30 @@ public final class ViewRootImpl implements ViewParent, reportDrawFinished(t, seqId); } }); + + // Only trigger once per {@link ViewRootImpl} instance, so don't add listener if + // {link mTransactionCompletedTimeNs} has already been set. + if (mAppStartInfoTimestampsFlagValue && !mAppStartTrackingStarted) { + mAppStartTrackingStarted = true; + Transaction transaction = new Transaction(); + transaction.addTransactionCompletedListener(mExecutor, + new Consumer<TransactionStats>() { + @Override + public void accept(TransactionStats transactionStats) { + SyncFence presentFence = transactionStats.getPresentFence(); + if (presentFence.awaitForever()) { + if (mFirstFramePresentedTimeNs == -1) { + // Only trigger once per {@link ViewRootImpl} instance. + mFirstFramePresentedTimeNs = presentFence.getSignalTime(); + maybeSendAppStartTimes(); + } + } + presentFence.close(); + } + }); + applyTransactionOnDraw(transaction); + } + if (DEBUG_BLAST) { Log.d(mTag, "Setup new sync=" + mWmsRequestSyncGroup.getName()); } @@ -4379,6 +4425,45 @@ public final class ViewRootImpl implements ViewParent, mWmsRequestSyncGroup.add(this, null /* runnable */); } + private void maybeSendAppStartTimes() { + synchronized (this) { + if (mAppStartTimestampsSent) { + // Don't send timestamps more than once. + return; + } + + // If we already have {@link mRenderThreadDrawStartTimeNs} then pass it through, if not + // post to main thread and check if we have it there. + if (mRenderThreadDrawStartTimeNs != -1) { + sendAppStartTimesLocked(); + } else { + mHandler.post(new Runnable() { + @Override + public void run() { + synchronized (ViewRootImpl.this) { + if (mRenderThreadDrawStartTimeNs == -1) { + return; + } + sendAppStartTimesLocked(); + } + } + }); + } + } + } + + @GuardedBy("this") + private void sendAppStartTimesLocked() { + try { + ActivityManager.getService().reportStartInfoViewTimestamps( + mRenderThreadDrawStartTimeNs, mFirstFramePresentedTimeNs); + mAppStartTimestampsSent = true; + } catch (RemoteException e) { + // Ignore, timestamps may be lost. + if (DBG) Log.d(TAG, "Exception attempting to report start timestamps.", e); + } + } + /** * Helper used to notify the service to block projection when a sensitive * view (the view displays sensitive content) is attached to the window. @@ -5565,7 +5650,13 @@ public final class ViewRootImpl implements ViewParent, registerCallbackForPendingTransactions(); } + long timeNs = SystemClock.uptimeNanos(); mAttachInfo.mThreadedRenderer.draw(mView, mAttachInfo, this); + + // Only trigger once per {@link ViewRootImpl} instance. + if (mAppStartInfoTimestampsFlagValue && mRenderThreadDrawStartTimeNs == -1) { + mRenderThreadDrawStartTimeNs = timeNs; + } } else { // If we get here with a disabled & requested hardware renderer, something went // wrong (an invalidate posted right before we destroyed the hardware surface @@ -7415,8 +7506,6 @@ public final class ViewRootImpl implements ViewParent, final KeyEvent event = (KeyEvent)q.mEvent; if (mView.dispatchKeyEventPreIme(event)) { return FINISH_HANDLED; - } else if (q.forPreImeOnly()) { - return FINISH_NOT_HANDLED; } return FORWARD; } @@ -7836,7 +7925,11 @@ public final class ViewRootImpl implements ViewParent, private int processPointerEvent(QueuedInputEvent q) { final MotionEvent event = (MotionEvent)q.mEvent; final int action = event.getAction(); - boolean handled = mHandwritingInitiator.onTouchEvent(event); + boolean handled = false; + if (!disableHandwritingInitiatorForIme() + || mWindowAttributes.type != TYPE_INPUT_METHOD) { + handled = mHandwritingInitiator.onTouchEvent(event); + } if (handled) { // If handwriting is started, toolkit doesn't receive ACTION_UP. mLastClickToolType = event.getToolType(event.getActionIndex()); @@ -7988,7 +8081,9 @@ public final class ViewRootImpl implements ViewParent, } PointerIcon pointerIcon = null; - if (event.isStylusPointer() && mIsStylusPointerIconEnabled) { + if (event.isStylusPointer() && mIsStylusPointerIconEnabled + && (!disableHandwritingInitiatorForIme() + || mWindowAttributes.type != TYPE_INPUT_METHOD)) { pointerIcon = mHandwritingInitiator.onResolvePointerIcon(mContext, event); } if (pointerIcon == null) { @@ -9907,7 +10002,6 @@ public final class ViewRootImpl implements ViewParent, public static final int FLAG_RESYNTHESIZED = 1 << 4; public static final int FLAG_UNHANDLED = 1 << 5; public static final int FLAG_MODIFIED_FOR_COMPATIBILITY = 1 << 6; - public static final int FLAG_PRE_IME_ONLY = 1 << 7; public QueuedInputEvent mNext; @@ -9915,13 +10009,6 @@ public final class ViewRootImpl implements ViewParent, public InputEventReceiver mReceiver; public int mFlags; - public boolean forPreImeOnly() { - if ((mFlags & FLAG_PRE_IME_ONLY) != 0) { - return true; - } - return false; - } - public boolean shouldSkipIme() { if ((mFlags & FLAG_DELIVER_POST_IME) != 0) { return true; @@ -9948,7 +10035,6 @@ public final class ViewRootImpl implements ViewParent, hasPrevious = flagToString("FINISHED_HANDLED", FLAG_FINISHED_HANDLED, hasPrevious, sb); hasPrevious = flagToString("RESYNTHESIZED", FLAG_RESYNTHESIZED, hasPrevious, sb); hasPrevious = flagToString("UNHANDLED", FLAG_UNHANDLED, hasPrevious, sb); - hasPrevious = flagToString("FLAG_PRE_IME_ONLY", FLAG_PRE_IME_ONLY, hasPrevious, sb); if (!hasPrevious) { sb.append("0"); } @@ -10005,7 +10091,7 @@ public final class ViewRootImpl implements ViewParent, } @UnsupportedAppUsage - QueuedInputEvent enqueueInputEvent(InputEvent event, + void enqueueInputEvent(InputEvent event, InputEventReceiver receiver, int flags, boolean processImmediately) { QueuedInputEvent q = obtainQueuedInputEvent(event, receiver, flags); @@ -10044,7 +10130,6 @@ public final class ViewRootImpl implements ViewParent, } else { scheduleProcessInputEvents(); } - return q; } private void scheduleProcessInputEvents() { @@ -12366,45 +12451,29 @@ public final class ViewRootImpl implements ViewParent, + "IWindow:%s Session:%s", mOnBackInvokedDispatcher, mBasePackageName, mWindow, mWindowSession)); } - mOnBackInvokedDispatcher.attachToWindow(mWindowSession, mWindow, this, + mOnBackInvokedDispatcher.attachToWindow(mWindowSession, mWindow, mImeBackAnimationController); } - /** - * Sends {@link KeyEvent#ACTION_DOWN ACTION_DOWN} and {@link KeyEvent#ACTION_UP ACTION_UP} - * back key events - * - * @param preImeOnly whether the back events should be sent to the pre-ime stage only - * @return whether the event was handled (i.e. onKeyPreIme consumed it if preImeOnly=true) - */ - public boolean injectBackKeyEvents(boolean preImeOnly) { - boolean consumed; - try { - processingBackKey(true); - sendBackKeyEvent(KeyEvent.ACTION_DOWN, preImeOnly); - consumed = sendBackKeyEvent(KeyEvent.ACTION_UP, preImeOnly); - } finally { - processingBackKey(false); - } - return consumed; - } - - private boolean sendBackKeyEvent(int action, boolean preImeOnly) { + private void sendBackKeyEvent(int action) { long when = SystemClock.uptimeMillis(); final KeyEvent ev = new KeyEvent(when, when, action, KeyEvent.KEYCODE_BACK, 0 /* repeat */, 0 /* metaState */, KeyCharacterMap.VIRTUAL_KEYBOARD, 0 /* scancode */, KeyEvent.FLAG_FROM_SYSTEM | KeyEvent.FLAG_VIRTUAL_HARD_KEY, InputDevice.SOURCE_KEYBOARD); - int flags = preImeOnly ? QueuedInputEvent.FLAG_PRE_IME_ONLY : 0; - QueuedInputEvent q = enqueueInputEvent(ev, null /* receiver */, flags, - true /* processImmediately */); - return (q.mFlags & QueuedInputEvent.FLAG_FINISHED_HANDLED) != 0; + enqueueInputEvent(ev, null /* receiver */, 0 /* flags */, true /* processImmediately */); } private void registerCompatOnBackInvokedCallback() { mCompatOnBackInvokedCallback = () -> { - injectBackKeyEvents(/* preImeOnly */ false); + try { + processingBackKey(true); + sendBackKeyEvent(KeyEvent.ACTION_DOWN); + sendBackKeyEvent(KeyEvent.ACTION_UP); + } finally { + processingBackKey(false); + } }; if (mOnBackInvokedDispatcher.hasImeOnBackInvokedDispatcher()) { Log.d(TAG, "Skip registering CompatOnBackInvokedCallback on IME dispatcher"); @@ -12836,8 +12905,13 @@ public final class ViewRootImpl implements ViewParent, + mFrameRateCompatibility); } if (sToolkitFrameRateFunctionEnablingReadOnlyFlagValue) { - mFrameRateTransaction.setFrameRate(mSurfaceControl, preferredFrameRate, - mFrameRateCompatibility).applyAsyncUnsafe(); + if (preferredFrameRate > 0) { + mFrameRateTransaction.setFrameRate(mSurfaceControl, preferredFrameRate, + mFrameRateCompatibility); + } else { + mFrameRateTransaction.clearFrameRate(mSurfaceControl); + } + mFrameRateTransaction.applyAsyncUnsafe(); } mLastPreferredFrameRate = preferredFrameRate; } @@ -12852,13 +12926,13 @@ public final class ViewRootImpl implements ViewParent, private boolean shouldSetFrameRateCategory() { // use toolkitSetFrameRate flag to gate the change - return mSurface.isValid() && shouldEnableDvrr(); + return shouldEnableDvrr() && mSurface.isValid() && shouldEnableDvrr(); } private boolean shouldSetFrameRate() { // use toolkitSetFrameRate flag to gate the change - return mSurface.isValid() && mPreferredFrameRate >= 0 - && shouldEnableDvrr() && !mIsFrameRateConflicted; + return shouldEnableDvrr() && mSurface.isValid() && mPreferredFrameRate >= 0 + && !mIsFrameRateConflicted; } private boolean shouldTouchBoost(int motionEventAction, int windowType) { @@ -12893,7 +12967,7 @@ public final class ViewRootImpl implements ViewParent, * @param view The View with the ThreadedRenderer animation that started. */ public void addThreadedRendererView(View view) { - if (!mThreadedRendererViews.contains(view)) { + if (shouldEnableDvrr() && !mThreadedRendererViews.contains(view)) { mThreadedRendererViews.add(view); } } @@ -12905,7 +12979,8 @@ public final class ViewRootImpl implements ViewParent, */ public void removeThreadedRendererView(View view) { mThreadedRendererViews.remove(view); - if (!mInvalidationIdleMessagePosted && sSurfaceFlingerBugfixFlagValue) { + if (shouldEnableDvrr() + && !mInvalidationIdleMessagePosted && sSurfaceFlingerBugfixFlagValue) { mInvalidationIdleMessagePosted = true; mHandler.sendEmptyMessageDelayed(MSG_CHECK_INVALIDATION_IDLE, IDLE_TIME_MILLIS); } @@ -12976,6 +13051,7 @@ public final class ViewRootImpl implements ViewParent, } if (frameRateCompatibility == FRAME_RATE_COMPATIBILITY_GTE) { mIsTouchBoosting = false; + mIsFrameRateBoosting = false; if (!sToolkitFrameRateVelocityMappingReadOnlyFlagValue) { mPreferredFrameRateCategory = FRAME_RATE_CATEGORY_HIGH; mFrameRateCategoryHighCount = FRAME_RATE_CATEGORY_COUNT; @@ -13125,7 +13201,7 @@ public final class ViewRootImpl implements ViewParent, private boolean shouldEnableDvrr() { // uncomment this when we are ready for enabling dVRR - if (sToolkitFrameRateViewEnablingReadOnlyFlagValue) { + if (sEnableVrr && sToolkitFrameRateViewEnablingReadOnlyFlagValue) { return sToolkitSetFrameRateReadOnlyFlagValue && isFrameRatePowerSavingsBalanced(); } return false; diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java index 42bf420b9812..7b7ead4afb11 100644 --- a/core/java/android/view/WindowManager.java +++ b/core/java/android/view/WindowManager.java @@ -963,7 +963,7 @@ public interface WindowManager extends ViewManager { * true: * <ul> * <li>Activity has requested orientation more than two times within one-second timer - * <li>Activity is not letterboxed for fixed orientation + * <li>Activity is not letterboxed for fixed-orientation apps * </ul> * * <p>Setting this property to {@code false} informs the system that the app must be @@ -1055,22 +1055,22 @@ public interface WindowManager extends ViewManager { * for an app to inform the system that the app should be excluded from the camera compatibility * force rotation treatment. * - * <p>The camera compatibility treatment aligns orientations of portrait app window and natural - * orientation of the device and set opposite to natural orientation for a landscape app - * window. Mismatch between them can lead to camera issues like sideways or stretched + * <p>The camera compatibility treatment aligns portrait app windows with the natural + * orientation of the device and landscape app windows opposite the device natural orientation. + * Mismatch between the orientations can lead to camera issues like a sideways or stretched * viewfinder since this is one of the strongest assumptions that apps make when they implement - * camera previews. Since app and natural display orientations aren't guaranteed to match, the - * rotation can cause letterboxing. The forced rotation is triggered as soon as app opens to + * camera previews. Since app and device natural orientations aren't guaranteed to match, the + * rotation can cause letterboxing. The forced rotation is triggered as soon as an app opens the * camera and is removed once camera is closed. * - * <p>The camera compatibility can be enabled by device manufacturers on displays that have the - * ignore requested orientation display setting enabled (enables compatibility mode for fixed - * orientation on Android 12 (API level 31) or higher; see - * <a href="https://developer.android.com/guide/practices/enhanced-letterboxing">Enhanced - * letterboxing</a> for more details). + * <p>Camera compatibility can be enabled by device manufacturers on displays that have the + * ignore requested orientation display setting enabled, which enables compatibility mode for + * fixed-orientation apps on Android 12 (API level 31) or higher. See + * <a href="{@docRoot}guide/practices/device-compatibility-mode">Device compatibility mode</a> + * for more details. * * <p>With this property set to {@code true} or unset, the system may apply the force rotation - * treatment to fixed orientation activities. Device manufacturers can exclude packages from the + * treatment to fixed-orientation activities. Device manufacturers can exclude packages from the * treatment using their discretion to improve display compatibility. * * <p>With this property set to {@code false}, the system will not apply the force rotation @@ -1093,12 +1093,12 @@ public interface WindowManager extends ViewManager { * for an app to inform the system that the app should be excluded from the activity "refresh" * after the camera compatibility force rotation treatment. * - * <p>The camera compatibility treatment aligns orientations of portrait app window and natural - * orientation of the device and set opposite to natural orientation for a landscape app - * window. Mismatch between them can lead to camera issues like sideways or stretched + * <p>The camera compatibility treatment aligns portrait app windows with the natural + * orientation of the device and landscape app windows opposite the device natural orientation. + * Mismatch between the orientations can lead to camera issues like a sideways or stretched * viewfinder since this is one of the strongest assumptions that apps make when they implement - * camera previews. Since app and natural display orientations aren't guaranteed to match, the - * rotation can cause letterboxing. The forced rotation is triggered as soon as app opens to + * camera previews. Since app and device natural orientations aren't guaranteed to match, the + * rotation can cause letterboxing. The forced rotation is triggered as soon as an app opens the * camera and is removed once camera is closed. * * <p>Force rotation is followed by the "Refresh" of the activity by going through "resumed -> @@ -1109,10 +1109,10 @@ public interface WindowManager extends ViewManager { * rotation. * * <p>The camera compatibility can be enabled by device manufacturers on displays that have the - * ignore requested orientation display setting enabled (enables compatibility mode for fixed - * orientation on Android 12 (API level 31) or higher; see - * <a href="https://developer.android.com/guide/practices/enhanced-letterboxing">Enhanced - * letterboxing</a> for more details). + * ignore requested orientation display setting enabled, which enables compatibility mode for + * fixed-orientation apps on Android 12 (API level 31) or higher. See + * <a href="{@docRoot}guide/practices/device-compatibility-mode">Device compatibility mode</a> + * for more details. * * <p>With this property set to {@code true} or unset, the system may "refresh" activity after * the force rotation treatment. Device manufacturers can exclude packages from the "refresh" @@ -1140,12 +1140,11 @@ public interface WindowManager extends ViewManager { * "stopped -> resumed". * * <p>The camera compatibility treatment aligns orientations of portrait app window and natural - * orientation of the device and set opposite to natural orientation for a landscape app - * window. Mismatch between them can lead to camera issues like sideways or stretched - * viewfinder since this is one of the strongest assumptions that apps make when they implement - * camera previews. Since app and natural display orientations aren't guaranteed to match, the - * rotation can cause letterboxing. The forced rotation is triggered as soon as app opens to - * camera and is removed once camera is closed. + * orientation of the device. Mismatch between the orientations can lead to camera issues like a + * sideways or stretched viewfinder since this is one of the strongest assumptions that apps + * make when they implement camera previews. Since app and natural display orientations aren't + * guaranteed to match, the rotation can cause letterboxing. The forced rotation is triggered as + * soon as app opens the camera and is removed once camera is closed. * * <p>Force rotation is followed by the "Refresh" of the activity by going through "resumed -> * ... -> stopped -> ... -> resumed" cycle (by default) or "resumed -> paused -> resumed" cycle @@ -1154,10 +1153,10 @@ public interface WindowManager extends ViewManager { * to sideways or stretching issues persisting even after force rotation. * * <p>The camera compatibility can be enabled by device manufacturers on displays that have the - * ignore requested orientation display setting enabled (enables compatibility mode for fixed - * orientation on Android 12 (API level 31) or higher; see - * <a href="https://developer.android.com/guide/practices/enhanced-letterboxing">Enhanced - * letterboxing</a> for more details). + * ignore requested orientation display setting enabled, which enables compatibility mode for + * fixed-orientation apps on Android 12 (API level 31) or higher. See + * <a href="{@docRoot}guide/practices/device-compatibility-mode">Device compatibility mode</a> + * for more details. * * <p>Device manufacturers can override packages to "refresh" via "resumed -> paused -> resumed" * cycle using their discretion to improve display compatibility. @@ -1203,7 +1202,7 @@ public interface WindowManager extends ViewManager { * <p>With this property set to {@code true} or unset, device manufacturers can override * orientation for the app using their discretion to improve display compatibility. * - * <p>With this property set to {@code false}, device manufactured per-app override for + * <p>With this property set to {@code false}, device manufacturer per-app override for * orientation won't be applied. * * <p><b>Syntax:</b> @@ -1227,15 +1226,15 @@ public interface WindowManager extends ViewManager { * <p>When this compat override is enabled and while display is fixed to the landscape natural * orientation, the orientation requested by the activity will be still respected by bounds * resolution logic. For instance, if an activity requests portrait orientation, then activity - * will appear in the letterbox mode for fixed orientation with the display rotated to the - * lanscape natural orientation. + * appears in letterbox mode for fixed-orientation apps with the display rotated to the lanscape + * natural orientation. * * <p>The treatment is disabled by default but device manufacturers can enable the treatment * using their discretion to improve display compatibility on displays that have the ignore - * orientation request display setting enabled by OEMs on the device (enables compatibility mode - * for fixed orientation on Android 12 (API level 31) or higher; see - * <a href="https://developer.android.com/guide/practices/enhanced-letterboxing">Enhanced - * letterboxing</a> for more details). + * orientation request display setting enabled by OEMs on the device, which enables + * compatibility mode for fixed-orientation apps on Android 12 (API level 31) or higher. See + * <a href="{@docRoot}guide/practices/device-compatibility-mode">Device compatibility mode</a> + * for more details. * * <p>With this property set to {@code true} or unset, the system wiil use landscape display * orientation when the following conditions are met: @@ -1246,7 +1245,7 @@ public interface WindowManager extends ViewManager { * <li>Device manufacturer enabled the treatment. * </ul> * - * <p>With this property set to {@code false}, device manufactured per-app override for + * <p>With this property set to {@code false}, device manufacturer per-app override for * display orientation won't be applied. * * <p><b>Syntax:</b> @@ -1344,13 +1343,11 @@ public interface WindowManager extends ViewManager { * see {@link #PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_FULLSCREEN_OVERRIDE} to * disable the full-screen option only. * - * <p>The user override is intended to improve the app experience on devices - * that have the ignore orientation request display setting enabled by OEMs - * (enables compatibility mode for fixed orientation on Android 12 (API - * level 31) or higher; see - * <a href="https://developer.android.com/guide/topics/large-screens/large-screen-compatibility-mode"> - * Large screen compatibility mode</a> - * for more details). + * <p>The user override is intended to improve the app experience on devices that have the + * ignore orientation request display setting enabled by OEMs, which enables compatibility mode + * for fixed-orientation apps on Android 12 (API level 31) or higher. See + * <a href="{@docRoot}guide/practices/device-compatibility-mode">Device compatibility mode</a> + * for more details. * * <p>To opt out of the user aspect ratio compatibility override, add this property * to your app manifest and set the value to {@code false}. Your app will be excluded @@ -1383,13 +1380,11 @@ public interface WindowManager extends ViewManager { * <p>When users apply the full-screen compatibility override, the orientation * of the activity is forced to {@link android.content.pm.ActivityInfo#SCREEN_ORIENTATION_USER}. * - * <p>The user override is intended to improve the app experience on devices - * that have the ignore orientation request display setting enabled by OEMs - * (enables compatibility mode for fixed orientation on Android 12 (API - * level 31) or higher; see - * <a href="https://developer.android.com/guide/topics/large-screens/large-screen-compatibility-mode"> - * Large screen compatibility mode</a> - * for more details). + * <p>The user override is intended to improve the app experience on devices that have the + * ignore orientation request display setting enabled by OEMs, which enables compatibility mode + * for fixed-orientation apps on Android 12 (API level 31) or higher. See + * <a href="{@docRoot}guide/practices/device-compatibility-mode">Device compatibility mode</a> + * for more details. * * <p>To opt out of the full-screen option of the user aspect ratio compatibility * override, add this property to your app manifest and set the value to {@code false}. diff --git a/core/java/android/view/accessibility/AccessibilityDisplayProxy.java b/core/java/android/view/accessibility/AccessibilityDisplayProxy.java index 1fe8180aa7b2..12e08148a651 100644 --- a/core/java/android/view/accessibility/AccessibilityDisplayProxy.java +++ b/core/java/android/view/accessibility/AccessibilityDisplayProxy.java @@ -302,10 +302,6 @@ public abstract class AccessibilityDisplayProxy { } @Override - public void onMagnificationSystemUIConnectionChanged(boolean connected) { - } - - @Override public void onMagnificationChanged(int displayId, @NonNull Region region, MagnificationConfig config) { } diff --git a/core/java/android/view/accessibility/OWNERS b/core/java/android/view/accessibility/OWNERS index b0943e9cff62..f62b33f1f753 100644 --- a/core/java/android/view/accessibility/OWNERS +++ b/core/java/android/view/accessibility/OWNERS @@ -5,7 +5,6 @@ include /services/accessibility/OWNERS # Android members outside of Accessibility adamp@google.com #{LAST_RESORT_SUGGESTION} -alanv@google.com #{LAST_RESORT_SUGGESTION} aurimas@google.com #{LAST_RESORT_SUGGESTION} jjaggi@google.com #{LAST_RESORT_SUGGESTION} ogunwale@google.com #{LAST_RESORT_SUGGESTION} diff --git a/core/java/android/view/accessibility/flags/accessibility_flags.aconfig b/core/java/android/view/accessibility/flags/accessibility_flags.aconfig index edf33875b765..ab7b2261dc17 100644 --- a/core/java/android/view/accessibility/flags/accessibility_flags.aconfig +++ b/core/java/android/view/accessibility/flags/accessibility_flags.aconfig @@ -169,13 +169,3 @@ flag { description: "Feature flag for declaring system pinch zoom opt-out apis" bug: "315089687" } - -flag { - name: "wait_magnification_system_ui_connection_to_notify_service_connected" - namespace: "accessibility" - description: "Decide whether AccessibilityService needs to wait until magnification system ui connection is ready to trigger onServiceConnected" - bug: "337800504" - metadata { - purpose: PURPOSE_BUGFIX - } -} diff --git a/core/java/android/view/animation/OWNERS b/core/java/android/view/animation/OWNERS index 9b8f4d995975..2fa01c36ea96 100644 --- a/core/java/android/view/animation/OWNERS +++ b/core/java/android/view/animation/OWNERS @@ -2,5 +2,4 @@ romainguy@google.com tianliu@google.com -alanv@google.com adamp@google.com diff --git a/core/java/android/view/autofill/AutofillFeatureFlags.java b/core/java/android/view/autofill/AutofillFeatureFlags.java index 46b41aead027..199a69a723d8 100644 --- a/core/java/android/view/autofill/AutofillFeatureFlags.java +++ b/core/java/android/view/autofill/AutofillFeatureFlags.java @@ -524,7 +524,7 @@ public class AutofillFeatureFlags { return DeviceConfig.getBoolean( DeviceConfig.NAMESPACE_AUTOFILL, DEVICE_CONFIG_INCLUDE_INVISIBLE_VIEW_GROUP_IN_ASSIST_STRUCTURE, - false); + true); } /** @hide */ @@ -548,7 +548,7 @@ public class AutofillFeatureFlags { return DeviceConfig.getBoolean( DeviceConfig.NAMESPACE_AUTOFILL, DEVICE_CONFIG_FILL_FIELDS_FROM_CURRENT_SESSION_ONLY, - false); + true); } /** diff --git a/core/java/android/view/autofill/AutofillId.java b/core/java/android/view/autofill/AutofillId.java index 15ba1a16cbfa..6b608582c1dd 100644 --- a/core/java/android/view/autofill/AutofillId.java +++ b/core/java/android/view/autofill/AutofillId.java @@ -61,6 +61,11 @@ public final class AutofillId implements Parcelable { } /** @hide */ + public AutofillId(@NonNull AutofillId hostId, int virtualChildId, int sessionId) { + this(FLAG_IS_VIRTUAL_INT | FLAG_HAS_SESSION, hostId.mViewId, virtualChildId, sessionId); + } + + /** @hide */ @TestApi public AutofillId(@NonNull AutofillId hostId, long virtualChildId, int sessionId) { this(FLAG_IS_VIRTUAL_LONG | FLAG_HAS_SESSION, hostId.mViewId, virtualChildId, sessionId); @@ -236,9 +241,9 @@ public final class AutofillId implements Parcelable { public String toString() { final StringBuilder builder = new StringBuilder().append(mViewId); if (isVirtualInt()) { - builder.append(':').append(mVirtualIntId); + builder.append(":i").append(mVirtualIntId); } else if (isVirtualLong()) { - builder.append(':').append(mVirtualLongId); + builder.append(":l").append(mVirtualLongId); } if (hasSession()) { diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java index 9cc4191d0c80..53d9429acd36 100644 --- a/core/java/android/view/autofill/AutofillManager.java +++ b/core/java/android/view/autofill/AutofillManager.java @@ -193,6 +193,7 @@ import sun.misc.Cleaner; @RequiresFeature(PackageManager.FEATURE_AUTOFILL) public final class AutofillManager { + private static final boolean DBG = false; private static final String TAG = "AutofillManager"; /** @@ -2027,7 +2028,31 @@ public final class AutofillManager { if (!hasAutofillFeature()) { return; } + if (DBG) { + Log.v(TAG, "notifyValueChanged() called with virtualId:" + virtualId + " value:" + + value); + } synchronized (mLock) { + if (mLastAutofilledData != null) { + AutofillId id = new AutofillId(view.getAutofillId(), virtualId, mSessionId); + if (mLastAutofilledData.containsKey(id)) { + if (Objects.equals(mLastAutofilledData.get(id), value)) { + // Indicates that the view was autofilled + if (sDebug) { + Log.v(TAG, "notifyValueChanged() virtual view autofilled successfully:" + + virtualId + " value:" + value); + } + try { + mService.setViewAutofilled(mSessionId, id, mContext.getUserId()); + } catch (RemoteException e) { + // The failure could be a consequence of something going wrong on the + // server side. Do nothing here since it's just logging, but it's + // possible follow-up actions may fail. + Log.w(TAG, "RemoteException caught but ignored " + e); + } + } + } + } if (!mEnabled || !isActiveLocked()) { if (sVerbose) { Log.v(TAG, "notifyValueChanged(" + view.getAutofillId() + ":" + virtualId @@ -3114,6 +3139,10 @@ public final class AutofillManager { ArrayList<AutofillId> failedIds = new ArrayList<>(); + if (mLastAutofilledData == null) { + mLastAutofilledData = new ParcelableMap(itemCount); + } + for (int i = 0; i < itemCount; i++) { final AutofillId id = ids.get(i); final AutofillValue value = values.get(i); @@ -3126,6 +3155,9 @@ public final class AutofillManager { failedIds.add(id); continue; } + // Mark the view as to be autofilled with 'value' + mLastAutofilledData.put(id, value); + if (id.isVirtualInt()) { if (virtualValues == null) { // Most likely there will be just one view with virtual children. @@ -3139,12 +3171,6 @@ public final class AutofillManager { } valuesByParent.put(id.getVirtualChildIntId(), value); } else { - // Mark the view as to be autofilled with 'value' - if (mLastAutofilledData == null) { - mLastAutofilledData = new ParcelableMap(itemCount - i); - } - mLastAutofilledData.put(id, value); - view.autofill(value); // Set as autofilled if the values match now, e.g. when the value was updated @@ -3575,40 +3601,14 @@ public final class AutofillManager { // isCredential field indicates that the developer might be calling Credman, and we should // suppress autofill dialogs. But it is not a good enough indicator that there is a valid // credman option. - if (view.isCredential()) { - return true; - } - return containsAutofillHintPrefix(view, View.AUTOFILL_HINT_CREDENTIAL_MANAGER); + return view.isCredential() || isCredmanRequested(view); } private boolean isCredmanRequested(View view) { if (view == null) { return false; } - if (view.getViewCredentialHandler() != null) { - return true; - } - - String[] hints = view.getAutofillHints(); - if (hints == null) { - return false; - } - // if hint starts with 'credential=', then we assume that there is a valid - // credential option set by the client. - return containsAutofillHintPrefix(view, View.AUTOFILL_HINT_CREDENTIAL_MANAGER + "="); - } - - private boolean containsAutofillHintPrefix(View view, String prefix) { - String[] hints = view.getAutofillHints(); - if (hints == null) { - return false; - } - for (String hint : hints) { - if (hint != null && hint.startsWith(prefix)) { - return true; - } - } - return false; + return view.getViewCredentialHandler() != null; } /** diff --git a/core/java/android/view/contentcapture/ContentCaptureManager.java b/core/java/android/view/contentcapture/ContentCaptureManager.java index bcef37f6e0c4..d74867c31bb6 100644 --- a/core/java/android/view/contentcapture/ContentCaptureManager.java +++ b/core/java/android/view/contentcapture/ContentCaptureManager.java @@ -366,6 +366,14 @@ public final class ContentCaptureManager { "enable_content_protection_receiver"; /** + * Whether AssistContent snapshot should be sent on activity start. + * + * @hide + */ + public static final String DEVICE_CONFIG_ENABLE_ACTIVITY_START_ASSIST_CONTENT = + "enable_activity_start_assist_content"; + + /** * Sets the size of the in-memory ring buffer for the content protection flow. * * @hide diff --git a/core/java/android/view/flags/view_flags.aconfig b/core/java/android/view/flags/view_flags.aconfig index c0d31fae4b8f..4d4e4afb621c 100644 --- a/core/java/android/view/flags/view_flags.aconfig +++ b/core/java/android/view/flags/view_flags.aconfig @@ -28,6 +28,14 @@ flag { } flag { + name: "enable_vector_cursor_a11y_settings" + namespace: "systemui" + description: "Feature flag to enable accessibility settings for vector cursors." + bug: "302275042" + is_fixed_read_only: true +} + +flag { name: "sensitive_content_app_protection_api" is_exported: true namespace: "permissions" diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java index cf128fbaf50f..0c63e583f326 100644 --- a/core/java/android/view/inputmethod/InputMethodManager.java +++ b/core/java/android/view/inputmethod/InputMethodManager.java @@ -129,6 +129,7 @@ import java.io.FileDescriptor; import java.io.PrintWriter; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.lang.ref.WeakReference; import java.lang.reflect.Proxy; import java.util.Arrays; import java.util.Collections; @@ -707,6 +708,7 @@ public final class InputMethodManager { private static final int MSG_BIND_ACCESSIBILITY_SERVICE = 11; private static final int MSG_UNBIND_ACCESSIBILITY_SERVICE = 12; private static final int MSG_SET_INTERACTIVE = 13; + private static final int MSG_SET_VISIBILITY = 14; private static final int MSG_ON_SHOW_REQUESTED = 31; private static final int MSG_START_INPUT_RESULT = 40; @@ -904,6 +906,21 @@ public final class InputMethodManager { if (mCurRootView == viewRootImpl) { mCurRootViewWindowFocused = false; + if (Flags.refactorInsetsController() && mCurRootView != null) { + final int softInputMode = mCurRootView.mWindowAttributes.softInputMode; + final int state = + softInputMode & WindowManager.LayoutParams.SOFT_INPUT_MASK_STATE; + if (state == WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN) { + // when losing focus (e.g., by going to another window), we reset the + // requestedVisibleTypes of WindowInsetsController by hiding the IME + if (DEBUG) { + Log.d(TAG, "onWindowLostFocus, hiding IME because " + + "of STATE_ALWAYS_HIDDEN"); + } + mCurRootView.getInsetsController().hide(WindowInsets.Type.ime()); + } + } + clearCurRootViewIfNeeded(); } } @@ -1332,6 +1349,19 @@ public final class InputMethodManager { } return; } + case MSG_SET_VISIBILITY: + final boolean visible = msg.arg1 != 0; + synchronized (mH) { + if (visible) { + showSoftInput(mServedView, /* flags */ 0); + } else { + if (mCurRootView != null + && mCurRootView.getInsetsController() != null) { + mCurRootView.getInsetsController().hide(WindowInsets.Type.ime()); + } + } + } + break; case MSG_SEND_INPUT_EVENT: { sendInputEventAndReportResultOnMainLooper((PendingEvent)msg.obj); return; @@ -1429,6 +1459,11 @@ public final class InputMethodManager { } @Override + public void setImeVisibility(boolean visible) { + mH.obtainMessage(MSG_SET_VISIBILITY, visible ? 1 : 0, 0).sendToTarget(); + } + + @Override public void scheduleStartInputIfNecessary(boolean fullscreen) { // TODO(b/149859205): See if we can optimize this by having a fused dedicated operation. mH.obtainMessage(MSG_SET_ACTIVE, 0 /* active */, fullscreen ? 1 : 0).sendToTarget(); @@ -2298,19 +2333,32 @@ public final class InputMethodManager { ImeTracker.forLogging().onProgress(statsToken, ImeTracker.PHASE_CLIENT_VIEW_SERVED); - // Makes sure to call ImeInsetsSourceConsumer#onShowRequested on the UI thread. - // TODO(b/229426865): call WindowInsetsController#show instead. - mH.executeOrSendMessage(Message.obtain(mH, MSG_ON_SHOW_REQUESTED)); - Log.d(TAG, "showSoftInput() view=" + view + " flags=" + flags + " reason=" - + InputMethodDebug.softInputDisplayReasonToString(reason)); - return IInputMethodManagerGlobalInvoker.showSoftInput( - mClient, - view.getWindowToken(), - statsToken, - flags, - mCurRootView.getLastClickToolType(), - resultReceiver, - reason); + if (Flags.refactorInsetsController()) { + // In case of a running show IME animation, it should not be requested visible, + // otherwise the animation would jump and not be controlled by the user anymore + if ((mCurRootView.getInsetsController().computeUserAnimatingTypes() + & WindowInsets.Type.ime()) == 0) { + // TODO(b/322992891) handle case of SHOW_IMPLICIT + view.getWindowInsetsController().show(WindowInsets.Type.ime()); + return true; + } else { + return false; + } + } else { + // Makes sure to call ImeInsetsSourceConsumer#onShowRequested on the UI thread. + // TODO(b/229426865): call WindowInsetsController#show instead. + mH.executeOrSendMessage(Message.obtain(mH, MSG_ON_SHOW_REQUESTED)); + Log.d(TAG, "showSoftInput() view=" + view + " flags=" + flags + " reason=" + + InputMethodDebug.softInputDisplayReasonToString(reason)); + return IInputMethodManagerGlobalInvoker.showSoftInput( + mClient, + view.getWindowToken(), + statsToken, + flags, + mCurRootView.getLastClickToolType(), + resultReceiver, + reason); + } } } @@ -2447,8 +2495,14 @@ public final class InputMethodManager { ImeTracker.forLogging().onProgress(statsToken, ImeTracker.PHASE_CLIENT_VIEW_SERVED); - return IInputMethodManagerGlobalInvoker.hideSoftInput(mClient, windowToken, statsToken, - flags, resultReceiver, reason); + if (Flags.refactorInsetsController()) { + // TODO(b/322992891) handle case of HIDE_IMPLICIT_ONLY + servedView.getWindowInsetsController().hide(WindowInsets.Type.ime()); + return true; + } else { + return IInputMethodManagerGlobalInvoker.hideSoftInput(mClient, windowToken, + statsToken, flags, resultReceiver, reason); + } } } @@ -2577,13 +2631,17 @@ public final class InputMethodManager { return false; } if (useDelegation) { + WeakReference<Executor> executorRef = new WeakReference<>(executor); + WeakReference<Consumer<Boolean>> callbackRef = new WeakReference<>(callback); if (useCallback) { IBooleanListener listener = new IBooleanListener.Stub() { @Override public void onResult(boolean value) { - executor.execute(() -> { - callback.accept(value); - }); + Executor executor = executorRef.get(); + Consumer<Boolean> callback = callbackRef.get(); + if (executor != null && callback != null) { + executor.execute(() -> callback.accept(value)); + } } }; if (!IInputMethodManagerGlobalInvoker.acceptStylusHandwritingDelegationAsync( diff --git a/core/java/android/view/textclassifier/intent/OWNERS b/core/java/android/view/textclassifier/intent/OWNERS index ac80d9f4cdd0..3465fe62784f 100644 --- a/core/java/android/view/textclassifier/intent/OWNERS +++ b/core/java/android/view/textclassifier/intent/OWNERS @@ -4,5 +4,4 @@ mns@google.com toki@google.com svetoslavganov@android.com svetoslavganov@google.com -augale@google.com joannechung@google.com diff --git a/core/java/android/widget/CursorTreeAdapter.java b/core/java/android/widget/CursorTreeAdapter.java index 405e45ac3f5f..405e45ac3f5f 100755..100644 --- a/core/java/android/widget/CursorTreeAdapter.java +++ b/core/java/android/widget/CursorTreeAdapter.java diff --git a/core/java/android/widget/DatePickerCalendarDelegate.java b/core/java/android/widget/DatePickerCalendarDelegate.java index 536b81f77174..536b81f77174 100755..100644 --- a/core/java/android/widget/DatePickerCalendarDelegate.java +++ b/core/java/android/widget/DatePickerCalendarDelegate.java diff --git a/core/java/android/widget/ListPopupWindow.java b/core/java/android/widget/ListPopupWindow.java index 7766f1a0b350..7766f1a0b350 100755..100644 --- a/core/java/android/widget/ListPopupWindow.java +++ b/core/java/android/widget/ListPopupWindow.java diff --git a/core/java/android/widget/OWNERS b/core/java/android/widget/OWNERS index 1dc90edbd1e5..8b8090b67133 100644 --- a/core/java/android/widget/OWNERS +++ b/core/java/android/widget/OWNERS @@ -1,7 +1,6 @@ # Bug component: 25700 romainguy@google.com -alanv@google.com adamp@google.com aurimas@google.com siyamed@google.com diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java index 5430f8f6add3..3c5623f380f5 100644 --- a/core/java/android/widget/RemoteViews.java +++ b/core/java/android/widget/RemoteViews.java @@ -1044,11 +1044,6 @@ public class RemoteViews implements Parcelable, Filter { public int getActionTag() { return SET_PENDING_INTENT_TEMPLATE_TAG; } - - @Override - public void visitUris(@NonNull Consumer<Uri> visitor) { - mPendingIntentTemplate.visitUris(visitor); - } } /** @@ -1424,6 +1419,10 @@ public class RemoteViews implements Parcelable, Filter { context.unbindService(this); } + if (items == null) { + items = new RemoteCollectionItems.Builder().build(); + } + result.complete(items); } @@ -1524,11 +1523,6 @@ public class RemoteViews implements Parcelable, Filter { public int getActionTag() { return SET_REMOTE_VIEW_ADAPTER_INTENT_TAG; } - - @Override - public void visitUris(@NonNull Consumer<Uri> visitor) { - mIntent.visitUris(visitor); - } } /** @@ -1607,11 +1601,6 @@ public class RemoteViews implements Parcelable, Filter { public int getActionTag() { return SET_ON_CLICK_RESPONSE_TAG; } - - @Override - public void visitUris(@NonNull Consumer<Uri> visitor) { - mResponse.visitUris(visitor); - } } /** Helper action to configure handwriting delegation via {@link PendingIntent}. */ @@ -1659,11 +1648,6 @@ public class RemoteViews implements Parcelable, Filter { public int getActionTag() { return SET_ON_STYLUS_HANDWRITING_RESPONSE_TAG; } - - @Override - public void visitUris(@NonNull Consumer<Uri> visitor) { - mPendingIntent.visitUris(visitor); - } } /** @@ -1734,11 +1718,6 @@ public class RemoteViews implements Parcelable, Filter { public int getActionTag() { return SET_ON_CHECKED_CHANGE_RESPONSE_TAG; } - - @Override - public void visitUris(@NonNull Consumer<Uri> visitor) { - mResponse.visitUris(visitor); - } } /** @hide **/ @@ -2298,10 +2277,6 @@ public class RemoteViews implements Parcelable, Filter { final Icon icon = (Icon) getParameterValue(null); if (icon != null) visitIconUri(icon, visitor); break; - case INTENT: - final Intent intent = (Intent) getParameterValue(null); - if (intent != null) intent.visitUris(visitor); - break; // TODO(b/281044385): Should we do anything about type BUNDLE? } } @@ -6661,7 +6636,8 @@ public class RemoteViews implements Parcelable, Filter { public static final class ColorResources { // Set of valid colors resources. private static final int FIRST_RESOURCE_COLOR_ID = android.R.color.system_neutral1_0; - private static final int LAST_RESOURCE_COLOR_ID = android.R.color.system_accent3_1000; + private static final int LAST_RESOURCE_COLOR_ID = + android.R.color.system_error_1000; // Size, in bytes, of an entry in the array of colors in an ARSC file. private static final int ARSC_ENTRY_SIZE = 16; @@ -7221,20 +7197,6 @@ public class RemoteViews implements Parcelable, Filter { mElementNames = parcel.createStringArrayList(); } - /** - * See {@link RemoteViews#visitUris(Consumer)}. - * - * @hide - */ - public void visitUris(@NonNull Consumer<Uri> visitor) { - if (mPendingIntent != null) { - mPendingIntent.visitUris(visitor); - } - if (mFillIntent != null) { - mFillIntent.visitUris(visitor); - } - } - private void handleViewInteraction( View v, InteractionHandler handler) { diff --git a/core/java/android/widget/SearchView.java b/core/java/android/widget/SearchView.java index b4d7a943ff17..b4d7a943ff17 100755..100644 --- a/core/java/android/widget/SearchView.java +++ b/core/java/android/widget/SearchView.java diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index 78dd3b18c2a6..fd3837f3fe12 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -4817,7 +4817,11 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener if (mFontWeightAdjustment != 0 && mFontWeightAdjustment != Configuration.FONT_WEIGHT_ADJUSTMENT_UNDEFINED) { if (tf == null) { - tf = Typeface.DEFAULT; + if (Flags.fixNullTypefaceBolding()) { + tf = Typeface.DEFAULT_BOLD; + } else { + tf = Typeface.DEFAULT; + } } else { int newWeight = Math.min( Math.max(tf.getWeight() + mFontWeightAdjustment, FontStyle.FONT_WEIGHT_MIN), diff --git a/core/java/android/window/BackNavigationInfo.java b/core/java/android/window/BackNavigationInfo.java index f24bc74dab7d..57bded7ff2a0 100644 --- a/core/java/android/window/BackNavigationInfo.java +++ b/core/java/android/window/BackNavigationInfo.java @@ -23,6 +23,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.TestApi; import android.graphics.Color; +import android.graphics.Rect; import android.os.Bundle; import android.os.Parcel; import android.os.Parcelable; @@ -113,6 +114,8 @@ public final class BackNavigationInfo implements Parcelable { private final CustomAnimationInfo mCustomAnimationInfo; private final int mLetterboxColor; + @NonNull + private final Rect mTouchableRegion; /** * Create a new {@link BackNavigationInfo} instance. @@ -128,7 +131,8 @@ public final class BackNavigationInfo implements Parcelable { boolean isPrepareRemoteAnimation, boolean isAnimationCallback, @Nullable CustomAnimationInfo customAnimationInfo, - int letterboxColor) { + int letterboxColor, + @Nullable Rect touchableRegion) { mType = type; mOnBackNavigationDone = onBackNavigationDone; mOnBackInvokedCallback = onBackInvokedCallback; @@ -136,6 +140,7 @@ public final class BackNavigationInfo implements Parcelable { mAnimationCallback = isAnimationCallback; mCustomAnimationInfo = customAnimationInfo; mLetterboxColor = letterboxColor; + mTouchableRegion = new Rect(touchableRegion); } private BackNavigationInfo(@NonNull Parcel in) { @@ -146,6 +151,7 @@ public final class BackNavigationInfo implements Parcelable { mAnimationCallback = in.readBoolean(); mCustomAnimationInfo = in.readTypedObject(CustomAnimationInfo.CREATOR); mLetterboxColor = in.readInt(); + mTouchableRegion = in.readTypedObject(Rect.CREATOR); } /** @hide */ @@ -158,6 +164,7 @@ public final class BackNavigationInfo implements Parcelable { dest.writeBoolean(mAnimationCallback); dest.writeTypedObject(mCustomAnimationInfo, flags); dest.writeInt(mLetterboxColor); + dest.writeTypedObject(mTouchableRegion, flags); } /** @@ -206,6 +213,16 @@ public final class BackNavigationInfo implements Parcelable { public int getLetterboxColor() { return mLetterboxColor; } + + /** + * @return The app window region where the client can handle touch event. + * @hide + */ + @NonNull + public Rect getTouchableRegion() { + return mTouchableRegion; + } + /** * Callback to be called when the back preview is finished in order to notify the server that * it can clean up the resources created for the animation. @@ -402,6 +419,7 @@ public final class BackNavigationInfo implements Parcelable { private boolean mAnimationCallback = false; private int mLetterboxColor = Color.TRANSPARENT; + private Rect mTouchableRegion; /** * @see BackNavigationInfo#getType() @@ -478,6 +496,13 @@ public final class BackNavigationInfo implements Parcelable { } /** + * @param rect Non-empty for frame of current focus window. + */ + public Builder setTouchableRegion(Rect rect) { + mTouchableRegion = new Rect(rect); + return this; + } + /** * Builds and returns an instance of {@link BackNavigationInfo} */ public BackNavigationInfo build() { @@ -486,7 +511,8 @@ public final class BackNavigationInfo implements Parcelable { mPrepareRemoteAnimation, mAnimationCallback, mCustomAnimationInfo, - mLetterboxColor); + mLetterboxColor, + mTouchableRegion); } } } diff --git a/core/java/android/window/BackProgressAnimator.java b/core/java/android/window/BackProgressAnimator.java index 94d7811677ae..d28500c0a1ea 100644 --- a/core/java/android/window/BackProgressAnimator.java +++ b/core/java/android/window/BackProgressAnimator.java @@ -19,8 +19,12 @@ package android.window; import android.annotation.NonNull; import android.annotation.Nullable; import android.util.FloatProperty; +import android.util.TimeUtils; +import android.view.Choreographer; import com.android.internal.dynamicanimation.animation.DynamicAnimation; +import com.android.internal.dynamicanimation.animation.FlingAnimation; +import com.android.internal.dynamicanimation.animation.FloatValueHolder; import com.android.internal.dynamicanimation.animation.SpringAnimation; import com.android.internal.dynamicanimation.animation.SpringForce; @@ -40,6 +44,7 @@ public class BackProgressAnimator implements DynamicAnimation.OnAnimationUpdateL * always receive progress values in [0, 1]. */ private static final float SCALE_FACTOR = 100f; + private static final float FLING_FRICTION = 8f; private final SpringAnimation mSpring; private ProgressCallback mCallback; private float mProgress = 0; @@ -48,11 +53,17 @@ public class BackProgressAnimator implements DynamicAnimation.OnAnimationUpdateL private boolean mBackAnimationInProgress = false; @Nullable private Runnable mBackCancelledFinishRunnable; + @Nullable + private Runnable mBackInvokedFinishRunnable; + private FlingAnimation mBackInvokedFlingAnim; private final DynamicAnimation.OnAnimationEndListener mOnAnimationEndListener = (animation, canceled, value, velocity) -> { - invokeBackCancelledRunnable(); + if (mBackCancelledFinishRunnable != null) invokeBackCancelledRunnable(); + if (mBackInvokedFinishRunnable != null) invokeBackInvokedRunnable(); reset(); }; + private final DynamicAnimation.OnAnimationUpdateListener mOnBackInvokedFlingUpdateListener = + (animation, progress, velocity) -> updateProgressValue(progress, velocity); private void setProgress(float progress) { @@ -78,7 +89,7 @@ public class BackProgressAnimator implements DynamicAnimation.OnAnimationUpdateL @Override public void onAnimationUpdate(DynamicAnimation animation, float value, float velocity) { - updateProgressValue(value, velocity); + if (mBackInvokedFinishRunnable == null) updateProgressValue(value, velocity); } @@ -134,6 +145,12 @@ public class BackProgressAnimator implements DynamicAnimation.OnAnimationUpdateL // Ensure that last progress value that apps see is 0 updateProgressValue(0, 0); invokeBackCancelledRunnable(); + } else if (mBackInvokedFinishRunnable != null) { + invokeBackInvokedRunnable(); + } + if (mBackInvokedFlingAnim != null) { + mBackInvokedFlingAnim.cancel(); + mBackInvokedFlingAnim = null; } mSpring.animateToFinalPosition(0); if (mSpring.canSkipToEnd()) { @@ -149,6 +166,30 @@ public class BackProgressAnimator implements DynamicAnimation.OnAnimationUpdateL } /** + * Animate the back progress animation a bit further with a high friction considering the + * current progress and velocity. + * + * @param finishCallback the callback to be invoked when the final destination is reached + */ + public void onBackInvoked(@NonNull Runnable finishCallback) { + mBackInvokedFinishRunnable = finishCallback; + mSpring.animateToFinalPosition(0); + + mBackInvokedFlingAnim = new FlingAnimation(new FloatValueHolder()) + .setStartValue(mProgress) + .setFriction(FLING_FRICTION) + .setStartVelocity(mVelocity) + .setMinValue(0) + .setMaxValue(SCALE_FACTOR); + mBackInvokedFlingAnim.addUpdateListener(mOnBackInvokedFlingUpdateListener); + mBackInvokedFlingAnim.addEndListener(mOnAnimationEndListener); + mBackInvokedFlingAnim.start(); + // do an animation-frame immediately to prevent idle frame + mBackInvokedFlingAnim.doAnimationFrame( + Choreographer.getInstance().getLastFrameTimeNanos() / TimeUtils.NANOS_PER_MS); + } + + /** * Animate the back progress animation from current progress to start position. * This should be called when back is cancelled. * @@ -196,4 +237,11 @@ public class BackProgressAnimator implements DynamicAnimation.OnAnimationUpdateL mBackCancelledFinishRunnable = null; } + private void invokeBackInvokedRunnable() { + mBackInvokedFlingAnim.removeUpdateListener(mOnBackInvokedFlingUpdateListener); + mBackInvokedFlingAnim.removeEndListener(mOnAnimationEndListener); + mBackInvokedFinishRunnable.run(); + mBackInvokedFinishRunnable = null; + } + }
\ No newline at end of file diff --git a/core/java/android/window/ImeOnBackInvokedDispatcher.java b/core/java/android/window/ImeOnBackInvokedDispatcher.java index 2a12507679f5..ce1f9869b690 100644 --- a/core/java/android/window/ImeOnBackInvokedDispatcher.java +++ b/core/java/android/window/ImeOnBackInvokedDispatcher.java @@ -55,6 +55,9 @@ public class ImeOnBackInvokedDispatcher implements OnBackInvokedDispatcher, Parc static final int RESULT_CODE_UNREGISTER = 1; @NonNull private final ResultReceiver mResultReceiver; + // The handler to run callbacks on. This should be on the same thread + // the ViewRootImpl holding IME's WindowOnBackInvokedDispatcher is created on. + private Handler mHandler; public ImeOnBackInvokedDispatcher(Handler handler) { mResultReceiver = new ResultReceiver(handler) { @@ -68,6 +71,10 @@ public class ImeOnBackInvokedDispatcher implements OnBackInvokedDispatcher, Parc }; } + void setHandler(@NonNull Handler handler) { + mHandler = handler; + } + /** * Override this method to return the {@link WindowOnBackInvokedDispatcher} of the window * that should receive the forwarded callback. @@ -326,7 +333,7 @@ public class ImeOnBackInvokedDispatcher implements OnBackInvokedDispatcher, Parc @Override public void onBackInvoked() { - mCallback.onBackInvoked(); + mHandler.post(mCallback::onBackInvoked); } @Override @@ -336,7 +343,7 @@ public class ImeOnBackInvokedDispatcher implements OnBackInvokedDispatcher, Parc private void maybeRunOnAnimationCallback(Consumer<OnBackAnimationCallback> block) { if (mCallback instanceof OnBackAnimationCallback) { - block.accept((OnBackAnimationCallback) mCallback); + mHandler.post(() -> block.accept((OnBackAnimationCallback) mCallback)); } } } diff --git a/core/java/android/window/TaskFragmentAnimationParams.java b/core/java/android/window/TaskFragmentAnimationParams.java index c8f632707966..85e96c9c0290 100644 --- a/core/java/android/window/TaskFragmentAnimationParams.java +++ b/core/java/android/window/TaskFragmentAnimationParams.java @@ -16,17 +16,21 @@ package android.window; +import static android.window.TransitionInfo.AnimationOptions.DEFAULT_ANIMATION_RESOURCES_ID; + +import android.annotation.AnimRes; import android.annotation.ColorInt; import android.annotation.NonNull; import android.annotation.Nullable; import android.os.Parcel; import android.os.Parcelable; +import java.util.Objects; + /** * Data object for animation related override of TaskFragment. * @hide */ -// TODO(b/206557124): Add more animation customization options. public final class TaskFragmentAnimationParams implements Parcelable { /** The default {@link TaskFragmentAnimationParams} to use when there is no app override. */ @@ -43,8 +47,22 @@ public final class TaskFragmentAnimationParams implements Parcelable { @ColorInt private final int mAnimationBackgroundColor; - private TaskFragmentAnimationParams(@ColorInt int animationBackgroundColor) { + @AnimRes + private final int mOpenAnimationResId; + + @AnimRes + private final int mChangeAnimationResId; + + @AnimRes + private final int mCloseAnimationResId; + + private TaskFragmentAnimationParams(@ColorInt int animationBackgroundColor, + @AnimRes int openAnimationResId, @AnimRes int changeAnimationResId, + @AnimRes int closeAnimationResId) { mAnimationBackgroundColor = animationBackgroundColor; + mOpenAnimationResId = openAnimationResId; + mChangeAnimationResId = changeAnimationResId; + mCloseAnimationResId = closeAnimationResId; } /** @@ -58,13 +76,52 @@ public final class TaskFragmentAnimationParams implements Parcelable { return mAnimationBackgroundColor; } + /** + * Returns the resources ID of open animation that applies to this TaskFragment. + * <p> + * The default value is {@link DEFAULT_ANIMATION_RESOURCES_ID}, which is to use the system + * default animation. + */ + @AnimRes + public int getOpenAnimationResId() { + return mOpenAnimationResId; + } + + /** + * Returns the resources ID of change animation that applies to this TaskFragment. + * <p> + * The default value is {@link DEFAULT_ANIMATION_RESOURCES_ID}, which is to use the system + * default animation. + */ + @AnimRes + public int getChangeAnimationResId() { + return mChangeAnimationResId; + } + + /** + * Returns the resources ID of close animation that applies to this TaskFragment. + * <p> + * The default value is {@link DEFAULT_ANIMATION_RESOURCES_ID}, which is to use the system + * default animation. + */ + @AnimRes + public int getCloseAnimationResId() { + return mCloseAnimationResId; + } + private TaskFragmentAnimationParams(Parcel in) { mAnimationBackgroundColor = in.readInt(); + mOpenAnimationResId = in.readInt(); + mChangeAnimationResId = in.readInt(); + mCloseAnimationResId = in.readInt(); } @Override public void writeToParcel(@NonNull Parcel dest, int flags) { dest.writeInt(mAnimationBackgroundColor); + dest.writeInt(mOpenAnimationResId); + dest.writeInt(mChangeAnimationResId); + dest.writeInt(mCloseAnimationResId); } @NonNull @@ -85,21 +142,37 @@ public final class TaskFragmentAnimationParams implements Parcelable { public String toString() { return "TaskFragmentAnimationParams{" + " animationBgColor=" + Integer.toHexString(mAnimationBackgroundColor) + + " openAnimResId=" + mOpenAnimationResId + + " changeAnimResId=" + mChangeAnimationResId + + " closeAnimResId=" + mCloseAnimationResId + "}"; } @Override public int hashCode() { - return mAnimationBackgroundColor; + return Objects.hash(mAnimationBackgroundColor, mOpenAnimationResId, mChangeAnimationResId, + mCloseAnimationResId); } @Override public boolean equals(@Nullable Object obj) { - if (!(obj instanceof TaskFragmentAnimationParams)) { + if (!(obj instanceof TaskFragmentAnimationParams other)) { return false; } - final TaskFragmentAnimationParams other = (TaskFragmentAnimationParams) obj; - return mAnimationBackgroundColor == other.mAnimationBackgroundColor; + return mAnimationBackgroundColor == other.mAnimationBackgroundColor + && mOpenAnimationResId == other.mOpenAnimationResId + && mChangeAnimationResId == other.mChangeAnimationResId + && mCloseAnimationResId == other.mCloseAnimationResId; + } + + /** + * Returns {@code true} if one of {@link #getOpenAnimationResId()}, + * {@link #getChangeAnimationResId()} or {@link #getCloseAnimationResId()} is specified. + */ + public boolean hasOverrideAnimation() { + return mOpenAnimationResId != DEFAULT_ANIMATION_RESOURCES_ID + || mChangeAnimationResId != DEFAULT_ANIMATION_BACKGROUND_COLOR + || mCloseAnimationResId != DEFAULT_ANIMATION_RESOURCES_ID; } @Override @@ -113,6 +186,15 @@ public final class TaskFragmentAnimationParams implements Parcelable { @ColorInt private int mAnimationBackgroundColor = DEFAULT_ANIMATION_BACKGROUND_COLOR; + @AnimRes + private int mOpenAnimationResId = DEFAULT_ANIMATION_RESOURCES_ID; + + @AnimRes + private int mChangeAnimationResId = DEFAULT_ANIMATION_RESOURCES_ID; + + @AnimRes + private int mCloseAnimationResId = DEFAULT_ANIMATION_RESOURCES_ID; + /** * Sets the {@link ColorInt} to use for the background during the animation with this * TaskFragment if the animation requires a background. The default value is @@ -128,10 +210,50 @@ public final class TaskFragmentAnimationParams implements Parcelable { return this; } + /** + * Sets the open animation resources ID this TaskFragment. The default value is + * {@link DEFAULT_ANIMATION_RESOURCES_ID}, which is to use the system default animation. + * + * @param resId the open animation resources ID. + * @return this {@link Builder}. + */ + @NonNull + public Builder setOpenAnimationResId(@AnimRes int resId) { + mOpenAnimationResId = resId; + return this; + } + + /** + * Sets the change animation resources ID this TaskFragment. The default value is + * {@link DEFAULT_ANIMATION_RESOURCES_ID}, which is to use the system default animation. + * + * @param resId the change animation resources ID. + * @return this {@link Builder}. + */ + @NonNull + public Builder setChangeAnimationResId(@AnimRes int resId) { + mChangeAnimationResId = resId; + return this; + } + + /** + * Sets the close animation resources ID this TaskFragment. The default value is + * {@link DEFAULT_ANIMATION_RESOURCES_ID}, which is to use the system default animation. + * + * @param resId the close animation resources ID. + * @return this {@link Builder}. + */ + @NonNull + public Builder setCloseAnimationResId(@AnimRes int resId) { + mCloseAnimationResId = resId; + return this; + } + /** Constructs the {@link TaskFragmentAnimationParams}. */ @NonNull public TaskFragmentAnimationParams build() { - return new TaskFragmentAnimationParams(mAnimationBackgroundColor); + return new TaskFragmentAnimationParams(mAnimationBackgroundColor, + mOpenAnimationResId, mChangeAnimationResId, mCloseAnimationResId); } } } diff --git a/core/java/android/window/TransitionInfo.java b/core/java/android/window/TransitionInfo.java index 8a79754398db..8bd39fbe3a52 100644 --- a/core/java/android/window/TransitionInfo.java +++ b/core/java/android/window/TransitionInfo.java @@ -1107,13 +1107,14 @@ public final class TransitionInfo implements Parcelable { private int mType; private @AnimRes int mEnterResId = DEFAULT_ANIMATION_RESOURCES_ID; + private @AnimRes int mChangeResId = DEFAULT_ANIMATION_RESOURCES_ID; private @AnimRes int mExitResId = DEFAULT_ANIMATION_RESOURCES_ID; private boolean mOverrideTaskTransition; private String mPackageName; private final Rect mTransitionBounds = new Rect(); private HardwareBuffer mThumbnail; private int mAnimations; - // TODO(b/295805497): Extract it from AnimationOptions + // TODO(b/295805497): Extract mBackgroundColor from AnimationOptions private @ColorInt int mBackgroundColor; // Customize activity transition animation private CustomActivityTransition mCustomActivityOpenTransition; @@ -1126,6 +1127,7 @@ public final class TransitionInfo implements Parcelable { private AnimationOptions(Parcel in) { mType = in.readInt(); mEnterResId = in.readInt(); + mChangeResId = in.readInt(); mExitResId = in.readInt(); mBackgroundColor = in.readInt(); mOverrideTaskTransition = in.readBoolean(); @@ -1189,9 +1191,27 @@ public final class TransitionInfo implements Parcelable { public static AnimationOptions makeCustomAnimOptions(@NonNull String packageName, @AnimRes int enterResId, @AnimRes int exitResId, @ColorInt int backgroundColor, boolean overrideTaskTransition) { + return makeCustomAnimOptions(packageName, enterResId, DEFAULT_ANIMATION_RESOURCES_ID, + exitResId, backgroundColor, overrideTaskTransition); + } + + /** + * Creates a {@link android.app.ActivityOptions#ANIM_CUSTOM} {@link AnimationOptions}. + * + * @param packageName the package name that includes the animation resources. + * @param enterResId the resources ID of open animation. + * @param changeResId the resources ID of change animation. + * @param exitResId the resources ID of close animation. + * @param overrideTaskTransition indicates whether to override task transition. + */ + @NonNull + public static AnimationOptions makeCustomAnimOptions(@NonNull String packageName, + @AnimRes int enterResId, @AnimRes int changeResId, @AnimRes int exitResId, + @ColorInt int backgroundColor, boolean overrideTaskTransition) { AnimationOptions options = new AnimationOptions(ANIM_CUSTOM); options.mPackageName = packageName; options.mEnterResId = enterResId; + options.mChangeResId = changeResId; options.mExitResId = exitResId; options.mBackgroundColor = backgroundColor; options.mOverrideTaskTransition = overrideTaskTransition; @@ -1251,6 +1271,11 @@ public final class TransitionInfo implements Parcelable { } @AnimRes + public int getChangeResId() { + return mChangeResId; + } + + @AnimRes public int getExitResId() { return mExitResId; } @@ -1292,6 +1317,7 @@ public final class TransitionInfo implements Parcelable { public void writeToParcel(@NonNull Parcel dest, int flags) { dest.writeInt(mType); dest.writeInt(mEnterResId); + dest.writeInt(mChangeResId); dest.writeInt(mExitResId); dest.writeInt(mBackgroundColor); dest.writeBoolean(mOverrideTaskTransition); @@ -1352,6 +1378,9 @@ public final class TransitionInfo implements Parcelable { if (mEnterResId != DEFAULT_ANIMATION_RESOURCES_ID) { sb.append(" enterResId=").append(mEnterResId); } + if (mChangeResId != DEFAULT_ANIMATION_RESOURCES_ID) { + sb.append(" changeResId=").append(mChangeResId); + } if (mExitResId != DEFAULT_ANIMATION_RESOURCES_ID) { sb.append(" exitResId=").append(mExitResId); } diff --git a/core/java/android/window/WindowContainerTransaction.java b/core/java/android/window/WindowContainerTransaction.java index 5e88d97c805e..f4f6c8aa3636 100644 --- a/core/java/android/window/WindowContainerTransaction.java +++ b/core/java/android/window/WindowContainerTransaction.java @@ -375,7 +375,23 @@ public final class WindowContainerTransaction implements Parcelable { */ @NonNull public WindowContainerTransaction reorder(@NonNull WindowContainerToken child, boolean onTop) { - mHierarchyOps.add(HierarchyOp.createForReorder(child.asBinder(), onTop)); + return reorder(child, onTop, false /* includingParents */); + } + + /** + * Reorders a container within its parent with an option to reorder all the parents in the + * hierarchy above among their respective siblings. + * + * @param onTop When {@code true}, the child goes to the top of parent; otherwise it goes to + * the bottom. + * @param includingParents When {@code true}, all the parents in the hierarchy above are also + * reordered among their respective siblings. + * @hide + */ + @NonNull + public WindowContainerTransaction reorder(@NonNull WindowContainerToken child, boolean onTop, + boolean includingParents) { + mHierarchyOps.add(HierarchyOp.createForReorder(child.asBinder(), onTop, includingParents)); return this; } @@ -1451,6 +1467,8 @@ public final class WindowContainerTransaction implements Parcelable { @Nullable private Rect mBounds; + private boolean mIncludingParents; + private boolean mAlwaysOnTop; private boolean mReparentLeafTaskIfRelaunch; @@ -1464,11 +1482,22 @@ public final class WindowContainerTransaction implements Parcelable { .build(); } - public static HierarchyOp createForReorder(@NonNull IBinder container, boolean toTop) { + /** + * Creates the {@link HierarchyOp} for the reorder operation. + * + * @param container which needs to be reordered + * @param toTop if true, the container reorders + * @param includingParents if true, all the parents in the hierarchy above are also + * reoredered among their respective siblings + * @return + */ + public static HierarchyOp createForReorder(@NonNull IBinder container, boolean toTop, + boolean includingParents) { return new HierarchyOp.Builder(HIERARCHY_OP_TYPE_REORDER) .setContainer(container) .setReparentContainer(container) .setToTop(toTop) + .setIncludingParents(includingParents) .build(); } @@ -1555,6 +1584,7 @@ public final class WindowContainerTransaction implements Parcelable { mType = copy.mType; mContainer = copy.mContainer; mBounds = copy.mBounds; + mIncludingParents = copy.mIncludingParents; mReparent = copy.mReparent; mInsetsFrameProvider = copy.mInsetsFrameProvider; mInsetsFrameOwner = copy.mInsetsFrameOwner; @@ -1575,6 +1605,7 @@ public final class WindowContainerTransaction implements Parcelable { mType = in.readInt(); mContainer = in.readStrongBinder(); mBounds = in.readTypedObject(Rect.CREATOR); + mIncludingParents = in.readBoolean(); mReparent = in.readStrongBinder(); mInsetsFrameProvider = in.readTypedObject(InsetsFrameProvider.CREATOR); mInsetsFrameOwner = in.readStrongBinder(); @@ -1678,6 +1709,12 @@ public final class WindowContainerTransaction implements Parcelable { return mBounds; } + /** Denotes whether the parents should also be included in the op. */ + @NonNull + public boolean includingParents() { + return mIncludingParents; + } + /** Gets a string representation of a hierarchy-op type. */ public static String hopToString(int type) { switch (type) { @@ -1789,6 +1826,7 @@ public final class WindowContainerTransaction implements Parcelable { dest.writeInt(mType); dest.writeStrongBinder(mContainer); dest.writeTypedObject(mBounds, flags); + dest.writeBoolean(mIncludingParents); dest.writeStrongBinder(mReparent); dest.writeTypedObject(mInsetsFrameProvider, flags); dest.writeStrongBinder(mInsetsFrameOwner); @@ -1866,6 +1904,8 @@ public final class WindowContainerTransaction implements Parcelable { @Nullable private Rect mBounds; + private boolean mIncludingParents; + private boolean mAlwaysOnTop; private boolean mReparentLeafTaskIfRelaunch; @@ -1955,6 +1995,11 @@ public final class WindowContainerTransaction implements Parcelable { return this; } + Builder setIncludingParents(boolean value) { + mIncludingParents = value; + return this; + } + HierarchyOp build() { final HierarchyOp hierarchyOp = new HierarchyOp(mType); hierarchyOp.mContainer = mContainer; @@ -1976,6 +2021,7 @@ public final class WindowContainerTransaction implements Parcelable { hierarchyOp.mTaskFragmentOperation = mTaskFragmentOperation; hierarchyOp.mShortcutInfo = mShortcutInfo; hierarchyOp.mBounds = mBounds; + hierarchyOp.mIncludingParents = mIncludingParents; hierarchyOp.mReparentLeafTaskIfRelaunch = mReparentLeafTaskIfRelaunch; return hierarchyOp; diff --git a/core/java/android/window/WindowOnBackInvokedDispatcher.java b/core/java/android/window/WindowOnBackInvokedDispatcher.java index 0fb5e3482178..4ca64e73ad7c 100644 --- a/core/java/android/window/WindowOnBackInvokedDispatcher.java +++ b/core/java/android/window/WindowOnBackInvokedDispatcher.java @@ -37,7 +37,6 @@ import android.view.IWindow; import android.view.IWindowSession; import android.view.ImeBackAnimationController; import android.view.MotionEvent; -import android.view.ViewRootImpl; import androidx.annotation.VisibleForTesting; @@ -69,7 +68,6 @@ import java.util.function.Supplier; public class WindowOnBackInvokedDispatcher implements OnBackInvokedDispatcher { private IWindowSession mWindowSession; private IWindow mWindow; - private ViewRootImpl mViewRoot; @VisibleForTesting public final BackTouchTracker mTouchTracker = new BackTouchTracker(); @VisibleForTesting @@ -136,12 +134,10 @@ public class WindowOnBackInvokedDispatcher implements OnBackInvokedDispatcher { * is attached a window. */ public void attachToWindow(@NonNull IWindowSession windowSession, @NonNull IWindow window, - @Nullable ViewRootImpl viewRoot, @Nullable ImeBackAnimationController imeBackAnimationController) { synchronized (mLock) { mWindowSession = windowSession; mWindow = window; - mViewRoot = viewRoot; mImeBackAnimationController = imeBackAnimationController; if (!mAllCallbacks.isEmpty()) { setTopOnBackInvokedCallback(getTopCallback()); @@ -155,7 +151,6 @@ public class WindowOnBackInvokedDispatcher implements OnBackInvokedDispatcher { clear(); mWindow = null; mWindowSession = null; - mViewRoot = null; mImeBackAnimationController = null; } } @@ -181,6 +176,8 @@ public class WindowOnBackInvokedDispatcher implements OnBackInvokedDispatcher { return; } if (callback instanceof ImeOnBackInvokedDispatcher.ImeOnBackInvokedCallback) { + // Fall back to compat back key injection if legacy back behaviour should be used. + if (!isOnBackInvokedCallbackEnabled()) return; if (callback instanceof ImeOnBackInvokedDispatcher.DefaultImeOnBackAnimationCallback && mImeBackAnimationController != null) { // register ImeBackAnimationController instead to play predictive back animation @@ -312,7 +309,7 @@ public class WindowOnBackInvokedDispatcher implements OnBackInvokedDispatcher { if (callback != null) { int priority = mAllCallbacks.get(callback); final IOnBackInvokedCallback iCallback = new OnBackInvokedCallbackWrapper( - callback, mTouchTracker, mProgressAnimator, mHandler, mViewRoot); + callback, mTouchTracker, mProgressAnimator, mHandler); callbackInfo = new OnBackInvokedCallbackInfo( iCallback, priority, @@ -402,20 +399,16 @@ public class WindowOnBackInvokedDispatcher implements OnBackInvokedDispatcher { private final BackTouchTracker mTouchTracker; @NonNull private final Handler mHandler; - @Nullable - private ViewRootImpl mViewRoot; OnBackInvokedCallbackWrapper( @NonNull OnBackInvokedCallback callback, @NonNull BackTouchTracker touchTracker, @NonNull BackProgressAnimator progressAnimator, - @NonNull Handler handler, - @Nullable ViewRootImpl viewRoot) { + @NonNull Handler handler) { mCallback = new WeakReference<>(callback); mTouchTracker = touchTracker; mProgressAnimator = progressAnimator; mHandler = handler; - mViewRoot = viewRoot; } @Override @@ -442,7 +435,16 @@ public class WindowOnBackInvokedDispatcher implements OnBackInvokedDispatcher { } @Override - public void onBackProgressed(BackMotionEvent backEvent) { } + public void onBackProgressed(BackMotionEvent backEvent) { + // This is only called in some special cases such as when activity embedding is active + // or when the activity is letterboxed. Otherwise mProgressAnimator#onBackProgressed is + // called from WindowOnBackInvokedDispatcher#onMotionEvent + mHandler.post(() -> { + if (getBackAnimationCallback() != null) { + mProgressAnimator.onBackProgressed(backEvent); + } + }); + } @Override public void onBackCancelled() { @@ -458,12 +460,10 @@ public class WindowOnBackInvokedDispatcher implements OnBackInvokedDispatcher { public void onBackInvoked() throws RemoteException { mHandler.post(() -> { mTouchTracker.reset(); - if (consumedByOnKeyPreIme()) return; boolean isInProgress = mProgressAnimator.isBackAnimationInProgress(); - mProgressAnimator.reset(); - // TODO(b/333957271): Re-introduce auto fling progress generation. final OnBackInvokedCallback callback = mCallback.get(); if (callback == null) { + mProgressAnimator.reset(); Log.d(TAG, "Trying to call onBackInvoked() on a null callback reference."); return; } @@ -471,28 +471,14 @@ public class WindowOnBackInvokedDispatcher implements OnBackInvokedDispatcher { Log.w(TAG, "ProgressAnimator was not in progress, skip onBackInvoked()."); return; } - callback.onBackInvoked(); - }); - } - - private boolean consumedByOnKeyPreIme() { - final OnBackInvokedCallback callback = mCallback.get(); - if ((callback instanceof ImeBackAnimationController - || callback instanceof ImeOnBackInvokedDispatcher.ImeOnBackInvokedCallback) - && mViewRoot != null && !isOnBackInvokedCallbackEnabled(mViewRoot.mContext)) { - // call onKeyPreIme API if the current callback is an IME callback and the app has - // not set enableOnBackInvokedCallback="false" - boolean consumed = mViewRoot.injectBackKeyEvents(/*preImeOnly*/ true); - if (consumed) { - // back event intercepted by app in onKeyPreIme -> cancel the IME animation. - final OnBackAnimationCallback animationCallback = getBackAnimationCallback(); - if (animationCallback != null) { - mProgressAnimator.onBackCancelled(animationCallback::onBackCancelled); - } - return true; + OnBackAnimationCallback animationCallback = getBackAnimationCallback(); + if (animationCallback != null) { + mProgressAnimator.onBackInvoked(callback::onBackInvoked); + } else { + mProgressAnimator.reset(); + callback.onBackInvoked(); } - } - return false; + }); } @Override @@ -532,6 +518,7 @@ public class WindowOnBackInvokedDispatcher implements OnBackInvokedDispatcher { public void setImeOnBackInvokedDispatcher( @NonNull ImeOnBackInvokedDispatcher imeDispatcher) { mImeDispatcher = imeDispatcher; + mImeDispatcher.setHandler(mHandler); } /** Returns true if a non-null {@link ImeOnBackInvokedDispatcher} has been set. **/ diff --git a/core/java/android/window/flags/lse_desktop_experience.aconfig b/core/java/android/window/flags/lse_desktop_experience.aconfig index 0590c407d7e4..ca125dafc2f7 100644 --- a/core/java/android/window/flags/lse_desktop_experience.aconfig +++ b/core/java/android/window/flags/lse_desktop_experience.aconfig @@ -52,6 +52,13 @@ flag { } flag { + name: "enable_desktop_windowing_scvh_cache" + namespace: "lse_desktop_experience" + description: "Enables a SurfaceControlViewHost cache for window decorations" + bug: "345146928" +} + +flag { name: "enable_desktop_windowing_wallpaper_activity" namespace: "lse_desktop_experience" description: "Enables desktop wallpaper activity to show wallpaper in the desktop mode" @@ -92,3 +99,24 @@ flag { description: "Makes the App Header style adapt to the system's and app's light/dark theme" bug: "328668781" } + +flag { + name: "enable_camera_compat_for_desktop_windowing" + namespace: "lse_desktop_experience" + description: "Whether to apply Camera Compat treatment to fixed-orientation apps in desktop windowing mode" + bug: "314952133" +} + +flag { + name: "enable_task_stack_observer_in_shell" + namespace: "lse_desktop_experience" + description: "Introduces a new observer in shell to track the task stack." + bug: "341932484" +} + +flag { + name: "enable_desktop_windowing_size_constraints" + namespace: "lse_desktop_experience" + description: "Whether to enable min/max window size constraints when resizing a window in desktop windowing mode" + bug: "327589741" +} diff --git a/core/java/android/window/flags/windowing_frontend.aconfig b/core/java/android/window/flags/windowing_frontend.aconfig index d6f65f8c9d8b..b71468247e37 100644 --- a/core/java/android/window/flags/windowing_frontend.aconfig +++ b/core/java/android/window/flags/windowing_frontend.aconfig @@ -19,6 +19,16 @@ flag { } flag { + name: "blast_sync_notification_shade_on_display_switch" + namespace: "windowing_frontend" + description: "Make the buffer content of notification shade synchronize with display switch" + bug: "337154331" + 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" diff --git a/core/java/android/window/flags/windowing_sdk.aconfig b/core/java/android/window/flags/windowing_sdk.aconfig index 9e69f8910bab..0a4762d91610 100644 --- a/core/java/android/window/flags/windowing_sdk.aconfig +++ b/core/java/android/window/flags/windowing_sdk.aconfig @@ -169,3 +169,25 @@ flag { purpose: PURPOSE_BUGFIX } } + +flag { + namespace: "windowing_sdk" + name: "disable_object_pool" + description: "Whether to disable object pool and let the GC handle lifecycle items" + bug: "311089192" + is_fixed_read_only: true + metadata { + purpose: PURPOSE_BUGFIX + } +} + +flag { + namespace: "windowing_sdk" + name: "rear_display_disable_force_desktop_system_decorations" + description: "Block system decorations from being added to a rear display when desktop mode is forced" + bug: "346103150" + is_fixed_read_only: true + metadata { + purpose: PURPOSE_BUGFIX + } +} diff --git a/core/java/com/android/internal/app/IBatteryStats.aidl b/core/java/com/android/internal/app/IBatteryStats.aidl index 99b3f9a16355..ebcae277c62b 100644 --- a/core/java/com/android/internal/app/IBatteryStats.aidl +++ b/core/java/com/android/internal/app/IBatteryStats.aidl @@ -21,6 +21,7 @@ import android.os.BatteryUsageStats; import android.os.BatteryUsageStatsQuery; import android.os.BluetoothBatteryStats; import android.os.ParcelFileDescriptor; +import android.os.ResultReceiver; import android.os.WakeLockStats; import android.os.WorkSource; import android.os.connectivity.CellularBatteryStats; @@ -33,6 +34,9 @@ import android.telephony.ModemActivityInfo; import android.telephony.SignalStrength; interface IBatteryStats { + /** @hide */ + const String KEY_UID_SNAPSHOTS = "uid_snapshots"; + // These first methods are also called by native code, so must // be kept in sync with frameworks/native/libs/binder/include_batterystats/batterystats/IBatteryStats.h @EnforcePermission("UPDATE_DEVICE_STATS") @@ -256,6 +260,9 @@ interface IBatteryStats { @PermissionManuallyEnforced HealthStatsParceler[] takeUidSnapshots(in int[] uid); + @PermissionManuallyEnforced + oneway void takeUidSnapshotsAsync(in int[] uid, in ResultReceiver result); + @EnforcePermission("UPDATE_DEVICE_STATS") oneway void noteBluetoothControllerActivity(in BluetoothActivityEnergyInfo info); @EnforcePermission("UPDATE_DEVICE_STATS") diff --git a/core/java/com/android/internal/app/NoOpResolverComparator.java b/core/java/com/android/internal/app/NoOpResolverComparator.java new file mode 100644 index 000000000000..51eaa8196d4a --- /dev/null +++ b/core/java/com/android/internal/app/NoOpResolverComparator.java @@ -0,0 +1,88 @@ +/* + * 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.internal.app; + +import android.content.Context; +import android.content.Intent; +import android.content.pm.ResolveInfo; +import android.os.Message; +import android.os.UserHandle; + +import androidx.annotation.Nullable; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.app.ResolverActivity.ResolvedComponentInfo; +import com.android.internal.app.chooser.TargetInfo; + +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; + + +/** + * A basic {@link AbstractResolverComparator} implementation that sorts items into the same order as + * they appeared in the list provided to {@link #doCompute(List)}. "Unknown" items that didn't + * appear in the original list are ordered arbitrarily at the end. + */ +public class NoOpResolverComparator extends AbstractResolverComparator { + @Nullable + private List<ResolveInfo> mOriginalTargetOrder = null; + + public NoOpResolverComparator( + Context launchedFromContext, + Intent intent, + List<UserHandle> targetUserSpaceList) { + super(launchedFromContext, intent, targetUserSpaceList); + } + + @Override + public void doCompute(List<ResolvedComponentInfo> targets) { + mOriginalTargetOrder = new ArrayList<>(); + for (ResolvedComponentInfo target : targets) { + mOriginalTargetOrder.add(target.getResolveInfoAt(0)); + } + afterCompute(); + } + + @Override + public int compare(ResolveInfo lhs, ResolveInfo rhs) { + Comparator<ResolveInfo> c = Comparator.comparingDouble(r -> getScore((ResolveInfo) r)); + c = c.reversed(); + return c.compare(lhs, rhs); + } + + @Override + public float getScore(TargetInfo targetInfo) { + return getScore(targetInfo.getResolveInfo()); + } + + @Override + public void handleResultMessage(Message message) {} + + @VisibleForTesting + public float getScore(ResolveInfo resolveInfo) { + if (!mOriginalTargetOrder.contains(resolveInfo)) { + return 0; + } + + // Assign a score from 1 (for the first item in the original list) down + // to 1/(n+1) for the last item (which is still greater than 0, the + // score we assign to any unknown items). + float rank = mOriginalTargetOrder.indexOf(resolveInfo); + return 1.0f - (rank / (1 + mOriginalTargetOrder.size())); + } +} diff --git a/core/java/com/android/internal/app/PlatLogoActivity.java b/core/java/com/android/internal/app/PlatLogoActivity.java index 71bbccb3d989..b8f7a3d0231a 100644 --- a/core/java/com/android/internal/app/PlatLogoActivity.java +++ b/core/java/com/android/internal/app/PlatLogoActivity.java @@ -69,7 +69,7 @@ public class PlatLogoActivity extends Activity { private static final long LAUNCH_TIME = 5000L; - private static final String U_EGG_UNLOCK_SETTING = "egg_mode_u"; + private static final String EGG_UNLOCK_SETTING = "egg_mode_v"; private static final float MIN_WARP = 1f; private static final float MAX_WARP = 10f; // after all these years @@ -309,13 +309,12 @@ public class PlatLogoActivity extends Activity { private void launchNextStage(boolean locked) { final ContentResolver cr = getContentResolver(); - try { if (shouldWriteSettings()) { Log.v(TAG, "Saving egg locked=" + locked); syncTouchPressure(); Settings.System.putLong(cr, - U_EGG_UNLOCK_SETTING, + EGG_UNLOCK_SETTING, locked ? 0 : System.currentTimeMillis()); } } catch (RuntimeException e) { @@ -499,4 +498,4 @@ public class PlatLogoActivity extends Activity { mDt = dt; } } -}
\ No newline at end of file +} diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java index 17adee4cc49e..920981e2b8fe 100644 --- a/core/java/com/android/internal/app/ResolverActivity.java +++ b/core/java/com/android/internal/app/ResolverActivity.java @@ -170,6 +170,10 @@ public class ResolverActivity extends Activity implements // Expected to be true if this object is ResolverActivity or is ResolverWrapperActivity. private final boolean mIsIntentPicker; + // Whether this activity was instantiated with a specialized constructor that predefines a list + // of resolutions to be displayed for the target intent (as in, e.g., the NFC use case). + private boolean mHasSubclassSpecifiedResolutions; + // Whether or not this activity supports choosing a default handler for the intent. @VisibleForTesting protected boolean mSupportsAlwaysUseOption; @@ -421,6 +425,8 @@ public class ResolverActivity extends Activity implements setTheme(appliedThemeResId()); super.onCreate(savedInstanceState); + mHasSubclassSpecifiedResolutions = (rList != null); + mQuietModeManager = createQuietModeManager(); // Determine whether we should show that intent is forwarded @@ -917,7 +923,7 @@ public class ResolverActivity extends Activity implements mSystemWindowInsets = insets.getSystemWindowInsets(); mResolverDrawerLayout.setPadding(mSystemWindowInsets.left, mSystemWindowInsets.top, - mSystemWindowInsets.right, mSystemWindowInsets.bottom); + mSystemWindowInsets.right, 0); resetButtonBar(); @@ -946,7 +952,7 @@ public class ResolverActivity extends Activity implements if (mSystemWindowInsets != null) { mResolverDrawerLayout.setPadding(mSystemWindowInsets.left, mSystemWindowInsets.top, - mSystemWindowInsets.right, mSystemWindowInsets.bottom); + mSystemWindowInsets.right, 0); } } @@ -1698,17 +1704,25 @@ public class ResolverActivity extends Activity implements isAudioCaptureDevice, initialIntentsUserSpace); } + private AbstractResolverComparator makeResolverComparator(UserHandle userHandle) { + if (mHasSubclassSpecifiedResolutions) { + return new NoOpResolverComparator( + this, getTargetIntent(), getResolverRankerServiceUserHandleList(userHandle)); + } else { + return new ResolverRankerServiceResolverComparator( + this, + getTargetIntent(), + getReferrerPackageName(), + null, + null, + getResolverRankerServiceUserHandleList(userHandle)); + } + } + @VisibleForTesting protected ResolverListController createListController(UserHandle userHandle) { UserHandle queryIntentsUser = getQueryIntentsUser(userHandle); - ResolverRankerServiceResolverComparator resolverComparator = - new ResolverRankerServiceResolverComparator( - this, - getTargetIntent(), - getReferrerPackageName(), - null, - null, - getResolverRankerServiceUserHandleList(userHandle)); + AbstractResolverComparator resolverComparator = makeResolverComparator(userHandle); return new ResolverListController( this, mPm, diff --git a/core/java/com/android/internal/dynamicanimation/animation/FlingAnimation.java b/core/java/com/android/internal/dynamicanimation/animation/FlingAnimation.java new file mode 100644 index 000000000000..2bd0568c3af5 --- /dev/null +++ b/core/java/com/android/internal/dynamicanimation/animation/FlingAnimation.java @@ -0,0 +1,203 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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.dynamicanimation.animation; + +import android.annotation.FloatRange; +/** + * <p>Fling animation is an animation that continues an initial momentum (most often from gesture + * velocity) and gradually slows down. The fling animation will come to a stop when the velocity of + * the animation is below the threshold derived from {@link #setMinimumVisibleChange(float)}, + * or when the value of the animation has gone beyond the min or max value defined via + * {@link DynamicAnimation#setMinValue(float)} or {@link DynamicAnimation#setMaxValue(float)}. + * It is recommended to restrict the fling animation with min and/or max value, such that the + * animation can end when it goes beyond screen bounds, thus preserving CPU cycles and resources. + * + * <p>For example, you can create a fling animation that animates the translationX of a view: + * <pre class="prettyprint"> + * FlingAnimation flingAnim = new FlingAnimation(view, DynamicAnimation.TRANSLATION_X) + * // Sets the start velocity to -2000 (pixel/s) + * .setStartVelocity(-2000) + * // Optional but recommended to set a reasonable min and max range for the animation. + * // In this particular case, we set the min and max to -200 and 2000 respectively. + * .setMinValue(-200).setMaxValue(2000); + * flingAnim.start(); + * </pre> + */ +public final class FlingAnimation extends DynamicAnimation<FlingAnimation> { + private final DragForce mFlingForce = new DragForce(); + /** + * <p>This creates a FlingAnimation that animates a {@link FloatValueHolder} instance. During + * the animation, the {@link FloatValueHolder} instance will be updated via + * {@link FloatValueHolder#setValue(float)} each frame. The caller can obtain the up-to-date + * animation value via {@link FloatValueHolder#getValue()}. + * + * <p><strong>Note:</strong> changing the value in the {@link FloatValueHolder} via + * {@link FloatValueHolder#setValue(float)} outside of the animation during an + * animation run will not have any effect on the on-going animation. + * + * @param floatValueHolder the property to be animated + */ + public FlingAnimation(FloatValueHolder floatValueHolder) { + super(floatValueHolder); + mFlingForce.setValueThreshold(getValueThreshold()); + } + /** + * Sets the friction for the fling animation. The greater the friction is, the sooner the + * animation will slow down. When not set, the friction defaults to 1. + * + * @param friction the friction used in the animation + * @return the animation whose friction will be scaled + * @throws IllegalArgumentException if the input friction is not positive + */ + public FlingAnimation setFriction( + @FloatRange(from = 0.0, fromInclusive = false) float friction) { + if (friction <= 0) { + throw new IllegalArgumentException("Friction must be positive"); + } + mFlingForce.setFrictionScalar(friction); + return this; + } + /** + * Returns the friction being set on the animation via {@link #setFriction(float)}. If the + * friction has not been set, the default friction of 1 will be returned. + * + * @return friction being used in the animation + */ + public float getFriction() { + return mFlingForce.getFrictionScalar(); + } + /** + * Sets the min value of the animation. When a fling animation reaches the min value, the + * animation will end immediately. Animations will not animate beyond the min value. + * + * @param minValue minimum value of the property to be animated + * @return the Animation whose min value is being set + */ + @Override + public FlingAnimation setMinValue(float minValue) { + super.setMinValue(minValue); + return this; + } + /** + * Sets the max value of the animation. When a fling animation reaches the max value, the + * animation will end immediately. Animations will not animate beyond the max value. + * + * @param maxValue maximum value of the property to be animated + * @return the Animation whose max value is being set + */ + @Override + public FlingAnimation setMaxValue(float maxValue) { + super.setMaxValue(maxValue); + return this; + } + /** + * Start velocity of the animation. Default velocity is 0. Unit: pixel/second + * + * <p>A <b>non-zero</b> start velocity is required for a FlingAnimation. If no start velocity is + * set through {@link #setStartVelocity(float)}, the start velocity defaults to 0. In that + * case, the fling animation will consider itself done in the next frame. + * + * <p>Note when using a fixed value as the start velocity (as opposed to getting the velocity + * through touch events), it is recommended to define such a value in dp/second and convert it + * to pixel/second based on the density of the screen to achieve a consistent look across + * different screens. + * + * <p>To convert from dp/second to pixel/second: + * <pre class="prettyprint"> + * float pixelPerSecond = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dpPerSecond, + * getResources().getDisplayMetrics()); + * </pre> + * + * @param startVelocity start velocity of the animation in pixel/second + * @return the Animation whose start velocity is being set + */ + @Override + public FlingAnimation setStartVelocity(float startVelocity) { + super.setStartVelocity(startVelocity); + return this; + } + @Override + boolean updateValueAndVelocity(long deltaT) { + MassState state = mFlingForce.updateValueAndVelocity(mValue, mVelocity, deltaT); + mValue = state.mValue; + mVelocity = state.mVelocity; + // When the animation hits the max/min value, consider animation done. + if (mValue < mMinValue) { + mValue = mMinValue; + return true; + } + if (mValue > mMaxValue) { + mValue = mMaxValue; + return true; + } + if (isAtEquilibrium(mValue, mVelocity)) { + return true; + } + return false; + } + @Override + float getAcceleration(float value, float velocity) { + return mFlingForce.getAcceleration(value, velocity); + } + @Override + boolean isAtEquilibrium(float value, float velocity) { + return value >= mMaxValue + || value <= mMinValue + || mFlingForce.isAtEquilibrium(value, velocity); + } + @Override + void setValueThreshold(float threshold) { + mFlingForce.setValueThreshold(threshold); + } + private static final class DragForce implements Force { + private static final float DEFAULT_FRICTION = -4.2f; + // This multiplier is used to calculate the velocity threshold given a certain value + // threshold. The idea is that if it takes >= 1 frame to move the value threshold amount, + // then the velocity is a reasonable threshold. + private static final float VELOCITY_THRESHOLD_MULTIPLIER = 1000f / 16f; + private float mFriction = DEFAULT_FRICTION; + private float mVelocityThreshold; + // Internal state to hold a value/velocity pair. + private final DynamicAnimation.MassState mMassState = new DynamicAnimation.MassState(); + void setFrictionScalar(float frictionScalar) { + mFriction = frictionScalar * DEFAULT_FRICTION; + } + float getFrictionScalar() { + return mFriction / DEFAULT_FRICTION; + } + MassState updateValueAndVelocity(float value, float velocity, long deltaT) { + mMassState.mVelocity = (float) (velocity * Math.exp((deltaT / 1000f) * mFriction)); + mMassState.mValue = (float) (value - velocity / mFriction + + velocity / mFriction * Math.exp(mFriction * deltaT / 1000f)); + if (isAtEquilibrium(mMassState.mValue, mMassState.mVelocity)) { + mMassState.mVelocity = 0f; + } + return mMassState; + } + @Override + public float getAcceleration(float position, float velocity) { + return velocity * mFriction; + } + @Override + public boolean isAtEquilibrium(float value, float velocity) { + return Math.abs(velocity) < mVelocityThreshold; + } + void setValueThreshold(float threshold) { + mVelocityThreshold = threshold * VELOCITY_THRESHOLD_MULTIPLIER; + } + } +} + diff --git a/core/java/com/android/internal/inputmethod/IInputMethodClient.aidl b/core/java/com/android/internal/inputmethod/IInputMethodClient.aidl index babd9a0950fd..6a7fa99f1caf 100644 --- a/core/java/com/android/internal/inputmethod/IInputMethodClient.aidl +++ b/core/java/com/android/internal/inputmethod/IInputMethodClient.aidl @@ -30,6 +30,7 @@ oneway interface IInputMethodClient { void onUnbindAccessibilityService(int sequence, int id); void setActive(boolean active, boolean fullscreen); void setInteractive(boolean active, boolean fullscreen); + void setImeVisibility(boolean visible); void scheduleStartInputIfNecessary(boolean fullscreen); void reportFullscreenMode(boolean fullscreen); void setImeTraceEnabled(boolean enabled); diff --git a/core/java/com/android/internal/os/BatteryStatsHistory.java b/core/java/com/android/internal/os/BatteryStatsHistory.java index 244165f5e814..5c270e0e874c 100644 --- a/core/java/com/android/internal/os/BatteryStatsHistory.java +++ b/core/java/com/android/internal/os/BatteryStatsHistory.java @@ -1514,6 +1514,36 @@ public class BatteryStatsHistory { } /** + * Records an event when some state2 flag changes to true. + */ + public void recordState2StartEvent(long elapsedRealtimeMs, long uptimeMs, int stateFlags, + int uid, String name) { + synchronized (this) { + mHistoryCur.states2 |= stateFlags; + mHistoryCur.eventCode = EVENT_STATE_CHANGE | EVENT_FLAG_START; + mHistoryCur.eventTag = mHistoryCur.localEventTag; + mHistoryCur.eventTag.uid = uid; + mHistoryCur.eventTag.string = name; + writeHistoryItem(elapsedRealtimeMs, uptimeMs); + } + } + + /** + * Records an event when some state2 flag changes to false. + */ + public void recordState2StopEvent(long elapsedRealtimeMs, long uptimeMs, int stateFlags, + int uid, String name) { + synchronized (this) { + mHistoryCur.states2 &= ~stateFlags; + mHistoryCur.eventCode = EVENT_STATE_CHANGE | EVENT_FLAG_FINISH; + mHistoryCur.eventTag = mHistoryCur.localEventTag; + mHistoryCur.eventTag.uid = uid; + mHistoryCur.eventTag.string = name; + writeHistoryItem(elapsedRealtimeMs, uptimeMs); + } + } + + /** * Records an event when some state2 flag changes to false. */ public void recordState2StopEvent(long elapsedRealtimeMs, long uptimeMs, int stateFlags) { diff --git a/core/java/com/android/internal/policy/DecorView.java b/core/java/com/android/internal/policy/DecorView.java index c14a6c1ca5ba..63ff598bae61 100644 --- a/core/java/com/android/internal/policy/DecorView.java +++ b/core/java/com/android/internal/policy/DecorView.java @@ -1227,7 +1227,10 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind requestApplyInsets(); } } - if (insets != null) { + if (insets != null && (consumedLeft > 0 + || consumedTop > 0 + || consumedRight > 0 + || consumedBottom > 0)) { insets = insets.inset(consumedLeft, consumedTop, consumedRight, consumedBottom); } } diff --git a/core/java/com/android/internal/protolog/ProtoLogGroup.java b/core/java/com/android/internal/protolog/ProtoLogGroup.java index f2783c4215c8..bb3507caf7f4 100644 --- a/core/java/com/android/internal/protolog/ProtoLogGroup.java +++ b/core/java/com/android/internal/protolog/ProtoLogGroup.java @@ -18,6 +18,8 @@ package com.android.internal.protolog; import com.android.internal.protolog.common.IProtoLogGroup; +import java.util.UUID; + /** * Defines logging groups for ProtoLog. * @@ -153,10 +155,18 @@ public enum ProtoLogGroup implements IProtoLogGroup { this.mLogToLogcat = logToLogcat; } + @Override + public int getId() { + return Consts.START_ID + this.ordinal(); + } + private static class Consts { private static final String TAG_WM = "WindowManager"; private static final boolean ENABLE_DEBUG = true; private static final boolean ENABLE_LOG_TO_PROTO_DEBUG = true; + private static final int START_ID = (int) ( + UUID.nameUUIDFromBytes(ProtoLogGroup.class.getName().getBytes()) + .getMostSignificantBits() % Integer.MAX_VALUE); } } diff --git a/core/java/com/android/internal/protolog/common/IProtoLogGroup.java b/core/java/com/android/internal/protolog/common/IProtoLogGroup.java index 149aa7aa7170..91b24fdab869 100644 --- a/core/java/com/android/internal/protolog/common/IProtoLogGroup.java +++ b/core/java/com/android/internal/protolog/common/IProtoLogGroup.java @@ -64,4 +64,9 @@ public interface IProtoLogGroup { * returns name of the logging group. */ String name(); + + /** + * returns the id of the logging group (unique for each group). + */ + int getId(); } diff --git a/core/java/com/android/internal/statusbar/StatusBarIcon.java b/core/java/com/android/internal/statusbar/StatusBarIcon.java index 4f80afaab696..76ce452858ad 100644 --- a/core/java/com/android/internal/statusbar/StatusBarIcon.java +++ b/core/java/com/android/internal/statusbar/StatusBarIcon.java @@ -22,7 +22,21 @@ import android.os.Parcelable; import android.os.UserHandle; import android.text.TextUtils; +import androidx.annotation.NonNull; + 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. + SystemIcon + } + public UserHandle user; public String pkg; public Icon icon; @@ -30,9 +44,10 @@ public class StatusBarIcon implements Parcelable { public boolean visible = true; public int number; public CharSequence contentDescription; + public Type type; public StatusBarIcon(UserHandle user, String resPackage, Icon icon, int iconLevel, int number, - CharSequence contentDescription) { + CharSequence contentDescription, Type type) { if (icon.getType() == Icon.TYPE_RESOURCE && TextUtils.isEmpty(icon.getResPackage())) { // This is an odd situation where someone's managed to hand us an icon without a @@ -46,15 +61,17 @@ public class StatusBarIcon implements Parcelable { this.iconLevel = iconLevel; this.number = number; this.contentDescription = contentDescription; + this.type = type; } public StatusBarIcon(String iconPackage, UserHandle user, int iconId, int iconLevel, int number, - CharSequence contentDescription) { + CharSequence contentDescription, Type type) { this(user, iconPackage, Icon.createWithResource(iconPackage, iconId), - iconLevel, number, contentDescription); + iconLevel, number, contentDescription, type); } + @NonNull @Override public String toString() { return "StatusBarIcon(icon=" + icon @@ -65,10 +82,11 @@ public class StatusBarIcon implements Parcelable { + " )"; } + @NonNull @Override public StatusBarIcon clone() { StatusBarIcon that = new StatusBarIcon(this.user, this.pkg, this.icon, - this.iconLevel, this.number, this.contentDescription); + this.iconLevel, this.number, this.contentDescription, this.type); that.visible = this.visible; return that; } @@ -88,6 +106,7 @@ public class StatusBarIcon implements Parcelable { this.visible = in.readInt() != 0; this.number = in.readInt(); this.contentDescription = in.readCharSequence(); + this.type = Type.valueOf(in.readString()); } public void writeToParcel(Parcel out, int flags) { @@ -98,6 +117,7 @@ public class StatusBarIcon implements Parcelable { out.writeInt(this.visible ? 1 : 0); out.writeInt(this.number); out.writeCharSequence(this.contentDescription); + out.writeString(this.type.name()); } public int describeContents() { diff --git a/core/java/com/android/internal/util/function/pooled/OmniFunction.java b/core/java/com/android/internal/util/function/pooled/OmniFunction.java index 931477f0b332..931477f0b332 100755..100644 --- a/core/java/com/android/internal/util/function/pooled/OmniFunction.java +++ b/core/java/com/android/internal/util/function/pooled/OmniFunction.java diff --git a/core/java/com/android/internal/util/function/pooled/PooledLambda.java b/core/java/com/android/internal/util/function/pooled/PooledLambda.java index 1d6d69cee967..1d6d69cee967 100755..100644 --- a/core/java/com/android/internal/util/function/pooled/PooledLambda.java +++ b/core/java/com/android/internal/util/function/pooled/PooledLambda.java diff --git a/core/java/com/android/internal/util/function/pooled/PooledLambdaImpl.java b/core/java/com/android/internal/util/function/pooled/PooledLambdaImpl.java index 19f0816e3e48..19f0816e3e48 100755..100644 --- a/core/java/com/android/internal/util/function/pooled/PooledLambdaImpl.java +++ b/core/java/com/android/internal/util/function/pooled/PooledLambdaImpl.java diff --git a/core/java/com/android/internal/widget/ActionBarContainer.java b/core/java/com/android/internal/widget/ActionBarContainer.java index eef33684e883..606e038a1a4b 100644 --- a/core/java/com/android/internal/widget/ActionBarContainer.java +++ b/core/java/com/android/internal/widget/ActionBarContainer.java @@ -93,8 +93,7 @@ public class ActionBarContainer extends FrameLayout { if (bg != null) { bg.setCallback(this); if (mActionBarView != null) { - mBackground.setBounds(mActionBarView.getLeft(), mActionBarView.getTop(), - mActionBarView.getRight(), mActionBarView.getBottom()); + bg.setBounds(0, 0, getMeasuredWidth(), getMeasuredHeight()); } } setWillNotDraw(mIsSplit ? mSplitBackground == null : @@ -293,6 +292,7 @@ public class ActionBarContainer extends FrameLayout { if (mActionBarView == null) return; if (mTabContainer != null && mTabContainer.getVisibility() != GONE) { + final int verticalPadding = getPaddingTop() + getPaddingBottom(); int nonTabMaxHeight = 0; final int childCount = getChildCount(); for (int i = 0; i < childCount; i++) { @@ -307,7 +307,9 @@ public class ActionBarContainer extends FrameLayout { final int maxHeight = mode == MeasureSpec.AT_MOST ? MeasureSpec.getSize(heightMeasureSpec) : Integer.MAX_VALUE; setMeasuredDimension(getMeasuredWidth(), - Math.min(nonTabMaxHeight + getMeasuredHeightWithMargins(mTabContainer), + Math.min( + verticalPadding + nonTabMaxHeight + + getMeasuredHeightWithMargins(mTabContainer), maxHeight)); } } @@ -335,13 +337,9 @@ public class ActionBarContainer extends FrameLayout { } } else { if (mBackground != null) { - if (mActionBarView.getVisibility() == View.VISIBLE) { - mBackground.setBounds(mActionBarView.getLeft(), mActionBarView.getTop(), - mActionBarView.getRight(), mActionBarView.getBottom()); - } else if (mActionContextView != null && - mActionContextView.getVisibility() == View.VISIBLE) { - mBackground.setBounds(mActionContextView.getLeft(), mActionContextView.getTop(), - mActionContextView.getRight(), mActionContextView.getBottom()); + if ((mActionBarView.getVisibility() == View.VISIBLE) || (mActionContextView != null + && mActionContextView.getVisibility() == View.VISIBLE)) { + mBackground.setBounds(0, 0, getMeasuredWidth(), getMeasuredHeight()); } else { mBackground.setBounds(0, 0, 0, 0); } diff --git a/core/java/com/android/internal/widget/ActionBarOverlayLayout.java b/core/java/com/android/internal/widget/ActionBarOverlayLayout.java index 0992db91356d..68328252abaf 100644 --- a/core/java/com/android/internal/widget/ActionBarOverlayLayout.java +++ b/core/java/com/android/internal/widget/ActionBarOverlayLayout.java @@ -24,6 +24,7 @@ import android.content.pm.ActivityInfo; import android.content.res.Configuration; import android.content.res.TypedArray; import android.graphics.Canvas; +import android.graphics.Insets; import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.os.Build; @@ -51,6 +52,7 @@ import com.android.internal.view.menu.MenuPresenter; */ public class ActionBarOverlayLayout extends ViewGroup implements DecorContentParent { private static final String TAG = "ActionBarOverlayLayout"; + private static final Rect EMPTY_RECT = new Rect(); private int mActionBarHeight; //private WindowDecorActionBar mActionBar; @@ -77,10 +79,13 @@ public class ActionBarOverlayLayout extends ViewGroup implements DecorContentPar private final Rect mBaseContentInsets = new Rect(); private final Rect mLastBaseContentInsets = new Rect(); private final Rect mContentInsets = new Rect(); + private final Rect mSystemInsets = new Rect(); private WindowInsets mBaseInnerInsets = WindowInsets.CONSUMED; private WindowInsets mLastBaseInnerInsets = WindowInsets.CONSUMED; private WindowInsets mInnerInsets = WindowInsets.CONSUMED; private WindowInsets mLastInnerInsets = WindowInsets.CONSUMED; + private boolean mDecorFitsSystemWindows = true; + private boolean mActionBarExtendsIntoSystemInsets = false; private ActionBarVisibilityCallback mActionBarVisibilityCallback; @@ -268,7 +273,8 @@ public class ActionBarOverlayLayout extends ViewGroup implements DecorContentPar // We want the bar to be visible if it is not being hidden, // or the app has not turned on a stable UI mode (meaning they // are performing explicit layout around the action bar). - mActionBarVisibilityCallback.enableContentAnimations(!stable); + mActionBarVisibilityCallback.enableContentAnimations( + !stable && !mActionBarExtendsIntoSystemInsets); if (barVisible || !stable) mActionBarVisibilityCallback.showForSystem(); else mActionBarVisibilityCallback.hideForSystem(); } @@ -288,10 +294,39 @@ public class ActionBarOverlayLayout extends ViewGroup implements DecorContentPar } } - private boolean applyInsets(View view, Rect insets, boolean left, boolean top, - boolean bottom, boolean right) { + private boolean applyInsets(View view, Rect insets, boolean toPadding, + boolean left, boolean top, boolean right, boolean bottom) { + boolean changed; + if (toPadding) { + changed = setMargin(view, EMPTY_RECT, left, top, right, bottom); + changed |= setPadding(view, insets, left, top, right, bottom); + } else { + changed = setPadding(view, EMPTY_RECT, left, top, right, bottom); + changed |= setMargin(view, insets, left, top, right, bottom); + } + return changed; + } + + private boolean setPadding(View view, Rect insets, + boolean left, boolean top, boolean right, boolean bottom) { + if ((left && view.getPaddingLeft() != insets.left) + || (top && view.getPaddingTop() != insets.top) + || (right && view.getPaddingRight() != insets.right) + || (bottom && view.getPaddingBottom() != insets.bottom)) { + view.setPadding( + left ? insets.left : view.getPaddingLeft(), + top ? insets.top : view.getPaddingTop(), + right ? insets.right : view.getPaddingRight(), + bottom ? insets.bottom : view.getPaddingBottom()); + return true; + } + return false; + } + + private boolean setMargin(View view, Rect insets, + boolean left, boolean top, boolean right, boolean bottom) { + final LayoutParams lp = (LayoutParams) view.getLayoutParams(); boolean changed = false; - LayoutParams lp = (LayoutParams)view.getLayoutParams(); if (left && lp.leftMargin != insets.left) { changed = true; lp.leftMargin = insets.left; @@ -316,12 +351,28 @@ public class ActionBarOverlayLayout extends ViewGroup implements DecorContentPar pullChildren(); final int vis = getWindowSystemUiVisibility(); - final Rect systemInsets = insets.getSystemWindowInsetsAsRect(); + final boolean stable = (vis & SYSTEM_UI_FLAG_LAYOUT_STABLE) != 0; + final boolean layoutIntoSystemInsets = (vis & SYSTEM_UI_LAYOUT_FLAGS) != 0; + mDecorFitsSystemWindows = hasContentOnApplyWindowInsetsListener(); + + // Only extend action bar into system insets area if the app doesn't fit system insets. + mActionBarExtendsIntoSystemInsets = + !mDecorFitsSystemWindows || (stable && layoutIntoSystemInsets); + + if (mActionBarVisibilityCallback != null) { + mActionBarVisibilityCallback.enableContentAnimations( + !stable && !mActionBarExtendsIntoSystemInsets); + } + + final Insets sysInsets = insets.getSystemWindowInsets(); + mSystemInsets.set(sysInsets.left, sysInsets.top, sysInsets.right, sysInsets.bottom); // The top and bottom action bars are always within the content area. - boolean changed = applyInsets(mActionBarTop, systemInsets, true, true, false, true); + boolean changed = applyInsets(mActionBarTop, mSystemInsets, + mActionBarExtendsIntoSystemInsets, true, true, true, false); if (mActionBarBottom != null) { - changed |= applyInsets(mActionBarBottom, systemInsets, true, false, true, true); + changed |= applyInsets(mActionBarBottom, mSystemInsets, + mActionBarExtendsIntoSystemInsets, true, false, true, true); } // Cannot use the result of computeSystemWindowInsets, because that consumes the @@ -406,6 +457,9 @@ public class ActionBarOverlayLayout extends ViewGroup implements DecorContentPar // This is the standard space needed for the action bar. For stable measurement, // we can't depend on the size currently reported by it -- this must remain constant. topInset = mActionBarHeight; + if (mActionBarExtendsIntoSystemInsets) { + topInset += mSystemInsets.top; + } if (mHasNonEmbeddedTabs) { final View tabs = mActionBarTop.getTabContainer(); if (tabs != null) { @@ -424,6 +478,9 @@ public class ActionBarOverlayLayout extends ViewGroup implements DecorContentPar if (mActionBarBottom != null) { if (stable) { bottomInset = mActionBarHeight; + if (mActionBarExtendsIntoSystemInsets) { + bottomInset += mSystemInsets.bottom; + } } else { bottomInset = mActionBarBottom.getMeasuredHeight(); } @@ -436,21 +493,35 @@ public class ActionBarOverlayLayout extends ViewGroup implements DecorContentPar // overlay. mContentInsets.set(mBaseContentInsets); mInnerInsets = mBaseInnerInsets; - if (!mOverlayMode && !stable && hasContentOnApplyWindowInsetsListener()) { - mContentInsets.top += topInset; - mContentInsets.bottom += bottomInset; + if (!mOverlayMode && !stable && mDecorFitsSystemWindows) { + if (mActionBarExtendsIntoSystemInsets) { + mContentInsets.top = Math.max(mContentInsets.top, topInset); + mContentInsets.bottom = Math.max(mContentInsets.bottom, bottomInset); + } else { + mContentInsets.top += topInset; + mContentInsets.bottom += bottomInset; + } // Content view has been shrunk, shrink all insets to match. mInnerInsets = mInnerInsets.inset(0 /* left */, topInset, 0 /* right */, bottomInset); } else { // Add ActionBar to system window inset, but leave other insets untouched. - mInnerInsets = mInnerInsets.replaceSystemWindowInsets( - mInnerInsets.getSystemWindowInsetLeft(), - mInnerInsets.getSystemWindowInsetTop() + topInset, - mInnerInsets.getSystemWindowInsetRight(), - mInnerInsets.getSystemWindowInsetBottom() + bottomInset - ); - } - applyInsets(mContent, mContentInsets, true, true, true, true); + if (mActionBarExtendsIntoSystemInsets) { + mInnerInsets = mInnerInsets.replaceSystemWindowInsets( + mInnerInsets.getSystemWindowInsetLeft(), + Math.max(mInnerInsets.getSystemWindowInsetTop(), topInset), + mInnerInsets.getSystemWindowInsetRight(), + Math.max(mInnerInsets.getSystemWindowInsetBottom(), bottomInset) + ); + } else { + mInnerInsets = mInnerInsets.replaceSystemWindowInsets( + mInnerInsets.getSystemWindowInsetLeft(), + mInnerInsets.getSystemWindowInsetTop() + topInset, + mInnerInsets.getSystemWindowInsetRight(), + mInnerInsets.getSystemWindowInsetBottom() + bottomInset + ); + } + } + setMargin(mContent, mContentInsets, true, true, true, true); if (!mLastInnerInsets.equals(mInnerInsets)) { // If the inner insets have changed, we need to dispatch this down to 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 55f2dee95a34..00262be06623 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/CoreDocument.java +++ b/core/java/com/android/internal/widget/remotecompose/core/CoreDocument.java @@ -30,7 +30,7 @@ public class CoreDocument { ArrayList<Operation> mOperations; RemoteComposeState mRemoteComposeState = new RemoteComposeState(); - + TimeVariables mTimeVariables = new TimeVariables(); // Semantic version of the document Version mVersion = new Version(0, 1, 0); @@ -70,6 +70,7 @@ public class CoreDocument { public void setWidth(int width) { this.mWidth = width; + mRemoteComposeState.setWindowWidth(width); } public int getHeight() { @@ -78,6 +79,8 @@ public class CoreDocument { public void setHeight(int height) { this.mHeight = height; + mRemoteComposeState.setWindowHeight(height); + } public RemoteComposeBuffer getBuffer() { @@ -111,21 +114,21 @@ public class CoreDocument { /** * Sets the way the player handles the content * - * @param scroll set the horizontal behavior (NONE|SCROLL_HORIZONTAL|SCROLL_VERTICAL) + * @param scroll set the horizontal behavior (NONE|SCROLL_HORIZONTAL|SCROLL_VERTICAL) * @param alignment set the alignment of the content (TOP|CENTER|BOTTOM|START|END) - * @param sizing set the type of sizing for the content (NONE|SIZING_LAYOUT|SIZING_SCALE) - * @param mode set the mode of sizing, either LAYOUT modes or SCALE modes - * the LAYOUT modes are: - * - LAYOUT_MATCH_PARENT - * - LAYOUT_WRAP_CONTENT - * or adding an horizontal mode and a vertical mode: - * - LAYOUT_HORIZONTAL_MATCH_PARENT - * - LAYOUT_HORIZONTAL_WRAP_CONTENT - * - LAYOUT_HORIZONTAL_FIXED - * - LAYOUT_VERTICAL_MATCH_PARENT - * - LAYOUT_VERTICAL_WRAP_CONTENT - * - LAYOUT_VERTICAL_FIXED - * The LAYOUT_*_FIXED modes will use the intrinsic document size + * @param sizing set the type of sizing for the content (NONE|SIZING_LAYOUT|SIZING_SCALE) + * @param mode set the mode of sizing, either LAYOUT modes or SCALE modes + * the LAYOUT modes are: + * - LAYOUT_MATCH_PARENT + * - LAYOUT_WRAP_CONTENT + * or adding an horizontal mode and a vertical mode: + * - LAYOUT_HORIZONTAL_MATCH_PARENT + * - LAYOUT_HORIZONTAL_WRAP_CONTENT + * - LAYOUT_HORIZONTAL_FIXED + * - LAYOUT_VERTICAL_MATCH_PARENT + * - LAYOUT_VERTICAL_WRAP_CONTENT + * - LAYOUT_VERTICAL_FIXED + * The LAYOUT_*_FIXED modes will use the intrinsic document size */ public void setRootContentBehavior(int scroll, int alignment, int sizing, int mode) { this.mContentScroll = scroll; @@ -138,8 +141,8 @@ public class CoreDocument { * Given dimensions w x h of where to paint the content, returns the corresponding scale factor * according to the contentSizing information * - * @param w horizontal dimension of the rendering area - * @param h vertical dimension of the rendering area + * @param w horizontal dimension of the rendering area + * @param h vertical dimension of the rendering area * @param scaleOutput will contain the computed scale factor */ public void computeScale(float w, float h, float[] scaleOutput) { @@ -154,37 +157,43 @@ public class CoreDocument { float scale = Math.min(1f, Math.min(scaleX, scaleY)); contentScaleX = scale; contentScaleY = scale; - } break; + } + break; case RootContentBehavior.SCALE_FIT: { float scaleX = w / mWidth; float scaleY = h / mHeight; float scale = Math.min(scaleX, scaleY); contentScaleX = scale; contentScaleY = scale; - } break; + } + break; case RootContentBehavior.SCALE_FILL_WIDTH: { float scale = w / mWidth; contentScaleX = scale; contentScaleY = scale; - } break; + } + break; case RootContentBehavior.SCALE_FILL_HEIGHT: { float scale = h / mHeight; contentScaleX = scale; contentScaleY = scale; - } break; + } + break; case RootContentBehavior.SCALE_CROP: { float scaleX = w / mWidth; float scaleY = h / mHeight; float scale = Math.max(scaleX, scaleY); contentScaleX = scale; contentScaleY = scale; - } break; + } + break; case RootContentBehavior.SCALE_FILL_BOUNDS: { float scaleX = w / mWidth; float scaleY = h / mHeight; contentScaleX = scaleX; contentScaleY = scaleY; - } break; + } + break; default: // nothing } @@ -197,10 +206,10 @@ public class CoreDocument { * Given dimensions w x h of where to paint the content, returns the corresponding translation * according to the contentAlignment information * - * @param w horizontal dimension of the rendering area - * @param h vertical dimension of the rendering area - * @param contentScaleX the horizontal scale we are going to use for the content - * @param contentScaleY the vertical scale we are going to use for the content + * @param w horizontal dimension of the rendering area + * @param h vertical dimension of the rendering area + * @param contentScaleX the horizontal scale we are going to use for the content + * @param contentScaleY the vertical scale we are going to use for the content * @param translateOutput will contain the computed translation */ private void computeTranslate(float w, float h, float contentScaleX, float contentScaleY, @@ -215,26 +224,32 @@ public class CoreDocument { switch (horizontalContentAlignment) { case RootContentBehavior.ALIGNMENT_START: { // nothing - } break; + } + break; case RootContentBehavior.ALIGNMENT_HORIZONTAL_CENTER: { translateX = (w - contentWidth) / 2f; - } break; + } + break; case RootContentBehavior.ALIGNMENT_END: { translateX = w - contentWidth; - } break; + } + break; default: // nothing (same as alignment_start) } switch (verticalContentAlignment) { case RootContentBehavior.ALIGNMENT_TOP: { // nothing - } break; + } + break; case RootContentBehavior.ALIGNMENT_VERTICAL_CENTER: { translateY = (h - contentHeight) / 2f; - } break; + } + break; case RootContentBehavior.ALIGNMENT_BOTTOM: { translateY = h - contentHeight; - } break; + } + break; default: // nothing (same as alignment_top) } @@ -291,7 +306,13 @@ public class CoreDocument { this.mMetadata = metadata; } - public boolean contains(float x, float y) { + /** + * Returns true if x,y coordinate is within bounds + * @param x x-coordinate + * @param y y-coordinate + * @return x,y coordinate is within bounds + */ + public boolean contains(float x, float y) { return x >= mLeft && x < mRight && y >= mTop && y < mBottom; } @@ -341,16 +362,22 @@ public class CoreDocument { public void initializeContext(RemoteContext context) { mRemoteComposeState.reset(); mClickAreas.clear(); - + mRemoteComposeState.setNextId(RemoteComposeState.START_ID); context.mDocument = this; context.mRemoteComposeState = mRemoteComposeState; - // mark context to be in DATA mode, which will skip the painting ops. context.mMode = RemoteContext.ContextMode.DATA; - for (Operation op: mOperations) { + mTimeVariables.updateTime(context); + + for (Operation op : mOperations) { + if (op instanceof VariableSupport) { + ((VariableSupport) op).updateVariables(context); + ((VariableSupport) op).registerListening(context); + } op.apply(context); } context.mMode = RemoteContext.ContextMode.UNSET; + } /////////////////////////////////////////////////////////////////////////////////////////////// @@ -375,7 +402,7 @@ public class CoreDocument { * @param minorVersion minor version number, increased when adding new features * @param patch patch level, increased upon bugfixes */ - void setVersion(int majorVersion, int minorVersion, int patch) { + void setVersion(int majorVersion, int minorVersion, int patch) { mVersion = new Version(majorVersion, minorVersion, patch); } @@ -389,13 +416,13 @@ public class CoreDocument { * the click coordinates will be the one reported; the order of addition of those click areas * is therefore meaningful. * - * @param id the id of the area, which will be reported on click + * @param id the id of the area, which will be reported on click * @param contentDescription the content description (used for accessibility) - * @param left the left coordinate of the click area (in pixels) - * @param top the top coordinate of the click area (in pixels) - * @param right the right coordinate of the click area (in pixels) - * @param bottom the bottom coordinate of the click area (in pixels) - * @param metadata arbitrary metadata associated with the are, also reported on click + * @param left the left coordinate of the click area (in pixels) + * @param top the top coordinate of the click area (in pixels) + * @param right the right coordinate of the click area (in pixels) + * @param bottom the bottom coordinate of the click area (in pixels) + * @param metadata arbitrary metadata associated with the are, also reported on click */ public void addClickArea(int id, String contentDescription, float left, float top, float right, float bottom, String metadata) { @@ -417,7 +444,7 @@ public class CoreDocument { * listeners. */ public void onClick(float x, float y) { - for (ClickAreaRepresentation clickArea: mClickAreas) { + for (ClickAreaRepresentation clickArea : mClickAreas) { if (clickArea.contains(x, y)) { warnClickListeners(clickArea); } @@ -430,7 +457,7 @@ public class CoreDocument { * @param id the click area id */ public void performClick(int id) { - for (ClickAreaRepresentation clickArea: mClickAreas) { + for (ClickAreaRepresentation clickArea : mClickAreas) { if (clickArea.mId == id) { warnClickListeners(clickArea); } @@ -441,17 +468,36 @@ public class CoreDocument { * Warn click listeners when a click area is activated */ private void warnClickListeners(ClickAreaRepresentation clickArea) { - for (ClickCallbacks listener: mClickListeners) { + for (ClickCallbacks listener : mClickListeners) { listener.click(clickArea.mId, clickArea.mMetadata); } } - /////////////////////////////////////////////////////////////////////////////////////////////// + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + for (Operation op : mOperations) { + builder.append(op.toString()); + builder.append("\n"); + } + return builder.toString(); + } + + ////////////////////////////////////////////////////////////////////////// // Painting - /////////////////////////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////// private final float[] mScaleOutput = new float[2]; private final float[] mTranslateOutput = new float[2]; + private int mRepaintNext = -1; // delay to next repaint -1 = don't 1 = asap + + /** + * Returns > 0 if it needs to repaint + * @return + */ + public int needsRepaint() { + return mRepaintNext; + } /** * Paint the document @@ -475,6 +521,11 @@ public class CoreDocument { context.mPaintContext.translate(mTranslateOutput[0], mTranslateOutput[1]); context.mPaintContext.scale(mScaleOutput[0], mScaleOutput[1]); } + mTimeVariables.updateTime(context); + context.loadFloat(RemoteContext.ID_WINDOW_WIDTH, getWidth()); + context.loadFloat(RemoteContext.ID_WINDOW_HEIGHT, getHeight()); + mRepaintNext = context.updateOps(); + for (Operation op : mOperations) { // operations will only be executed if no theme is set (ie UNSPECIFIED) // or the theme is equal as the one passed in argument to paint. 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 54b277a2ac58..4b45ab691215 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/Operations.java +++ b/core/java/com/android/internal/widget/remotecompose/core/Operations.java @@ -19,6 +19,7 @@ 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; import com.android.internal.widget.remotecompose.core.operations.ClipRect; +import com.android.internal.widget.remotecompose.core.operations.ColorExpression; import com.android.internal.widget.remotecompose.core.operations.DrawArc; import com.android.internal.widget.remotecompose.core.operations.DrawBitmap; import com.android.internal.widget.remotecompose.core.operations.DrawBitmapInt; @@ -28,9 +29,12 @@ import com.android.internal.widget.remotecompose.core.operations.DrawOval; import com.android.internal.widget.remotecompose.core.operations.DrawPath; import com.android.internal.widget.remotecompose.core.operations.DrawRect; import com.android.internal.widget.remotecompose.core.operations.DrawRoundRect; +import com.android.internal.widget.remotecompose.core.operations.DrawText; +import com.android.internal.widget.remotecompose.core.operations.DrawTextAnchored; import com.android.internal.widget.remotecompose.core.operations.DrawTextOnPath; -import com.android.internal.widget.remotecompose.core.operations.DrawTextRun; import com.android.internal.widget.remotecompose.core.operations.DrawTweenPath; +import com.android.internal.widget.remotecompose.core.operations.FloatConstant; +import com.android.internal.widget.remotecompose.core.operations.FloatExpression; import com.android.internal.widget.remotecompose.core.operations.Header; import com.android.internal.widget.remotecompose.core.operations.MatrixRestore; import com.android.internal.widget.remotecompose.core.operations.MatrixRotate; @@ -42,7 +46,10 @@ import com.android.internal.widget.remotecompose.core.operations.PaintData; import com.android.internal.widget.remotecompose.core.operations.PathData; import com.android.internal.widget.remotecompose.core.operations.RootContentBehavior; import com.android.internal.widget.remotecompose.core.operations.RootContentDescription; +import com.android.internal.widget.remotecompose.core.operations.ShaderData; import com.android.internal.widget.remotecompose.core.operations.TextData; +import com.android.internal.widget.remotecompose.core.operations.TextFromFloat; +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.utilities.IntMap; @@ -67,9 +74,10 @@ public class Operations { public static final int DRAW_BITMAP = 44; public static final int DRAW_BITMAP_INT = 66; public static final int DATA_BITMAP = 101; + public static final int DATA_SHADER = 45; public static final int DATA_TEXT = 102; -/////////////////////////////===================== + /////////////////////////////===================== public static final int CLIP_PATH = 38; public static final int CLIP_RECT = 39; public static final int PAINT_VALUES = 40; @@ -91,6 +99,12 @@ public class Operations { public static final int MATRIX_SAVE = 130; public static final int MATRIX_RESTORE = 131; public static final int MATRIX_SET = 132; + public static final int DATA_FLOAT = 80; + public static final int ANIMATED_FLOAT = 81; + public static final int DRAW_TEXT_ANCHOR = 133; + public static final int COLOR_EXPRESSIONS = 134; + public static final int TEXT_FROM_FLOAT = 135; + public static final int TEXT_MERGE = 136; /////////////////////////////////////////====================== public static IntMap<CompanionOperation> map = new IntMap<>(); @@ -114,7 +128,7 @@ public class Operations { map.put(DRAW_RECT, DrawRect.COMPANION); map.put(DRAW_ROUND_RECT, DrawRoundRect.COMPANION); map.put(DRAW_TEXT_ON_PATH, DrawTextOnPath.COMPANION); - map.put(DRAW_TEXT_RUN, DrawTextRun.COMPANION); + map.put(DRAW_TEXT_RUN, DrawText.COMPANION); map.put(DRAW_TWEEN_PATH, DrawTweenPath.COMPANION); map.put(DATA_PATH, PathData.COMPANION); map.put(PAINT_VALUES, PaintData.COMPANION); @@ -126,6 +140,13 @@ public class Operations { map.put(MATRIX_TRANSLATE, MatrixTranslate.COMPANION); map.put(CLIP_PATH, ClipPath.COMPANION); map.put(CLIP_RECT, ClipRect.COMPANION); + map.put(DATA_SHADER, ShaderData.COMPANION); + map.put(DATA_FLOAT, FloatConstant.COMPANION); + map.put(ANIMATED_FLOAT, FloatExpression.COMPANION); + map.put(DRAW_TEXT_ANCHOR, DrawTextAnchored.COMPANION); + map.put(COLOR_EXPRESSIONS, ColorExpression.COMPANION); + map.put(TEXT_FROM_FLOAT, TextFromFloat.COMPANION); + map.put(TEXT_MERGE, TextMerge.COMPANION); } 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 eece8ad52b60..ecd0efceacf3 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/PaintContext.java +++ b/core/java/com/android/internal/widget/remotecompose/core/PaintContext.java @@ -68,7 +68,35 @@ public abstract class PaintContext { public abstract void drawTextOnPath(int textId, int pathId, float hOffset, float vOffset); - public abstract void drawTextRun(int textID, + /** + * Return the dimensions (left, top, right, bottom). + * Relative to a drawTextRun x=0, y=0; + * + * @param textId + * @param start + * @param end if end is -1 it means the whole string + * @param monospace measure with better support for monospace + * @param bounds the bounds (left, top, right, bottom) + */ + public abstract void getTextBounds(int textId, + int start, + int end, + boolean monospace, + float[]bounds); + + /** + * Draw a text starting ast x,y + * + * @param textId reference to the text + * @param start + * @param end + * @param contextStart + * @param contextEnd + * @param x + * @param y + * @param rtl + */ + public abstract void drawTextRun(int textId, int start, int end, int contextStart, @@ -77,6 +105,14 @@ public abstract class PaintContext { float y, boolean rtl); + /** + * Draw an interpolation between two paths + * @param path1Id + * @param path2Id + * @param tween 0.0 = is path1 1.0 is path2 + * @param start + * @param stop + */ public abstract void drawTweenPath(int path1Id, int path2Id, float tween, @@ -85,21 +121,70 @@ public abstract class PaintContext { public abstract void applyPaint(PaintBundle mPaintData); - public abstract void mtrixScale(float scaleX, float scaleY, float centerX, float centerY); - + /** + * Scale the rendering by scaleX and saleY (1.0 = no scale). + * Scaling is done about centerX,centerY. + * + * @param scaleX + * @param scaleY + * @param centerX + * @param centerY + */ + public abstract void matrixScale(float scaleX, float scaleY, float centerX, float centerY); + + /** + * Translate the rendering + * @param translateX + * @param translateY + */ public abstract void matrixTranslate(float translateX, float translateY); + /** + * Skew the rendering + * @param skewX + * @param skewY + */ public abstract void matrixSkew(float skewX, float skewY); + /** + * Rotate the rendering. + * Note rotates are cumulative. + * @param rotate angle to rotate + * @param pivotX x-coordinate about which to rotate + * @param pivotY y-coordinate about which to rotate + */ public abstract void matrixRotate(float rotate, float pivotX, float pivotY); + /** + * Save the current state of the transform + */ public abstract void matrixSave(); + /** + * Restore the previously saved state of the transform + */ public abstract void matrixRestore(); + /** + * Set the clip to a rectangle. + * Drawing outside the current clip region will have no effect + * @param left + * @param top + * @param right + * @param bottom + */ public abstract void clipRect(float left, float top, float right, float bottom); + /** + * Clip based on a path. + * @param pathId + * @param regionOp + */ public abstract void clipPath(int pathId, int regionOp); + /** + * Reset the paint + */ + public abstract void reset(); } 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 c2e81318c09a..52fc3143d721 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeBuffer.java +++ b/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeBuffer.java @@ -19,6 +19,7 @@ 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; import com.android.internal.widget.remotecompose.core.operations.ClipRect; +import com.android.internal.widget.remotecompose.core.operations.ColorExpression; import com.android.internal.widget.remotecompose.core.operations.DrawArc; import com.android.internal.widget.remotecompose.core.operations.DrawBitmap; import com.android.internal.widget.remotecompose.core.operations.DrawBitmapInt; @@ -28,9 +29,12 @@ import com.android.internal.widget.remotecompose.core.operations.DrawOval; import com.android.internal.widget.remotecompose.core.operations.DrawPath; import com.android.internal.widget.remotecompose.core.operations.DrawRect; import com.android.internal.widget.remotecompose.core.operations.DrawRoundRect; +import com.android.internal.widget.remotecompose.core.operations.DrawText; +import com.android.internal.widget.remotecompose.core.operations.DrawTextAnchored; import com.android.internal.widget.remotecompose.core.operations.DrawTextOnPath; -import com.android.internal.widget.remotecompose.core.operations.DrawTextRun; import com.android.internal.widget.remotecompose.core.operations.DrawTweenPath; +import com.android.internal.widget.remotecompose.core.operations.FloatConstant; +import com.android.internal.widget.remotecompose.core.operations.FloatExpression; import com.android.internal.widget.remotecompose.core.operations.Header; import com.android.internal.widget.remotecompose.core.operations.MatrixRestore; import com.android.internal.widget.remotecompose.core.operations.MatrixRotate; @@ -43,8 +47,12 @@ import com.android.internal.widget.remotecompose.core.operations.PathData; import com.android.internal.widget.remotecompose.core.operations.RootContentBehavior; import com.android.internal.widget.remotecompose.core.operations.RootContentDescription; import com.android.internal.widget.remotecompose.core.operations.TextData; +import com.android.internal.widget.remotecompose.core.operations.TextFromFloat; +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.Utils; import com.android.internal.widget.remotecompose.core.operations.paint.PaintBundle; +import com.android.internal.widget.remotecompose.core.operations.utilities.easing.FloatAnimation; import java.io.File; import java.io.FileInputStream; @@ -58,9 +66,20 @@ import java.util.Arrays; * Provides an abstract buffer to encode/decode RemoteCompose operations */ public class RemoteComposeBuffer { + public static final int EASING_CUBIC_STANDARD = FloatAnimation.CUBIC_STANDARD; + public static final int EASING_CUBIC_ACCELERATE = FloatAnimation.CUBIC_ACCELERATE; + public static final int EASING_CUBIC_DECELERATE = FloatAnimation.CUBIC_DECELERATE; + public static final int EASING_CUBIC_LINEAR = FloatAnimation.CUBIC_LINEAR; + public static final int EASING_CUBIC_ANTICIPATE = FloatAnimation.CUBIC_ANTICIPATE; + public static final int EASING_CUBIC_OVERSHOOT = FloatAnimation.CUBIC_OVERSHOOT; + public static final int EASING_CUBIC_CUSTOM = FloatAnimation.CUBIC_CUSTOM; + public static final int EASING_SPLINE_CUSTOM = FloatAnimation.SPLINE_CUSTOM; + 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; RemoteComposeState mRemoteComposeState; + private static final boolean DEBUG = false; /** * Provides an abstract buffer to encode/decode RemoteCompose operations @@ -171,7 +190,7 @@ public class RemoteComposeBuffer { * * @param text the string to inject in the buffer */ - int addText(String text) { + public int addText(String text) { int id = mRemoteComposeState.dataGetId(text); if (id == -1) { id = mRemoteComposeState.cache(text); @@ -350,7 +369,6 @@ public class RemoteComposeBuffer { addDrawPath(id); } - /** * Draw the specified path * @@ -426,12 +444,160 @@ public class RemoteComposeBuffer { float y, boolean rtl) { int textId = addText(text); - DrawTextRun.COMPANION.apply( + DrawText.COMPANION.apply( mBuffer, textId, start, end, contextStart, contextEnd, x, y, rtl); } /** + * Draw the text, with origin at (x,y). The origin is interpreted + * based on the Align setting in the paint. + * + * @param textId The text to be drawn + * @param start The index of the first character in text to draw + * @param end (end - 1) is the index of the last character in text to draw + * @param contextStart + * @param contextEnd + * @param x The x-coordinate of the origin of the text being drawn + * @param y The y-coordinate of the baseline of the text being drawn + * @param rtl Draw RTTL + */ + public void addDrawTextRun(int textId, + int start, + int end, + int contextStart, + int contextEnd, + float x, + float y, + boolean rtl) { + DrawText.COMPANION.apply( + mBuffer, textId, start, end, + contextStart, contextEnd, x, y, rtl); + } + + /** + * Draw a text on canvas at relative to position (x, y), + * offset panX and panY. + * <br> + * The panning factors (panX, panY) mapped to the + * resulting bounding box of the text, in such a way that a + * panning factor of (0.0, 0.0) would center the text at (x, y) + * <ul> + * <li> Panning of -1.0, -1.0 - the text above & right of x,y.</li> + * <li>Panning of 1.0, 1.0 - the text is below and to the left</li> + * <li>Panning of 1.0, 0.0 - the test is centered & to the right of x,y</li> + * </ul> + * Setting panY to NaN results in y being the baseline of the text. + * + * @param text text to draw + * @param x Coordinate of the Anchor + * @param y Coordinate of the Anchor + * @param panX justifies text -1.0=right, 0.0=center, 1.0=left + * @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) { + int textId = addText(text); + DrawTextAnchored.COMPANION.apply( + mBuffer, textId, + x, y, + panX, panY, + flags); + } + + /** + * Add a text and id so that it can be used + * + * @param text + * @return + */ + public int createTextId(String text) { + return addText(text); + } + + /** + * Merge two text (from id's) output one id + * @param id1 left id + * @param id2 right id + * @return new id that merges the two text + */ + public int textMerge(int id1, int id2) { + int textId = addText(id1 + "+" + id2); + TextMerge.COMPANION.apply(mBuffer, textId, id1, id2); + return textId; + } + + public static final int PAD_AFTER_SPACE = TextFromFloat.PAD_AFTER_SPACE; + public static final int PAD_AFTER_NONE = TextFromFloat.PAD_AFTER_NONE; + public static final int PAD_AFTER_ZERO = TextFromFloat.PAD_AFTER_ZERO; + public static final int PAD_PRE_SPACE = TextFromFloat.PAD_PRE_SPACE; + public static final int PAD_PRE_NONE = TextFromFloat.PAD_PRE_NONE; + public static final int PAD_PRE_ZERO = TextFromFloat.PAD_PRE_ZERO; + + /** + * Create a TextFromFloat command which creates text from a Float. + * + * @param value The value to convert + * @param digitsBefore the digits before the decimal point + * @param digitsAfter the digits after the decimal point + * @param flags configure the behaviour using PAD_PRE_* and PAD_AFTER* flags + * @return id of the string that can be passed to drawTextAnchored + */ + public int createTextFromFloat(float value, short digitsBefore, + short digitsAfter, int flags) { + String placeHolder = Utils.floatToString(value) + + "(" + digitsBefore + "," + digitsAfter + "," + flags + ")"; + int id = mRemoteComposeState.dataGetId(placeHolder); + if (id == -1) { + id = mRemoteComposeState.cache(placeHolder); + // TextData.COMPANION.apply(mBuffer, id, text); + } + TextFromFloat.COMPANION.apply(mBuffer, id, value, digitsBefore, + digitsAfter, flags); + return id; + } + + /** + * Draw a text on canvas at relative to position (x, y), + * offset panX and panY. + * <br> + * The panning factors (panX, panY) mapped to the + * resulting bounding box of the text, in such a way that a + * panning factor of (0.0, 0.0) would center the text at (x, y) + * <ul> + * <li> Panning of -1.0, -1.0 - the text above & right of x,y.</li> + * <li>Panning of 1.0, 1.0 - the text is below and to the left</li> + * <li>Panning of 1.0, 0.0 - the test is centered & to the right of x,y</li> + * </ul> + * Setting panY to NaN results in y being the baseline of the text. + * + * @param textId text to draw + * @param x Coordinate of the Anchor + * @param y Coordinate of the Anchor + * @param panX justifies text -1.0=right, 0.0=center, 1.0=left + * @param panY position text -1.0=above, 0.0=center, 1.0=below, Nan=baseline + * @param flags 1 = RTL + */ + public void drawTextAnchored(int textId, + float x, + float y, + float panX, + float panY, + int flags) { + + DrawTextAnchored.COMPANION.apply( + mBuffer, textId, + x, y, + panX, panY, + flags); + } + + /** * draw an interpolation between two paths that have the same pattern * <p> * Warning paths objects are not immutable and this is not taken into consideration @@ -490,6 +656,10 @@ public class RemoteComposeBuffer { return id; } + /** + * Adds a paint Bundle to the doc + * @param paint + */ public void addPaint(PaintBundle paint) { PaintData.COMPANION.apply(mBuffer, paint); } @@ -499,7 +669,9 @@ public class RemoteComposeBuffer { mBuffer.setIndex(0); while (mBuffer.available()) { int opId = mBuffer.readByte(); - System.out.println(">>> " + opId); + if (DEBUG) { + Utils.log(">> " + opId); + } CompanionOperation operation = Operations.map.get(opId); if (operation == null) { throw new RuntimeException("Unknown operation encountered " + opId); @@ -519,7 +691,6 @@ public class RemoteComposeBuffer { Theme.COMPANION.apply(mBuffer, theme); } - static String version() { return "v1.0"; } @@ -654,8 +825,8 @@ public class RemoteComposeBuffer { /** * Add a pre-concat of the current matrix with the specified scale. * - * @param scaleX The amount to scale in X - * @param scaleY The amount to scale in Y + * @param scaleX The amount to scale in X + * @param scaleY The amount to scale in Y */ public void addMatrixScale(float scaleX, float scaleY) { MatrixScale.COMPANION.apply(mBuffer, scaleX, scaleY, Float.NaN, Float.NaN); @@ -673,12 +844,174 @@ public class RemoteComposeBuffer { MatrixScale.COMPANION.apply(mBuffer, scaleX, scaleY, centerX, centerY); } + /** + * sets the clip based on clip id + * @param pathId 0 clears the clip + */ public void addClipPath(int pathId) { ClipPath.COMPANION.apply(mBuffer, pathId); } + /** + * Sets the clip based on clip rec + * @param left + * @param top + * @param right + * @param bottom + */ public void addClipRect(float left, float top, float right, float bottom) { ClipRect.COMPANION.apply(mBuffer, left, top, right, bottom); } + + /** + * Add a float return a NaN number pointing to that float + * @param value + * @return + */ + public float addFloat(float value) { + int id = mRemoteComposeState.cacheFloat(value); + FloatConstant.COMPANION.apply(mBuffer, id, value); + return Utils.asNan(id); + } + + /** + * Add a float that is a computation based on variables + * @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) { + int id = mRemoteComposeState.cache(value); + FloatExpression.COMPANION.apply(mBuffer, id, value, null); + 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) { + int id = mRemoteComposeState.cache(value); + FloatExpression.COMPANION.apply(mBuffer, id, value, animation); + return Utils.asNan(id); + } + + /** + * Add a color that represents the tween between two colors + * @param color1 + * @param color2 + * @param tween + * @return id of the color (color ids are short) + */ + public short addColorExpression(int color1, int color2, float tween) { + ColorExpression c = new ColorExpression(0, 0, color1, color2, tween); + short id = (short) mRemoteComposeState.cache(c); + c.mId = id; + c.write(mBuffer); + return id; + } + + /** + * Add a color that represents the tween between two colors where color1 + * is the id of a color + * @param color1 + * @param color2 + * @param tween + * @return id of the color (color ids are short) + */ + public short addColorExpression(short color1, int color2, float tween) { + ColorExpression c = new ColorExpression(0, 1, color1, color2, tween); + short id = (short) mRemoteComposeState.cache(c); + c.mId = id; + c.write(mBuffer); + return id; + } + + /** + * Add a color that represents the tween between two colors where color2 + * is the id of a color + * @param color1 + * @param color2 + * @param tween + * @return id of the color (color ids are short) + */ + public short addColorExpression(int color1, short color2, float tween) { + ColorExpression c = new ColorExpression(0, 2, color1, color2, tween); + short id = (short) mRemoteComposeState.cache(c); + c.mId = id; + c.write(mBuffer); + return id; + } + + /** + * Add a color that represents the tween between two colors where color1 & + * color2 are the ids of colors + * @param color1 + * @param color2 + * @param tween + * @return id of the color (color ids are short) + */ + public short addColorExpression(short color1, short color2, float tween) { + ColorExpression c = new ColorExpression(0, 3, color1, color2, tween); + short id = (short) mRemoteComposeState.cache(c); + c.mId = id; + c.write(mBuffer); + return id; + } + + /** + * Color calculated by Hue saturation and value. + * (as floats they can be variables used to create color transitions) + * @param hue + * @param sat + * @param value + * @return id of the color (color ids are short) + */ + public short addColorExpression(float hue, float sat, float value) { + ColorExpression c = new ColorExpression(0, hue, sat, value); + short id = (short) mRemoteComposeState.cache(c); + c.mId = id; + c.write(mBuffer); + return id; + } + + /** + * Color calculated by Alpha, Hue saturation and value. + * (as floats they can be variables used to create color transitions) + * @param alpha + * @param hue + * @param sat + * @param value + * @return id of the color (color ids are short) + */ + public short addColorExpression(int alpha, float hue, float sat, float value) { + ColorExpression c = new ColorExpression(0, alpha, hue, sat, value); + short id = (short) mRemoteComposeState.cache(c); + c.mId = id; + c.write(mBuffer); + return id; + } + + /** + * create and animation based on description and return as an array of + * floats. see addAnimatedFloat + * @param duration + * @param type + * @param spec + * @param initialValue + * @param wrap + * @return + */ + public static float[] packAnimation(float duration, + int type, + float[] spec, + float initialValue, + float wrap) { + + return FloatAnimation.packToFloatArray(duration, type, spec, initialValue, wrap); + } + } 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 17e8c839a080..66a37e677499 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeState.java +++ b/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeState.java @@ -15,26 +15,53 @@ */ package com.android.internal.widget.remotecompose.core; +import static com.android.internal.widget.remotecompose.core.RemoteContext.ID_CONTINUOUS_SEC; +import static com.android.internal.widget.remotecompose.core.RemoteContext.ID_TIME_IN_MIN; +import static com.android.internal.widget.remotecompose.core.RemoteContext.ID_TIME_IN_SEC; +import static com.android.internal.widget.remotecompose.core.RemoteContext.ID_WINDOW_HEIGHT; +import static com.android.internal.widget.remotecompose.core.RemoteContext.ID_WINDOW_WIDTH; + import com.android.internal.widget.remotecompose.core.operations.utilities.IntMap; +import java.util.ArrayList; import java.util.HashMap; /** * Represents runtime state for a RemoteCompose document + * State includes things like the value of variables */ public class RemoteComposeState { - + public static final int START_ID = 42; + private static final int MAX_FLOATS = 500; private final IntMap<Object> mIntDataMap = new IntMap<>(); private final IntMap<Boolean> mIntWrittenMap = new IntMap<>(); private final HashMap<Object, Integer> mDataIntMap = new HashMap(); + private final float[] mFloatMap = new float[MAX_FLOATS]; // efficient cache + private final int[] mColorMap = new int[MAX_FLOATS]; // efficient cache + private int mNextId = START_ID; - private static int sNextId = 42; + { + for (int i = 0; i < mFloatMap.length; i++) { + mFloatMap[i] = Float.NaN; + } + } - public Object getFromId(int id) { + /** + * Get Object based on id. The system will cache things like bitmaps + * Paths etc. They can be accessed with this command + * @param id + * @return + */ + public Object getFromId(int id) { return mIntDataMap.get(id); } - public boolean containsId(int id) { + /** + * true if the cache contain this id + * @param id + * @return + */ + public boolean containsId(int id) { return mIntDataMap.get(id) != null; } @@ -69,6 +96,65 @@ public class RemoteComposeState { } /** + * Insert an item in the cache + */ + public void update(int id, Object item) { + mDataIntMap.remove(mIntDataMap.get(id)); + mDataIntMap.put(item, id); + mIntDataMap.put(id, item); + } + + /** + * Insert an item in the cache + */ + public int cacheFloat(float item) { + int id = nextId(); + mFloatMap[id] = item; + return id; + } + + /** + * Insert an item in the cache + */ + public void cacheFloat(int id, float item) { + mFloatMap[id] = item; + } + + /** + * Insert an item in the cache + */ + public void updateFloat(int id, float item) { + mFloatMap[id] = item; + } + + /** + * get float + */ + public float getFloat(int id) { + return mFloatMap[id]; + } + + /** + * Get the float value + * + * @param id + * @return + */ + public int getColor(int id) { + return mColorMap[id]; + } + + /** + * Modify the color at id. + * @param id + * @param color + */ + public void updateColor(int id, int color) { + mColorMap[id] = color; + } + + + /** * Method to determine if a cached value has been written to the documents WireBuffer based on * its id. */ @@ -79,22 +165,90 @@ public class RemoteComposeState { /** * Method to mark that a value, represented by its id, has been written to the WireBuffer */ - public void markWritten(int id) { + public void markWritten(int id) { mIntWrittenMap.put(id, true); } /** - * Clear the record of the values that have been written to the WireBuffer. + * Clear the record of the values that have been written to the WireBuffer. */ void reset() { mIntWrittenMap.clear(); } - public static int nextId() { - return sNextId++; + /** + * Get the next available id + * @return + */ + public int nextId() { + return mNextId++; } - public static void setNextId(int id) { - sNextId = id; + + /** + * Set the next id + * @param id + */ + public void setNextId(int id) { + mNextId = id; + } + + IntMap<ArrayList<VariableSupport>> mVarListeners = new IntMap<>(); + ArrayList<VariableSupport> mAllVarListeners = new ArrayList<>(); + + private void add(int id, VariableSupport variableSupport) { + ArrayList<VariableSupport> v = mVarListeners.get(id); + if (v == null) { + v = new ArrayList<VariableSupport>(); + mVarListeners.put(id, v); + } + v.add(variableSupport); + mAllVarListeners.add(variableSupport); + } + + /** + * Commands that listen to variables add themselves. + * @param id + * @param variableSupport + */ + public void listenToVar(int id, VariableSupport variableSupport) { + add(id, variableSupport); + } + + /** + * List of Commands that need to be updated + * @param context + * @return + */ + public int getOpsToUpdate(RemoteContext context) { + for (VariableSupport vs : mAllVarListeners) { + vs.updateVariables(context); + } + if (mVarListeners.get(ID_CONTINUOUS_SEC) != null) { + return 1; + } + if (mVarListeners.get(ID_TIME_IN_SEC) != null) { + return 1000; + } + if (mVarListeners.get(ID_TIME_IN_MIN) != null) { + return 1000 * 60; + } + return -1; + } + + /** + * Set the width of the overall document on screen. + * @param width + */ + public void setWindowWidth(float width) { + updateFloat(ID_WINDOW_WIDTH, width); + } + + /** + * Set the width of the overall document on screen. + * @param height + */ + public void setWindowHeight(float height) { + updateFloat(ID_WINDOW_HEIGHT, height); } } 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 d16cbc5a1a16..7e721684be0a 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/RemoteContext.java +++ b/core/java/com/android/internal/widget/remotecompose/core/RemoteContext.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2023 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. @@ -15,7 +15,10 @@ */ package com.android.internal.widget.remotecompose.core; +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; +import com.android.internal.widget.remotecompose.core.operations.Utils; /** * Specify an abstract context used to playback RemoteCompose documents @@ -27,7 +30,7 @@ import com.android.internal.widget.remotecompose.core.operations.Theme; 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; ContextMode mMode = ContextMode.UNSET; @@ -37,9 +40,40 @@ public abstract class RemoteContext { public float mWidth = 0f; public float mHeight = 0f; + /** + * Load a path under an id. + * Paths can be use in clip drawPath and drawTweenPath + * @param instanceId + * @param floatPath + */ public abstract void loadPathData(int instanceId, float[] floatPath); /** + * Associate a name with a give id. + * + * @param varName + * @param varId + * @param varType + */ + public abstract void loadVariableName(String varName, int varId, int varType); + + /** + * Save a color under a given id + * @param id + * @param color + */ + public abstract void loadColor(int id, int color); + + /** + * gets the time animation clock as float in seconds + * @return a monotonic time in seconds (arbitrary zero point) + */ + public float getAnimationTime() { + return (System.nanoTime() - mStart) * 1E-9f; + } + + + /** * 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 loading a bitmap) should execute @@ -96,6 +130,8 @@ public abstract class RemoteContext { public void header(int majorVersion, int minorVersion, int patchVersion, int width, int height, long capabilities ) { + mRemoteComposeState.setWindowWidth(width); + mRemoteComposeState.setWindowHeight(height); mDocument.setVersion(majorVersion, minorVersion, patchVersion); mDocument.setWidth(width); mDocument.setHeight(height); @@ -137,9 +173,105 @@ public abstract class RemoteContext { /////////////////////////////////////////////////////////////////////////////////////////////// // Data handling /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Save a bitmap under an imageId + * @param imageId + * @param width + * @param height + * @param bitmap + */ public abstract void loadBitmap(int imageId, int width, int height, byte[] bitmap); + + /** + * Save a string under a given id + * @param id + * @param text + */ public abstract void loadText(int id, String text); + /** + * Get a string given an id + * @param id + * @return + */ + public abstract String getText(int id); + + /** + * Load a float + * @param id + * @param value + */ + public abstract void loadFloat(int id, float value); + + /** + * Load an animated float associated with an id + * Todo: Remove? + * @param id + * @param animatedFloat + */ + public abstract void loadAnimatedFloat(int id, FloatExpression animatedFloat); + + /** + * Save a shader under and ID + * @param id + * @param value + */ + public abstract void loadShader(int id, ShaderData value); + + /** + * Get a float given an id + * @param id + * @return + */ + public abstract float getFloat(int id); + + /** + * Get the color given and ID + * @param id + * @return + */ + public abstract int getColor(int id); + + /** + * called to notify system that a command is interested in a variable + * @param id + * @param variableSupport + */ + public abstract void listensTo(int id, VariableSupport variableSupport); + + /** + * Notify commands with variables have changed + * @return + */ + public abstract int updateOps(); + + /** + * Get a shader given the id + * @param id + * @return + */ + public abstract ShaderData getShader(int id); + + public static final int ID_CONTINUOUS_SEC = 1; + public static final int ID_TIME_IN_SEC = 2; + public static final int ID_TIME_IN_MIN = 3; + public static final int ID_TIME_IN_HR = 4; + public static final int ID_WINDOW_WIDTH = 5; + public static final int ID_WINDOW_HEIGHT = 6; + public static final int ID_COMPONENT_WIDTH = 7; + public static final int ID_COMPONENT_HEIGHT = 8; + public static final int ID_CALENDAR_MONTH = 9; + + public static final float FLOAT_CONTINUOUS_SEC = Utils.asNan(ID_CONTINUOUS_SEC); + public static final float FLOAT_TIME_IN_SEC = Utils.asNan(ID_TIME_IN_SEC); + public static final float FLOAT_TIME_IN_MIN = Utils.asNan(ID_TIME_IN_MIN); + public static final float FLOAT_TIME_IN_HR = Utils.asNan(ID_TIME_IN_HR); + public static final float FLOAT_CALENDAR_MONTH = Utils.asNan(ID_CALENDAR_MONTH); + public static final float FLOAT_WINDOW_WIDTH = Utils.asNan(ID_WINDOW_WIDTH); + 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); /////////////////////////////////////////////////////////////////////////////////////////////// // 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 new file mode 100644 index 000000000000..e9708b75de27 --- /dev/null +++ b/core/java/com/android/internal/widget/remotecompose/core/TimeVariables.java @@ -0,0 +1,51 @@ +/* + * 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; + +import java.time.LocalDateTime; + +/** + * This generates the standard system variables for time. + */ +public class TimeVariables { + /** + * This class populates all time variables in the system + * @param context + */ + public void updateTime(RemoteContext context) { + LocalDateTime dateTime = LocalDateTime.now(); + // This define the time in the format + // seconds run from Midnight=0 quantized to seconds hour 0..3599 + // minutes run from Midnight=0 quantized to minutes 0..1439 + // hours run from Midnight=0 quantized to Hours 0-23 + // CONTINUOUS_SEC is seconds from midnight looping every hour 0-3600 + // CONTINUOUS_SEC is accurate to milliseconds due to float precession + int month = dateTime.getDayOfMonth(); + int hour = dateTime.getHour(); + int minute = dateTime.getMinute(); + int seconds = dateTime.getSecond(); + int currentMinute = hour * 60 + minute; + int currentSeconds = minute * 60 + seconds; + float sec = currentSeconds + dateTime.getNano() * 1E-9f; + + context.loadFloat(RemoteContext.ID_CONTINUOUS_SEC, sec); + context.loadFloat(RemoteContext.ID_TIME_IN_SEC, currentSeconds); + context.loadFloat(RemoteContext.ID_TIME_IN_MIN, currentMinute); + context.loadFloat(RemoteContext.ID_TIME_IN_HR, hour); + context.loadFloat(RemoteContext.ID_CALENDAR_MONTH, month); + + } +} diff --git a/core/java/com/android/internal/widget/remotecompose/core/VariableSupport.java b/core/java/com/android/internal/widget/remotecompose/core/VariableSupport.java new file mode 100644 index 000000000000..d59b1bc65256 --- /dev/null +++ b/core/java/com/android/internal/widget/remotecompose/core/VariableSupport.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.widget.remotecompose.core; + +/** + * Interface for operators that interact with variables + * Threw this they register to listen to particular variables + * and are notified when they change + */ +public interface VariableSupport { + /** + * Call to allow an operator to register interest in variables. + * Typically they call context.listensTo(id, this) + * @param context + */ + void registerListening(RemoteContext context); + + /** + * Called to be notified that the variables you are interested have changed. + * @param context + */ + void updateVariables(RemoteContext context); +} 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 76b714443990..f1863225b766 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 @@ -51,11 +51,12 @@ public class BitmapData implements Operation { @Override public String toString() { - return "BITMAP DATA $imageId"; + return "BITMAP DATA " + mImageId; } public static class Companion implements CompanionOperation { - private Companion() {} + private Companion() { + } @Override public String name() { 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 8d4a787148ef..e6d5fe7043fd 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 @@ -24,6 +24,11 @@ import com.android.internal.widget.remotecompose.core.WireBuffer; import java.util.List; +/** + * Defines a path that clips a the subsequent drawing commands + * Use MatrixSave and MatrixRestore commands to remove clip + * TODO allow id 0 to mean null? + */ public class ClipPath extends PaintOperation { public static final Companion COMPANION = new Companion(); int mId; @@ -93,5 +98,4 @@ public class ClipPath extends PaintOperation { public void paint(PaintContext context) { context.clipPath(mId, mRegionOp); } -} - +}
\ No newline at end of file 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 803618a91737..613ecebf9100 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,88 +15,36 @@ */ package com.android.internal.widget.remotecompose.core.operations; -import com.android.internal.widget.remotecompose.core.CompanionOperation; 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.PaintOperation; -import com.android.internal.widget.remotecompose.core.WireBuffer; -import java.util.List; - -public class ClipRect extends PaintOperation { - public static final Companion COMPANION = new Companion(); - float mLeft; - float mTop; - float mRight; - float mBottom; +/** + * Support clip with a rectangle + */ +public class ClipRect extends DrawBase4 { + public static final Companion COMPANION = + new Companion(Operations.CLIP_RECT) { + @Override + public Operation construct(float x1, + float y1, + float x2, + float y2) { + return new ClipRect(x1, y1, x2, y2); + } + }; public ClipRect( float left, float top, float right, float bottom) { - mLeft = left; - mTop = top; - mRight = right; - mBottom = bottom; - - } - - @Override - public void write(WireBuffer buffer) { - COMPANION.apply(buffer, mLeft, mTop, mRight, mBottom); - } - - @Override - public String toString() { - return "ClipRect " + mLeft + " " + mTop - + " " + mRight + " " + mBottom + ";"; - } - - public static class Companion implements CompanionOperation { - private Companion() { - } - - @Override - public void read(WireBuffer buffer, List<Operation> operations) { - float sLeft = buffer.readFloat(); - float srcTop = buffer.readFloat(); - float srcRight = buffer.readFloat(); - float srcBottom = buffer.readFloat(); - - ClipRect op = new ClipRect(sLeft, srcTop, srcRight, srcBottom); - operations.add(op); - } - - @Override - public String name() { - return "ClipRect"; - } - - @Override - public int id() { - return Operations.CLIP_RECT; - } - - public void apply(WireBuffer buffer, - float left, - float top, - float right, - float bottom) { - buffer.start(Operations.CLIP_RECT); - buffer.writeFloat(left); - buffer.writeFloat(top); - buffer.writeFloat(right); - buffer.writeFloat(bottom); - } + super(left, top, right, bottom); + mName = "ClipRect"; } @Override public void paint(PaintContext context) { - context.clipRect(mLeft, - mTop, - mRight, - mBottom); + context.clipRect(mX1, mY1, mX2, mY2); } } 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 new file mode 100644 index 000000000000..7d28cea35850 --- /dev/null +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/ColorExpression.java @@ -0,0 +1,242 @@ +/* + * 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 com.android.internal.widget.remotecompose.core.CompanionOperation; +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.VariableSupport; +import com.android.internal.widget.remotecompose.core.WireBuffer; + +import java.util.List; + +/** + * Operation to Colors + * Color modes + * mMode = 0 two colors and a tween + * mMode = 1 color1 is a colorID. + * mMode = 2 color2 is a colorID. + * mMode = 3 color1 & color2 are ids + * mMode = 4 H S V mode + */ +public class ColorExpression implements Operation, VariableSupport { + public int mId; + int mMode; + public int mColor1; + public int mColor2; + public float mTween = 0.0f; + + + public float mHue = 0; // only in Mode 4 + public float mSat = 0; + public float mValue = 0; + public float mOutHue = 0; // only in Mode 4 + public float mOutSat = 0; + public float mOutValue = 0; + public int mAlpha = 0xFF; // only used in hsv mode + + public float mOutTween = 0.0f; + public int mOutColor1; + public int mOutColor2; + public static final Companion COMPANION = new Companion(); + public static final int HSV_MODE = 4; + public ColorExpression(int id, float hue, float sat, float value) { + mMode = HSV_MODE; + mAlpha = 0xFF; + mOutHue = mHue = hue; + mOutSat = mSat = sat; + mOutValue = mValue = value; + mColor1 = Float.floatToRawIntBits(hue); + mColor2 = Float.floatToRawIntBits(sat); + mTween = value; + } + public ColorExpression(int id, int alpha, float hue, float sat, float value) { + mMode = HSV_MODE; + mAlpha = alpha; + mOutHue = mHue = hue; + mOutSat = mSat = sat; + mOutValue = mValue = value; + mColor1 = Float.floatToRawIntBits(hue); + mColor2 = Float.floatToRawIntBits(sat); + mTween = value; + } + + public ColorExpression(int id, int mode, int color1, int color2, float tween) { + this.mId = id; + this.mMode = mode & 0xFF; + this.mAlpha = (mode >> 16) & 0xFF; + if (mMode == HSV_MODE) { + mOutHue = mHue = Float.intBitsToFloat(color1); + mOutSat = mSat = Float.intBitsToFloat(color2); + mOutValue = mValue = tween; + } + this.mColor1 = color1; + this.mColor2 = color2; + this.mTween = tween; + this.mOutTween = tween; + this.mOutColor1 = color1; + this.mOutColor2 = color2; + + } + + @Override + public void updateVariables(RemoteContext context) { + if (mMode == 4) { + if (Float.isNaN(mHue)) { + mOutHue = context.getFloat(Utils.idFromNan(mHue)); + } + if (Float.isNaN(mSat)) { + mOutSat = context.getFloat(Utils.idFromNan(mSat)); + } + if (Float.isNaN(mValue)) { + mOutValue = context.getFloat(Utils.idFromNan(mValue)); + } + } + if (Float.isNaN(mTween)) { + mOutTween = context.getFloat(Utils.idFromNan(mTween)); + } + if ((mMode & 1) == 1) { + mOutColor1 = context.getColor(mColor1); + } + if ((mMode & 2) == 2) { + mOutColor2 = context.getColor(mColor2); + } + } + + + @Override + public void registerListening(RemoteContext context) { + if (mMode == 4) { + if (Float.isNaN(mHue)) { + context.listensTo(Utils.idFromNan(mHue), this); + } + if (Float.isNaN(mSat)) { + context.listensTo(Utils.idFromNan(mSat), this); + } + if (Float.isNaN(mValue)) { + context.listensTo(Utils.idFromNan(mValue), this); + } + return; + } + if (Float.isNaN(mTween)) { + context.listensTo(Utils.idFromNan(mTween), this); + } + if ((mMode & 1) == 1) { + context.listensTo(mColor1, this); + } + if ((mMode & 2) == 2) { + context.listensTo(mColor2, this); + } + } + + @Override + public void apply(RemoteContext context) { + if (mMode == 4) { + context.loadColor(mId, (mAlpha << 24) + | (0xFFFFFF & Utils.hsvToRgb(mOutHue, mOutSat, mOutValue))); + return; + } + if (mOutTween == 0.0) { + context.loadColor(mId, mColor1); + } else { + if ((mMode & 1) == 1) { + mOutColor1 = context.getColor(mColor1); + } + if ((mMode & 2) == 2) { + mOutColor2 = context.getColor(mColor2); + } + + context.loadColor(mId, + Utils.interpolateColor(mOutColor1, mOutColor2, mOutTween)); + } + + } + + @Override + public void write(WireBuffer buffer) { + int mode = mMode | (mAlpha << 16); + COMPANION.apply(buffer, mId, mode, mColor1, mColor2, mTween); + } + + @Override + public String toString() { + if (mMode == 4) { + return "ColorExpression[" + mId + "] = hsv (" + Utils.floatToString(mHue) + + ", " + Utils.floatToString(mSat) + + ", " + Utils.floatToString(mValue) + ")"; + } + + String c1 = (mMode & 1) == 1 ? "[" + mColor1 + "]" : Utils.colorInt(mColor1); + String c2 = (mMode & 2) == 2 ? "[" + mColor2 + "]" : Utils.colorInt(mColor2); + return "ColorExpression[" + mId + "] = tween(" + c1 + + ", " + c2 + ", " + + Utils.floatToString(mTween) + ")"; + } + + public static class Companion implements CompanionOperation { + private Companion() { + } + + @Override + public String name() { + return "ColorExpression"; + } + + @Override + public int id() { + return Operations.COLOR_EXPRESSIONS; + } + + /** + * Call to write a ColorExpression object on the buffer + * @param buffer + * @param id of the ColorExpression object + * @param mode if colors are id or actual values + * @param color1 + * @param color2 + * @param tween + */ + public void apply(WireBuffer buffer, + int id, int mode, + int color1, int color2, float tween) { + buffer.start(Operations.COLOR_EXPRESSIONS); + buffer.writeInt(id); + buffer.writeInt(mode); + buffer.writeInt(color1); + buffer.writeInt(color2); + buffer.writeFloat(tween); + + } + + @Override + public void read(WireBuffer buffer, List<Operation> operations) { + int id = buffer.readInt(); + int mode = buffer.readInt(); + int color1 = buffer.readInt(); + int color2 = buffer.readInt(); + float tween = buffer.readFloat(); + + operations.add(new ColorExpression(id, mode, color1, color2, tween)); + } + } + + @Override + public String deepToString(String indent) { + return indent + toString(); + } + +} 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 e829975cd39b..c1768647bde6 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,107 +15,36 @@ */ package com.android.internal.widget.remotecompose.core.operations; -import com.android.internal.widget.remotecompose.core.CompanionOperation; 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.PaintOperation; -import com.android.internal.widget.remotecompose.core.WireBuffer; -import java.util.List; - -public class DrawArc extends PaintOperation { - public static final Companion COMPANION = new Companion(); - float mLeft; - float mTop; - float mRight; - float mBottom; - float mStartAngle; - float mSweepAngle; - - public DrawArc( - float left, - float top, - float right, - float bottom, - float startAngle, - float sweepAngle) { - mLeft = left; - mTop = top; - mRight = right; - mBottom = bottom; - mStartAngle = startAngle; - mSweepAngle = sweepAngle; - } - - @Override - public void write(WireBuffer buffer) { - COMPANION.apply(buffer, mLeft, - mTop, - mRight, - mBottom, - mStartAngle, - mSweepAngle); - } - - @Override - public String toString() { - return "DrawArc " + mLeft + " " + mTop - + " " + mRight + " " + mBottom + " " - + "- " + mStartAngle + " " + mSweepAngle + ";"; - } - - public static class Companion implements CompanionOperation { - private Companion() { - } - - @Override - public void read(WireBuffer buffer, List<Operation> operations) { - float sLeft = buffer.readFloat(); - float srcTop = buffer.readFloat(); - float srcRight = buffer.readFloat(); - float srcBottom = buffer.readFloat(); - float mStartAngle = buffer.readFloat(); - float mSweepAngle = buffer.readFloat(); - DrawArc op = new DrawArc(sLeft, srcTop, srcRight, srcBottom, - mStartAngle, mSweepAngle); - operations.add(op); - } - - @Override - public String name() { - return "DrawArc"; - } - - @Override - public int id() { - return Operations.DRAW_ARC; - } - - public void apply(WireBuffer buffer, - float left, - float top, - float right, - float bottom, - float startAngle, - float sweepAngle) { - buffer.start(Operations.DRAW_ARC); - buffer.writeFloat(left); - buffer.writeFloat(top); - buffer.writeFloat(right); - buffer.writeFloat(bottom); - buffer.writeFloat(startAngle); - buffer.writeFloat(sweepAngle); - } +public class DrawArc extends DrawBase6 { + public static final Companion COMPANION = + new Companion(Operations.DRAW_ARC) { + @Override + public Operation construct(float v1, + float v2, + float v3, + float v4, + float v5, + float v6) { + return new DrawArc(v1, v2, v3, v4, v5, v6); + } + }; + + public DrawArc(float v1, + float v2, + float v3, + float v4, + float v5, + float v6) { + super(v1, v2, v3, v4, v5, v6); + mName = "DrawArc"; } @Override public void paint(PaintContext context) { - context.drawArc(mLeft, - mTop, - mRight, - mBottom, - mStartAngle, - mSweepAngle); + 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 new file mode 100644 index 000000000000..0963c1337b70 --- /dev/null +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase2.java @@ -0,0 +1,134 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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.operations.Utils.floatToString; + +import com.android.internal.widget.remotecompose.core.CompanionOperation; +import com.android.internal.widget.remotecompose.core.Operation; +import com.android.internal.widget.remotecompose.core.Operations; +import com.android.internal.widget.remotecompose.core.PaintOperation; +import com.android.internal.widget.remotecompose.core.RemoteContext; +import com.android.internal.widget.remotecompose.core.VariableSupport; +import com.android.internal.widget.remotecompose.core.WireBuffer; + +import java.util.List; + +/** + * Base class for commands that take 3 float + */ +public abstract class DrawBase2 extends PaintOperation + implements VariableSupport { + public static final Companion COMPANION = + new Companion(Operations.DRAW_CIRCLE) { + @Override + public Operation construct(float x1, float y1) { + // subclass should return new DrawX(x1, y1); + return null; + } + }; + protected String mName = "DrawRectBase"; + float mV1; + float mV2; + float mValue1; + float mValue2; + + public DrawBase2(float v1, float v2) { + mValue1 = v1; + mValue2 = v2; + mV1 = v1; + mV2 = v2; + } + + @Override + public void updateVariables(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) { + if (Float.isNaN(mValue1)) { + context.listensTo(Utils.idFromNan(mValue1), this); + } + if (Float.isNaN(mValue2)) { + context.listensTo(Utils.idFromNan(mValue2), this); + } + } + + @Override + public void write(WireBuffer buffer) { + COMPANION.apply(buffer, mV1, mV2); + } + + @Override + public String toString() { + return mName + " " + floatToString(mV1) + " " + floatToString(mV2); + } + + public static class Companion implements CompanionOperation { + public final int OP_CODE; + + protected Companion(int code) { + OP_CODE = code; + } + + @Override + public void read(WireBuffer buffer, List<Operation> operations) { + float v1 = buffer.readFloat(); + float v2 = buffer.readFloat(); + + Operation op = construct(v1, v2); + operations.add(op); + } + + /** + * Override to construct a 2 float value operation + * @param x1 + * @param y1 + * @return + */ + public Operation construct(float x1, float y1) { + return null; + } + + @Override + public String name() { + return "DrawRect"; + } + + @Override + public int id() { + return OP_CODE; + } + + /** + * Writes out the operation to the buffer + * @param buffer + * @param x1 + * @param y1 + */ + public void apply(WireBuffer buffer, + float x1, + float y1) { + buffer.start(OP_CODE); + 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 new file mode 100644 index 000000000000..56b2f1f7bb86 --- /dev/null +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase3.java @@ -0,0 +1,157 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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.operations.Utils.floatToString; + +import com.android.internal.widget.remotecompose.core.CompanionOperation; +import com.android.internal.widget.remotecompose.core.Operation; +import com.android.internal.widget.remotecompose.core.Operations; +import com.android.internal.widget.remotecompose.core.PaintOperation; +import com.android.internal.widget.remotecompose.core.RemoteContext; +import com.android.internal.widget.remotecompose.core.VariableSupport; +import com.android.internal.widget.remotecompose.core.WireBuffer; + +import java.util.List; + +/** + * Base class for commands that take 3 float + */ +public abstract class DrawBase3 extends PaintOperation + implements VariableSupport { + public static final Companion COMPANION = + new Companion(Operations.DRAW_CIRCLE) { + @Override + public Operation construct(float x1, float y1, float x2) { + // subclass should return new DrawX(x1, y1, x2, y2); + return null; + } + }; + protected String mName = "DrawRectBase"; + float mV1; + float mV2; + float mV3; + float mValue1; + float mValue2; + float mValue3; + + public DrawBase3( + float v1, + float v2, + float v3) { + mValue1 = v1; + mValue2 = v2; + mValue3 = v3; + + mV1 = v1; + mV2 = v2; + mV3 = v3; + } + + @Override + public void updateVariables(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; + } + + @Override + public void registerListening(RemoteContext context) { + if (Float.isNaN(mValue1)) { + context.listensTo(Utils.idFromNan(mValue1), this); + } + if (Float.isNaN(mValue2)) { + context.listensTo(Utils.idFromNan(mValue2), this); + } + if (Float.isNaN(mValue3)) { + context.listensTo(Utils.idFromNan(mValue3), this); + } + } + + @Override + public void write(WireBuffer buffer) { + COMPANION.apply(buffer, mV1, mV2, mV3); + } + + @Override + public String toString() { + return mName + " " + floatToString(mV1) + " " + floatToString(mV2) + + " " + floatToString(mV3); + } + + public static class Companion implements CompanionOperation { + public final int OP_CODE; + + protected Companion(int code) { + OP_CODE = code; + } + + @Override + public void read(WireBuffer buffer, List<Operation> operations) { + float v1 = buffer.readFloat(); + float v2 = buffer.readFloat(); + float v3 = buffer.readFloat(); + + Operation op = construct(v1, v2, v3); + operations.add(op); + } + + /** + * Construct and Operation from the 3 variables. + * This must be overridden by subclasses + * @param x1 + * @param y1 + * @param x2 + * @return + */ + public Operation construct(float x1, + float y1, + float x2) { + return null; + } + + @Override + public String name() { + return "DrawRect"; + } + + @Override + public int id() { + return OP_CODE; + } + + /** + * Writes out the operation to the buffer + * @param buffer + * @param x1 + * @param y1 + * @param x2 + */ + public void apply(WireBuffer buffer, + float x1, + float y1, + float x2) { + buffer.start(OP_CODE); + buffer.writeFloat(x1); + buffer.writeFloat(y1); + buffer.writeFloat(x2); + + } + } +} 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 new file mode 100644 index 000000000000..ec35a160079c --- /dev/null +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase4.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 com.android.internal.widget.remotecompose.core.operations; + +import static com.android.internal.widget.remotecompose.core.operations.Utils.floatToString; + +import com.android.internal.widget.remotecompose.core.CompanionOperation; +import com.android.internal.widget.remotecompose.core.Operation; +import com.android.internal.widget.remotecompose.core.Operations; +import com.android.internal.widget.remotecompose.core.PaintOperation; +import com.android.internal.widget.remotecompose.core.RemoteContext; +import com.android.internal.widget.remotecompose.core.VariableSupport; +import com.android.internal.widget.remotecompose.core.WireBuffer; + +import java.util.List; + +/** + * Base class for draw commands that take 4 floats + */ +public abstract class DrawBase4 extends PaintOperation + implements VariableSupport { + public static final Companion COMPANION = + new Companion(Operations.DRAW_RECT) { + @Override + public Operation construct(float x1, float y1, float x2, float y2) { + // return new DrawRectBase(x1, y1, x2, y2); + return null; + } + }; + protected String mName = "DrawRectBase"; + float mX1; + float mY1; + float mX2; + float mY2; + float mX1Value; + float mY1Value; + float mX2Value; + float mY2Value; + + public DrawBase4( + float x1, + float y1, + float x2, + float y2) { + mX1Value = x1; + mY1Value = y1; + mX2Value = x2; + mY2Value = y2; + + mX1 = x1; + mY1 = y1; + mX2 = x2; + mY2 = y2; + } + + @Override + public void updateVariables(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; + mY2 = (Float.isNaN(mY2Value)) + ? context.getFloat(Utils.idFromNan(mY2Value)) : mY2Value; + } + + @Override + public void registerListening(RemoteContext context) { + if (Float.isNaN(mX1Value)) { + context.listensTo(Utils.idFromNan(mX1Value), this); + } + if (Float.isNaN(mY1Value)) { + context.listensTo(Utils.idFromNan(mY1Value), this); + } + if (Float.isNaN(mX2Value)) { + context.listensTo(Utils.idFromNan(mX2Value), this); + } + if (Float.isNaN(mY2Value)) { + context.listensTo(Utils.idFromNan(mY2Value), this); + } + } + + @Override + public void write(WireBuffer buffer) { + COMPANION.apply(buffer, mX1, mY1, mX2, mY2); + } + + @Override + public String toString() { + return mName + " " + floatToString(mX1Value, mX1) + " " + floatToString(mY1Value, mY1) + + " " + floatToString(mX2Value, mX2) + " " + floatToString(mY2Value, mY2); + } + + public static class Companion implements CompanionOperation { + public final int OP_CODE; + + protected Companion(int code) { + OP_CODE = code; + } + + @Override + public void read(WireBuffer buffer, List<Operation> operations) { + float sLeft = buffer.readFloat(); + float srcTop = buffer.readFloat(); + float srcRight = buffer.readFloat(); + float srcBottom = buffer.readFloat(); + + Operation op = construct(sLeft, srcTop, srcRight, srcBottom); + operations.add(op); + } + + /** + * Construct and Operation from the 3 variables. + * @param x1 + * @param y1 + * @param x2 + * @param y2 + * @return + */ + public Operation construct(float x1, + float y1, + float x2, + float y2) { + return null; + } + + @Override + public String name() { + return "DrawRect"; + } + + @Override + public int id() { + return OP_CODE; + } + + /** + * Writes out the operation to the buffer + * @param buffer + * @param x1 + * @param y1 + * @param x2 + * @param y2 + */ + public void apply(WireBuffer buffer, + float x1, + float y1, + float x2, + float y2) { + buffer.start(OP_CODE); + buffer.writeFloat(x1); + buffer.writeFloat(y1); + buffer.writeFloat(x2); + buffer.writeFloat(y2); + } + } +} 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 new file mode 100644 index 000000000000..2f4335e7f412 --- /dev/null +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase6.java @@ -0,0 +1,202 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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.operations.Utils.floatToString; + +import com.android.internal.widget.remotecompose.core.CompanionOperation; +import com.android.internal.widget.remotecompose.core.Operation; +import com.android.internal.widget.remotecompose.core.Operations; +import com.android.internal.widget.remotecompose.core.PaintOperation; +import com.android.internal.widget.remotecompose.core.RemoteContext; +import com.android.internal.widget.remotecompose.core.VariableSupport; +import com.android.internal.widget.remotecompose.core.WireBuffer; + +import java.util.List; + +/** + * Base class for draw commands the take 6 floats + */ +public abstract class DrawBase6 extends PaintOperation + implements VariableSupport { + public static final Companion COMPANION = + new Companion(Operations.DRAW_RECT) { + public Operation construct(float x1, float y1, float x2, float y2) { + // return new DrawRectBase(x1, y1, x2, y2); + return null; + } + }; + protected String mName = "DrawRectBase"; + float mV1; + float mV2; + float mV3; + float mV4; + float mV5; + float mV6; + float mValue1; + float mValue2; + float mValue3; + float mValue4; + float mValue5; + float mValue6; + + public DrawBase6( + float v1, + float v2, + float v3, + float v4, + float v5, + float v6) { + mValue1 = v1; + mValue2 = v2; + mValue3 = v3; + mValue4 = v4; + mValue5 = v5; + mValue6 = v6; + + mV1 = v1; + mV2 = v2; + mV3 = v3; + mV4 = v4; + mV5 = v5; + mV6 = v6; + } + + @Override + public void updateVariables(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; + mV4 = (Float.isNaN(mValue4)) + ? context.getFloat(Utils.idFromNan(mValue4)) : mValue4; + mV5 = (Float.isNaN(mValue5)) + ? context.getFloat(Utils.idFromNan(mValue5)) : mValue5; + mV6 = (Float.isNaN(mValue6)) + ? context.getFloat(Utils.idFromNan(mValue6)) : mValue6; + } + + @Override + public void registerListening(RemoteContext context) { + if (Float.isNaN(mValue1)) { + context.listensTo(Utils.idFromNan(mValue1), this); + } + if (Float.isNaN(mValue2)) { + context.listensTo(Utils.idFromNan(mValue2), this); + } + if (Float.isNaN(mValue3)) { + context.listensTo(Utils.idFromNan(mValue3), this); + } + if (Float.isNaN(mValue4)) { + context.listensTo(Utils.idFromNan(mValue4), this); + } + if (Float.isNaN(mValue5)) { + context.listensTo(Utils.idFromNan(mValue5), this); + } + if (Float.isNaN(mValue6)) { + context.listensTo(Utils.idFromNan(mValue6), this); + } + } + + @Override + public void write(WireBuffer buffer) { + COMPANION.apply(buffer, mV1, mV2, mV3, mV4, mV5, mV6); + } + + @Override + public String toString() { + return mName + " " + floatToString(mV1) + " " + floatToString(mV2) + + " " + floatToString(mV3) + " " + floatToString(mV4); + } + + public static class Companion implements CompanionOperation { + public final int OP_CODE; + + protected Companion(int code) { + OP_CODE = code; + } + + @Override + public void read(WireBuffer buffer, List<Operation> operations) { + float sv1 = buffer.readFloat(); + float sv2 = buffer.readFloat(); + float sv3 = buffer.readFloat(); + float sv4 = buffer.readFloat(); + float sv5 = buffer.readFloat(); + float sv6 = buffer.readFloat(); + + Operation op = construct(sv1, sv2, sv3, sv4, sv5, sv6); + operations.add(op); + } + + /** + * writes out a the operation to the buffer. + * @param v1 + * @param v2 + * @param v3 + * @param v4 + * @param v5 + * @param v6 + * @return + */ + public Operation construct(float v1, + float v2, + float v3, + float v4, + float v5, + float v6) { + return null; + } + + @Override + public String name() { + return "DrawRect"; + } + + @Override + public int id() { + return OP_CODE; + } + + /** + * Writes out the operation to the buffer + * @param buffer + * @param v1 + * @param v2 + * @param v3 + * @param v4 + * @param v5 + * @param v6 + */ + public void apply(WireBuffer buffer, + float v1, + float v2, + float v3, + float v4, + float v5, + float v6) { + buffer.start(OP_CODE); + buffer.writeFloat(v1); + buffer.writeFloat(v2); + buffer.writeFloat(v3); + buffer.writeFloat(v4); + buffer.writeFloat(v5); + buffer.writeFloat(v6); + } + } +} 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 2e971f533ed2..ca40d12fd3f9 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 @@ -20,16 +20,22 @@ 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.PaintOperation; +import com.android.internal.widget.remotecompose.core.RemoteContext; +import com.android.internal.widget.remotecompose.core.VariableSupport; import com.android.internal.widget.remotecompose.core.WireBuffer; import java.util.List; -public class DrawBitmap extends PaintOperation { +public class DrawBitmap extends PaintOperation implements VariableSupport { public static final Companion COMPANION = new Companion(); float mLeft; float mTop; float mRight; float mBottom; + float mOutputLeft; + float mOutputTop; + float mOutputRight; + float mOutputBottom; int mId; int mDescriptionId = 0; @@ -49,6 +55,34 @@ public class DrawBitmap extends PaintOperation { } @Override + public void updateVariables(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; + mOutputBottom = (Float.isNaN(mBottom)) + ? context.getFloat(Utils.idFromNan(mBottom)) : mBottom; + } + + @Override + public void registerListening(RemoteContext context) { + if (Float.isNaN(mLeft)) { + context.listensTo(Utils.idFromNan(mLeft), this); + } + if (Float.isNaN(mTop)) { + context.listensTo(Utils.idFromNan(mTop), this); + } + if (Float.isNaN(mRight)) { + context.listensTo(Utils.idFromNan(mRight), this); + } + if (Float.isNaN(mBottom)) { + context.listensTo(Utils.idFromNan(mBottom), this); + } + } + + @Override public void write(WireBuffer buffer) { COMPANION.apply(buffer, mId, mLeft, mTop, mRight, mBottom, mDescriptionId); } @@ -105,9 +139,9 @@ public class DrawBitmap extends PaintOperation { @Override public void paint(PaintContext context) { - context.drawBitmap(mId, mLeft, - mTop, - mRight, - mBottom); + context.drawBitmap(mId, mOutputLeft, + mOutputTop, + mOutputRight, + mOutputBottom); } } 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 9ce754da1b1b..3a22e4f72720 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 @@ -1,89 +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.internal.widget.remotecompose.core.operations; -import com.android.internal.widget.remotecompose.core.CompanionOperation; 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.PaintOperation; -import com.android.internal.widget.remotecompose.core.WireBuffer; -import java.util.List; - -public class DrawCircle extends PaintOperation { - public static final Companion COMPANION = new Companion(); - float mCenterX; - float mCenterY; - float mRadius; - - public DrawCircle(float centerX, float centerY, float radius) { - mCenterX = centerX; - mCenterY = centerY; - mRadius = radius; - } - - @Override - public void write(WireBuffer buffer) { - COMPANION.apply(buffer, mCenterX, - mCenterY, - mRadius); - } - - @Override - public String toString() { - return ""; - } - - public static class Companion implements CompanionOperation { - private Companion() { - } - - @Override - public void read(WireBuffer buffer, List<Operation> operations) { - float centerX = buffer.readFloat(); - float centerY = buffer.readFloat(); - float radius = buffer.readFloat(); - - DrawCircle op = new DrawCircle(centerX, centerY, radius); - operations.add(op); - } - - @Override - public String name() { - return ""; - } - - @Override - public int id() { - return 0; - } - - public void apply(WireBuffer buffer, float centerX, float centerY, float radius) { - buffer.start(Operations.DRAW_CIRCLE); - buffer.writeFloat(centerX); - buffer.writeFloat(centerY); - buffer.writeFloat(radius); - } +public class DrawCircle extends DrawBase3 { + public static final Companion COMPANION = + new Companion(Operations.DRAW_CIRCLE) { + @Override + public Operation construct(float x1, + float y1, + float x2 + ) { + return new DrawCircle(x1, y1, x2); + } + }; + + public DrawCircle( + float left, + float top, + float right) { + super(left, top, right); + mName = "DrawCircle"; } @Override public void paint(PaintContext context) { - context.drawCircle(mCenterX, - mCenterY, - mRadius); + context.drawCircle(mV1, mV2, mV3); } } 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 c7a8315a2274..c70c6eaa449d 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,83 +15,28 @@ */ package com.android.internal.widget.remotecompose.core.operations; -import com.android.internal.widget.remotecompose.core.CompanionOperation; 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.PaintOperation; -import com.android.internal.widget.remotecompose.core.WireBuffer; - -import java.util.List; - -public class DrawLine extends PaintOperation { - public static final Companion COMPANION = new Companion(); - float mX1; - float mY1; - float mX2; - float mY2; - - public DrawLine( - float x1, - float y1, - float x2, - float y2) { - mX1 = x1; - mY1 = y1; - mX2 = x2; - mY2 = y2; - } - - @Override - public void write(WireBuffer buffer) { - COMPANION.apply(buffer, mX1, - mY1, - mX2, - mY2); - } - - @Override - public String toString() { - return "DrawArc " + mX1 + " " + mY1 - + " " + mX2 + " " + mY2 + ";"; - } - - public static class Companion implements CompanionOperation { - private Companion() { - } - - @Override - public void read(WireBuffer buffer, List<Operation> operations) { - float x1 = buffer.readFloat(); - float y1 = buffer.readFloat(); - float x2 = buffer.readFloat(); - float y2 = buffer.readFloat(); - - DrawLine op = new DrawLine(x1, y1, x2, y2); - operations.add(op); - } - - @Override - public String name() { - return "DrawLine"; - } +public class DrawLine extends DrawBase4 { + public static final Companion COMPANION = new Companion(Operations.DRAW_LINE) { @Override - public int id() { - return Operations.DRAW_LINE; + public Operation construct(float x1, + float y1, + float x2, + float y2) { + return new DrawLine(x1, y1, x2, y2); } + }; - public void apply(WireBuffer buffer, - float x1, - float y1, - float x2, - float y2) { - buffer.start(Operations.DRAW_LINE); - buffer.writeFloat(x1); - buffer.writeFloat(y1); - buffer.writeFloat(x2); - buffer.writeFloat(y2); - } + public DrawLine( + float left, + float top, + float right, + float bottom) { + super(left, top, right, bottom); + mName = "DrawLine"; } @Override 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 714375335cb2..ba1799422e80 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,88 +15,33 @@ */ package com.android.internal.widget.remotecompose.core.operations; -import com.android.internal.widget.remotecompose.core.CompanionOperation; 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.PaintOperation; -import com.android.internal.widget.remotecompose.core.WireBuffer; - -import java.util.List; - -public class DrawOval extends PaintOperation { - public static final Companion COMPANION = new Companion(); - float mLeft; - float mTop; - float mRight; - float mBottom; +public class DrawOval extends DrawBase4 { + public static final Companion COMPANION = + new Companion(Operations.DRAW_OVAL) { + @Override + public Operation construct(float x1, + float y1, + float x2, + float y2) { + return new DrawOval(x1, y1, x2, y2); + } + }; public DrawOval( float left, float top, float right, float bottom) { - mLeft = left; - mTop = top; - mRight = right; - mBottom = bottom; - } - - @Override - public void write(WireBuffer buffer) { - COMPANION.apply(buffer, mLeft, mTop, mRight, mBottom); - } - - @Override - public String toString() { - return "DrawOval " + mLeft + " " + mTop - + " " + mRight + " " + mBottom + ";"; - } - - public static class Companion implements CompanionOperation { - private Companion() { - } - - @Override - public void read(WireBuffer buffer, List<Operation> operations) { - float sLeft = buffer.readFloat(); - float srcTop = buffer.readFloat(); - float srcRight = buffer.readFloat(); - float srcBottom = buffer.readFloat(); - - DrawOval op = new DrawOval(sLeft, srcTop, srcRight, srcBottom); - operations.add(op); - } - - @Override - public String name() { - return "DrawOval"; - } - - @Override - public int id() { - return Operations.DRAW_OVAL; - } - - public void apply(WireBuffer buffer, - float left, - float top, - float right, - float bottom) { - buffer.start(Operations.DRAW_OVAL); - buffer.writeFloat(left); - buffer.writeFloat(top); - buffer.writeFloat(right); - buffer.writeFloat(bottom); - } + super(left, top, right, bottom); + mName = "DrawOval"; } @Override public void paint(PaintContext context) { - context.drawOval(mLeft, - mTop, - mRight, - mBottom); + context.drawOval(mX1, mY1, mX2, mY2); } } 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 7b8a9e95d9cb..6dbc5a628c98 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 @@ -41,7 +41,7 @@ public class DrawPath extends PaintOperation { @Override public String toString() { - return "DrawPath " + ";"; + return "DrawPath " + "[" + mId + "]" + ", " + mStart + ", " + mEnd; } public static class Companion implements CompanionOperation { 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 4775241faa6f..633aed4a4dbe 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,88 +15,37 @@ */ package com.android.internal.widget.remotecompose.core.operations; -import com.android.internal.widget.remotecompose.core.CompanionOperation; 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.PaintOperation; -import com.android.internal.widget.remotecompose.core.WireBuffer; -import java.util.List; - -public class DrawRect extends PaintOperation { - public static final Companion COMPANION = new Companion(); - float mLeft; - float mTop; - float mRight; - float mBottom; +/** + * Draw a Rectangle + */ +public class DrawRect extends DrawBase4 { + public static final Companion COMPANION = + new Companion(Operations.DRAW_RECT) { + @Override + public Operation construct(float x1, + float y1, + float x2, + float y2) { + return new DrawRect(x1, y1, x2, y2); + } + }; public DrawRect( float left, float top, float right, float bottom) { - mLeft = left; - mTop = top; - mRight = right; - mBottom = bottom; - } - - @Override - public void write(WireBuffer buffer) { - COMPANION.apply(buffer, mLeft, mTop, mRight, mBottom); - } - - @Override - public String toString() { - return "DrawRect " + mLeft + " " + mTop - + " " + mRight + " " + mBottom + ";"; - } - - public static class Companion implements CompanionOperation { - private Companion() { - } - - @Override - public void read(WireBuffer buffer, List<Operation> operations) { - float sLeft = buffer.readFloat(); - float srcTop = buffer.readFloat(); - float srcRight = buffer.readFloat(); - float srcBottom = buffer.readFloat(); - - DrawRect op = new DrawRect(sLeft, srcTop, srcRight, srcBottom); - operations.add(op); - } - - @Override - public String name() { - return "DrawRect"; - } - - @Override - public int id() { - return Operations.DRAW_RECT; - } - - public void apply(WireBuffer buffer, - float left, - float top, - float right, - float bottom) { - buffer.start(Operations.DRAW_RECT); - buffer.writeFloat(left); - buffer.writeFloat(top); - buffer.writeFloat(right); - buffer.writeFloat(bottom); - } + super(left, top, right, bottom); + mName = "DrawRect"; } @Override public void paint(PaintContext context) { - context.drawRect(mLeft, - mTop, - mRight, - mBottom); + context.drawRect(mX1, mY1, mX2, mY2); } } 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 8da16e768b7f..b9d0a6728b95 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,104 +15,40 @@ */ package com.android.internal.widget.remotecompose.core.operations; -import com.android.internal.widget.remotecompose.core.CompanionOperation; 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.PaintOperation; -import com.android.internal.widget.remotecompose.core.WireBuffer; -import java.util.List; - -public class DrawRoundRect extends PaintOperation { - public static final Companion COMPANION = new Companion(); - float mLeft; - float mTop; - float mRight; - float mBottom; - float mRadiusX; - float mRadiusY; - - public DrawRoundRect( - float left, - float top, - float right, - float bottom, - float radiusX, - float radiusY) { - mLeft = left; - mTop = top; - mRight = right; - mBottom = bottom; - mRadiusX = radiusX; - mRadiusY = radiusY; - } - - @Override - public void write(WireBuffer buffer) { - COMPANION.apply(buffer, mLeft, mTop, mRight, mBottom, mRadiusX, mRadiusY); - } - - @Override - public String toString() { - return "DrawRoundRect " + mLeft + " " + mTop - + " " + mRight + " " + mBottom - + " (" + mRadiusX + " " + mRadiusY + ");"; - } - - public static class Companion implements CompanionOperation { - private Companion() { - } - - @Override - public void read(WireBuffer buffer, List<Operation> operations) { - float sLeft = buffer.readFloat(); - float srcTop = buffer.readFloat(); - float srcRight = buffer.readFloat(); - float srcBottom = buffer.readFloat(); - float srcRadiusX = buffer.readFloat(); - float srcRadiusY = buffer.readFloat(); - - DrawRoundRect op = new DrawRoundRect(sLeft, srcTop, srcRight, - srcBottom, srcRadiusX, srcRadiusY); - operations.add(op); - } - - @Override - public String name() { - return "DrawOval"; - } - - @Override - public int id() { - return Operations.DRAW_ROUND_RECT; - } - - public void apply(WireBuffer buffer, - float left, - float top, - float right, - float bottom, - float radiusX, - float radiusY) { - buffer.start(Operations.DRAW_ROUND_RECT); - buffer.writeFloat(left); - buffer.writeFloat(top); - buffer.writeFloat(right); - buffer.writeFloat(bottom); - buffer.writeFloat(radiusX); - buffer.writeFloat(radiusY); - } +/** + * Draw a rounded rectangle + */ +public class DrawRoundRect extends DrawBase6 { + public static final Companion COMPANION = + new Companion(Operations.DRAW_ROUND_RECT) { + @Override + public Operation construct(float v1, + float v2, + float v3, + float v4, + float v5, + float v6) { + return new DrawRoundRect(v1, v2, v3, v4, v5, v6); + } + }; + + public DrawRoundRect(float v1, + float v2, + float v3, + float v4, + float v5, + float v6) { + super(v1, v2, v3, v4, v5, v6); + mName = "ClipRect"; } @Override public void paint(PaintContext context) { - context.drawRoundRect(mLeft, - mTop, - mRight, - mBottom, - mRadiusX, - mRadiusY + context.drawRoundRect(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 new file mode 100644 index 000000000000..f8f8afdf68cd --- /dev/null +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawText.java @@ -0,0 +1,136 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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 com.android.internal.widget.remotecompose.core.CompanionOperation; +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.PaintOperation; +import com.android.internal.widget.remotecompose.core.WireBuffer; + +import java.util.List; + +/** + * Draw Text + */ +public class DrawText extends PaintOperation { + public static final Companion COMPANION = new Companion(); + int mTextID; + int mStart = 0; + int mEnd = 0; + int mContextStart = 0; + int mContextEnd = 0; + float mX = 0f; + float mY = 0f; + boolean mRtl = false; + + public DrawText(int textID, + int start, + int end, + int contextStart, + int contextEnd, + float x, + float y, + boolean rtl) { + mTextID = textID; + mStart = start; + mEnd = end; + mContextStart = contextStart; + mContextEnd = contextEnd; + mX = x; + mY = y; + mRtl = rtl; + } + + @Override + public void write(WireBuffer buffer) { + COMPANION.apply(buffer, mTextID, mStart, mEnd, mContextStart, mContextEnd, mX, mY, mRtl); + + } + + @Override + public String toString() { + return "DrawTextRun [" + mTextID + "] " + mStart + ", " + mEnd + ", " + mX + ", " + mY; + } + + public static class Companion implements CompanionOperation { + private Companion() { + } + + @Override + public void read(WireBuffer buffer, List<Operation> operations) { + int text = buffer.readInt(); + int start = buffer.readInt(); + int end = buffer.readInt(); + int contextStart = buffer.readInt(); + int contextEnd = buffer.readInt(); + float x = buffer.readFloat(); + float y = buffer.readFloat(); + boolean rtl = buffer.readBoolean(); + DrawText op = new DrawText(text, start, end, contextStart, contextEnd, x, y, rtl); + + operations.add(op); + } + + @Override + public String name() { + return ""; + } + + @Override + public int id() { + return 0; + } + + /** + * Writes out the operation to the buffer + * @param buffer + * @param textID + * @param start + * @param end + * @param contextStart + * @param contextEnd + * @param x + * @param y + * @param rtl + */ + public void apply(WireBuffer buffer, + int textID, + int start, + int end, + int contextStart, + int contextEnd, + float x, + float y, + boolean rtl) { + buffer.start(Operations.DRAW_TEXT_RUN); + buffer.writeInt(textID); + buffer.writeInt(start); + buffer.writeInt(end); + buffer.writeInt(contextStart); + buffer.writeInt(contextEnd); + buffer.writeFloat(x); + buffer.writeFloat(y); + buffer.writeBoolean(rtl); + } + } + + @Override + public void paint(PaintContext context) { + context.drawTextRun(mTextID, mStart, mEnd, mContextStart, mContextEnd, mX, mY, 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 new file mode 100644 index 000000000000..4f0641f34d84 --- /dev/null +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawTextAnchored.java @@ -0,0 +1,204 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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 com.android.internal.widget.remotecompose.core.CompanionOperation; +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.PaintOperation; +import com.android.internal.widget.remotecompose.core.RemoteContext; +import com.android.internal.widget.remotecompose.core.VariableSupport; +import com.android.internal.widget.remotecompose.core.WireBuffer; + +import java.util.List; + +/** + * Draw Text in Anchored to a point + */ +public class DrawTextAnchored extends PaintOperation implements VariableSupport { + public static final Companion COMPANION = new Companion(); + int mTextID; + float mX; + float mY; + float mPanX; + float mPanY; + int mFlags; + float mOutX; + float mOutY; + float mOutPanX; + float mOutPanY; + + public static final int ANCHOR_TEXT_RTL = 1; + public static final int ANCHOR_MONOSPACE_MEASURE = 2; + + public DrawTextAnchored(int textID, + float x, + float y, + float panX, + float panY, + int flags) { + mTextID = textID; + mX = x; + mY = y; + mOutX = mX; + mOutY = mY; + mFlags = flags; + mOutPanX = mPanX = panX; + mOutPanY = mPanY = panY; + } + + @Override + public void updateVariables(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; + mOutPanY = (Float.isNaN(mPanY)) + ? context.getFloat(Utils.idFromNan(mPanY)) : mPanY; + } + + @Override + public void registerListening(RemoteContext context) { + if (Float.isNaN(mX)) { + context.listensTo(Utils.idFromNan(mX), this); + } + if (Float.isNaN(mY)) { + context.listensTo(Utils.idFromNan(mY), this); + } + if (Float.isNaN(mPanX)) { + context.listensTo(Utils.idFromNan(mPanX), this); + } + if (Float.isNaN(mPanY) && Utils.idFromNan(mPanY) > 0) { + context.listensTo(Utils.idFromNan(mPanY), this); + } + } + + @Override + public void write(WireBuffer buffer) { + COMPANION.apply(buffer, mTextID, mX, + mY, + mPanX, + mPanY, + mFlags); + } + + @Override + public String toString() { + return "DrawTextAnchored [" + mTextID + "] " + floatToStr(mX) + ", " + + floatToStr(mY) + ", " + + floatToStr(mPanX) + ", " + floatToStr(mPanY) + ", " + + Integer.toBinaryString(mFlags); + } + + private static String floatToStr(float v) { + if (Float.isNaN(v)) { + return "[" + Utils.idFromNan(v) + "]"; + } + return Float.toString(v); + } + + public static class Companion implements CompanionOperation { + private Companion() { + } + + @Override + public void read(WireBuffer buffer, List<Operation> operations) { + int textID = buffer.readInt(); + float x = buffer.readFloat(); + float y = buffer.readFloat(); + float panX = buffer.readFloat(); + float panY = buffer.readFloat(); + int flags = buffer.readInt(); + + DrawTextAnchored op = new DrawTextAnchored(textID, + x, y, + panX, panY, + flags); + + operations.add(op); + } + + @Override + public String name() { + return ""; + } + + @Override + public int id() { + return 0; + } + + /** + * Writes out the operation to the buffer + * @param buffer + * @param textID + * @param x + * @param y + * @param panX + * @param panY + * @param flags + */ + public void apply(WireBuffer buffer, + int textID, + float x, + float y, + float panX, + float panY, + int flags) { + buffer.start(Operations.DRAW_TEXT_ANCHOR); + buffer.writeInt(textID); + buffer.writeFloat(x); + buffer.writeFloat(y); + buffer.writeFloat(panX); + buffer.writeFloat(panY); + buffer.writeInt(flags); + } + } + + float[] mBounds = new float[4]; + + private float getHorizontalOffset() { + // TODO scale TextSize / BaseTextSize; + float scale = 1.0f; + + float textWidth = scale * (mBounds[2] - mBounds[0]); + float boxWidth = 0; + return (boxWidth - textWidth) * (1 + mOutPanX) / 2.f + - (scale * mBounds[0]); + } + + private float getVerticalOffset() { + // TODO scale TextSize / BaseTextSize; + float scale = 1.0f; + float boxHeight = 0; + float textHeight = scale * (mBounds[3] - mBounds[1]); + return (boxHeight - textHeight) * (1 - mOutPanY) / 2 + - (scale * mBounds[1]); + } + + @Override + public void paint(PaintContext context) { + context.getTextBounds(mTextID, 0, -1, + (mFlags & ANCHOR_MONOSPACE_MEASURE) != 0, mBounds); + float x = mOutX + getHorizontalOffset(); + float y = (Float.isNaN(mOutPanY)) ? mOutY : mOutY + getVerticalOffset(); + context.drawTextRun(mTextID, 0, -1, 0, 1, x, y, + (mFlags & ANCHOR_TEXT_RTL) == 1); + } +} 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 1856e3097ec0..b1a01724c315 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 @@ -24,6 +24,9 @@ import com.android.internal.widget.remotecompose.core.WireBuffer; import java.util.List; +/** + * Draw text along a path. + */ public class DrawTextOnPath extends PaintOperation { public static final Companion COMPANION = new Companion(); int mPathId; @@ -45,7 +48,8 @@ public class DrawTextOnPath extends PaintOperation { @Override public String toString() { - return "DrawTextOnPath " + " " + mPathId + ";"; + return "DrawTextOnPath [" + mTextId + "] [" + mPathId + "] " + + mHOffset + ", " + mVOffset; } public static class Companion implements CompanionOperation { 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 ef0a4ad2eff3..48fc94ee5f27 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 @@ -58,7 +58,7 @@ public class DrawTweenPath extends PaintOperation { public String toString() { return "DrawTweenPath " + mPath1Id + " " + mPath2Id + " " + mTween + " " + mStart + " " - + "- " + mStop + ";"; + + "- " + mStop; } public static class Companion implements CompanionOperation { 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 new file mode 100644 index 000000000000..576b53f9fc6c --- /dev/null +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/FloatConstant.java @@ -0,0 +1,93 @@ +/* + * 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 com.android.internal.widget.remotecompose.core.CompanionOperation; +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 java.util.List; + +/** + * Operation to deal with Text data + */ +public class FloatConstant implements Operation { + public int mTextId; + public float mValue; + public static final Companion COMPANION = new Companion(); + public static final int MAX_STRING_SIZE = 4000; + + public FloatConstant(int textId, float value) { + this.mTextId = textId; + this.mValue = value; + } + + @Override + public void write(WireBuffer buffer) { + COMPANION.apply(buffer, mTextId, mValue); + } + + @Override + public String toString() { + return "FloatConstant[" + mTextId + "] = " + mValue + ""; + } + + public static class Companion implements CompanionOperation { + private Companion() {} + + @Override + public String name() { + return "FloatExpression"; + } + + @Override + public int id() { + return Operations.DATA_FLOAT; + } + + /** + * Writes out the operation to the buffer + * @param buffer + * @param textId + * @param value + */ + public void apply(WireBuffer buffer, int textId, float value) { + buffer.start(Operations.DATA_FLOAT); + buffer.writeInt(textId); + buffer.writeFloat(value); + } + + @Override + public void read(WireBuffer buffer, List<Operation> operations) { + int textId = buffer.readInt(); + + float value = buffer.readFloat(); + operations.add(new FloatConstant(textId, value)); + } + } + + @Override + public void apply(RemoteContext context) { + context.loadFloat(mTextId, mValue); + } + + @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 new file mode 100644 index 000000000000..354f41b813e0 --- /dev/null +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/FloatExpression.java @@ -0,0 +1,206 @@ +/* + * 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 com.android.internal.widget.remotecompose.core.CompanionOperation; +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.VariableSupport; +import com.android.internal.widget.remotecompose.core.WireBuffer; +import com.android.internal.widget.remotecompose.core.operations.utilities.AnimatedFloatExpression; +import com.android.internal.widget.remotecompose.core.operations.utilities.easing.FloatAnimation; + +import java.util.Arrays; +import java.util.List; + +/** + * Operation to deal with AnimatedFloats + * This is designed to be an optimized calculation for things like + * injecting the width of the component int draw rect + * As well as supporting generalized animation floats. + * The floats represent a RPN style calculator + */ +public class FloatExpression implements Operation, VariableSupport { + public int mId; + public float[] mSrcValue; + public float[] mSrcAnimation; + public FloatAnimation mFloatAnimation; + public float[] mPreCalcValue; + private float mLastChange = Float.NaN; + AnimatedFloatExpression mExp = new AnimatedFloatExpression(); + public static final Companion COMPANION = new Companion(); + public static final int MAX_STRING_SIZE = 4000; + + public FloatExpression(int id, float[] value, float[] animation) { + this.mId = id; + this.mSrcValue = value; + this.mSrcAnimation = animation; + if (mSrcAnimation != null) { + mFloatAnimation = new FloatAnimation(mSrcAnimation); + } + } + + @Override + public void updateVariables(RemoteContext context) { + if (mPreCalcValue == null || mPreCalcValue.length != mSrcValue.length) { + mPreCalcValue = new float[mSrcValue.length]; + } + //Utils.log("updateVariables "); + boolean value_changed = false; + for (int i = 0; i < mSrcValue.length; i++) { + float v = mSrcValue[i]; + if (Float.isNaN(v) && !AnimatedFloatExpression.isMathOperator(v)) { + float newValue = context.getFloat(Utils.idFromNan(v)); + if (mFloatAnimation != null) { + if (mPreCalcValue[i] != newValue) { + mLastChange = context.getAnimationTime(); + value_changed = true; + mPreCalcValue[i] = newValue; + } + } else { + mPreCalcValue[i] = newValue; + } + } else { + mPreCalcValue[i] = mSrcValue[i]; + } + } + if (value_changed && mFloatAnimation != null) { + float v = mExp.eval(Arrays.copyOf(mPreCalcValue, mPreCalcValue.length)); + if (Float.isNaN(mFloatAnimation.getTargetValue())) { + mFloatAnimation.setInitialValue(v); + } else { + mFloatAnimation.setInitialValue(mFloatAnimation.getTargetValue()); + } + mFloatAnimation.setTargetValue(v); + } + } + + @Override + public void registerListening(RemoteContext context) { + for (int i = 0; i < mSrcValue.length; i++) { + float v = mSrcValue[i]; + if (Float.isNaN(v) && !AnimatedFloatExpression.isMathOperator(v)) { + context.listensTo(Utils.idFromNan(v), this); + } + } + } + + @Override + public void apply(RemoteContext context) { + updateVariables(context); + float t = context.getAnimationTime(); + if (Float.isNaN(mLastChange)) { + mLastChange = t; + } + if (mFloatAnimation != null) { + float f = mFloatAnimation.get(t - mLastChange); + context.loadFloat(mId, f); + } else { + context.loadFloat(mId, mExp.eval(Arrays.copyOf(mPreCalcValue, mPreCalcValue.length))); + } + } + + @Override + public void write(WireBuffer buffer) { + COMPANION.apply(buffer, mId, mSrcValue, mSrcAnimation); + } + + @Override + public String toString() { + String[] labels = new String[mSrcValue.length]; + for (int i = 0; i < mSrcValue.length; i++) { + if (Float.isNaN(mSrcValue[i])) { + labels[i] = "[" + Utils.idFromNan(mSrcValue[i]) + "]"; + } + + } + return "FloatExpression[" + mId + "] = (" + + AnimatedFloatExpression.toString(mPreCalcValue, labels) + ")"; + } + + public static class Companion implements CompanionOperation { + private Companion() { + } + + @Override + public String name() { + return "FloatExpression"; + } + + @Override + public int id() { + return Operations.ANIMATED_FLOAT; + } + + /** + * Writes out the operation to the buffer + * @param buffer + * @param id + * @param value + * @param animation + */ + public void apply(WireBuffer buffer, int id, float[] value, float[] animation) { + buffer.start(Operations.ANIMATED_FLOAT); + buffer.writeInt(id); + + int len = value.length; + if (animation != null) { + len |= (animation.length << 16); + } + buffer.writeInt(len); + + for (int i = 0; i < value.length; i++) { + buffer.writeFloat(value[i]); + } + if (animation != null) { + for (int i = 0; i < animation.length; i++) { + buffer.writeFloat(animation[i]); + } + } + + } + + @Override + public void read(WireBuffer buffer, List<Operation> operations) { + int id = buffer.readInt(); + int len = buffer.readInt(); + int valueLen = len & 0xFFFF; + int animLen = (len >> 16) & 0xFFFF; + float[] values = new float[valueLen]; + for (int i = 0; i < values.length; i++) { + values[i] = buffer.readFloat(); + } + + float[] animation; + if (animLen != 0) { + animation = new float[animLen]; + for (int i = 0; i < animation.length; i++) { + animation[i] = buffer.readFloat(); + } + } else { + animation = null; + } + operations.add(new FloatExpression(id, values, animation)); + } + } + + @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 482e0e22bd57..0dad45ce356b 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 @@ -37,7 +37,7 @@ public class MatrixRestore extends PaintOperation { @Override public String toString() { - return "MatrixRestore;"; + return "MatrixRestore"; } public static class Companion implements CompanionOperation { 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 d6c89e0d2c64..bbf41351a1c5 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,68 +15,29 @@ */ package com.android.internal.widget.remotecompose.core.operations; -import com.android.internal.widget.remotecompose.core.CompanionOperation; 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.PaintOperation; -import com.android.internal.widget.remotecompose.core.WireBuffer; -import java.util.List; - -public class MatrixRotate extends PaintOperation { - public static final Companion COMPANION = new Companion(); - float mRotate, mPivotX, mPivotY; +public class MatrixRotate extends DrawBase3 { + public static final Companion COMPANION = + new Companion(Operations.MATRIX_ROTATE) { + @Override + public Operation construct(float rotate, + float pivotX, + float pivotY + ) { + return new MatrixRotate(rotate, pivotX, pivotY); + } + }; public MatrixRotate(float rotate, float pivotX, float pivotY) { - mRotate = rotate; - mPivotX = pivotX; - mPivotY = pivotY; - } - - @Override - public void write(WireBuffer buffer) { - COMPANION.apply(buffer, mRotate, mPivotX, mPivotY); - } - - @Override - public String toString() { - return "DrawArc " + mRotate + ", " + mPivotX + ", " + mPivotY + ";"; - } - - public static class Companion implements CompanionOperation { - private Companion() { - } - - @Override - public void read(WireBuffer buffer, List<Operation> operations) { - float rotate = buffer.readFloat(); - float pivotX = buffer.readFloat(); - float pivotY = buffer.readFloat(); - MatrixRotate op = new MatrixRotate(rotate, pivotX, pivotY); - operations.add(op); - } - - @Override - public String name() { - return "Matrix"; - } - - @Override - public int id() { - return Operations.MATRIX_ROTATE; - } - - public void apply(WireBuffer buffer, float rotate, float pivotX, float pivotY) { - buffer.start(Operations.MATRIX_ROTATE); - buffer.writeFloat(rotate); - buffer.writeFloat(pivotX); - buffer.writeFloat(pivotY); - } + super(rotate, pivotX, pivotY); + mName = "MatrixRotate"; } @Override public void paint(PaintContext context) { - context.matrixRotate(mRotate, mPivotX, mPivotY); + context.matrixRotate(mV1, mV2, mV3); } } 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 28aa68dd5884..04b940ba16c8 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,74 +15,30 @@ */ package com.android.internal.widget.remotecompose.core.operations; -import com.android.internal.widget.remotecompose.core.CompanionOperation; 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.PaintOperation; -import com.android.internal.widget.remotecompose.core.WireBuffer; -import java.util.List; - -public class MatrixScale extends PaintOperation { - public static final Companion COMPANION = new Companion(); - float mScaleX, mScaleY; - float mCenterX, mCenterY; +public class MatrixScale extends DrawBase4 { + public static final Companion COMPANION = + new Companion(Operations.MATRIX_SCALE) { + @Override + public Operation construct(float scaleX, + float scaleY, + float centerX, + float centerY + ) { + return new MatrixScale(scaleX, scaleY, centerX, centerY); + } + }; public MatrixScale(float scaleX, float scaleY, float centerX, float centerY) { - mScaleX = scaleX; - mScaleY = scaleY; - mCenterX = centerX; - mCenterY = centerY; - } - - @Override - public void write(WireBuffer buffer) { - COMPANION.apply(buffer, mScaleX, mScaleY, mCenterX, mCenterY); - } - - @Override - public String toString() { - return "MatrixScale " + mScaleY + ", " + mScaleY + ";"; - } - - public static class Companion implements CompanionOperation { - private Companion() { - } - - @Override - public void read(WireBuffer buffer, List<Operation> operations) { - float scaleX = buffer.readFloat(); - float scaleY = buffer.readFloat(); - float centerX = buffer.readFloat(); - float centerY = buffer.readFloat(); - MatrixScale op = new MatrixScale(scaleX, scaleY, centerX, centerY); - operations.add(op); - } - - @Override - public String name() { - return "Matrix"; - } - - @Override - public int id() { - return Operations.MATRIX_SCALE; - } - - public void apply(WireBuffer buffer, float scaleX, float scaleY, - float centerX, float centerY) { - buffer.start(Operations.MATRIX_SCALE); - buffer.writeFloat(scaleX); - buffer.writeFloat(scaleY); - buffer.writeFloat(centerX); - buffer.writeFloat(centerY); - - } + super(scaleX, scaleY, centerX, centerY); + mName = "MatrixScale"; } @Override public void paint(PaintContext context) { - context.mtrixScale(mScaleX, mScaleY, mCenterX, mCenterY); + context.matrixScale(mX1, mY1, mX2, mY2); } } 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 32987521e041..4f34e987e064 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,65 +15,28 @@ */ package com.android.internal.widget.remotecompose.core.operations; -import com.android.internal.widget.remotecompose.core.CompanionOperation; 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.PaintOperation; -import com.android.internal.widget.remotecompose.core.WireBuffer; -import java.util.List; - -public class MatrixTranslate extends PaintOperation { - public static final Companion COMPANION = new Companion(); - float mTranslateX, mTranslateY; +public class MatrixTranslate extends DrawBase2 { + public static final Companion COMPANION = + new Companion(Operations.MATRIX_TRANSLATE) { + @Override + public Operation construct(float x1, + float y1 + ) { + return new MatrixTranslate(x1, y1); + } + }; public MatrixTranslate(float translateX, float translateY) { - mTranslateX = translateX; - mTranslateY = translateY; - } - - @Override - public void write(WireBuffer buffer) { - COMPANION.apply(buffer, mTranslateX, mTranslateY); - } - - @Override - public String toString() { - return "DrawArc " + mTranslateY + ", " + mTranslateY + ";"; - } - - public static class Companion implements CompanionOperation { - private Companion() { - } - - @Override - public void read(WireBuffer buffer, List<Operation> operations) { - float translateX = buffer.readFloat(); - float translateY = buffer.readFloat(); - MatrixTranslate op = new MatrixTranslate(translateX, translateY); - operations.add(op); - } - - @Override - public String name() { - return "Matrix"; - } - - @Override - public int id() { - return Operations.MATRIX_TRANSLATE; - } - - public void apply(WireBuffer buffer, float translateX, float translateY) { - buffer.start(Operations.MATRIX_TRANSLATE); - buffer.writeFloat(translateX); - buffer.writeFloat(translateY); - } + super(translateX, translateY); + mName = "MatrixTranslate"; } @Override public void paint(PaintContext context) { - context.matrixTranslate(mTranslateX, mTranslateY); + context.matrixTranslate(mV1, mV2); } } 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 new file mode 100644 index 000000000000..0c5b286684d4 --- /dev/null +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/NamedVariable.java @@ -0,0 +1,99 @@ +/* + * 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 com.android.internal.widget.remotecompose.core.CompanionOperation; +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 java.util.List; + +/** + * Operation to deal with Text data + */ +public class NamedVariable implements Operation { + public int mVarId; + public String mVarName; + public int mVarType; + public static final Companion COMPANION = new Companion(); + public static final int MAX_STRING_SIZE = 4000; + + public NamedVariable(int varId, int varType, String name) { + this.mVarId = varId; + this.mVarType = varType; + this.mVarName = name; + } + + @Override + public void write(WireBuffer buffer) { + COMPANION.apply(buffer, mVarId, mVarType, mVarName); + } + + @Override + public String toString() { + return "VariableName[" + mVarId + "] = \"" + + Utils.trimString(mVarName, 10) + "\" type=" + mVarType; + } + + public static class Companion implements CompanionOperation { + private Companion() { + } + + @Override + public String name() { + return "TextData"; + } + + @Override + public int id() { + return Operations.DATA_TEXT; + } + + /** + * Writes out the operation to the buffer + * @param buffer + * @param varId + * @param varType + * @param text + */ + public void apply(WireBuffer buffer, int varId, int varType, String text) { + buffer.start(Operations.DATA_TEXT); + buffer.writeInt(varId); + buffer.writeInt(varType); + buffer.writeUTF8(text); + } + + @Override + public void read(WireBuffer buffer, 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)); + } + } + + @Override + public void apply(RemoteContext context) { + context.loadVariableName(mVarName, mVarId, mVarType); + } + + @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 e5683ece7919..0807bcdcfebb 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 @@ -20,12 +20,14 @@ 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.PaintOperation; +import com.android.internal.widget.remotecompose.core.RemoteContext; +import com.android.internal.widget.remotecompose.core.VariableSupport; import com.android.internal.widget.remotecompose.core.WireBuffer; import com.android.internal.widget.remotecompose.core.operations.paint.PaintBundle; import java.util.List; -public class PaintData extends PaintOperation { +public class PaintData extends PaintOperation implements VariableSupport { public PaintBundle mPaintData = new PaintBundle(); public static final Companion COMPANION = new Companion(); public static final int MAX_STRING_SIZE = 4000; @@ -34,6 +36,16 @@ public class PaintData extends PaintOperation { } @Override + public void updateVariables(RemoteContext context) { + mPaintData.updateVariables(context); + } + + @Override + public void registerListening(RemoteContext context) { + mPaintData.registerVars(context, this); + } + + @Override public void write(WireBuffer buffer) { COMPANION.apply(buffer, 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 2646b27b1f51..e467e7b7f31e 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,27 +18,50 @@ package com.android.internal.widget.remotecompose.core.operations; import com.android.internal.widget.remotecompose.core.CompanionOperation; 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.VariableSupport; import com.android.internal.widget.remotecompose.core.WireBuffer; +import java.util.Arrays; import java.util.List; -public class PathData implements Operation { +public class PathData implements Operation, VariableSupport { public static final Companion COMPANION = new Companion(); int mInstanceId; - float[] mRef; float[] mFloatPath; - float[] mRetFloats; + float[] mOutputPath; PathData(int instanceId, float[] floatPath) { mInstanceId = instanceId; mFloatPath = floatPath; + mOutputPath = Arrays.copyOf(mFloatPath, mFloatPath.length); + } + + @Override + public void updateVariables(RemoteContext context) { + for (int i = 0; i < mFloatPath.length; i++) { + float v = mFloatPath[i]; + if (Utils.isVariable(v)) { + mOutputPath[i] = (Float.isNaN(v)) + ? context.getFloat(Utils.idFromNan(v)) : v; + } else { + mOutputPath[i] = v; + } + } + } + + @Override + public void registerListening(RemoteContext context) { + for (int i = 0; i < mFloatPath.length; i++) { + if (Float.isNaN(mFloatPath[i])) { + context.listensTo(Utils.idFromNan(mFloatPath[i]), this); + } + } } @Override public void write(WireBuffer buffer) { - COMPANION.apply(buffer, mInstanceId, mFloatPath); + COMPANION.apply(buffer, mInstanceId, mOutputPath); } @Override @@ -46,29 +69,35 @@ public class PathData implements Operation { return pathString(mFloatPath); } - public float[] getFloatPath(PaintContext context) { - float[] ret = mRetFloats; // Assume retFloats is declared elsewhere - if (ret == null) { - return mFloatPath; // Assume floatPath is declared elsewhere - } - float[] localRef = mRef; // Assume ref is of type Float[] - if (localRef == null) { - for (int i = 0; i < mFloatPath.length; i++) { - ret[i] = mFloatPath[i]; - } - } else { - for (int i = 0; i < mFloatPath.length; i++) { - float lr = localRef[i]; - if (Float.isNaN(lr)) { - ret[i] = Utils.getActualValue(lr); - } else { - ret[i] = mFloatPath[i]; - } - } - } - return ret; + @Override + public String toString() { + return "PathData[" + mInstanceId + "] = " + "\"" + deepToString(" ") + "\""; } + /** + * public float[] getFloatPath(PaintContext context) { + * float[] ret = mRetFloats; // Assume retFloats is declared elsewhere + * if (ret == null) { + * return mFloatPath; // Assume floatPath is declared elsewhere + * } + * float[] localRef = mRef; // Assume ref is of type Float[] + * if (localRef == null) { + * for (int i = 0; i < mFloatPath.length; i++) { + * ret[i] = mFloatPath[i]; + * } + * } else { + * for (int i = 0; i < mFloatPath.length; i++) { + * float lr = localRef[i]; + * if (Float.isNaN(lr)) { + * ret[i] = Utils.getActualValue(lr); + * } else { + * ret[i] = mFloatPath[i]; + * } + * } + * } + * return ret; + * } + */ public static final int MOVE = 10; public static final int LINE = 11; public static final int QUADRATIC = 12; @@ -155,7 +184,7 @@ public class PathData implements Operation { str.append("."); break; default: - str.append("X"); + str.append("[" + id + "]"); break; } } else { @@ -170,7 +199,7 @@ public class PathData implements Operation { @Override public void apply(RemoteContext context) { - context.loadPathData(mInstanceId, mFloatPath); + 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 6d924eb70c50..997e8dc791ed 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 @@ -94,7 +94,6 @@ public class RootContentBehavior implements RemoteComposeOperation { public static final int SCALE_CROP = 5; public static final int SCALE_FILL_BOUNDS = 6; - public static final Companion COMPANION = new Companion(); /** 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 64c7f3ef2d44..076b28edf981 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 @@ -48,7 +48,7 @@ public class RootContentDescription implements RemoteComposeOperation { @Override public String toString() { - return "ROOT_CONTENT_DESCRIPTION " + mContentDescription; + return "RootContentDescription " + mContentDescription; } @Override 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 new file mode 100644 index 000000000000..8463ac576774 --- /dev/null +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/ShaderData.java @@ -0,0 +1,309 @@ +/* + * 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 com.android.internal.widget.remotecompose.core.CompanionOperation; +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.VariableSupport; +import com.android.internal.widget.remotecompose.core.WireBuffer; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; + +/** + * Operation to deal with bitmap data + * On getting an Image during a draw call the bitmap is compressed and saved + * in playback the image is decompressed + */ +public class ShaderData implements Operation, VariableSupport { + 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; + + public static final int MAX_IMAGE_DIMENSION = 8000; + + public static final Companion COMPANION = new Companion(); + + public ShaderData(int shaderID, + int shaderTextId, + HashMap<String, float[]> floatMap, + HashMap<String, int[]> intMap, + HashMap<String, Integer> bitmapMap) { + mShaderID = shaderID; + mShaderTextId = shaderTextId; + if (floatMap != null) { + mUniformFloatMap = new HashMap<>(); + mUniformRawFloatMap = new HashMap<>(); + + for (String name : floatMap.keySet()) { + mUniformRawFloatMap.put(name, floatMap.get(name)); + mUniformFloatMap.put(name, floatMap.get(name)); + } + } + + if (intMap != null) { + mUniformIntMap = new HashMap<>(); + for (String name : intMap.keySet()) { + mUniformIntMap.put(name, intMap.get(name)); + } + } + if (bitmapMap != null) { + mUniformBitmapMap = new HashMap<>(); + for (String name : bitmapMap.keySet()) { + mUniformBitmapMap.put(name, bitmapMap.get(name)); + } + } + + } + + public int getShaderTextId() { + return mShaderTextId; + } + + /** + * get names of all known floats + * @return + */ + public String[] getUniformFloatNames() { + if (mUniformFloatMap == null) return new String[0]; + return mUniformFloatMap.keySet().toArray(new String[0]); + } + + /** + * Get float values associated with the name + * @param name + * @return + */ + public float[] getUniformFloats(String name) { + return mUniformFloatMap.get(name); + } + + /** + * get the name of all know uniform integers + * @return + */ + public String[] getUniformIntegerNames() { + if (mUniformIntMap == null) return new String[0]; + return mUniformIntMap.keySet().toArray(new String[0]); + } + + /** + * Get Int value associated with the name + * @param name + * @return + */ + public int[] getUniformInts(String name) { + return mUniformIntMap.get(name); + } + + /** + * get list of uniform Bitmaps + * @return + */ + public String[] getUniformBitmapNames() { + if (mUniformBitmapMap == null) return new String[0]; + return mUniformBitmapMap.keySet().toArray(new String[0]); + } + + /** + * Get a bitmap stored under that name + * @param name + * @return + */ + public int getUniformBitmapId(String name) { + return mUniformBitmapMap.get(name); + } + + @Override + public void write(WireBuffer buffer) { + COMPANION.apply(buffer, mShaderID, mShaderTextId, + mUniformFloatMap, mUniformIntMap, mUniformBitmapMap); + } + + @Override + public String toString() { + return "SHADER DATA " + mShaderID; + } + + @Override + public void updateVariables(RemoteContext context) { + for (String name : mUniformRawFloatMap.keySet()) { + float[] value = mUniformRawFloatMap.get(name); + float[] out = null; + for (int i = 0; i < value.length; i++) { + if (Float.isNaN(value[i])) { + if (out == null) { // need to copy + out = Arrays.copyOf(value, value.length); + } + out[i] = context.getFloat(Utils.idFromNan(value[i])); + } + } + mUniformFloatMap.put(name, out == null ? value : out); + } + } + + @Override + public void registerListening(RemoteContext context) { + for (String name : mUniformRawFloatMap.keySet()) { + float[] value = mUniformRawFloatMap.get(name); + for (int i = 0; i < value.length; i++) { + if (Float.isNaN(value[i])) { + context.listensTo(Utils.idFromNan(value[i]), this); + } + } + } + } + + public static class Companion implements CompanionOperation { + private Companion() { + } + + @Override + public String name() { + return "BitmapData"; + } + + @Override + public int id() { + return Operations.DATA_SHADER; + } + + /** + * Writes out the operation to the buffer + * @param buffer + * @param shaderID + * @param shaderTextId + * @param floatMap + * @param intMap + * @param bitmapMap + */ + public void apply(WireBuffer buffer, int shaderID, int shaderTextId, + HashMap<String, float[]> floatMap, + HashMap<String, int[]> intMap, + HashMap<String, Integer> bitmapMap) { + buffer.start(Operations.DATA_SHADER); + buffer.writeInt(shaderID); + + buffer.writeInt(shaderTextId); + int floatSize = (floatMap == null) ? 0 : floatMap.size(); + int intSize = (intMap == null) ? 0 : intMap.size(); + int bitmapSize = (bitmapMap == null) ? 0 : bitmapMap.size(); + int sizes = floatSize | (intSize << 8) | (bitmapSize << 16); + buffer.writeInt(sizes); + + if (floatSize > 0) { + + for (String name : floatMap.keySet()) { + buffer.writeUTF8(name); + float[] values = floatMap.get(name); + buffer.writeInt(values.length); + + for (int i = 0; i < values.length; i++) { + buffer.writeFloat(values[i]); + } + } + } + + if (intSize > 0) { + for (String name : intMap.keySet()) { + buffer.writeUTF8(name); + int[] values = intMap.get(name); + buffer.writeInt(values.length); + for (int i = 0; i < values.length; i++) { + buffer.writeInt(values[i]); + } + } + } + if (bitmapSize > 0) { + for (String name : bitmapMap.keySet()) { + buffer.writeUTF8(name); + int value = bitmapMap.get(name); + buffer.writeInt(value); + } + } + } + + @Override + public void read(WireBuffer buffer, List<Operation> operations) { + int shaderID = buffer.readInt(); + int shaderTextId = buffer.readInt(); + HashMap<String, float[]> floatMap = null; + HashMap<String, int[]> intMap = null; + HashMap<String, Integer> bitmapMap = null; + + int sizes = buffer.readInt(); + + int floatMapSize = sizes & 0xFF; + if (floatMapSize > 0) { + floatMap = new HashMap<>(); + for (int i = 0; i < floatMapSize; i++) { + String name = buffer.readUTF8(); + int len = buffer.readInt(); + float[] val = new float[len]; + + for (int j = 0; j < len; j++) { + val[j] = buffer.readFloat(); + } + + floatMap.put(name, val); + } + } + int intMapSize = (sizes >> 8) & 0xFF; + + if (intMapSize > 0) { + + intMap = new HashMap<>(); + for (int i = 0; i < intMapSize; i++) { + String name = buffer.readUTF8(); + int len = buffer.readInt(); + int[] val = new int[len]; + for (int j = 0; j < len; j++) { + val[j] = buffer.readInt(); + } + intMap.put(name, val); + } + } + int bitmapMapSize = (sizes >> 16) & 0xFF; + + if (bitmapMapSize > 0) { + bitmapMap = new HashMap<>(); + for (int i = 0; i < bitmapMapSize; i++) { + String name = buffer.readUTF8(); + int val = buffer.readInt(); + bitmapMap.put(name, val); + } + } + operations.add(new ShaderData(shaderID, shaderTextId, + floatMap, intMap, bitmapMap)); + } + } + + @Override + public void apply(RemoteContext context) { + context.loadShader(mShaderID, this); + } + + @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 5b622ae96d0b..ed1344975256 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 @@ -44,11 +44,13 @@ public class TextData implements Operation { @Override public String toString() { - return "TEXT DATA " + mTextId + "\"" + mText + "\""; + return "TextData[" + mTextId + "] = \"" + + Utils.trimString(mText, 10) + "\""; } public static class Companion implements CompanionOperation { - private Companion() {} + private Companion() { + } @Override public String name() { 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 new file mode 100644 index 000000000000..65a39a1eba04 --- /dev/null +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/TextFromFloat.java @@ -0,0 +1,172 @@ +/* + * 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 com.android.internal.widget.remotecompose.core.CompanionOperation; +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.VariableSupport; +import com.android.internal.widget.remotecompose.core.WireBuffer; +import com.android.internal.widget.remotecompose.core.operations.utilities.StringUtils; + +import java.util.List; + +/** + * Operation convert floats to text + * This command is structured [command][textID][before,after][flags] + * before and after define number of digits before and after the decimal point + */ +public class TextFromFloat implements Operation, VariableSupport { + public int mTextId; + public float mValue; + public float mOutValue; + public short mDigitsBefore; + public short mDigitsAfter; + public int mFlags; + public static final Companion COMPANION = new Companion(); + public static final int MAX_STRING_SIZE = 4000; + char mPre = ' '; + char mAfter = ' '; + // Theses flags define what how to/if fill the space + public static final int PAD_AFTER_SPACE = 0; // pad past point with space + public static final int PAD_AFTER_NONE = 1; // do not pad past last digit + public static final int PAD_AFTER_ZERO = 3; // pad with 0 past last digit + public static final int PAD_PRE_SPACE = 0; // pad before number with spaces + public static final int PAD_PRE_NONE = 4; // pad before number with 0s + public static final int PAD_PRE_ZERO = 12; // do not pad before number + + public TextFromFloat(int textId, float value, short digitsBefore, + short digitsAfter, int flags) { + this.mTextId = textId; + this.mValue = value; + this.mDigitsAfter = digitsAfter; + this.mDigitsBefore = digitsBefore; + this.mFlags = flags; + mOutValue = mValue; + switch (mFlags & 3) { + case PAD_AFTER_SPACE: + mAfter = ' '; + break; + case PAD_AFTER_NONE: + mAfter = 0; + break; + case PAD_AFTER_ZERO: + mAfter = '0'; + break; + } + switch (mFlags & 12) { + case PAD_PRE_SPACE: + mPre = ' '; + break; + case PAD_PRE_NONE: + mPre = 0; + break; + case PAD_PRE_ZERO: + mPre = '0'; + break; + } + } + + @Override + public void write(WireBuffer buffer) { + COMPANION.apply(buffer, mTextId, mValue, mDigitsAfter, mDigitsBefore, mFlags); + } + + @Override + public String toString() { + return "TextFromFloat[" + mTextId + "] = " + + Utils.floatToString(mValue) + " " + mDigitsBefore + + "." + mDigitsAfter + " " + mFlags; + } + + + @Override + public void updateVariables(RemoteContext context) { + if (Float.isNaN(mValue)) { + mOutValue = context.getFloat(Utils.idFromNan(mValue)); + } + + } + + + @Override + public void registerListening(RemoteContext context) { + if (Float.isNaN(mValue)) { + context.listensTo(Utils.idFromNan(mValue), this); + } + } + + public static class Companion implements CompanionOperation { + private Companion() { + } + + @Override + public String name() { + return "TextData"; + } + + @Override + public int id() { + return Operations.TEXT_FROM_FLOAT; + } + + /** + * Writes out the operation to the buffer + * @param buffer + * @param textId + * @param value + * @param digitsBefore + * @param digitsAfter + * @param flags + */ + public void apply(WireBuffer buffer, int textId, + float value, short digitsBefore, + short digitsAfter, int flags) { + buffer.start(Operations.TEXT_FROM_FLOAT); + buffer.writeInt(textId); + buffer.writeFloat(value); + buffer.writeInt((digitsBefore << 16) | digitsAfter); + buffer.writeInt(flags); + + } + + @Override + public void read(WireBuffer buffer, List<Operation> operations) { + int textId = buffer.readInt(); + float value = buffer.readFloat(); + int tmp = buffer.readInt(); + short post = (short) (tmp & 0xFFFF); + short pre = (short) ((tmp >> 16) & 0xFFFF); + + int flags = buffer.readInt(); + operations.add(new TextFromFloat(textId, value, pre, post, flags)); + } + } + + @Override + public void apply(RemoteContext context) { + float v = mOutValue; + String s = StringUtils.floatToString(v, mDigitsBefore, + mDigitsAfter, mPre, mAfter); + context.loadText(mTextId, s); + } + + @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 new file mode 100644 index 000000000000..a0fc854f38f4 --- /dev/null +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/TextMerge.java @@ -0,0 +1,101 @@ +/* + * 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 com.android.internal.widget.remotecompose.core.CompanionOperation; +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 java.util.List; + +/** + * Operation to deal with Text data + */ +public class TextMerge implements Operation { + public int mTextId; + public int mSrcId1; + public int mSrcId2; + public static final Companion COMPANION = new Companion(); + public static final int MAX_STRING_SIZE = 4000; + + public TextMerge(int textId, int srcId1, int srcId2) { + this.mTextId = textId; + this.mSrcId1 = srcId1; + this.mSrcId2 = srcId2; + } + + @Override + public void write(WireBuffer buffer) { + COMPANION.apply(buffer, mTextId, mSrcId1, mSrcId2); + } + + @Override + public String toString() { + return "TextMerge[" + mTextId + "] = [" + mSrcId1 + " ] + [ " + mSrcId2 + "]"; + } + + public static class Companion implements CompanionOperation { + private Companion() { + } + + @Override + public String name() { + return "TextData"; + } + + @Override + public int id() { + return Operations.TEXT_MERGE; + } + + /** + * Writes out the operation to the buffer + * @param buffer + * @param textId + * @param srcId1 + * @param srcId2 + */ + public void apply(WireBuffer buffer, int textId, int srcId1, int srcId2) { + buffer.start(Operations.TEXT_MERGE); + buffer.writeInt(textId); + buffer.writeInt(srcId1); + buffer.writeInt(srcId2); + } + + @Override + public void read(WireBuffer buffer, List<Operation> operations) { + int textId = buffer.readInt(); + int srcId1 = buffer.readInt(); + int srcId2 = buffer.readInt(); + + operations.add(new TextMerge(textId, srcId1, srcId2)); + } + } + + @Override + public void apply(RemoteContext context) { + String str1 = context.getText(mSrcId1); + String str2 = context.getText(mSrcId2); + context.loadText(mTextId, str1 + str2); + } + + @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 00e2f2058e89..fdc68601bf4d 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,13 +15,16 @@ */ package com.android.internal.widget.remotecompose.core.operations; +/** + * Utilities to be used across all core operations + */ public class Utils { public static float asNan(int v) { return Float.intBitsToFloat(v | -0x800000); } public static int idFromNan(float value) { - int b = Float.floatToRawIntBits(value); + int b = Float.floatToRawIntBits(value); return b & 0xFFFFF; } @@ -29,13 +32,194 @@ public class Utils { return 0; } - String getFloatString(float value) { + /** + * trim a string to n characters if needing to trim + * end in "..." + * + * @param str + * @param n + * @return + */ + static String trimString(String str, int n) { + if (str.length() > n) { + str = str.substring(0, n - 3) + "..."; + } + return str; + } + + /** + * print the id and the value of a float + * @param idvalue + * @param value + * @return + */ + public static String floatToString(float idvalue, float value) { + if (Float.isNaN(idvalue)) { + return "[" + idFromNan(idvalue) + "]" + floatToString(value); + } + return floatToString(value); + } + + /** + * Convert float to string but render nan id in brackets [n] + * @param value + * @return + */ + public static String floatToString(float value) { if (Float.isNaN(value)) { - int id = idFromNan(value); - if (id > 0) { - return "NaN(" + id + ")"; - } + return "[" + idFromNan(value) + "]"; } - return "" + value; + return Float.toString(value); + } + + /** + * Debugging util to print a message and include the file/line it came from + * @param str + */ + public static void log(String str) { + StackTraceElement s = new Throwable().getStackTrace()[1]; + System.out.println("(" + s.getFileName() + ":" + s.getLineNumber() + ")." + str); + } + + /** + * Debugging util to print the stack + * @param str + * @param n + */ + public static void logStack(String str, int n) { + StackTraceElement[] st = new Throwable().getStackTrace(); + for (int i = 1; i < n + 1; i++) { + StackTraceElement s = st[i]; + String space = new String(new char[i]).replace('\0', ' '); + System.out.println(space + "(" + s.getFileName() + + ":" + s.getLineNumber() + ")." + str); + } + } + + /** + * Is a variable Allowed int calculation and references. + * + * @param v + * @return + */ + public static boolean isVariable(float v) { + if (Float.isNaN(v)) { + int id = idFromNan(v); + return id > 40 || id < 10; + } + return false; + } + + /** + * print a color in the familiar 0xAARRGGBB pattern + * + * @param color + * @return + */ + public static String colorInt(int color) { + String str = "000000000000" + Integer.toHexString(color); + return "0x" + str.substring(str.length() - 8); } + + /** + * Interpolate two colors. + * gamma corrected colors are interpolated in the form c1 * (1-t) + c2 * t + * + * @param c1 + * @param c2 + * @param t + * @return + */ + public static int interpolateColor(int c1, int c2, float t) { + if (Float.isNaN(t) || t == 0.0f) { + return c1; + } else if (t == 1.0f) { + return c2; + } + int a = 0xFF & (c1 >> 24); + int r = 0xFF & (c1 >> 16); + int g = 0xFF & (c1 >> 8); + int b = 0xFF & c1; + float f_r = (float) Math.pow(r / 255.0f, 2.2); + float f_g = (float) Math.pow(g / 255.0f, 2.2); + float f_b = (float) Math.pow(b / 255.0f, 2.2); + float c1fr = f_r; + float c1fg = f_g; + float c1fb = f_b; + float c1fa = a / 255f; + + a = 0xFF & (c2 >> 24); + r = 0xFF & (c2 >> 16); + g = 0xFF & (c2 >> 8); + b = 0xFF & c2; + f_r = (float) Math.pow(r / 255.0f, 2.2); + f_g = (float) Math.pow(g / 255.0f, 2.2); + f_b = (float) Math.pow(b / 255.0f, 2.2); + float c2fr = f_r; + float c2fg = f_g; + float c2fb = f_b; + float c2fa = a / 255f; + f_r = c1fr + t * (c2fr - c1fr); + f_g = c1fg + t * (c2fg - c1fg); + f_b = c1fb + t * (c2fb - c1fb); + float f_a = c1fa + t * (c2fa - c1fa); + + int outr = clamp((int) ((float) Math.pow(f_r, 1.0 / 2.2) * 255.0f)); + int outg = clamp((int) ((float) Math.pow(f_g, 1.0 / 2.2) * 255.0f)); + int outb = clamp((int) ((float) Math.pow(f_b, 1.0 / 2.2) * 255.0f)); + int outa = clamp((int) (f_a * 255.0f)); + + + return (outa << 24 | outr << 16 | outg << 8 | outb); + } + + /** + * Efficient clamping function + * + * @param c + * @return number between 0 and 255 + */ + public static int clamp(int c) { + int n = 255; + c &= ~(c >> 31); + c -= n; + c &= (c >> 31); + c += n; + return c; + } + + /** + * convert hue saturation and value to RGB + * + * @param hue 0..1 + * @param saturation 0..1 0=on the gray scale + * @param value 0..1 0=black + * @return + */ + public static int hsvToRgb(float hue, float saturation, float value) { + int h = (int) (hue * 6); + float f = hue * 6 - h; + int p = (int) (0.5f + 255 * value * (1 - saturation)); + int q = (int) (0.5f + 255 * value * (1 - f * saturation)); + int t = (int) (0.5f + 255 * value * (1 - (1 - f) * saturation)); + int v = (int) (0.5f + 255 * value); + switch (h) { + case 0: + return 0XFF000000 | (v << 16) + (t << 8) + p; + case 1: + return 0XFF000000 | (q << 16) + (v << 8) + p; + case 2: + return 0XFF000000 | (p << 16) + (v << 8) + t; + case 3: + return 0XFF000000 | (p << 16) + (q << 8) + v; + case 4: + return 0XFF000000 | (t << 16) + (p << 8) + v; + case 5: + return 0XFF000000 | (v << 16) + (p << 8) + q; + + } + return 0; + } + + } diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/paint/PaintBundle.java b/core/java/com/android/internal/widget/remotecompose/core/operations/paint/PaintBundle.java index 8abb0bfff338..a7d0ac6330f7 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/paint/PaintBundle.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/paint/PaintBundle.java @@ -15,43 +15,60 @@ */ package com.android.internal.widget.remotecompose.core.operations.paint; +import com.android.internal.widget.remotecompose.core.PaintContext; +import com.android.internal.widget.remotecompose.core.RemoteContext; +import com.android.internal.widget.remotecompose.core.VariableSupport; import com.android.internal.widget.remotecompose.core.WireBuffer; +import com.android.internal.widget.remotecompose.core.operations.Utils; import java.util.Arrays; +/** + * Paint Bundle represents a delta of changes to a paint object + */ public class PaintBundle { int[] mArray = new int[200]; + int[] mOutArray = null; int mPos = 0; - public void applyPaintChange(PaintChanges p) { + /** + * Apply changes to a PaintChanges interface + * @param paintContext + * @param p + */ + public void applyPaintChange(PaintContext paintContext, PaintChanges p) { int i = 0; int mask = 0; + if (mOutArray == null) { + mOutArray = mArray; + } while (i < mPos) { - int cmd = mArray[i++]; + int cmd = mOutArray[i++]; mask = mask | (1 << (cmd - 1)); switch (cmd & 0xFFFF) { case TEXT_SIZE: { - p.setTextSize(Float.intBitsToFloat(mArray[i++])); + p.setTextSize(Float.intBitsToFloat(mOutArray[i++])); break; } case TYPEFACE: int style = (cmd >> 16); int weight = style & 0x3ff; boolean italic = (style >> 10) > 0; - int font_type = mArray[i++]; + int font_type = mOutArray[i++]; p.setTypeFace(font_type, weight, italic); break; + case COLOR_ID: // mOutArray should have already decoded it case COLOR: { - p.setColor(mArray[i++]); + p.setColor(mOutArray[i++]); break; } case STROKE_WIDTH: { - p.setStrokeWidth(Float.intBitsToFloat(mArray[i++])); + p.setStrokeWidth(Float.intBitsToFloat(mOutArray[i++])); break; } case STROKE_MITER: { - p.setStrokeMiter(Float.intBitsToFloat(mArray[i++])); + p.setStrokeMiter(Float.intBitsToFloat(mOutArray[i++])); break; } case STROKE_CAP: { @@ -63,6 +80,7 @@ public class PaintBundle { break; } case SHADER: { + p.setShader(mOutArray[i++]); break; } case STROKE_JOIN: { @@ -81,17 +99,16 @@ public class PaintBundle { p.setFilterBitmap(!((cmd >> 16) == 0)); break; } - case GRADIENT: { - i = callSetGradient(cmd, mArray, i, p); + i = callSetGradient(cmd, mOutArray, i, p); break; } case COLOR_FILTER: { - p.setColorFilter(mArray[i++], cmd >> 16); + p.setColorFilter(mOutArray[i++], cmd >> 16); break; } case ALPHA: { - p.setAlpha(Float.intBitsToFloat(mArray[i++])); + p.setAlpha(Float.intBitsToFloat(mOutArray[i++])); break; } } @@ -106,7 +123,6 @@ public class PaintBundle { switch (id) { case TEXT_SIZE: return "TEXT_SIZE"; - case COLOR: return "COLOR"; case STROKE_WIDTH: @@ -133,7 +149,6 @@ public class PaintBundle { return "ALPHA"; case COLOR_FILTER: return "COLOR_FILTER"; - } return "????" + id + "????"; } @@ -154,6 +169,14 @@ public class PaintBundle { return str + "]"; } + private static String asFloatStr(int value) { + float fValue = Float.intBitsToFloat(value); + if (Float.isNaN(fValue)) { + return "[" + Utils.idFromNan(fValue) + "]"; + } + return Float.toString(fValue); + } + @Override public String toString() { StringBuilder ret = new StringBuilder("\n"); @@ -164,7 +187,8 @@ public class PaintBundle { switch (type) { case TEXT_SIZE: { - ret.append(" TextSize(" + Float.intBitsToFloat(mArray[i++])); + ret.append(" TextSize(" + + asFloatStr(mArray[i++])); } break; @@ -181,14 +205,18 @@ public class PaintBundle { ret.append(" Color(" + colorInt(mArray[i++])); } break; + case COLOR_ID: { + ret.append(" ColorId([" + mArray[i++] + "]"); + } + break; case STROKE_WIDTH: { ret.append(" StrokeWidth(" - + (Float.intBitsToFloat(mArray[i++]))); + + (asFloatStr(mArray[i++]))); } break; case STROKE_MITER: { ret.append(" StrokeMiter(" - + (Float.intBitsToFloat(mArray[i++]))); + + (asFloatStr(mArray[i++]))); } break; case STROKE_CAP: { @@ -207,11 +235,12 @@ public class PaintBundle { } break; case SHADER: { + ret.append(" Shader(" + mArray[i++]); } break; case ALPHA: { ret.append(" Alpha(" - + (Float.intBitsToFloat(mArray[i++]))); + + (asFloatStr(mArray[i++]))); } break; case IMAGE_FILTER_QUALITY: { @@ -244,7 +273,6 @@ public class PaintBundle { return ret.toString(); } - int callPrintGradient(int cmd, int[] array, int i, StringBuilder p) { int ret = i; int type = (cmd >> 16); @@ -258,26 +286,25 @@ public class PaintBundle { colors = new int[len]; for (int j = 0; j < colors.length; j++) { colors[j] = array[ret++]; - } } len = array[ret++]; - float[] stops = null; + String[] stops = null; if (len > 0) { - stops = new float[len]; + stops = new String[len]; for (int j = 0; j < stops.length; j++) { - stops[j] = Float.intBitsToFloat(array[ret++]); + stops[j] = asFloatStr(array[ret++]); } } p.append(" colors = " + colorInt(colors) + ",\n"); p.append(" stops = " + Arrays.toString(stops) + ",\n"); p.append(" start = "); - p.append("[" + Float.intBitsToFloat(array[ret++])); - p.append(", " + Float.intBitsToFloat(array[ret++]) + "],\n"); + p.append("[" + asFloatStr(array[ret++])); + p.append(", " + asFloatStr(array[ret++]) + "],\n"); p.append(" end = "); - p.append("[" + Float.intBitsToFloat(array[ret++])); - p.append(", " + Float.intBitsToFloat(array[ret++]) + "],\n"); + p.append("[" + asFloatStr(array[ret++])); + p.append(", " + asFloatStr(array[ret++]) + "],\n"); int tileMode = array[ret++]; p.append(" tileMode = " + tileMode + "\n "); } @@ -295,21 +322,21 @@ public class PaintBundle { } } len = array[ret++]; - float[] stops = null; + String[] stops = null; if (len > 0) { - stops = new float[len]; + stops = new String[len]; for (int j = 0; j < stops.length; j++) { - stops[j] = Float.intBitsToFloat(array[ret++]); + stops[j] = asFloatStr(array[ret++]); } } p.append(" colors = " + colorInt(colors) + ",\n"); p.append(" stops = " + Arrays.toString(stops) + ",\n"); p.append(" center = "); - p.append("[" + Float.intBitsToFloat(array[ret++])); - p.append(", " + Float.intBitsToFloat(array[ret++]) + "],\n"); + p.append("[" + asFloatStr(array[ret++])); + p.append(", " + asFloatStr(array[ret++]) + "],\n"); p.append(" radius ="); - p.append(" " + Float.intBitsToFloat(array[ret++]) + ",\n"); + p.append(" " + asFloatStr(array[ret++]) + ",\n"); int tileMode = array[ret++]; p.append(" tileMode = " + tileMode + "\n "); } @@ -327,20 +354,19 @@ public class PaintBundle { } } len = array[ret++]; - float[] stops = null; + String[] stops = null; if (len > 0) { - stops = new float[len]; + stops = new String[len]; for (int j = 0; j < stops.length; j++) { - stops[j] = Float.intBitsToFloat(array[ret++]); + stops[j] = asFloatStr(array[ret++]); } } - p.append(" colors = " + colorInt(colors) + ",\n"); p.append(" stops = " + Arrays.toString(stops) + ",\n"); p.append(" center = "); - p.append("[" + Float.intBitsToFloat(array[ret++])); - p.append(", " + Float.intBitsToFloat(array[ret++]) + "],\n "); - + p.append("[" + asFloatStr(array[ret++])); + p.append(", " + + asFloatStr(array[ret++]) + "],\n "); } break; default: { @@ -376,7 +402,6 @@ public class PaintBundle { return ret; } - switch (gradientType) { case LINEAR_GRADIENT: { @@ -433,7 +458,7 @@ public class PaintBundle { public static final int COLOR = 4; // int public static final int STROKE_WIDTH = 5; // float public static final int STROKE_MITER = 6; - public static final int STROKE_CAP = 7; // int + public static final int STROKE_CAP = 7; // int public static final int STYLE = 8; // int public static final int SHADER = 9; // int public static final int IMAGE_FILTER_QUALITY = 10; // int @@ -445,7 +470,7 @@ public class PaintBundle { public static final int TYPEFACE = 16; public static final int FILTER_BITMAP = 17; public static final int BLEND_MODE = 18; - + public static final int COLOR_ID = 19; // int public static final int BLEND_MODE_CLEAR = 0; public static final int BLEND_MODE_SRC = 1; @@ -634,8 +659,8 @@ public class PaintBundle { /** * @param fontType 0 = default 1 = sans serif 2 = serif 3 = monospace - * @param weight 100-1000 - * @param italic tur + * @param weight 100-1000 + * @param italic tur */ public void setTextStyle(int fontType, int weight, boolean italic) { int style = (weight & 0x3FF) | (italic ? 2048 : 0); // pack the weight and italic @@ -658,6 +683,10 @@ public class PaintBundle { mPos++; } + /** + * Set the Color based on Color + * @param color + */ public void setColor(int color) { mArray[mPos] = COLOR; mPos++; @@ -666,6 +695,18 @@ public class PaintBundle { } /** + * Set the Color based on ID + * @param color + */ + public void setColorId(int color) { + mArray[mPos] = COLOR_ID; + mPos++; + mArray[mPos] = color; + mPos++; + } + + + /** * Set the paint's Cap. * * @param cap set the paint's line cap style, used whenever the paint's @@ -676,16 +717,29 @@ public class PaintBundle { mPos++; } + /** + * Set the style STROKE and/or FILL + * @param style + */ public void setStyle(int style) { mArray[mPos] = STYLE | (style << 16); mPos++; } - public void setShader(int shader, String shaderString) { - mArray[mPos] = SHADER | (shader << 16); + /** + * Set the shader id to use + * @param shaderId + */ + public void setShader(int shaderId) { + mArray[mPos] = SHADER; + mPos++; + mArray[mPos] = shaderId; mPos++; } + /** + * Set the Alpha value + */ public void setAlpha(float alpha) { mArray[mPos] = ALPHA; mPos++; @@ -729,7 +783,6 @@ public class PaintBundle { * destination pixels * (content of the render target). * - * * @param blendmode The blend mode to be installed in the paint */ public void setBlendMode(int blendmode) { @@ -825,5 +878,216 @@ public class PaintBundle { return "null"; } -} + /** + * Check all the floats for Nan(id) floats and call listenTo + * @param context + * @param support + */ + public void registerVars(RemoteContext context, VariableSupport support) { + int i = 0; + while (i < mPos) { + int cmd = mArray[i++]; + int type = cmd & 0xFFFF; + switch (type) { + case STROKE_MITER: + case STROKE_WIDTH: + case ALPHA: + case TEXT_SIZE: + float v = Float.intBitsToFloat(mArray[i++]); + if (Float.isNaN(v)) { + context.listensTo(Utils.idFromNan(v), support); + } + break; + case COLOR_ID: + context.listensTo(mArray[i++], support); + break; + case COLOR: + + case TYPEFACE: + case SHADER: + case COLOR_FILTER: + i++; + break; + case STROKE_JOIN: + case FILTER_BITMAP: + case STROKE_CAP: + case STYLE: + case IMAGE_FILTER_QUALITY: + case BLEND_MODE: + case ANTI_ALIAS: + break; + + case GRADIENT: { + // TODO gradients should be handled correctly + i = callPrintGradient(cmd, mArray, i, new StringBuilder()); + } + } + } + } + + /** + * Update variables if any are float ids + * @param context + */ + public void updateVariables(RemoteContext context) { + if (mOutArray == null) { + mOutArray = Arrays.copyOf(mArray, mArray.length); + } else { + System.arraycopy(mArray, 0, mOutArray, 0, mArray.length); + } + int i = 0; + while (i < mPos) { + int cmd = mArray[i++]; + int type = cmd & 0xFFFF; + switch (type) { + case STROKE_MITER: + case STROKE_WIDTH: + case ALPHA: + case TEXT_SIZE: + mOutArray[i] = fixFloatVar(mArray[i], context); + i++; + break; + case COLOR_ID: + mOutArray[i] = fixColor(mArray[i], context); + i++; + break; + case COLOR: + case TYPEFACE: + case SHADER: + case COLOR_FILTER: + i++; + break; + case STROKE_JOIN: + case FILTER_BITMAP: + case STROKE_CAP: + case STYLE: + case IMAGE_FILTER_QUALITY: + case BLEND_MODE: + case ANTI_ALIAS: + break; + + case GRADIENT: { + // TODO gradients should be handled correctly + i = updateFloatsInGradient(cmd, mOutArray, mArray, i, context); + } + } + } + } + + private int fixFloatVar(int val, RemoteContext context) { + float v = Float.intBitsToFloat(val); + if (Float.isNaN(v)) { + int id = Utils.idFromNan(v); + return Float.floatToRawIntBits(context.getFloat(id)); + } + return val; + } + + private int fixColor(int colorId, RemoteContext context) { + int n = context.getColor(colorId); + return n; + } + + int updateFloatsInGradient(int cmd, int[] out, int[] array, + int i, + RemoteContext context) { + int ret = i; + int type = (cmd >> 16); + switch (type) { + case 0: { + int len = array[ret++]; + if (len > 0) { + for (int j = 0; j < len; j++) { + ret++; + } + } + len = array[ret++]; + + if (len > 0) { + for (int j = 0; j < len; j++) { + out[ret] = fixFloatVar(array[ret], context); + ret++; + } + } + + out[ret] = fixFloatVar(array[ret], context); + ret++; + out[ret] = fixFloatVar(array[ret], context); + ret++; + + // end + out[ret] = fixFloatVar(array[ret], context); + ret++; + out[ret] = fixFloatVar(array[ret], context); + ret++; + ret++; // tileMode + } + + break; + case 1: { + // RadialGradient + int len = array[ret++]; + if (len > 0) { + for (int j = 0; j < len; j++) { + ret++; + } + } + len = array[ret++]; + if (len > 0) { + for (int j = 0; j < len; j++) { + out[ret] = fixFloatVar(array[ret], context); + ret++; + } + } + + + // center + out[ret] = fixFloatVar(array[ret], context); + ret++; + out[ret] = fixFloatVar(array[ret], context); + ret++; + // radius + out[ret] = fixFloatVar(array[ret], context); + ret++; + ret++; // tileMode + + } + + break; + case 2: { + // SweepGradient + int len = array[ret++]; + int[] colors = null; + if (len > 0) { + colors = new int[len]; + for (int j = 0; j < colors.length; j++) { + colors[j] = array[ret++]; + + } + } + len = array[ret++]; + float[] stops = null; + if (len > 0) { + stops = new float[len]; + for (int j = 0; j < stops.length; j++) { + out[ret] = fixFloatVar(array[ret], context); + ret++; + } + } + + // center + out[ret] = fixFloatVar(array[ret], context); + ret++; + out[ret] = fixFloatVar(array[ret], context); + ret++; + } + break; + default: { + System.err.println("gradient type unknown"); + } + } + + return ret; + } +}
\ No newline at end of file diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/paint/PaintChangeAdapter.java b/core/java/com/android/internal/widget/remotecompose/core/operations/paint/PaintChangeAdapter.java index 994bf6d7e327..28fe63a03c67 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/paint/PaintChangeAdapter.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/paint/PaintChangeAdapter.java @@ -27,7 +27,6 @@ public class PaintChangeAdapter implements PaintChanges { } - @Override public void setStrokeWidth(float width) { @@ -49,7 +48,7 @@ public class PaintChangeAdapter implements PaintChanges { } @Override - public void setShader(int shader, String shaderString) { + public void setShader(int shader) { } diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/paint/PaintChanges.java b/core/java/com/android/internal/widget/remotecompose/core/operations/paint/PaintChanges.java index 87e58ac35930..d5dc3889add3 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/paint/PaintChanges.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/paint/PaintChanges.java @@ -15,9 +15,14 @@ */ package com.android.internal.widget.remotecompose.core.operations.paint; +/** + * Interface to a paint object + * For more details see Android Paint + */ public interface PaintChanges { - + // MASK to be set/cleared + int CLEAR_TEXT_SIZE = 1 << (PaintBundle.TEXT_SIZE - 1); int CLEAR_TEXT_STYLE = 1 << (PaintBundle.TYPEFACE - 1); int CLEAR_COLOR = 1 << (PaintBundle.COLOR - 1); int CLEAR_STROKE_WIDTH = 1 << (PaintBundle.STROKE_WIDTH - 1); @@ -32,21 +37,101 @@ public interface PaintChanges { int CLEAR_COLOR_FILTER = 1 << (PaintBundle.COLOR_FILTER - 1); int VALID_BITS = 0x1FFF; // only the first 13 bit are valid now - + /** + * Set the size of text + * @param size + */ void setTextSize(float size); + + /** + * Set the width of lines + * @param width + */ void setStrokeWidth(float width); + + /** + * Set the color to use + * @param color + */ void setColor(int color); + + /** + * Set the Stroke Cap + * @param cap + */ void setStrokeCap(int cap); + + /** + * Set the Stroke style FILL and/or STROKE + * @param style + */ void setStyle(int style); - void setShader(int shader, String shaderString); + + /** + * Set the id of the shader to use + * @param shader + */ + void setShader(int shader); + + /** + * Set the way image is interpolated + * @param quality + */ void setImageFilterQuality(int quality); + + /** + * Set the alpha to draw under + * @param a + */ void setAlpha(float a); + + /** + * Set the Stroke Miter + * @param miter + */ void setStrokeMiter(float miter); + + /** + * Set the Stroke Join + * @param join + */ void setStrokeJoin(int join); + + /** + * Should bitmaps be interpolated + * @param filter + */ void setFilterBitmap(boolean filter); + + /** + * Set the blend mode can be porterduff + others + * @param mode + */ void setBlendMode(int mode); + + /** + * Set the AntiAlias. Typically true + * Set to off when you need pixilated look (e.g. QR codes) + * @param aa + */ void setAntiAlias(boolean aa); + + /** + * Clear some sub set of the settings + * @param mask + */ void clear(long mask); + + /** + * Set a linear gradient fill + * @param colorsArray + * @param stopsArray + * @param startX + * @param startY + * @param endX + * @param endY + * @param tileMode + */ void setLinearGradient( int[] colorsArray, float[] stopsArray, @@ -57,6 +142,15 @@ public interface PaintChanges { int tileMode ); + /** + * Set a radial gradient fill + * @param colorsArray + * @param stopsArray + * @param centerX + * @param centerY + * @param radius + * @param tileMode + */ void setRadialGradient( int[] colorsArray, float[] stopsArray, @@ -66,6 +160,13 @@ public interface PaintChanges { int tileMode ); + /** + * Set a sweep gradient fill + * @param colorsArray + * @param stopsArray + * @param centerX + * @param centerY + */ void setSweepGradient( int[] colorsArray, float[] stopsArray, @@ -73,9 +174,19 @@ public interface PaintChanges { float centerY ); - + /** + * Set Color filter mod + * @param color + * @param mode + */ void setColorFilter(int color, int mode); + /** + * Set TypeFace 0,1,2 + * TODO above should point to a string to be decoded + * @param fontType + * @param weight + * @param italic + */ void setTypeFace(int fontType, int weight, boolean italic); -} - +}
\ No newline at end of file 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 new file mode 100644 index 000000000000..616048d424ec --- /dev/null +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/AnimatedFloatExpression.java @@ -0,0 +1,452 @@ +/* + * 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.utilities; + +/** + * high performance floating point expression evaluator used in animation + */ +public class AnimatedFloatExpression { + static IntMap<String> sNames = new IntMap<>(); + public static final int OFFSET = 0x100; + public static final float ADD = asNan(OFFSET + 1); + public static final float SUB = asNan(OFFSET + 2); + public static final float MUL = asNan(OFFSET + 3); + public static final float DIV = asNan(OFFSET + 4); + public static final float MOD = asNan(OFFSET + 5); + public static final float MIN = asNan(OFFSET + 6); + public static final float MAX = asNan(OFFSET + 7); + public static final float POW = asNan(OFFSET + 8); + public static final float SQRT = asNan(OFFSET + 9); + public static final float ABS = asNan(OFFSET + 10); + public static final float SIGN = asNan(OFFSET + 11); + public static final float COPY_SIGN = asNan(OFFSET + 12); + public static final float EXP = asNan(OFFSET + 13); + public static final float FLOOR = asNan(OFFSET + 14); + public static final float LOG = asNan(OFFSET + 15); + public static final float LN = asNan(OFFSET + 16); + public static final float ROUND = asNan(OFFSET + 17); + public static final float SIN = asNan(OFFSET + 18); + public static final float COS = asNan(OFFSET + 19); + public static final float TAN = asNan(OFFSET + 20); + public static final float ASIN = asNan(OFFSET + 21); + public static final float ACOS = asNan(OFFSET + 22); + + public static final float ATAN = asNan(OFFSET + 23); + + public static final float ATAN2 = asNan(OFFSET + 24); + public static final float MAD = asNan(OFFSET + 25); + public static final float IFELSE = asNan(OFFSET + 26); + + public static final float CLAMP = asNan(OFFSET + 27); + public static final float CBRT = asNan(OFFSET + 28); + public static final float DEG = asNan(OFFSET + 29); + public static final float RAD = asNan(OFFSET + 30); + public static final float CEIL = asNan(OFFSET + 31); + + + public static final float LAST_OP = 31; + + + public static final float VAR1 = asNan(OFFSET + 27); + public static final float VAR2 = asNan(OFFSET + 28); + + // TODO CLAMP, CBRT, DEG, RAD, EXPM1, CEIL, FLOOR + private static final float FP_PI = (float) Math.PI; + private static final float FP_TO_RAD = 57.29577951f; // 180/PI + private static final float FP_TO_DEG = 0.01745329252f; // 180/PI + + float[] mStack; + float[] mLocalStack = new float[128]; + float[] mVar; + + /** + * is float a math operator + * @param v + * @return + */ + public static boolean isMathOperator(float v) { + if (Float.isNaN(v)) { + int pos = fromNaN(v); + return pos > OFFSET && pos <= OFFSET + LAST_OP; + } + return false; + } + + interface Op { + int eval(int sp); + } + + /** + * Evaluate a float expression + * @param exp + * @param var + * @return + */ + public float eval(float[] exp, float... var) { + mStack = exp; + mVar = var; + int sp = -1; + for (int i = 0; i < mStack.length; i++) { + float v = mStack[i]; + if (Float.isNaN(v)) { + sp = mOps[fromNaN(v) - OFFSET].eval(sp); + } else { + mStack[++sp] = v; + } + } + return mStack[sp]; + } + + /** + * Evaluate a float expression + * @param exp + * @param len + * @param var + * @return + */ + public float eval(float[] exp, int len, float... var) { + System.arraycopy(exp, 0, mLocalStack, 0, len); + mStack = mLocalStack; + mVar = var; + int sp = -1; + for (int i = 0; i < len; i++) { + float v = mStack[i]; + if (Float.isNaN(v)) { + sp = mOps[fromNaN(v) - OFFSET].eval(sp); + } else { + mStack[++sp] = v; + } + } + return mStack[sp]; + } + + /** + * Evaluate a float expression + * @param exp + * @param var + * @return + */ + public float evalDB(float[] exp, float... var) { + mStack = exp; + mVar = var; + int sp = -1; + for (float v : exp) { + if (Float.isNaN(v)) { + System.out.print(" " + sNames.get((fromNaN(v) - OFFSET))); + sp = mOps[fromNaN(v) - OFFSET].eval(sp); + } else { + System.out.print(" " + v); + mStack[++sp] = v; + } + } + return mStack[sp]; + } + + Op[] mOps = { + 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) -> { // 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; + }, + }; + + static { + int k = 0; + sNames.put(k++, "NOP"); + sNames.put(k++, "+"); + sNames.put(k++, "-"); + sNames.put(k++, "*"); + sNames.put(k++, "/"); + sNames.put(k++, "%"); + sNames.put(k++, "min"); + sNames.put(k++, "max"); + sNames.put(k++, "pow"); + sNames.put(k++, "sqrt"); + sNames.put(k++, "abs"); + sNames.put(k++, "sign"); + sNames.put(k++, "copySign"); + sNames.put(k++, "exp"); + sNames.put(k++, "floor"); + sNames.put(k++, "log"); + sNames.put(k++, "ln"); + sNames.put(k++, "round"); + sNames.put(k++, "sin"); + sNames.put(k++, "cos"); + sNames.put(k++, "tan"); + sNames.put(k++, "asin"); + sNames.put(k++, "acos"); + sNames.put(k++, "atan"); + sNames.put(k++, "atan2"); + sNames.put(k++, "mad"); + sNames.put(k++, "ifElse"); + sNames.put(k++, "clamp"); + sNames.put(k++, "cbrt"); + sNames.put(k++, "deg"); + sNames.put(k++, "rad"); + sNames.put(k++, "ceil"); + sNames.put(k++, "a[0]"); + sNames.put(k++, "a[1]"); + sNames.put(k++, "a[2]"); + } + + /** + * given a float command return its math name (e.g sin, cos etc.) + * @param f + * @return + */ + public static String toMathName(float f) { + int id = fromNaN(f) - OFFSET; + return sNames.get(id); + } + + /** + * Convert an expression encoded as an array of floats int ot a string + * @param exp + * @param labels + * @return + */ + public static String toString(float[] exp, String[] labels) { + StringBuilder s = new StringBuilder(); + for (int i = 0; i < exp.length; i++) { + float v = exp[i]; + if (Float.isNaN(v)) { + if (isMathOperator(v)) { + s.append(toMathName(v)); + } else { + s.append("["); + s.append(fromNaN(v)); + s.append("]"); + } + } else { + if (labels[i] != null) { + s.append(labels[i]); + } + s.append(v); + } + s.append(" "); + } + return s.toString(); + } + + static String toString(float[] exp, int sp) { + String[] str = new String[exp.length]; + if (Float.isNaN(exp[sp])) { + int id = fromNaN(exp[sp]) - OFFSET; + switch (NO_OF_OPS[id]) { + case -1: + return "nop"; + case 1: + return sNames.get(id) + "(" + toString(exp, sp + 1) + ") "; + case 2: + if (infix(id)) { + return "(" + toString(exp, sp + 1) + + sNames.get(id) + " " + + toString(exp, sp + 2) + ") "; + } else { + return sNames.get(id) + "(" + + toString(exp, sp + 1) + ", " + + toString(exp, sp + 2) + ")"; + } + case 3: + if (infix(id)) { + return "((" + toString(exp, sp + 1) + ") ? " + + toString(exp, sp + 2) + ":" + + toString(exp, sp + 3) + ")"; + } else { + return sNames.get(id) + + "(" + toString(exp, sp + 1) + + ", " + toString(exp, sp + 2) + + ", " + toString(exp, sp + 3) + ")"; + } + } + } + return Float.toString(exp[sp]); + } + + 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] + }; + + /** + * to be used by parser to determine if command is infix + * @param n + * @return + */ + static boolean infix(int n) { + return ((n < 6) || (n == 25) || (n == 26)); + } + + /** + * Convert an id into a NaN object + * @param v + * @return + */ + public static float asNan(int v) { + return Float.intBitsToFloat(v | -0x800000); + } + + /** + * Get ID from a NaN float + * @param v + * @return + */ + public static int fromNaN(float v) { + int b = Float.floatToRawIntBits(v); + return b & 0xFFFFF; + } + +} diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/ColorUtils.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/ColorUtils.java new file mode 100644 index 000000000000..0ea28a8bb900 --- /dev/null +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/ColorUtils.java @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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; + +/** + * These are tools to use long Color as variables + * long colors are stored a 0xXXXXXXXX XXXXXX?? + * in SRGB the colors are stored 0xAARRGGBB,00000000 + * SRGB color sapce is color space 0 + * Our Color will use color float with a + * Current android supports + * SRGB, LINEAR_SRGB, EXTENDED_SRGB, LINEAR_EXTENDED_SRGB, BT709, BT2020, + * DCI_P3, DISPLAY_P3, NTSC_1953, SMPTE_C, ADOBE_RGB, PRO_PHOTO_RGB, ACES, + * ACESCG, CIE_XYZ, CIE_LAB, BT2020_HLG, BT2020_PQ 0..17 respectively + * + * Our color space will be 62 (MAX_ID-1). (0x3E) + * Storing the default value in SRGB format and having the + * id of the color between the ARGB values and the 62 i.e. + * 0xAARRGGBB 00 00 00 3E + * + */ +public class ColorUtils { + public static int RC_COLOR = 62; + + long packRCColor(int defaultARGB, int id) { + long l = defaultARGB; + return (l << 32) | id << 8 | RC_COLOR; + } + + boolean isRCColor(long color) { + return ((color & 0x3F) == 62); + } + + int getID(long color) { + if (isRCColor(color)) { + return (int) ((color & 0xFFFFFF00) >> 8); + } + return -1; + } + + /** + * get default color from long color + * @param color + * @return + */ + public int getDefaultColor(long color) { + if (isRCColor(color)) { + return (int) (color >> 32); + } + if (((color & 0xFF) == 0)) { + return (int) (color >> 32); + } + return 0; + } +} 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 8051ef1ab37c..0512fa6be710 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 @@ -50,7 +50,6 @@ public class IntMap<T> { return insert(key, value); } - public T get(int key) { int index = findKey(key); if (index == -1) { diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/NanMap.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/NanMap.java new file mode 100644 index 000000000000..f4cd504d650f --- /dev/null +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/NanMap.java @@ -0,0 +1,75 @@ +/* + * 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.utilities; + +import com.android.internal.widget.remotecompose.core.operations.Utils; + +/** + * This defines the major id maps and ranges used by remote compose + * Generally ids ranging from 0 ... FFF (4095) are for ids + * 0x1000-0x1100 are used for path operations in PathData + * 0x1100-0x1200 are used for math operations in Animated float + * 0x + */ +public class NanMap { + + public static final int MOVE = 0x1000; + public static final int LINE = 0x1001; + public static final int QUADRATIC = 0x1002; + public static final int CONIC = 0x1003; + public static final int CUBIC = 0x1004; + public static final int CLOSE = 0x1005; + public static final int DONE = 0x1006; + public static final float MOVE_NAN = Utils.asNan(MOVE); + public static final float LINE_NAN = Utils.asNan(LINE); + public static final float QUADRATIC_NAN = Utils.asNan(QUADRATIC); + public static final float CONIC_NAN = Utils.asNan(CONIC); + public static final float CUBIC_NAN = Utils.asNan(CUBIC); + public static final float CLOSE_NAN = Utils.asNan(CLOSE); + public static final float DONE_NAN = Utils.asNan(DONE); + + /** + * + */ + public static final float ADD = asNan(0x1100); + public static final float SUB = asNan(0x1101); + public static final float MUL = asNan(0x1102); + public static final float DIV = asNan(0x1103); + public static final float MOD = asNan(0x1104); + public static final float MIN = asNan(0x1105); + public static final float MAX = asNan(0x1106); + public static final float POW = asNan(0x1107); + + + /** + * Get ID from Nan float + * @param v + * @return + */ + public static int fromNaN(float v) { + int b = Float.floatToRawIntBits(v); + return b & 0xFFFFF; + } + + /** + * Given id return as a Nan float + * @param v + * @return + */ + public static float asNan(int v) { + return Float.intBitsToFloat(v | 0xFF800000); + } +} 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 new file mode 100644 index 000000000000..8dd5405ddf12 --- /dev/null +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/StringUtils.java @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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; + +import java.util.Arrays; + +/** + * Utilities for string manipulation + */ +public class StringUtils { + /** + * Converts a float into a string. + * Providing a defined number of characters before and after the + * decimal point. + * + * @param value The value to convert to string + * @param beforeDecimalPoint digits before the decimal point + * @param afterDecimalPoint digits after the decimal point + * @param pre character to pad width 0 = no pad typically ' ' or '0' + * @param post character to pad width 0 = no pad typically ' ' or '0' + * @return + */ + public static String floatToString(float value, + int beforeDecimalPoint, + int afterDecimalPoint, + char pre, char post) { + + int integerPart = (int) value; + float fractionalPart = value % 1; + + // Convert integer part to string and pad with spaces + String integerPartString = String.valueOf(integerPart); + int iLen = integerPartString.length(); + if (iLen < beforeDecimalPoint) { + int spacesToPad = beforeDecimalPoint - iLen; + if (pre != 0) { + char[] pad = new char[spacesToPad]; + Arrays.fill(pad, pre); + integerPartString = new String(pad) + integerPartString; + } + + + } else if (iLen > beforeDecimalPoint) { + integerPartString = integerPartString.substring(iLen - beforeDecimalPoint); + } + if (afterDecimalPoint == 0) { + return integerPartString; + } + // Convert fractional part to string and pad with zeros + + for (int i = 0; i < afterDecimalPoint; i++) { + fractionalPart *= 10; + } + + fractionalPart = Math.round(fractionalPart); + + for (int i = 0; i < afterDecimalPoint; i++) { + fractionalPart *= .1; + } + + String fact = Float.toString(fractionalPart); + fact = fact.substring(2, Math.min(fact.length(), afterDecimalPoint + 2)); + int trim = fact.length(); + for (int i = fact.length() - 1; i >= 0; i--) { + if (fact.charAt(i) != '0') { + break; + } + trim--; + } + if (trim != fact.length()) { + fact = fact.substring(0, trim); + } + int len = fact.length(); + if (post != 0 && len < afterDecimalPoint) { + char[] c = new char[afterDecimalPoint - len]; + Arrays.fill(c, post); + fact = fact + new String(c); + } + + return integerPartString + "." + fact; + } + +} diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/BounceCurve.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/BounceCurve.java new file mode 100644 index 000000000000..c3cd5ae9c79d --- /dev/null +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/BounceCurve.java @@ -0,0 +1,67 @@ +/* + * 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.utilities.easing; + +/** + * Provide a specific bouncing easing function + */ +public class BounceCurve extends Easing { + private static final float N1 = 7.5625f; + private static final float D1 = 2.75f; + + BounceCurve(int type) { + mType = type; + } + + @Override + public float get(float x) { + float t = x; + if (t < 0) { + return 0f; + } + if (t < 1 / D1) { + return 1 / (1 + 1 / D1) * (N1 * t * t + t); + } else if (t < 2 / D1) { + t -= 1.5f / D1; + return N1 * t * t + 0.75f; + } else if (t < 2.5 / D1) { + t -= 2.25f / D1; + return N1 * t * t + 0.9375f; + } else if (t <= 1) { + t -= 2.625f / D1; + return N1 * t * t + 0.984375f; + } + return 1f; + } + + @Override + public float getDiff(float x) { + if (x < 0) { + return 0f; + } + if (x < 1 / D1) { + return 2 * N1 * x / (1 + 1 / D1) + 1 / (1 + 1 / D1); + } else if (x < 2 / D1) { + return 2 * N1 * (x - 1.5f / D1); + } else if (x < 2.5 / D1) { + return 2 * N1 * (x - 2.25f / D1); + } else if (x <= 1) { + return 2 * N1 * (x - 2.625f / D1); + } + return 0f; + } + +} 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 new file mode 100644 index 000000000000..fd1ee036e475 --- /dev/null +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/CubicEasing.java @@ -0,0 +1,157 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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.easing; + +class CubicEasing extends Easing { + float mType = 0; + float mX1 = 0f; + float mY1 = 0f; + float mX2 = 0f; + float mY2 = 0f; + + private static final float[] STANDARD = {0.4f, 0.0f, 0.2f, 1f}; + private static final float[] ACCELERATE = {0.4f, 0.05f, 0.8f, 0.7f}; + private static final float[] DECELERATE = {0.0f, 0.0f, 0.2f, 0.95f}; + private static final float[] LINEAR = {1f, 1f, 0f, 0f}; + private static final float[] ANTICIPATE = {0.36f, 0f, 0.66f, -0.56f}; + private static final float[] OVERSHOOT = {0.34f, 1.56f, 0.64f, 1f}; + + CubicEasing(int type) { + mType = type; + config(type); + } + + CubicEasing(float x1, float y1, float x2, float y2) { + setup(x1, y1, x2, y2); + } + + public void config(int type) { + + switch (type) { + case CUBIC_STANDARD: + setup(STANDARD); + break; + case CUBIC_ACCELERATE: + setup(ACCELERATE); + break; + case CUBIC_DECELERATE: + setup(DECELERATE); + break; + case CUBIC_LINEAR: + setup(LINEAR); + break; + case CUBIC_ANTICIPATE: + setup(ANTICIPATE); + break; + case CUBIC_OVERSHOOT: + setup(OVERSHOOT); + break; + } + mType = type; + } + + void setup(float[] values) { + setup(values[0], values[1], values[2], values[3]); + } + + void setup(float x1, float y1, float x2, float y2) { + mX1 = x1; + mY1 = y1; + mX2 = x2; + mY2 = y2; + } + + private float getX(float t) { + float t1 = 1 - t; + // no need for because start at 0,0 float f0 = (1 - t) * (1 - t) * (1 - t) + float f1 = 3 * t1 * t1 * t; + float f2 = 3 * t1 * t * t; + float f3 = t * t * t; + return mX1 * f1 + mX2 * f2 + f3; + } + + private float getY(float t) { + float t1 = 1 - t; + // no need for testing because start at 0,0 float f0 = (1 - t) * (1 - t) * (1 - t) + float f1 = 3 * t1 * t1 * t; + float f2 = 3 * t1 * t * t; + float f3 = t * t * t; + return mY1 * f1 + mY2 * f2 + f3; + } + + private float getDiffX(float t) { + float t1 = 1 - t; + return 3 * t1 * t1 * mX1 + 6 * t1 * t * (mX2 - mX1) + 3 * t * t * (1 - mX2); + } + + private float getDiffY(float t) { + float t1 = 1 - t; + return 3 * t1 * t1 * mY1 + 6 * t1 * t * (mY2 - mY1) + 3 * t * t * (1 - mY2); + } + + /** + * binary search for the region and linear interpolate the answer + */ + public float getDiff(float x) { + float t = 0.5f; + float range = 0.5f; + while (range > D_ERROR) { + float tx = getX(t); + range *= 0.5; + if (tx < x) { + t += range; + } else { + t -= range; + } + } + float x1 = getX(t - range); + float x2 = getX(t + range); + float y1 = getY(t - range); + float y2 = getY(t + range); + return (y2 - y1) / (x2 - x1); + } + + /** + * binary search for the region and linear interpolate the answer + */ + public float get(float x) { + if (x <= 0.0f) { + return 0f; + } + if (x >= 1.0f) { + return 1.0f; + } + float t = 0.5f; + float range = 0.5f; + while (range > ERROR) { + float tx = getX(t); + range *= 0.5f; + if (tx < x) { + t += range; + } else { + t -= range; + } + } + float x1 = getX(t - range); + float x2 = getX(t + range); + float y1 = getY(t - range); + float y2 = getY(t + range); + return (y2 - y1) * (x - x1) / (x2 - x1) + y1; + } + + private static final float ERROR = 0.01f; + private static final float D_ERROR = 0.0001f; +} diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/Easing.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/Easing.java new file mode 100644 index 000000000000..4ed955069d78 --- /dev/null +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/Easing.java @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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.easing; + +/** + * The standard interface to Easing functions + */ +public abstract class Easing { + int mType; + /** + * get the value at point x + */ + public abstract float get(float x); + + /** + * get the slope of the easing function at at x + */ + public abstract float getDiff(float x); + + public int getType() { + return mType; + } + + public static final int CUBIC_STANDARD = 1; + public static final int CUBIC_ACCELERATE = 2; + public static final int CUBIC_DECELERATE = 3; + public static final int CUBIC_LINEAR = 4; + public static final int CUBIC_ANTICIPATE = 5; + public static final int CUBIC_OVERSHOOT = 6; + public static final int CUBIC_CUSTOM = 11; + public static final int SPLINE_CUSTOM = 12; + public static final int EASE_OUT_BOUNCE = 13; + public static final int EASE_OUT_ELASTIC = 14; + +} diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/ElasticOutCurve.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/ElasticOutCurve.java new file mode 100644 index 000000000000..e26958302e3c --- /dev/null +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/ElasticOutCurve.java @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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.easing; + +/** + * Provide a bouncing Easing function + */ +public class ElasticOutCurve extends Easing { + private static final float F_PI = (float) Math.PI; + private static final float C4 = 2 * F_PI / 3; + private static final float TWENTY_PI = 20 * F_PI; + private static final float LOG_8 = (float) Math.log(8.0f); + + @Override + public float get(float x) { + if (x <= 0) { + return 0.0f; + } + if (x >= 1) { + return 1.0f; + } else + return (float) (Math.pow(2.0f, -10 * x) + * Math.sin((x * 10 - 0.75f) * C4) + 1); + } + + @Override + public float getDiff(float x) { + if (x < 0 || x > 1) { + return 0.0f; + } else + return (float) ((5 * Math.pow(2.0f, (1 - 10 * x)) + * (LOG_8 * Math.cos(TWENTY_PI * x / 3) + 2 + * F_PI * Math.sin(TWENTY_PI * x / 3)) + / 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 new file mode 100644 index 000000000000..4f484de1045e --- /dev/null +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/FloatAnimation.java @@ -0,0 +1,259 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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.easing; + +/** + * Support Animation of the FloatExpression + */ +public class FloatAnimation extends Easing { + float[] mSpec; + // mSpec[0] = duration + // int(mSpec[1]) = num_of_param << 16 | type + // mSpec[2..1+num_of_param] params + // mSpec[2+num_of_param] starting Value + Easing mEasingCurve; + private int mType = CUBIC_STANDARD; + private float mDuration = 1; + private float mWrap = Float.NaN; + private float mInitialValue = Float.NaN; + private float mTargetValue = Float.NaN; + private float mScale = 1; + float mOffset = 0; + + @Override + public String toString() { + + String str = "type " + mType; + if (!Float.isNaN(mInitialValue)) { + str += " " + mInitialValue; + } + if (!Float.isNaN(mTargetValue)) { + str += " -> " + mTargetValue; + } + if (!Float.isNaN(mWrap)) { + str += " % " + mWrap; + } + + return str; + } + + public FloatAnimation() { + } + + public FloatAnimation(float... description) { + setAnimationDescription(description); + } + + public FloatAnimation(int type, + float duration, + float[] description, + float initialValue, + float wrap) { + setAnimationDescription(packToFloatArray(duration, + type, description, initialValue, wrap)); + } + + /** + * packs spec into a float array + * + * @param duration + * @param type + * @param spec + * @param initialValue + * @return + */ + public static float[] packToFloatArray(float duration, + int type, + float[] spec, + float initialValue, + float wrap) { + int count = 0; + + if (!Float.isNaN(initialValue)) { + count++; + } + if (spec != null) { + count++; + } + if (spec != null || type != CUBIC_STANDARD) { + count++; + count += (spec == null) ? 0 : spec.length; + } + if (duration != 1 || count > 0) { + count++; + } + if (!Float.isNaN(initialValue)) { + count++; + } + if (!Float.isNaN(wrap)) { + count++; + } + float[] ret = new float[count]; + int pos = 0; + int specLen = (spec == null) ? 0 : spec.length; + + if (ret.length > 0) { + ret[pos++] = duration; + + } + if (ret.length > 1) { + int wrapBit = (Float.isNaN(wrap)) ? 0 : 1; + int initBit = (Float.isNaN(initialValue)) ? 0 : 2; + int bits = type | ((wrapBit | initBit) << 8); + ret[pos++] = Float.intBitsToFloat(specLen << 16 | bits); + } + + if (specLen > 0) { + System.arraycopy(spec, 0, ret, pos, spec.length); + pos += spec.length; + } + if (!Float.isNaN(initialValue)) { + ret[pos++] = initialValue; + } + if (!Float.isNaN(wrap)) { + ret[pos] = wrap; + } + return ret; + } + + /** + * Create an animation based on a float encoding of the animation + * @param description + */ + public void setAnimationDescription(float[] description) { + mSpec = description; + mDuration = (mSpec.length == 0) ? 1 : mSpec[0]; + int len = 0; + if (mSpec.length > 1) { + int num_type = Float.floatToRawIntBits(mSpec[1]); + mType = 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) { + mInitialValue = mSpec[off++]; + } + if (wrap) { + mWrap = mSpec[off]; + } + } + create(mType, description, 2, len); + } + + private void create(int type, float[] params, int offset, int len) { + switch (type) { + case CUBIC_STANDARD: + case CUBIC_ACCELERATE: + case CUBIC_DECELERATE: + case CUBIC_LINEAR: + case CUBIC_ANTICIPATE: + case CUBIC_OVERSHOOT: + mEasingCurve = new CubicEasing(type); + break; + case CUBIC_CUSTOM: + mEasingCurve = new CubicEasing(params[offset + 0], + params[offset + 1], + params[offset + 2], + params[offset + 3] + ); + break; + case EASE_OUT_BOUNCE: + mEasingCurve = new BounceCurve(type); + break; + case EASE_OUT_ELASTIC: + mEasingCurve = new ElasticOutCurve(); + break; + case SPLINE_CUSTOM: + mEasingCurve = new StepCurve(params, offset, len); + break; + } + } + + /** + * Get the duration the interpolate is to take + * @return duration in seconds + */ + public float getDuration() { + return mDuration; + } + + /** + * Set the initial Value + * @param value + */ + public void setInitialValue(float value) { + + if (Float.isNaN(mWrap)) { + mInitialValue = value; + } else { + mInitialValue = value % mWrap; + } + setScaleOffset(); + } + + /** + * 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; + + } else { + mTargetValue = value % mWrap; + } + } + setScaleOffset(); + } + + public float getTargetValue() { + return mTargetValue; + } + + private void setScaleOffset() { + if (!Float.isNaN(mInitialValue) && !Float.isNaN(mTargetValue)) { + mScale = (mTargetValue - mInitialValue); + mOffset = mInitialValue; + } else { + mScale = 1; + mOffset = 0; + } + } + + /** + * get the value at time t in seconds since start + */ + public float get(float t) { + return mEasingCurve.get(t / mDuration) + * (mTargetValue - mInitialValue) + mInitialValue; + } + + /** + * get the slope of the easing function at at x + */ + public float getDiff(float t) { + return mEasingCurve.getDiff(t / mDuration) * (mTargetValue - mInitialValue); + } + + public float getInitialValue() { + return mInitialValue; + } +} 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 new file mode 100644 index 000000000000..693deafc5b00 --- /dev/null +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/GeneralEasing.java @@ -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.internal.widget.remotecompose.core.operations.utilities.easing; + +/** + * Provides and interface to create easing functions + */ +public class GeneralEasing extends Easing{ + float[] mEasingData = new float[0]; + Easing mEasingCurve = new CubicEasing(CUBIC_STANDARD); + + /** + * Set the curve based on the float encoding of it + * @param data + */ + public void setCurveSpecification(float[] data) { + mEasingData = data; + createEngine(); + } + + public float[] getCurveSpecification() { + return mEasingData; + } + + void createEngine() { + int type = Float.floatToRawIntBits(mEasingData[0]); + switch (type) { + case CUBIC_STANDARD: + case CUBIC_ACCELERATE: + case CUBIC_DECELERATE: + case CUBIC_LINEAR: + case CUBIC_ANTICIPATE: + case CUBIC_OVERSHOOT: + mEasingCurve = new CubicEasing(type); + break; + case CUBIC_CUSTOM: + mEasingCurve = new CubicEasing(mEasingData[1], + mEasingData[2], + mEasingData[3], + mEasingData[5] + ); + break; + case EASE_OUT_BOUNCE: + mEasingCurve = new BounceCurve(type); + break; + } + } + + /** + * get the value at point x + */ + public float get(float x) { + return mEasingCurve.get(x); + } + + /** + * get the slope of the easing function at at x + */ + public float getDiff(float x) { + return mEasingCurve.getDiff(x); + } + + public int getType() { + return mEasingCurve.getType(); + } + + +} 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 new file mode 100644 index 000000000000..23930b92a282 --- /dev/null +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/MonotonicCurveFit.java @@ -0,0 +1,370 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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.easing; + +import java.util.Arrays; + +/** + * This performs a spline interpolation in multiple dimensions + * + * + */ +public class MonotonicCurveFit { + private static final String TAG = "MonotonicCurveFit"; + private double[] mT; + private double[][] mY; + private double[][] mTangent; + private boolean mExtrapolate = true; + double[] mSlopeTemp; + + /** + * create a collection of curves + * @param time the point along the curve + * @param y the parameter at those points + */ + public MonotonicCurveFit(double[] time, double[][] y) { + final int n = time.length; + final int dim = y[0].length; + mSlopeTemp = new double[dim]; + double[][] slope = new double[n - 1][dim]; // could optimize this out + double[][] tangent = new double[n][dim]; + for (int j = 0; j < dim; j++) { + for (int i = 0; i < n - 1; i++) { + double dt = time[i + 1] - time[i]; + slope[i][j] = (y[i + 1][j] - y[i][j]) / dt; + if (i == 0) { + tangent[i][j] = slope[i][j]; + } else { + tangent[i][j] = (slope[i - 1][j] + slope[i][j]) * 0.5f; + } + } + tangent[n - 1][j] = slope[n - 2][j]; + } + + for (int i = 0; i < n - 1; i++) { + for (int j = 0; j < dim; j++) { + if (slope[i][j] == 0.) { + tangent[i][j] = 0.; + tangent[i + 1][j] = 0.; + } else { + double a = tangent[i][j] / slope[i][j]; + double b = tangent[i + 1][j] / slope[i][j]; + double h = Math.hypot(a, b); + if (h > 9.0) { + double t = 3. / h; + tangent[i][j] = t * a * slope[i][j]; + tangent[i + 1][j] = t * b * slope[i][j]; + } + } + } + } + mT = time; + mY = y; + mTangent = tangent; + } + + /** + * Get the position of all curves at time t + * @param t + * @param v + */ + public void getPos(double t, double[] v) { + final int n = mT.length; + final int dim = mY[0].length; + if (mExtrapolate) { + if (t <= mT[0]) { + getSlope(mT[0], mSlopeTemp); + for (int j = 0; j < dim; j++) { + v[j] = mY[0][j] + (t - mT[0]) * mSlopeTemp[j]; + } + return; + } + if (t >= mT[n - 1]) { + getSlope(mT[n - 1], mSlopeTemp); + for (int j = 0; j < dim; j++) { + v[j] = mY[n - 1][j] + (t - mT[n - 1]) * mSlopeTemp[j]; + } + return; + } + } else { + if (t <= mT[0]) { + for (int j = 0; j < dim; j++) { + v[j] = mY[0][j]; + } + return; + } + if (t >= mT[n - 1]) { + for (int j = 0; j < dim; j++) { + v[j] = mY[n - 1][j]; + } + return; + } + } + + for (int i = 0; i < n - 1; i++) { + if (t == mT[i]) { + for (int j = 0; j < dim; j++) { + v[j] = mY[i][j]; + } + } + if (t < mT[i + 1]) { + double h = mT[i + 1] - mT[i]; + double x = (t - mT[i]) / h; + for (int j = 0; j < dim; j++) { + double y1 = mY[i][j]; + double y2 = mY[i + 1][j]; + double t1 = mTangent[i][j]; + double t2 = mTangent[i + 1][j]; + v[j] = interpolate(h, x, y1, y2, t1, t2); + } + return; + } + } + } + + /** + * Get the position of all curves at time t + * @param t + * @param v + */ + public void getPos(double t, float[] v) { + final int n = mT.length; + final int dim = mY[0].length; + if (mExtrapolate) { + if (t <= mT[0]) { + getSlope(mT[0], mSlopeTemp); + for (int j = 0; j < dim; j++) { + v[j] = (float) (mY[0][j] + (t - mT[0]) * mSlopeTemp[j]); + } + return; + } + if (t >= mT[n - 1]) { + getSlope(mT[n - 1], mSlopeTemp); + for (int j = 0; j < dim; j++) { + v[j] = (float) (mY[n - 1][j] + (t - mT[n - 1]) * mSlopeTemp[j]); + } + return; + } + } else { + if (t <= mT[0]) { + for (int j = 0; j < dim; j++) { + v[j] = (float) mY[0][j]; + } + return; + } + if (t >= mT[n - 1]) { + for (int j = 0; j < dim; j++) { + v[j] = (float) mY[n - 1][j]; + } + return; + } + } + + for (int i = 0; i < n - 1; i++) { + if (t == mT[i]) { + for (int j = 0; j < dim; j++) { + v[j] = (float) mY[i][j]; + } + } + if (t < mT[i + 1]) { + double h = mT[i + 1] - mT[i]; + double x = (t - mT[i]) / h; + for (int j = 0; j < dim; j++) { + double y1 = mY[i][j]; + double y2 = mY[i + 1][j]; + double t1 = mTangent[i][j]; + double t2 = mTangent[i + 1][j]; + v[j] = (float) interpolate(h, x, y1, y2, t1, t2); + } + return; + } + } + } + + /** + * Get the position of the jth curve at time t + * @param t + * @param j + * @return + */ + public double getPos(double t, int j) { + final int n = mT.length; + if (mExtrapolate) { + if (t <= mT[0]) { + return mY[0][j] + (t - mT[0]) * getSlope(mT[0], j); + } + if (t >= mT[n - 1]) { + return mY[n - 1][j] + (t - mT[n - 1]) * getSlope(mT[n - 1], j); + } + } else { + if (t <= mT[0]) { + return mY[0][j]; + } + if (t >= mT[n - 1]) { + return mY[n - 1][j]; + } + } + + for (int i = 0; i < n - 1; i++) { + if (t == mT[i]) { + return mY[i][j]; + } + if (t < mT[i + 1]) { + double h = mT[i + 1] - mT[i]; + double x = (t - mT[i]) / h; + double y1 = mY[i][j]; + double y2 = mY[i + 1][j]; + double t1 = mTangent[i][j]; + double t2 = mTangent[i + 1][j]; + return interpolate(h, x, y1, y2, t1, t2); + + } + } + return 0; // should never reach here + } + + /** + * Get the slope of all the curves at position t + * @param t + * @param v + */ + public void getSlope(double t, double[] v) { + final int n = mT.length; + int dim = mY[0].length; + if (t <= mT[0]) { + t = mT[0]; + } else if (t >= mT[n - 1]) { + t = mT[n - 1]; + } + + for (int i = 0; i < n - 1; i++) { + if (t <= mT[i + 1]) { + double h = mT[i + 1] - mT[i]; + double x = (t - mT[i]) / h; + for (int j = 0; j < dim; j++) { + double y1 = mY[i][j]; + double y2 = mY[i + 1][j]; + double t1 = mTangent[i][j]; + double t2 = mTangent[i + 1][j]; + v[j] = diff(h, x, y1, y2, t1, t2) / h; + } + break; + } + } + return; + } + + /** + * Get the slope of the j curve at position t + * @param t + * @param j + * @return + */ + public double getSlope(double t, int j) { + final int n = mT.length; + + if (t < mT[0]) { + t = mT[0]; + } else if (t >= mT[n - 1]) { + t = mT[n - 1]; + } + for (int i = 0; i < n - 1; i++) { + if (t <= mT[i + 1]) { + double h = mT[i + 1] - mT[i]; + double x = (t - mT[i]) / h; + double y1 = mY[i][j]; + double y2 = mY[i + 1][j]; + double t1 = mTangent[i][j]; + double t2 = mTangent[i + 1][j]; + return diff(h, x, y1, y2, t1, t2) / h; + } + } + return 0; // should never reach here + } + + public double[] getTimePoints() { + return mT; + } + + /** + * Cubic Hermite spline + */ + private static double interpolate(double h, + double x, + double y1, + double y2, + double t1, + double t2) { + double x2 = x * x; + double x3 = x2 * x; + return -2 * x3 * y2 + 3 * x2 * y2 + 2 * x3 * y1 - 3 * x2 * y1 + y1 + + h * t2 * x3 + h * t1 * x3 - h * t2 * x2 - 2 * h * t1 * x2 + + h * t1 * x; + } + + /** + * Cubic Hermite spline slope differentiated + */ + private static double diff(double h, double x, double y1, double y2, double t1, double t2) { + double x2 = x * x; + return -6 * x2 * y2 + 6 * x * y2 + 6 * x2 * y1 - 6 * x * y1 + 3 * h * t2 * x2 + + 3 * h * t1 * x2 - 2 * h * t2 * x - 4 * h * t1 * x + h * t1; + } + + /** + * This builds a monotonic spline to be used as a wave function + */ + public static MonotonicCurveFit buildWave(String configString) { + // done this way for efficiency + String str = configString; + double[] values = new double[str.length() / 2]; + int start = configString.indexOf('(') + 1; + int off1 = configString.indexOf(',', start); + int count = 0; + while (off1 != -1) { + String tmp = configString.substring(start, off1).trim(); + values[count++] = Double.parseDouble(tmp); + off1 = configString.indexOf(',', start = off1 + 1); + } + off1 = configString.indexOf(')', start); + String tmp = configString.substring(start, off1).trim(); + values[count++] = Double.parseDouble(tmp); + + return buildWave(Arrays.copyOf(values, count)); + } + + private static MonotonicCurveFit buildWave(double[] values) { + int length = values.length * 3 - 2; + int len = values.length - 1; + double gap = 1.0 / len; + double[][] points = new double[length][1]; + double[] time = new double[length]; + for (int i = 0; i < values.length; i++) { + double v = values[i]; + points[i + len][0] = v; + time[i + len] = i * gap; + if (i > 0) { + points[i + len * 2][0] = v + 1; + time[i + len * 2] = i * gap + 1; + + points[i - 1][0] = v - 1 - gap; + time[i - 1] = i * gap + -1 - gap; + } + } + + return new MonotonicCurveFit(time, points); + } +} 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 new file mode 100644 index 000000000000..6ed6548405d2 --- /dev/null +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/StepCurve.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.internal.widget.remotecompose.core.operations.utilities.easing; + + +/** + * 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)" it should start at 0 and end at one 1 + */ +public class StepCurve extends Easing { + private static final boolean DEBUG = false; + MonotonicCurveFit mCurveFit; + + public StepCurve(float[] params, int offset, int len) { + mCurveFit = genSpline(params, offset, len); + } + + private static MonotonicCurveFit genSpline(float[] values, int off, int arrayLen) { + int length = arrayLen * 3 - 2; + int len = arrayLen - 1; + double gap = 1.0 / len; + double[][] points = new double[length][1]; + double[] time = new double[length]; + for (int i = 0; i < arrayLen; i++) { + double v = values[i + off]; + points[i + len][0] = v; + time[i + len] = i * gap; + if (i > 0) { + points[i + len * 2][0] = v + 1; + time[i + len * 2] = i * gap + 1; + + points[i - 1][0] = v - 1 - gap; + time[i - 1] = i * gap + -1 - gap; + } + } + + MonotonicCurveFit ms = new MonotonicCurveFit(time, points); + + return ms; + } + + @Override + public float getDiff(float x) { + return (float) mCurveFit.getSlope(x, 0); + } + + + @Override + public float get(float x) { + return (float) mCurveFit.getPos(x, 0); + } +} 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 bcda27a40f64..d1c4d46f2c22 100644 --- a/core/java/com/android/internal/widget/remotecompose/player/RemoteComposeDocument.java +++ b/core/java/com/android/internal/widget/remotecompose/player/RemoteComposeDocument.java @@ -79,6 +79,15 @@ public class RemoteComposeDocument { } /** + * The delay in milliseconds to next repaint -1 = not needed 0 = asap + * + * @return delay in milliseconds to next repaint or -1 + */ + public int needsRepaint() { + return mDocument.needsRepaint(); + } + + /** * Returns true if the document can be displayed given this version of the player * * @param majorVersion the max major version supported by the player @@ -89,5 +98,10 @@ public class RemoteComposeDocument { return mDocument.canBeDisplayed(majorVersion, minorVersion, capabilities); } + @Override + public String toString() { + return "Document{\n" + + mDocument + '}'; + } } 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 d0d6e6982a16..ecb68bb21fb3 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,7 @@ import android.graphics.PorterDuffColorFilter; import android.graphics.RadialGradient; import android.graphics.Rect; import android.graphics.RectF; +import android.graphics.RuntimeShader; import android.graphics.Shader; import android.graphics.SweepGradient; import android.graphics.Typeface; @@ -33,6 +34,8 @@ import android.graphics.Typeface; import com.android.internal.widget.remotecompose.core.PaintContext; import com.android.internal.widget.remotecompose.core.RemoteContext; import com.android.internal.widget.remotecompose.core.operations.ClipPath; +import com.android.internal.widget.remotecompose.core.operations.ShaderData; +import com.android.internal.widget.remotecompose.core.operations.Utils; import com.android.internal.widget.remotecompose.core.operations.paint.PaintBundle; import com.android.internal.widget.remotecompose.core.operations.paint.PaintChanges; @@ -43,6 +46,7 @@ import com.android.internal.widget.remotecompose.core.operations.paint.PaintChan public class AndroidPaintContext extends PaintContext { Paint mPaint = new Paint(); Canvas mCanvas; + Rect mTmpRect = new Rect(); // use in calculation of bounds public AndroidPaintContext(RemoteContext context, Canvas canvas) { super(context); @@ -177,6 +181,22 @@ public class AndroidPaintContext extends PaintContext { } @Override + public void getTextBounds(int textId, int start, int end, boolean monospace, float[] bounds) { + String str = getText(textId); + if (end == -1) { + end = str.length(); + } + + mPaint.getTextBounds(str, start, end, mTmpRect); + + bounds[0] = mTmpRect.left; + bounds[1] = mTmpRect.top; + bounds[2] = monospace ? (mPaint.measureText(str, start, end) - mTmpRect.left) + : mTmpRect.right; + bounds[3] = mTmpRect.bottom; + } + + @Override public void drawTextRun(int textID, int start, int end, @@ -185,7 +205,16 @@ public class AndroidPaintContext extends PaintContext { float x, float y, boolean rtl) { - String textToPaint = getText(textID).substring(start, end); + + String textToPaint = getText(textID); + if (end == -1) { + if (start != 0) { + textToPaint = textToPaint.substring(start); + } + } else { + textToPaint = textToPaint.substring(start, end); + } + mCanvas.drawText(textToPaint, x, y, mPaint); } @@ -308,7 +337,7 @@ public class AndroidPaintContext extends PaintContext { @Override public void applyPaint(PaintBundle mPaintData) { - mPaintData.applyPaintChange(new PaintChanges() { + mPaintData.applyPaintChange((PaintContext) this, new PaintChanges() { @Override public void setTextSize(float size) { mPaint.setTextSize(size); @@ -361,10 +390,8 @@ public class AndroidPaintContext extends PaintContext { } } - } - @Override public void setStrokeWidth(float width) { mPaint.setStrokeWidth(width); @@ -386,13 +413,37 @@ public class AndroidPaintContext extends PaintContext { } @Override - public void setShader(int shader, String shaderString) { - + public void setShader(int shaderId) { + // TODO this stuff should check the shader creation + if (shaderId == 0) { + mPaint.setShader(null); + return; + } + ShaderData data = getShaderData(shaderId); + RuntimeShader shader = new RuntimeShader(getText(data.getShaderTextId())); + String[] names = data.getUniformFloatNames(); + for (int i = 0; i < names.length; i++) { + String name = names[i]; + float[] val = data.getUniformFloats(name); + shader.setFloatUniform(name, val); + } + names = data.getUniformIntegerNames(); + for (int i = 0; i < names.length; i++) { + String name = names[i]; + int[] val = data.getUniformInts(name); + shader.setIntUniform(name, val); + } + names = data.getUniformBitmapNames(); + for (int i = 0; i < names.length; i++) { + String name = names[i]; + int val = data.getUniformBitmapId(name); + } + mPaint.setShader(shader); } @Override public void setImageFilterQuality(int quality) { - System.out.println(">>>>>>>>>>>> "); + Utils.log(" quality =" + quality); } @Override @@ -420,7 +471,6 @@ public class AndroidPaintContext extends PaintContext { mPaint.setFilterBitmap(filter); } - @Override public void setAntiAlias(boolean aa) { mPaint.setAntiAlias(aa); @@ -437,7 +487,6 @@ public class AndroidPaintContext extends PaintContext { case PaintBundle.COLOR_FILTER: mPaint.setColorFilter(null); - System.out.println(">>>>>>>>>>>>> CLEAR!!!!"); break; } } @@ -446,12 +495,11 @@ public class AndroidPaintContext extends PaintContext { } } - Shader.TileMode[] mTilesModes = new Shader.TileMode[]{ + Shader.TileMode[] mTileModes = new Shader.TileMode[]{ Shader.TileMode.CLAMP, Shader.TileMode.REPEAT, Shader.TileMode.MIRROR}; - @Override public void setLinearGradient(int[] colors, float[] stops, @@ -463,7 +511,7 @@ public class AndroidPaintContext extends PaintContext { mPaint.setShader(new LinearGradient(startX, startY, endX, - endY, colors, stops, mTilesModes[tileMode])); + endY, colors, stops, mTileModes[tileMode])); } @@ -475,7 +523,7 @@ public class AndroidPaintContext extends PaintContext { float radius, int tileMode) { mPaint.setShader(new RadialGradient(centerX, centerY, radius, - colors, stops, mTilesModes[tileMode])); + colors, stops, mTileModes[tileMode])); } @Override @@ -490,7 +538,6 @@ public class AndroidPaintContext extends PaintContext { @Override public void setColorFilter(int color, int mode) { PorterDuff.Mode pmode = origamiToPorterDuffMode(mode); - System.out.println("setting color filter to " + pmode.name()); if (pmode != null) { mPaint.setColorFilter( new PorterDuffColorFilter(color, pmode)); @@ -500,10 +547,10 @@ public class AndroidPaintContext extends PaintContext { } @Override - public void mtrixScale(float scaleX, - float scaleY, - float centerX, - float centerY) { + public void matrixScale(float scaleX, + float scaleY, + float centerX, + float centerY) { if (Float.isNaN(centerX)) { mCanvas.scale(scaleX, scaleY); } else { @@ -556,6 +603,11 @@ public class AndroidPaintContext extends PaintContext { } } + @Override + public void reset() { + mPaint.reset(); + } + private Path getPath(int path1Id, int path2Id, float tween, @@ -599,5 +651,9 @@ public class AndroidPaintContext extends PaintContext { private String getText(int id) { return (String) mContext.mRemoteComposeState.getFromId(id); } + + private ShaderData getShaderData(int id) { + return (ShaderData) mContext.mRemoteComposeState.getFromId(id); + } } 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 270e96f11942..6e4893bc0ee6 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,10 +20,15 @@ import android.graphics.BitmapFactory; import android.graphics.Canvas; import com.android.internal.widget.remotecompose.core.RemoteContext; +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; + +import java.util.HashMap; /** * An implementation of Context for Android. - * + * <p> * This is used to play the RemoteCompose operations on Android. */ class AndroidRemoteContext extends RemoteContext { @@ -33,6 +38,7 @@ class AndroidRemoteContext extends RemoteContext { mPaintContext = new AndroidPaintContext(this, canvas); } else { // need to make sure to update the canvas for the current one + mPaintContext.reset(); ((AndroidPaintContext) mPaintContext).setCanvas(canvas); } mWidth = canvas.getWidth(); @@ -50,13 +56,32 @@ class AndroidRemoteContext extends RemoteContext { } } + static class VarName { + String mName; + int mId; + int mType; + + VarName(String name, int id, int type) { + mName = name; + mId = id; + mType = type; + } + } + + HashMap<String, VarName> mVarNameHashMap = new HashMap<>(); + + @Override + public void loadVariableName(String varName, int varId, int varType) { + mVarNameHashMap.put(varName, new VarName(varName, varId, varType)); + } + /** * Decode a byte array into an image and cache it using the given imageId * - * @oaram imageId the id of the image - * @param width with of image to be loaded + * @param width with of image to be loaded * @param height height of image to be loaded * @param bitmap a byte array containing the image information + * @oaram imageId the id of the image */ @Override public void loadBitmap(int imageId, int width, int height, byte[] bitmap) { @@ -70,14 +95,66 @@ class AndroidRemoteContext extends RemoteContext { public void loadText(int id, String text) { if (!mRemoteComposeState.containsId(id)) { mRemoteComposeState.cache(id, text); + } else { + mRemoteComposeState.update(id, text); } } + @Override + public String getText(int id) { + return (String) mRemoteComposeState.getFromId(id); + } + + @Override + public void loadFloat(int id, float value) { + mRemoteComposeState.updateFloat(id, value); + } + + + @Override + public void loadColor(int id, int color) { + mRemoteComposeState.updateColor(id, color); + } + + @Override + public void loadAnimatedFloat(int id, FloatExpression animatedFloat) { + mRemoteComposeState.cache(id, animatedFloat); + } + + @Override + public void loadShader(int id, ShaderData value) { + mRemoteComposeState.cache(id, value); + } + + @Override + public float getFloat(int id) { + return (float) mRemoteComposeState.getFloat(id); + } + + @Override + public int getColor(int id) { + return mRemoteComposeState.getColor(id); + } + + @Override + public void listensTo(int id, VariableSupport variableSupport) { + mRemoteComposeState.listenToVar(id, variableSupport); + } + + @Override + public int updateOps() { + return mRemoteComposeState.getOpsToUpdate(this); + } + + @Override + public ShaderData getShader(int id) { + return (ShaderData) mRemoteComposeState.getFromId(id); + } + /////////////////////////////////////////////////////////////////////////////////////////////// // Click handling /////////////////////////////////////////////////////////////////////////////////////////////// - @Override public void addClickArea(int id, int contentDescriptionId, @@ -87,7 +164,7 @@ class AndroidRemoteContext extends RemoteContext { float bottom, int metadataId) { String contentDescription = (String) mRemoteComposeState.getFromId(contentDescriptionId); - String metadata = (String) mRemoteComposeState.getFromId(metadataId); + String metadata = (String) mRemoteComposeState.getFromId(metadataId); mDocument.addClickArea(id, contentDescription, left, top, right, bottom, metadata); } } diff --git a/core/java/com/android/internal/widget/remotecompose/player/platform/ClickAreaView.java b/core/java/com/android/internal/widget/remotecompose/player/platform/ClickAreaView.java index 672dae32d8e0..329178abe8b5 100644 --- a/core/java/com/android/internal/widget/remotecompose/player/platform/ClickAreaView.java +++ b/core/java/com/android/internal/widget/remotecompose/player/platform/ClickAreaView.java @@ -20,7 +20,6 @@ import android.graphics.Canvas; import android.graphics.Paint; import android.view.View; - /** * Implementation for the click handling */ @@ -40,7 +39,6 @@ class ClickAreaView extends View { setContentDescription(contentDescription); } - public void setDebug(boolean value) { if (mDebug != value) { mDebug = value; 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 a3bb73e22c59..97d23c84b5bd 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 @@ -85,6 +85,7 @@ public class RemoteComposeCanvas extends FrameLayout implements View.OnAttachSta mDocument.initializeContext(mARContext); setContentDescription(mDocument.getDocument().getContentDescription()); requestLayout(); + invalidate(); } AndroidRemoteContext mARContext = new AndroidRemoteContext(); @@ -119,8 +120,7 @@ public class RemoteComposeCanvas extends FrameLayout implements View.OnAttachSta removeAllViews(); } - - public interface ClickCallbacks { + public interface ClickCallbacks { void click(int id, String metadata); } @@ -213,6 +213,9 @@ public class RemoteComposeCanvas extends FrameLayout implements View.OnAttachSta setMeasuredDimension(w, h); } + private int mCount; + private long mTime = System.nanoTime(); + @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); @@ -224,6 +227,17 @@ public class RemoteComposeCanvas extends FrameLayout implements View.OnAttachSta mARContext.mWidth = getWidth(); mARContext.mHeight = getHeight(); mDocument.paint(mARContext, mTheme); + if (mDebug) { + mCount++; + if (System.nanoTime() - mTime > 1000000000L) { + System.out.println(" count " + mCount + " fps"); + mCount = 0; + mTime = System.nanoTime(); + } + } + if (mDocument.needsRepaint() > 0) { + invalidate(); + } } } diff --git a/core/jni/android_database_SQLiteRawStatement.cpp b/core/jni/android_database_SQLiteRawStatement.cpp index 8fc13a82e74e..961486474821 100644 --- a/core/jni/android_database_SQLiteRawStatement.cpp +++ b/core/jni/android_database_SQLiteRawStatement.cpp @@ -83,6 +83,16 @@ static void throwIfInvalidColumn(JNIEnv *env, jlong stmtPtr, jint col) { } } +// If the last operation failed, throw an exception and return true. Otherwise return false. +static bool throwIfError(JNIEnv *env, jlong stmtPtr) { + switch (sqlite3_errcode(db(stmtPtr))) { + case SQLITE_OK: + case SQLITE_DONE: + case SQLITE_ROW: return false; + } + throw_sqlite3_exception(env, db(stmtPtr), nullptr); + return true; +} static jint bindParameterCount(JNIEnv* env, jclass, jlong stmtPtr) { return sqlite3_bind_parameter_count(stmt(stmtPtr)); @@ -223,17 +233,24 @@ static jstring columnName(JNIEnv* env, jclass, jlong stmtPtr, jint col) { static jint columnBytes(JNIEnv* env, jclass, jlong stmtPtr, jint col) { throwIfInvalidColumn(env, stmtPtr, col); - return sqlite3_column_bytes16(stmt(stmtPtr), col); + int r = sqlite3_column_bytes16(stmt(stmtPtr), col); + throwIfError(env, stmtPtr); + return r; } - static jbyteArray columnBlob(JNIEnv* env, jclass, jlong stmtPtr, jint col) { throwIfInvalidColumn(env, stmtPtr, col); const void* blob = sqlite3_column_blob(stmt(stmtPtr), col); if (blob == nullptr) { + if (throwIfError(env, stmtPtr)) { + return NULL; + } return (sqlite3_column_type(stmt(stmtPtr), col) == SQLITE_NULL) ? NULL : emptyArray; } size_t size = sqlite3_column_bytes(stmt(stmtPtr), col); + if (throwIfError(env, stmtPtr)) { + return NULL; + } jbyteArray result = env->NewByteArray(size); if (result == nullptr) { // An OutOfMemory exception will have been thrown. @@ -248,9 +265,13 @@ static int columnBuffer(JNIEnv* env, jclass, jlong stmtPtr, jint col, throwIfInvalidColumn(env, stmtPtr, col); const void* blob = sqlite3_column_blob(stmt(stmtPtr), col); if (blob == nullptr) { + throwIfError(env, stmtPtr); return 0; } jsize bsize = sqlite3_column_bytes(stmt(stmtPtr), col); + if (throwIfError(env, stmtPtr)) { + return 0; + } if (bsize == 0 || bsize <= srcOffset) { return 0; } @@ -278,9 +299,13 @@ static jstring columnText(JNIEnv* env, jclass, jlong stmtPtr, jint col) { throwIfInvalidColumn(env, stmtPtr, col); const jchar* text = static_cast<const jchar*>(sqlite3_column_text16(stmt(stmtPtr), col)); if (text == nullptr) { + throwIfError(env, stmtPtr); return NULL; } size_t length = sqlite3_column_bytes16(stmt(stmtPtr), col) / sizeof(jchar); + if (throwIfError(env, stmtPtr)) { + return NULL; + } return env->NewString(text, length); } diff --git a/core/jni/android_tracing_PerfettoDataSource.cpp b/core/jni/android_tracing_PerfettoDataSource.cpp index 17129d8913d7..fec28987e7e6 100644 --- a/core/jni/android_tracing_PerfettoDataSource.cpp +++ b/core/jni/android_tracing_PerfettoDataSource.cpp @@ -245,7 +245,6 @@ static jlong nativeGetFinalizer(JNIEnv* /* env */, jclass /* clazz */) { } void nativeWritePackets(JNIEnv* env, jclass clazz, jlong ds_ptr, jobjectArray packets) { - ALOG(LOG_DEBUG, LOG_TAG, "nativeWritePackets(%p)", (void*)ds_ptr); sp<PerfettoDataSource> datasource = reinterpret_cast<PerfettoDataSource*>(ds_ptr); datasource->WritePackets(env, packets); } diff --git a/core/jni/android_view_MotionEvent.cpp b/core/jni/android_view_MotionEvent.cpp index f914bee8da1b..d32486c73db2 100644 --- a/core/jni/android_view_MotionEvent.cpp +++ b/core/jni/android_view_MotionEvent.cpp @@ -203,55 +203,52 @@ static bool validatePointerProperties(JNIEnv* env, jobject pointerPropertiesObj) return true; } -static void pointerCoordsToNative(JNIEnv* env, jobject pointerCoordsObj, - float xOffset, float yOffset, PointerCoords* outRawPointerCoords) { - outRawPointerCoords->clear(); - outRawPointerCoords->setAxisValue(AMOTION_EVENT_AXIS_X, - env->GetFloatField(pointerCoordsObj, gPointerCoordsClassInfo.x) - xOffset); - outRawPointerCoords->setAxisValue(AMOTION_EVENT_AXIS_Y, - env->GetFloatField(pointerCoordsObj, gPointerCoordsClassInfo.y) - yOffset); - outRawPointerCoords->setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, - env->GetFloatField(pointerCoordsObj, gPointerCoordsClassInfo.pressure)); - outRawPointerCoords->setAxisValue(AMOTION_EVENT_AXIS_SIZE, - env->GetFloatField(pointerCoordsObj, gPointerCoordsClassInfo.size)); - outRawPointerCoords->setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, - env->GetFloatField(pointerCoordsObj, gPointerCoordsClassInfo.touchMajor)); - outRawPointerCoords->setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, - env->GetFloatField(pointerCoordsObj, gPointerCoordsClassInfo.touchMinor)); - outRawPointerCoords->setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, - env->GetFloatField(pointerCoordsObj, gPointerCoordsClassInfo.toolMajor)); - outRawPointerCoords->setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, - env->GetFloatField(pointerCoordsObj, gPointerCoordsClassInfo.toolMinor)); - outRawPointerCoords->setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, - env->GetFloatField(pointerCoordsObj, gPointerCoordsClassInfo.orientation)); - outRawPointerCoords->setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, - env->GetFloatField(pointerCoordsObj, - gPointerCoordsClassInfo.relativeX)); - outRawPointerCoords->setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, - env->GetFloatField(pointerCoordsObj, - gPointerCoordsClassInfo.relativeY)); - outRawPointerCoords->isResampled = - env->GetBooleanField(pointerCoordsObj, gPointerCoordsClassInfo.isResampled); +static PointerCoords pointerCoordsToNative(JNIEnv* env, jobject pointerCoordsObj) { + PointerCoords out{}; + out.setAxisValue(AMOTION_EVENT_AXIS_X, + env->GetFloatField(pointerCoordsObj, gPointerCoordsClassInfo.x)); + out.setAxisValue(AMOTION_EVENT_AXIS_Y, + env->GetFloatField(pointerCoordsObj, gPointerCoordsClassInfo.y)); + out.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, + env->GetFloatField(pointerCoordsObj, gPointerCoordsClassInfo.pressure)); + out.setAxisValue(AMOTION_EVENT_AXIS_SIZE, + env->GetFloatField(pointerCoordsObj, gPointerCoordsClassInfo.size)); + out.setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, + env->GetFloatField(pointerCoordsObj, gPointerCoordsClassInfo.touchMajor)); + out.setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, + env->GetFloatField(pointerCoordsObj, gPointerCoordsClassInfo.touchMinor)); + out.setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, + env->GetFloatField(pointerCoordsObj, gPointerCoordsClassInfo.toolMajor)); + out.setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, + env->GetFloatField(pointerCoordsObj, gPointerCoordsClassInfo.toolMinor)); + out.setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, + env->GetFloatField(pointerCoordsObj, gPointerCoordsClassInfo.orientation)); + out.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, + env->GetFloatField(pointerCoordsObj, gPointerCoordsClassInfo.relativeX)); + out.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, + env->GetFloatField(pointerCoordsObj, gPointerCoordsClassInfo.relativeY)); + out.isResampled = env->GetBooleanField(pointerCoordsObj, gPointerCoordsClassInfo.isResampled); BitSet64 bits = BitSet64(env->GetLongField(pointerCoordsObj, gPointerCoordsClassInfo.mPackedAxisBits)); if (!bits.isEmpty()) { - jfloatArray valuesArray = jfloatArray(env->GetObjectField(pointerCoordsObj, - gPointerCoordsClassInfo.mPackedAxisValues)); + jfloatArray valuesArray = jfloatArray( + env->GetObjectField(pointerCoordsObj, gPointerCoordsClassInfo.mPackedAxisValues)); if (valuesArray) { - jfloat* values = static_cast<jfloat*>( - env->GetPrimitiveArrayCritical(valuesArray, NULL)); + jfloat* values = + static_cast<jfloat*>(env->GetPrimitiveArrayCritical(valuesArray, NULL)); uint32_t index = 0; do { uint32_t axis = bits.clearFirstMarkedBit(); - outRawPointerCoords->setAxisValue(axis, values[index++]); + out.setAxisValue(axis, values[index++]); } while (!bits.isEmpty()); env->ReleasePrimitiveArrayCritical(valuesArray, values, JNI_ABORT); env->DeleteLocalRef(valuesArray); } } + return out; } static jfloatArray obtainPackedAxisValuesArray(JNIEnv* env, uint32_t minSize, @@ -303,14 +300,13 @@ static void pointerCoordsFromNative(JNIEnv* env, const PointerCoords* rawPointer env->SetLongField(outPointerCoordsObj, gPointerCoordsClassInfo.mPackedAxisBits, outBits); } -static void pointerPropertiesToNative(JNIEnv* env, jobject pointerPropertiesObj, - PointerProperties* outPointerProperties) { - outPointerProperties->clear(); - outPointerProperties->id = env->GetIntField(pointerPropertiesObj, - gPointerPropertiesClassInfo.id); - const int32_t toolType = env->GetIntField(pointerPropertiesObj, - gPointerPropertiesClassInfo.toolType); - outPointerProperties->toolType = static_cast<ToolType>(toolType); +static PointerProperties pointerPropertiesToNative(JNIEnv* env, jobject pointerPropertiesObj) { + PointerProperties out{}; + out.id = env->GetIntField(pointerPropertiesObj, gPointerPropertiesClassInfo.id); + const int32_t toolType = + env->GetIntField(pointerPropertiesObj, gPointerPropertiesClassInfo.toolType); + out.toolType = static_cast<ToolType>(toolType); + return out; } static void pointerPropertiesFromNative(JNIEnv* env, const PointerProperties* pointerProperties, @@ -343,15 +339,21 @@ static jlong android_view_MotionEvent_nativeInitialize( event = std::make_unique<MotionEvent>(); } - PointerProperties pointerProperties[pointerCount]; - PointerCoords rawPointerCoords[pointerCount]; + ui::Transform transform; + transform.set(xOffset, yOffset); + const ui::Transform inverseTransform = transform.inverse(); + + std::vector<PointerProperties> pointerProperties; + pointerProperties.reserve(pointerCount); + std::vector<PointerCoords> rawPointerCoords; + rawPointerCoords.reserve(pointerCount); for (jint i = 0; i < pointerCount; i++) { jobject pointerPropertiesObj = env->GetObjectArrayElement(pointerPropertiesObjArray, i); if (!pointerPropertiesObj) { return 0; } - pointerPropertiesToNative(env, pointerPropertiesObj, &pointerProperties[i]); + pointerProperties.emplace_back(pointerPropertiesToNative(env, pointerPropertiesObj)); env->DeleteLocalRef(pointerPropertiesObj); jobject pointerCoordsObj = env->GetObjectArrayElement(pointerCoordsObjArray, i); @@ -359,19 +361,24 @@ static jlong android_view_MotionEvent_nativeInitialize( jniThrowNullPointerException(env, "pointerCoords"); return 0; } - pointerCoordsToNative(env, pointerCoordsObj, xOffset, yOffset, &rawPointerCoords[i]); + rawPointerCoords.emplace_back(pointerCoordsToNative(env, pointerCoordsObj)); + PointerCoords& coords = rawPointerCoords.back(); + if (coords.getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION) != 0.f) { + flags |= AMOTION_EVENT_PRIVATE_FLAG_SUPPORTS_ORIENTATION | + AMOTION_EVENT_PRIVATE_FLAG_SUPPORTS_DIRECTIONAL_ORIENTATION; + } + MotionEvent::calculateTransformedCoordsInPlace(coords, source, flags, inverseTransform); env->DeleteLocalRef(pointerCoordsObj); } - ui::Transform transform; - transform.set(xOffset, yOffset); - ui::Transform identityTransform; + static const ui::Transform kIdentityTransform; event->initialize(InputEvent::nextId(), deviceId, source, ui::LogicalDisplayId{displayId}, INVALID_HMAC, action, 0, flags, edgeFlags, metaState, buttonState, static_cast<MotionClassification>(classification), transform, xPrecision, yPrecision, AMOTION_EVENT_INVALID_CURSOR_POSITION, - AMOTION_EVENT_INVALID_CURSOR_POSITION, identityTransform, downTimeNanos, - eventTimeNanos, pointerCount, pointerProperties, rawPointerCoords); + AMOTION_EVENT_INVALID_CURSOR_POSITION, kIdentityTransform, downTimeNanos, + eventTimeNanos, pointerCount, pointerProperties.data(), + rawPointerCoords.data()); return reinterpret_cast<jlong>(event.release()); } @@ -391,7 +398,10 @@ static void android_view_MotionEvent_nativeAddBatch(JNIEnv* env, jclass clazz, return; } - PointerCoords rawPointerCoords[pointerCount]; + const ui::Transform inverseTransform = event->getTransform().inverse(); + + std::vector<PointerCoords> rawPointerCoords; + rawPointerCoords.reserve(pointerCount); for (size_t i = 0; i < pointerCount; i++) { jobject pointerCoordsObj = env->GetObjectArrayElement(pointerCoordsObjArray, i); @@ -399,12 +409,13 @@ static void android_view_MotionEvent_nativeAddBatch(JNIEnv* env, jclass clazz, jniThrowNullPointerException(env, "pointerCoords"); return; } - pointerCoordsToNative(env, pointerCoordsObj, event->getRawXOffset(), event->getRawYOffset(), - &rawPointerCoords[i]); + rawPointerCoords.emplace_back(pointerCoordsToNative(env, pointerCoordsObj)); + MotionEvent::calculateTransformedCoordsInPlace(rawPointerCoords.back(), event->getSource(), + event->getFlags(), inverseTransform); env->DeleteLocalRef(pointerCoordsObj); } - event->addSample(eventTimeNanos, rawPointerCoords); + event->addSample(eventTimeNanos, rawPointerCoords.data()); event->setMetaState(event->getMetaState() | metaState); } @@ -685,13 +696,15 @@ static jboolean android_view_MotionEvent_nativeIsTouchEvent( static jint android_view_MotionEvent_nativeGetFlags(CRITICAL_JNI_PARAMS_COMMA jlong nativePtr) { MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr); - return event->getFlags(); + // Prevent private flags from being used in Java. + return event->getFlags() & ~AMOTION_EVENT_PRIVATE_FLAG_MASK; } static void android_view_MotionEvent_nativeSetFlags(CRITICAL_JNI_PARAMS_COMMA jlong nativePtr, jint flags) { MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr); - event->setFlags(flags); + // Prevent private flags from being used from Java. + event->setFlags(flags & ~AMOTION_EVENT_PRIVATE_FLAG_MASK); } static jint android_view_MotionEvent_nativeGetEdgeFlags(CRITICAL_JNI_PARAMS_COMMA jlong nativePtr) { diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp index e831a7d229d6..536583840d8f 100644 --- a/core/jni/android_view_SurfaceControl.cpp +++ b/core/jni/android_view_SurfaceControl.cpp @@ -358,7 +358,8 @@ public: jobject stats = env->NewObject(gTransactionStatsClassInfo.clazz, gTransactionStatsClassInfo.ctor, - latchTime, presentFence.get()); + latchTime, + static_cast<jlong>(reinterpret_cast<uintptr_t>(presentFence.get()))); env->CallVoidMethod(mTransactionCompletedListenerObject, gConsumerClassInfo.accept, stats); env->DeleteLocalRef(stats); DieIfException(env, "Uncaught exception in TransactionCompletedListener."); diff --git a/core/jni/com_android_internal_content_NativeLibraryHelper.cpp b/core/jni/com_android_internal_content_NativeLibraryHelper.cpp index 9b8dab78b342..fba0d81d431f 100644 --- a/core/jni/com_android_internal_content_NativeLibraryHelper.cpp +++ b/core/jni/com_android_internal_content_NativeLibraryHelper.cpp @@ -115,7 +115,8 @@ sumFiles(JNIEnv*, void* arg, ZipFileRO* zipFile, ZipEntryRO zipEntry, const char size_t* total = (size_t*) arg; uint32_t uncompLen; - if (!zipFile->getEntryInfo(zipEntry, nullptr, &uncompLen, nullptr, nullptr, nullptr, nullptr)) { + if (!zipFile->getEntryInfo(zipEntry, nullptr, &uncompLen, nullptr, nullptr, nullptr, nullptr, + nullptr)) { return INSTALL_FAILED_INVALID_APK; } diff --git a/core/jni/fd_utils.cpp b/core/jni/fd_utils.cpp index 8e4addd6f331..0eb7c4aee287 100644 --- a/core/jni/fd_utils.cpp +++ b/core/jni/fd_utils.cpp @@ -49,6 +49,9 @@ static const char* kPathAllowlist[] = { "/dev/dri/renderD129", // Fixes b/31172436 "/dev/stune/foreground/tasks", "/dev/blkio/tasks", + "/metadata/aconfig/maps/system.package.map", + "/metadata/aconfig/maps/system.flag.map", + "/metadata/aconfig/boot/system.val" }; static const char kFdPath[] = "/proc/self/fd"; diff --git a/core/proto/android/nfc/apdu_service_info.proto b/core/proto/android/nfc/apdu_service_info.proto index fd110c44483c..9efdfcbea3d3 100644 --- a/core/proto/android/nfc/apdu_service_info.proto +++ b/core/proto/android/nfc/apdu_service_info.proto @@ -27,6 +27,20 @@ option java_multiple_files = true; message ApduServiceInfoProto { option (.android.msg_privacy).dest = DEST_EXPLICIT; + message AutoTransactMapping { + option (.android.msg_privacy).dest = DEST_EXPLICIT; + + optional string aid = 1; + optional bool should_auto_transact = 2; + } + + message AutoTransactPattern { + option (.android.msg_privacy).dest = DEST_EXPLICIT; + + optional string regexp_pattern = 1; + optional bool should_auto_transact = 2; + } + optional .android.content.ComponentNameProto component_name = 1; optional string description = 2; optional bool on_host = 3; @@ -35,4 +49,7 @@ message ApduServiceInfoProto { repeated AidGroupProto static_aid_groups = 6; repeated AidGroupProto dynamic_aid_groups = 7; optional string settings_activity_name = 8; + optional bool should_default_to_observe_mode = 9; + repeated AutoTransactMapping auto_transact_mapping = 10; + repeated AutoTransactPattern auto_transact_patterns = 11; } diff --git a/core/proto/android/nfc/card_emulation.proto b/core/proto/android/nfc/card_emulation.proto index 9c3c6d704922..81da30dd8bbf 100644 --- a/core/proto/android/nfc/card_emulation.proto +++ b/core/proto/android/nfc/card_emulation.proto @@ -59,6 +59,7 @@ message PreferredServicesProto { optional .android.content.ComponentNameProto foreground_requested = 5; optional .android.content.ComponentNameProto settings_default = 6; optional bool prefer_foreground = 7; + optional .android.content.ComponentNameProto wallet_role_holder_payment_service = 8; } // Debugging information for com.android.nfc.cardemulation.EnabledNfcFServices diff --git a/core/proto/android/providers/settings/system.proto b/core/proto/android/providers/settings/system.proto index 5fc2a59e3028..6a0ec1dcb3f2 100644 --- a/core/proto/android/providers/settings/system.proto +++ b/core/proto/android/providers/settings/system.proto @@ -122,6 +122,13 @@ message SystemSettingsProto { } optional Notification notification = 17; + message Pointer { + option (android.msg_privacy).dest = DEST_EXPLICIT; + + optional SettingProto pointer_fill_style = 1 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto pointer_scale = 3 [ (android.privacy).dest = DEST_AUTOMATIC ]; + } + optional Pointer pointer = 37; optional SettingProto pointer_speed = 18 [ (android.privacy).dest = DEST_AUTOMATIC ]; message Ringtone { @@ -268,5 +275,5 @@ message SystemSettingsProto { // Please insert fields in alphabetical order and group them into messages // if possible (to avoid reaching the method limit). - // Next tag = 37; + // Next tag = 38; } diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 6dbe44b483d2..c71f9bde6bf1 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -3249,16 +3249,20 @@ <permission android:name="android.permission.INTERACT_ACROSS_PROFILES" android:protectionLevel="signature|appop" /> - <!-- Allows applications to access profiles with ACCESS_HIDDEN_PROFILES user property - <p>Protection level: normal - @FlaggedApi("android.multiuser.enable_permission_to_access_hidden_profiles") --> + <!-- Allows applications to access profiles with + {@code android.content.pm.UserProperties#PROFILE_API_VISIBILITY_HIDDEN} user property, e.g. + {@link android.os.UserManager#USER_TYPE_PROFILE_PRIVATE}. + <p>Protection level: normal + @FlaggedApi("android.multiuser.enable_permission_to_access_hidden_profiles") --> <permission android:name="android.permission.ACCESS_HIDDEN_PROFILES" android:label="@string/permlab_accessHiddenProfile" android:description="@string/permdesc_accessHiddenProfile" android:protectionLevel="normal" /> - <!-- @SystemApi @hide Allows privileged applications to get details about hidden profile - users. + <!-- @SystemApi @hide Allows privileged applications to get details about profiles with + {@code android.content.pm.UserProperties#PROFILE_API_VISIBILITY_HIDDEN} user property, e.g. + {@link android.os.UserManager#USER_TYPE_PROFILE_PRIVATE}. Removes extra requirements such + as having {@link android.app.role.RoleManager#ROLE_HOME} role for LauncherApps APIs. @FlaggedApi("android.multiuser.enable_permission_to_access_hidden_profiles") --> <permission android:name="android.permission.ACCESS_HIDDEN_PROFILES_FULL" @@ -3322,13 +3326,18 @@ <!-- Allows an application to manage device policy relating to time. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call - APIs protected by this permission on users different to the calling user.--> + APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. + --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_TIME" android:protectionLevel="internal|role" /> <!-- Allows an application to set the grant state of runtime permissions on packages. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_RUNTIME_PERMISSIONS" android:protectionLevel="internal|role" /> @@ -3336,6 +3345,8 @@ <!-- Allows an application to manage the identity of the managing organization. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_ORGANIZATION_IDENTITY" android:protectionLevel="internal|role" /> @@ -3344,6 +3355,8 @@ active policy. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_SUPPORT_MESSAGE" android:protectionLevel="internal|role" /> @@ -3351,6 +3364,8 @@ <!-- Allows an application to manage backup service policy. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_BACKUP_SERVICE" android:protectionLevel="internal|role" /> @@ -3358,6 +3373,8 @@ <!-- Allows an application to manage lock task policy. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_LOCK_TASK" android:protectionLevel="internal|role" /> @@ -3365,6 +3382,8 @@ <!-- Allows an application to manage policy regarding modifying applications. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_APPS_CONTROL" android:protectionLevel="internal|role" /> @@ -3372,6 +3391,8 @@ <!-- Allows an application to manage installing from unknown sources policy. <p>MANAGE_SECURITY_CRITICAL_DEVICE_POLICY_ACROSS_USERS is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_INSTALL_UNKNOWN_SOURCES" android:protectionLevel="internal|role" /> @@ -3379,6 +3400,8 @@ <!-- Allows an application to manage application restrictions. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_APP_RESTRICTIONS" android:protectionLevel="internal|role" /> @@ -3386,6 +3409,8 @@ <!-- Allows an application to manage calling policy. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_CALLS" android:protectionLevel="internal|role" /> @@ -3393,6 +3418,8 @@ <!-- Allows an application to manage debugging features policy. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_DEBUGGING_FEATURES" android:protectionLevel="internal|role" /> @@ -3400,6 +3427,8 @@ <!-- Allows an application to manage policy preventing users from modifying users. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call APIs protected by this permission on users different to the calling user + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_MODIFY_USERS" android:protectionLevel="internal|role" /> @@ -3407,6 +3436,8 @@ <!-- Allows an application to manage safe boot policy. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_SAFE_BOOT" android:protectionLevel="internal|role" /> @@ -3415,6 +3446,8 @@ enable and disable the microphone. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_MICROPHONE" android:protectionLevel="internal|role" /> @@ -3423,6 +3456,8 @@ enable and disable the camera. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_CAMERA" android:protectionLevel="internal|role" /> @@ -3430,6 +3465,8 @@ <!-- Allows an application to manage policy related to keyguard. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_SECURITY_CRITICAL} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_KEYGUARD" android:protectionLevel="internal|role" /> @@ -3437,6 +3474,8 @@ <!-- Allows an application to set policy related to account management. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_ACCOUNT_MANAGEMENT" android:protectionLevel="internal|role" /> @@ -3444,6 +3483,8 @@ <!-- Allows an application to set policy related to hiding and suspending packages. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_PACKAGE_STATE" android:protectionLevel="internal|role" /> @@ -3452,17 +3493,24 @@ challenge on current user. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_RESET_PASSWORD" android:protectionLevel="internal|role" /> - <!-- Allows an application to set policy related to the status bar.--> + <!-- Allows an application to set policy related to the status bar. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. + --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_STATUS_BAR" android:protectionLevel="internal|role" /> <!-- Allows an application to set policy related to bluetooth. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_BLUETOOTH" android:protectionLevel="internal|role" /> @@ -3470,6 +3518,8 @@ <!-- Allows an application to set policy related to fun. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_FUN" android:protectionLevel="internal|role" /> @@ -3477,6 +3527,8 @@ <!-- Allows an application to set policy related to airplane mode. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_AIRPLANE_MODE" android:protectionLevel="internal|role" /> @@ -3484,6 +3536,8 @@ <!-- Allows an application to set policy related to mobile networks. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_MOBILE_NETWORK" android:protectionLevel="internal|role" /> @@ -3491,6 +3545,8 @@ <!-- Allows an application to set policy related to physical media. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_PHYSICAL_MEDIA" android:protectionLevel="internal|role" /> @@ -3498,6 +3554,8 @@ <!-- Allows an application to set policy related to sms. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_SMS" android:protectionLevel="internal|role" /> @@ -3505,6 +3563,8 @@ <!-- Allows an application to set policy related to usb file transfers. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_USB_FILE_TRANSFER" android:protectionLevel="internal|role" /> @@ -3512,6 +3572,8 @@ <!-- Allows an application to set policy related to lock credentials. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_SECURITY_CRITICAL} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_LOCK_CREDENTIALS" android:protectionLevel="internal|role" /> @@ -3519,6 +3581,8 @@ <!-- Allows an application to set policy related to Wifi. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_WIFI" android:protectionLevel="internal|role" /> @@ -3526,6 +3590,8 @@ <!-- Allows an application to set policy related to screen capture. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_SCREEN_CAPTURE" android:protectionLevel="internal|role" /> @@ -3533,6 +3599,8 @@ <!-- Allows an application to set policy related to input methods. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_INPUT_METHODS" android:protectionLevel="internal|role" /> @@ -3541,6 +3609,8 @@ private DNS. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_RESTRICT_PRIVATE_DNS" android:protectionLevel="internal|role" /> @@ -3548,6 +3618,8 @@ <!-- Allows an application to set policy related to the default sms application. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_DEFAULT_SMS" android:protectionLevel="internal|role" /> @@ -3555,6 +3627,8 @@ <!-- Allows an application to set policy related to profiles. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_PROFILES" android:protectionLevel="internal|role" /> @@ -3563,6 +3637,8 @@ cross-profile copy and paste). <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_PROFILE_INTERACTION" android:protectionLevel="internal|role" /> @@ -3570,6 +3646,8 @@ <!-- Allows an application to set policy related to VPNs. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_VPN" android:protectionLevel="internal|role" /> @@ -3577,6 +3655,8 @@ <!-- Allows an application to set policy related to audio output. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_AUDIO_OUTPUT" android:protectionLevel="internal|role" /> @@ -3584,6 +3664,8 @@ <!-- Allows an application to set policy related to the display. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_DISPLAY" android:protectionLevel="internal|role" /> @@ -3591,6 +3673,8 @@ <!-- Allows an application to set policy related to location. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_LOCATION" android:protectionLevel="internal|role" /> @@ -3598,6 +3682,8 @@ <!-- Allows an application to set policy related to factory reset. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_FACTORY_RESET" android:protectionLevel="internal|role" /> @@ -3605,6 +3691,8 @@ <!-- Allows an application to set policy related to the wallpaper. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_WALLPAPER" android:protectionLevel="internal|role" /> @@ -3612,6 +3700,8 @@ <!-- Allows an application to set policy related to the usage of the contents of the screen. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_SCREEN_CONTENT" android:protectionLevel="internal|role" /> @@ -3619,6 +3709,8 @@ <!-- Allows an application to set policy related to system dialogs. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_SYSTEM_DIALOGS" android:protectionLevel="internal|role" /> @@ -3626,6 +3718,8 @@ <!-- Allows an application to set policy related to users running in the background. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_RUN_IN_BACKGROUND" android:protectionLevel="internal|role" /> @@ -3633,6 +3727,8 @@ <!-- Allows an application to set policy related to printing. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_PRINTING" android:protectionLevel="internal|role" /> @@ -3641,12 +3737,16 @@ nearby streaming). <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_NEARBY_COMMUNICATION" android:protectionLevel="internal|role" /> <!-- Allows an application to set policy related to <a href="https://www.threadgroup.org">Thread</a> network. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. @FlaggedApi("com.android.net.thread.platform.flags.thread_user_restriction_enabled") --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_THREAD_NETWORK" @@ -3654,6 +3754,8 @@ <!-- Allows an application to set policy related to sending assist content to a privileged app such as the Assistant app. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. @FlaggedApi("android.app.admin.flags.assist_content_user_restriction_enabled") --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_ASSIST_CONTENT" @@ -3662,6 +3764,8 @@ <!-- Allows an application to set policy related to windows. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_WINDOWS" android:protectionLevel="internal|role" /> @@ -3669,6 +3773,8 @@ <!-- Allows an application to set policy related to locale. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_LOCALE" android:protectionLevel="internal|role" /> @@ -3676,6 +3782,8 @@ <!-- Allows an application to set policy related to autofill. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_AUTOFILL" android:protectionLevel="internal|role" /> @@ -3683,6 +3791,8 @@ <!-- Allows an application to set policy related to users. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_USERS" android:protectionLevel="internal|role" /> @@ -3690,6 +3800,8 @@ <!-- Allows an application to set policy related to certificates. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_CERTIFICATES" android:protectionLevel="internal|role" /> @@ -3697,6 +3809,8 @@ <!-- Allows an application to set policy related to override APNs. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_OVERRIDE_APN" android:protectionLevel="internal|role" /> @@ -3704,6 +3818,8 @@ <!-- Allows an application to set policy related to security logging. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_SECURITY_LOGGING" android:protectionLevel="internal|role" /> @@ -3719,6 +3835,8 @@ <!-- Allows an application to set policy related to system updates. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_SYSTEM_UPDATES" android:protectionLevel="internal|role" /> @@ -3726,6 +3844,8 @@ <!-- Allows an application query system updates. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_QUERY_SYSTEM_UPDATES" android:protectionLevel="internal|role" /> @@ -3733,6 +3853,8 @@ <!-- Allows an application to set policy related to private DNS. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_PRIVATE_DNS" android:protectionLevel="internal|role" /> @@ -3740,6 +3862,8 @@ <!-- Allows an application to set policy related to settings. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_SETTINGS" android:protectionLevel="internal|role" /> @@ -3747,17 +3871,24 @@ <!-- Allows an application to set policy related to network logging. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_NETWORK_LOGGING" android:protectionLevel="internal|role" /> - <!-- Allows an application to set policy related to usb data signalling.--> + <!-- Allows an application to set policy related to usb data signalling. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. + --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_USB_DATA_SIGNALLING" android:protectionLevel="internal|role" /> <!-- Allows an application to set policy related to suspending personal apps. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_SUSPEND_PERSONAL_APPS" android:protectionLevel="internal|role" /> @@ -3765,13 +3896,17 @@ <!-- Allows an application to set policy related to keeping uninstalled packages. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_KEEP_UNINSTALLED_PACKAGES" android:protectionLevel="internal|role" /> <!-- Allows an application to manage policy related to accessibility. - <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call - APIs protected by this permission on users different to the calling user. + <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to + call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_ACCESSIBILITY" android:protectionLevel="internal|role" /> @@ -3779,6 +3914,8 @@ <!-- Allows an application to manage policy related to common criteria mode. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_COMMON_CRITERIA_MODE" android:protectionLevel="internal|role" /> @@ -3786,6 +3923,8 @@ <!-- Allows an application to manage policy related to metered data. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_METERED_DATA" android:protectionLevel="internal|role" /> @@ -3793,6 +3932,8 @@ <!-- Allows an application to set a network-independent global HTTP proxy. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_PROXY" android:protectionLevel="internal|role" /> @@ -3800,6 +3941,8 @@ <!-- Allows an application to request bugreports with user consent. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_BUGREPORT" android:protectionLevel="internal|role" /> @@ -3807,6 +3950,8 @@ <!-- Allows an application to manage policy related to application user data. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_APP_USER_DATA" android:protectionLevel="internal|role" /> @@ -3815,6 +3960,8 @@ permission. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_LOCK" android:protectionLevel="internal|role" /> @@ -3830,6 +3977,8 @@ <!-- Allows an application to manage policy related to system apps. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_SYSTEM_APPS" android:protectionLevel="internal|role" /> @@ -3837,16 +3986,23 @@ <!-- Allows an application to manage policy related to wiping data. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_WIPE_DATA" android:protectionLevel="internal|role" /> <!-- Allows an application to manage policy related to the Memory Tagging Extension (MTE). + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_MTE" android:protectionLevel="internal|role" /> - <!-- Allows an application to manage policy related to device identifiers. --> + <!-- Allows an application to manage policy related to device identifiers. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. + --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_DEVICE_IDENTIFIERS" android:protectionLevel="internal|role" /> @@ -3859,24 +4015,33 @@ <!-- Allows an application to set policy related to subscriptions downloaded by an admin. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call - APIs protected by this permission on users different to the calling user. - @FlaggedApi("android.app.admin.flags.esim_management_enabled") --> + APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. + @FlaggedApi("android.app.admin.flags.esim_management_enabled") + --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_MANAGED_SUBSCRIPTIONS" android:protectionLevel="internal|role" /> <!-- Allows an application to manage policy related to block package uninstallation. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. @FlaggedApi("android.app.admin.flags.dedicated_device_control_api_enabled") --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_BLOCK_UNINSTALL" android:protectionLevel="internal|role" /> <!-- Allows an application to manage policy related to camera toggle. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. @FlaggedApi("android.app.admin.flags.dedicated_device_control_api_enabled") --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_CAMERA_TOGGLE" android:protectionLevel="internal|role" /> <!-- Allows an application to manage policy related to microphone toggle. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. @FlaggedApi("android.app.admin.flags.dedicated_device_control_api_enabled") --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_MICROPHONE_TOGGLE" @@ -3885,16 +4050,21 @@ <!-- Allows an application to set device policies outside the current user that are critical for securing data within the current user. <p>Holding this permission allows the use of other held MANAGE_DEVICE_POLICY_* - permissions across all users on the device provided they are required for securing data - within the current user.--> + permissions across all users on the device provided they are required for securing data + within the current user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. + --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_ACROSS_USERS_SECURITY_CRITICAL" android:protectionLevel="internal|role" /> <!-- Allows an application to set device policies outside the current user that are required for securing device ownership without accessing user data. <p>Holding this permission allows the use of other held MANAGE_DEVICE_POLICY_* - permissions across all users on the device provided they do not grant access to user - data. --> + permissions across all users on the device provided they do not grant access to user data. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. + --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_ACROSS_USERS" android:protectionLevel="internal|role" /> @@ -3902,7 +4072,10 @@ <p>Fuller form of {@link android.Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS} that removes the restriction on accessing user data. <p>Holding this permission allows the use of any other held MANAGE_DEVICE_POLICY_* - permissions across all users on the device.--> + permissions across all users on the device. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. + --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL" android:protectionLevel="internal|role" /> @@ -8194,6 +8367,17 @@ <permission android:name="android.permission.SETUP_FSVERITY" android:protectionLevel="signature|privileged"/> + <!-- + @TestApi + Signature permission reserved for testing. This should never be used to + gate any actual functionality. + <p> + Protection level: signature + @hide + --> + <permission android:name="android.permission.RESERVED_FOR_TESTING_SIGNATURE" + android:protectionLevel="signature"/> + <!-- Attribution for Geofencing service. --> <attribution android:tag="GeofencingService" android:label="@string/geofencing_service"/> <!-- Attribution for Country Detector. --> @@ -8351,7 +8535,7 @@ <activity android:name="android.accounts.GrantCredentialsPermissionActivity" android:excludeFromRecents="true" android:exported="true" - android:theme="@style/Theme.DeviceDefault.Light.DialogWhenLarge" + android:theme="@style/GrantCredentialsPermissionActivity" android:process=":ui" android:visibleToInstantApps="true"> </activity> diff --git a/core/res/OWNERS b/core/res/OWNERS index a7d1a8665ca5..b2b58d557e6f 100644 --- a/core/res/OWNERS +++ b/core/res/OWNERS @@ -69,6 +69,11 @@ per-file res/values/symbols.xml = file:/PERFORMANCE_OWNERS per-file res/xml/power_profile.xml = file:/BATTERY_STATS_OWNERS per-file res/xml/power_profile_test.xml = file:/BATTERY_STATS_OWNERS +# RemoteView color resources +per-file remote_color_resources_res/symbols.xml = pbdr@google.com +per-file remote_color_resources_res/values/public.xml = pbdr@google.com +per-file remote_color_resources_res/values/colors.xml = pbdr@google.com + # Telephony per-file res/values/config_telephony.xml = file:/platform/frameworks/opt/telephony:/OWNERS per-file res/xml/sms_short_codes.xml = file:/platform/frameworks/opt/telephony:/OWNERS @@ -82,4 +87,4 @@ per-file res/values/attrs.xml = arteiro@google.com per-file res/values/styles.xml = arteiro@google.com per-file res/values/symbols.xml = arteiro@google.com per-file res/values/themes_device_defaults.xml = arteiro@google.com -per-file res/values/styles_material.xml = arteiro@google.com
\ No newline at end of file +per-file res/values/styles_material.xml = arteiro@google.com diff --git a/core/res/remote_color_resources_res/values/colors.xml b/core/res/remote_color_resources_res/values/colors.xml index aff3a9592645..dcafb831809a 100644 --- a/core/res/remote_color_resources_res/values/colors.xml +++ b/core/res/remote_color_resources_res/values/colors.xml @@ -66,4 +66,120 @@ <color name="system_neutral2_800">#303030</color> <color name="system_neutral2_900">#1b1b1b</color> <color name="system_neutral2_1000">#000000</color> + <color name="system_primary_container_light">#DAE2FF</color> + <color name="system_on_primary_container_light">#001849</color> + <color name="system_primary_light">#495D92</color> + <color name="system_on_primary_light">#FFFFFF</color> + <color name="system_secondary_container_light">#DDE2F9</color> + <color name="system_on_secondary_container_light">#151B2C</color> + <color name="system_secondary_light">#585E71</color> + <color name="system_on_secondary_light">#FFFFFF</color> + <color name="system_tertiary_container_light">#FED6F9</color> + <color name="system_on_tertiary_container_light">#2B122B</color> + <color name="system_tertiary_light">#735471</color> + <color name="system_on_tertiary_light">#FFFFFF</color> + <color name="system_background_light">#FAF8FF</color> + <color name="system_on_background_light">#1A1B21</color> + <color name="system_surface_light">#FAF8FF</color> + <color name="system_on_surface_light">#1A1B21</color> + <color name="system_surface_container_low_light">#F4F3FA</color> + <color name="system_surface_container_lowest_light">#FFFFFF</color> + <color name="system_surface_container_light">#EEEDF4</color> + <color name="system_surface_container_high_light">#E8E7EF</color> + <color name="system_surface_container_highest_light">#E3E2E9</color> + <color name="system_surface_bright_light">#FAF8FF</color> + <color name="system_surface_dim_light">#DAD9E0</color> + <color name="system_surface_variant_light">#E1E2EC</color> + <color name="system_on_surface_variant_light">#45464F</color> + <color name="system_outline_light">#757780</color> + <color name="system_outline_variant_light">#C5C6D0</color> + <color name="system_error_light">#BA1A1A</color> + <color name="system_on_error_light">#FFFFFF</color> + <color name="system_error_container_light">#FFDAD6</color> + <color name="system_on_error_container_light">#410002</color> + <color name="system_control_activated_light">#DAE2FF</color> + <color name="system_control_normal_light">#45464F</color> + <color name="system_control_highlight_light">#000000</color> + <color name="system_text_primary_inverse_light">#E2E2E9</color> + <color name="system_text_secondary_and_tertiary_inverse_light">#C5C6D0</color> + <color name="system_text_primary_inverse_disable_only_light">#E3E2E9</color> + <color name="system_text_secondary_and_tertiary_inverse_disabled_light">#E3E2E9</color> + <color name="system_text_hint_inverse_light">#E3E2E9</color> + <color name="system_palette_key_color_primary_light">#6275AC</color> + <color name="system_palette_key_color_secondary_light">#71768B</color> + <color name="system_palette_key_color_tertiary_light">#8E6D8B</color> + <color name="system_palette_key_color_neutral_light">#76777D</color> + <color name="system_palette_key_color_neutral_variant_light">#767780</color> + <color name="system_primary_container_dark">#314578</color> + <color name="system_on_primary_container_dark">#DAE2FF</color> + <color name="system_primary_dark">#B2C5FF</color> + <color name="system_on_primary_dark">#182E60</color> + <color name="system_secondary_container_dark">#414659</color> + <color name="system_on_secondary_container_dark">#DDE2F9</color> + <color name="system_secondary_dark">#C0C6DD</color> + <color name="system_on_secondary_dark">#2A3042</color> + <color name="system_tertiary_container_dark">#5A3D59</color> + <color name="system_on_tertiary_container_dark">#FED6F9</color> + <color name="system_tertiary_dark">#E1BBDC</color> + <color name="system_on_tertiary_dark">#422741</color> + <color name="system_background_dark">#121318</color> + <color name="system_on_background_dark">#E3E2E9</color> + <color name="system_surface_dark">#121318</color> + <color name="system_on_surface_dark">#E3E2E9</color> + <color name="system_surface_container_low_dark">#1A1B21</color> + <color name="system_surface_container_lowest_dark">#0D0E13</color> + <color name="system_surface_container_dark">#1E1F25</color> + <color name="system_surface_container_high_dark">#292A2F</color> + <color name="system_surface_container_highest_dark">#33343A</color> + <color name="system_surface_bright_dark">#38393F</color> + <color name="system_surface_dim_dark">#121318</color> + <color name="system_surface_variant_dark">#45464F</color> + <color name="system_on_surface_variant_dark">#C5C6D0</color> + <color name="system_outline_dark">#8F909A</color> + <color name="system_outline_variant_dark">#45464F</color> + <color name="system_error_dark">#FFB4AB</color> + <color name="system_on_error_dark">#690005</color> + <color name="system_error_container_dark">#93000a</color> + <color name="system_on_error_container_dark">#FFDAD6</color> + <color name="system_control_activated_dark">#314578</color> + <color name="system_control_normal_dark">#C5C6D0</color> + <color name="system_control_highlight_dark">#FFFFFF</color> + <color name="system_text_primary_inverse_dark">#1A1B21</color> + <color name="system_text_secondary_and_tertiary_inverse_dark">#45464F</color> + <color name="system_text_primary_inverse_disable_only_dark">#1A1B21</color> + <color name="system_text_secondary_and_tertiary_inverse_disabled_dark">#1A1B21</color> + <color name="system_text_hint_inverse_dark">#1A1B21</color> + <color name="system_palette_key_color_primary_dark">#6275AC</color> + <color name="system_palette_key_color_secondary_dark">#71768B</color> + <color name="system_palette_key_color_tertiary_dark">#8E6D8B</color> + <color name="system_palette_key_color_neutral_dark">#76777D</color> + <color name="system_palette_key_color_neutral_variant_dark">#767780</color> + <color name="system_primary_fixed">#DAE2FF</color> + <color name="system_primary_fixed_dim">#B2C5FF</color> + <color name="system_on_primary_fixed">#001849</color> + <color name="system_on_primary_fixed_variant">#314578</color> + <color name="system_secondary_fixed">#DDE2F9</color> + <color name="system_secondary_fixed_dim">#C0C6DD</color> + <color name="system_on_secondary_fixed">#151B2C</color> + <color name="system_on_secondary_fixed_variant">#414659</color> + <color name="system_tertiary_fixed">#FED6F9</color> + <color name="system_tertiary_fixed_dim">#E1BBDC</color> + <color name="system_on_tertiary_fixed">#2B122B</color> + <color name="system_on_tertiary_fixed_variant">#5A3D59</color> + <color name="system_surface_disabled">#faf8ff</color> + <color name="system_on_surface_disabled">#1a1b21</color> + <color name="system_outline_disabled">#757780</color> + <color name="system_error_0">#ffffff</color> + <color name="system_error_10">#fffbf9</color> + <color name="system_error_50">#fceeee</color> + <color name="system_error_100">#f9dedc</color> + <color name="system_error_200">#f2b8b5</color> + <color name="system_error_300">#ec928e</color> + <color name="system_error_400">#e46962</color> + <color name="system_error_500">#dc362e</color> + <color name="system_error_600">#b3261e</color> + <color name="system_error_700">#8c1d18</color> + <color name="system_error_800">#601410</color> + <color name="system_error_900">#410e0b</color> + <color name="system_error_1000">#000000</color> </resources> diff --git a/core/res/remote_color_resources_res/values/public.xml b/core/res/remote_color_resources_res/values/public.xml index 4b0a89202ad1..d639ed69cfd7 100644 --- a/core/res/remote_color_resources_res/values/public.xml +++ b/core/res/remote_color_resources_res/values/public.xml @@ -66,5 +66,121 @@ <public name="system_neutral2_800" /> <public name="system_neutral2_900" /> <public name="system_neutral2_1000" /> + <public name="system_primary_container_light" /> + <public name="system_on_primary_container_light" /> + <public name="system_primary_light" /> + <public name="system_on_primary_light" /> + <public name="system_secondary_container_light" /> + <public name="system_on_secondary_container_light" /> + <public name="system_secondary_light" /> + <public name="system_on_secondary_light" /> + <public name="system_tertiary_container_light" /> + <public name="system_on_tertiary_container_light" /> + <public name="system_tertiary_light" /> + <public name="system_on_tertiary_light" /> + <public name="system_background_light" /> + <public name="system_on_background_light" /> + <public name="system_surface_light" /> + <public name="system_on_surface_light" /> + <public name="system_surface_container_low_light" /> + <public name="system_surface_container_lowest_light" /> + <public name="system_surface_container_light" /> + <public name="system_surface_container_high_light" /> + <public name="system_surface_container_highest_light" /> + <public name="system_surface_bright_light" /> + <public name="system_surface_dim_light" /> + <public name="system_surface_variant_light" /> + <public name="system_on_surface_variant_light" /> + <public name="system_outline_light" /> + <public name="system_error_light" /> + <public name="system_on_error_light" /> + <public name="system_error_container_light" /> + <public name="system_on_error_container_light" /> + <public name="system_control_activated_light" /> + <public name="system_control_normal_light" /> + <public name="system_control_highlight_light" /> + <public name="system_text_primary_inverse_light" /> + <public name="system_text_secondary_and_tertiary_inverse_light" /> + <public name="system_text_primary_inverse_disable_only_light" /> + <public name="system_text_secondary_and_tertiary_inverse_disabled_light" /> + <public name="system_text_hint_inverse_light" /> + <public name="system_palette_key_color_primary_light" /> + <public name="system_palette_key_color_secondary_light" /> + <public name="system_palette_key_color_tertiary_light" /> + <public name="system_palette_key_color_neutral_light" /> + <public name="system_palette_key_color_neutral_variant_light" /> + <public name="system_primary_container_dark" /> + <public name="system_on_primary_container_dark" /> + <public name="system_primary_dark" /> + <public name="system_on_primary_dark" /> + <public name="system_secondary_container_dark" /> + <public name="system_on_secondary_container_dark" /> + <public name="system_secondary_dark" /> + <public name="system_on_secondary_dark" /> + <public name="system_tertiary_container_dark" /> + <public name="system_on_tertiary_container_dark" /> + <public name="system_tertiary_dark" /> + <public name="system_on_tertiary_dark" /> + <public name="system_background_dark" /> + <public name="system_on_background_dark" /> + <public name="system_surface_dark" /> + <public name="system_on_surface_dark" /> + <public name="system_surface_container_low_dark" /> + <public name="system_surface_container_lowest_dark" /> + <public name="system_surface_container_dark" /> + <public name="system_surface_container_high_dark" /> + <public name="system_surface_container_highest_dark" /> + <public name="system_surface_bright_dark" /> + <public name="system_surface_dim_dark" /> + <public name="system_surface_variant_dark" /> + <public name="system_on_surface_variant_dark" /> + <public name="system_outline_dark" /> + <public name="system_error_dark" /> + <public name="system_on_error_dark" /> + <public name="system_error_container_dark" /> + <public name="system_on_error_container_dark" /> + <public name="system_control_activated_dark" /> + <public name="system_control_normal_dark" /> + <public name="system_control_highlight_dark" /> + <public name="system_text_primary_inverse_dark" /> + <public name="system_text_secondary_and_tertiary_inverse_dark" /> + <public name="system_text_primary_inverse_disable_only_dark" /> + <public name="system_text_secondary_and_tertiary_inverse_disabled_dark" /> + <public name="system_text_hint_inverse_dark" /> + <public name="system_palette_key_color_primary_dark" /> + <public name="system_palette_key_color_secondary_dark" /> + <public name="system_palette_key_color_tertiary_dark" /> + <public name="system_palette_key_color_neutral_dark" /> + <public name="system_palette_key_color_neutral_variant_dark" /> + <public name="system_primary_fixed" /> + <public name="system_primary_fixed_dim" /> + <public name="system_on_primary_fixed" /> + <public name="system_on_primary_fixed_variant" /> + <public name="system_secondary_fixed" /> + <public name="system_secondary_fixed_dim" /> + <public name="system_on_secondary_fixed" /> + <public name="system_on_secondary_fixed_variant" /> + <public name="system_tertiary_fixed" /> + <public name="system_tertiary_fixed_dim" /> + <public name="system_on_tertiary_fixed" /> + <public name="system_on_tertiary_fixed_variant" /> + <public name="system_outline_variant_light" /> + <public name="system_outline_variant_dark" /> + <public name="system_surface_disabled" /> + <public name="system_on_surface_disabled" /> + <public name="system_outline_disabled" /> + <public name="system_error_0" /> + <public name="system_error_10" /> + <public name="system_error_50" /> + <public name="system_error_100" /> + <public name="system_error_200" /> + <public name="system_error_300" /> + <public name="system_error_400" /> + <public name="system_error_500" /> + <public name="system_error_600" /> + <public name="system_error_700" /> + <public name="system_error_800" /> + <public name="system_error_900" /> + <public name="system_error_1000" /> </public-group> </resources> diff --git a/core/res/res/drawable/floating_popup_background_dark.xml b/core/res/res/drawable/floating_popup_background.xml index c4b44484d046..99acedf06e2d 100644 --- a/core/res/res/drawable/floating_popup_background_dark.xml +++ b/core/res/res/drawable/floating_popup_background.xml @@ -16,8 +16,9 @@ */ --> <shape xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:androidprv="http://schemas.android.com/apk/prv/res/android" android:shape="rectangle"> - <solid android:color="@color/background_floating_material_dark" /> + <solid android:color="?androidprv:attr/materialColorSurfaceContainerHighest"/> <corners android:radius="?android:attr/dialogCornerRadius" /> </shape> diff --git a/core/res/res/drawable/floating_popup_background_light.xml b/core/res/res/drawable/floating_popup_background_light.xml deleted file mode 100644 index 767140d9d5c1..000000000000 --- a/core/res/res/drawable/floating_popup_background_light.xml +++ /dev/null @@ -1,23 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -/* Copyright 2015, The Android Open Source Project -** -** Licensed under the Apache License, Version 2.0 (the "License"); -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. -*/ ---> -<shape xmlns:android="http://schemas.android.com/apk/res/android" - android:shape="rectangle"> - <solid android:color="@color/background_floating_material_light" /> - <corners android:radius="?android:attr/dialogCornerRadius" /> -</shape> - diff --git a/core/res/res/drawable/ic_swipe_down.xml b/core/res/res/drawable/ic_swipe_down.xml new file mode 100644 index 000000000000..15712d6fa4e9 --- /dev/null +++ b/core/res/res/drawable/ic_swipe_down.xml @@ -0,0 +1,26 @@ +<?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="24dp" + android:height="24dp" + android:viewportWidth="960" + android:viewportHeight="960" + android:tint="?attr/colorControlNormal"> + <path + android:fillColor="@android:color/white" + android:pathData="M180,600L40,460L82,418L152,488Q146,461 143,434Q140,407 140,380Q140,298 167,221Q194,144 245,80L288,123Q245,179 222.5,244.5Q200,310 200,380Q200,406 203,431.5Q206,457 213,482L278,418L320,460L180,600ZM658,833Q635,841 611.5,840.5Q588,840 566,829L304,707L322,667Q332,647 350,634.5Q368,622 390,620L458,615L346,308Q340,292 347,277.5Q354,263 370,257Q386,251 400.5,258Q415,265 421,281L569,688L469,695L600,756Q607,759 615,759.5Q623,760 630,758L787,701Q818,690 832,659.5Q846,629 835,598L780,448Q774,432 781,417.5Q788,403 804,397Q820,391 834.5,398Q849,405 855,421L910,571Q933,634 905.5,693.5Q878,753 815,776L658,833ZM568,568L514,417Q508,401 515,386.5Q522,372 538,366Q554,360 568.5,367Q583,374 589,390L644,540L568,568ZM681,527L640,414Q634,398 641,383.5Q648,369 664,363Q680,357 694.5,364Q709,371 715,387L756,499L681,527ZM689,605L689,605L689,605Q689,605 689,605Q689,605 689,605L689,605Q689,605 689,605Q689,605 689,605L689,605L689,605Z"/> +</vector>
\ No newline at end of file diff --git a/core/res/res/drawable/immersive_cling_light_bg_circ.xml b/core/res/res/drawable/immersive_cling_bg.xml index df5d5ad522c0..de29c32390e1 100644 --- a/core/res/res/drawable/immersive_cling_light_bg_circ.xml +++ b/core/res/res/drawable/immersive_cling_bg.xml @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="utf-8"?> <!-- - ~ Copyright (C) 2015 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. @@ -15,12 +15,10 @@ ~ limitations under the License --> <shape xmlns:android="http://schemas.android.com/apk/res/android" - android:shape="oval" > - - <solid android:color="#80ffffff" /> - - <size - android:height="76dp" - android:width="76dp" /> - + xmlns:androidprv="http://schemas.android.com/apk/prv/res/android" + android:shape="rectangle"> + <corners + android:bottomLeftRadius="28dp" + android:bottomRightRadius="28dp"/> + <solid android:color="?androidprv:attr/materialColorSurfaceContainer" /> </shape> diff --git a/core/res/res/drawable/immersive_cling_bg_circ.xml b/core/res/res/drawable/immersive_cling_bg_circ.xml deleted file mode 100644 index 4731bbd038fb..000000000000 --- a/core/res/res/drawable/immersive_cling_bg_circ.xml +++ /dev/null @@ -1,26 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - ~ Copyright (C) 2015 The Android Open Source Project - ~ - ~ Licensed under the Apache License, Version 2.0 (the "License"); - ~ you may not use this file except in compliance with the License. - ~ You may obtain a copy of the License at - ~ - ~ http://www.apache.org/licenses/LICENSE-2.0 - ~ - ~ Unless required by applicable law or agreed to in writing, software - ~ distributed under the License is distributed on an "AS IS" BASIS, - ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - ~ See the License for the specific language governing permissions and - ~ limitations under the License - --> -<shape xmlns:android="http://schemas.android.com/apk/res/android" - android:shape="oval" > - - <solid android:color="@color/white" /> - - <size - android:height="56dp" - android:width="56dp" /> - -</shape> diff --git a/core/res/res/drawable/immersive_cling_btn_bg.xml b/core/res/res/drawable/immersive_cling_btn_bg.xml new file mode 100644 index 000000000000..df49e38b4f1e --- /dev/null +++ b/core/res/res/drawable/immersive_cling_btn_bg.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. + --> +<inset xmlns:android="http://schemas.android.com/apk/res/android"> + <ripple android:color="?android:attr/colorControlHighlight"> + <item android:id="@android:id/mask"> + <shape android:shape="rectangle"> + <solid android:color="@android:color/white" /> + <corners android:radius="28dp" /> + </shape> + </item> + <item> + <shape android:shape="rectangle"> + <corners android:radius="28dp" /> + <solid android:color="?android:attr/colorAccent" /> + </shape> + </item> + </ripple> +</inset>
\ No newline at end of file diff --git a/core/res/res/drawable/pointer_alias_vector.xml b/core/res/res/drawable/pointer_alias_vector.xml index 74dd6a0b2a23..035a099cf632 100644 --- a/core/res/res/drawable/pointer_alias_vector.xml +++ b/core/res/res/drawable/pointer_alias_vector.xml @@ -28,6 +28,6 @@ android:fillColor="#FFFFFF" android:pathData="M15.313 12.177a3 3 0 0 0-.416-.633l-.459-.534-.353.609a4.2 4.2 0 0 1-1.801 1.675 4.2 4.2 0 0 1-1.977.429l-.704-.02.213.671q.066.208.164.409l.975 1.995a2.967 2.967 0 1 0 5.332-2.606zm-.827 5.066a1.97 1.97 0 0 1-2.632-.904l-.81-1.658a5.2 5.2 0 0 0 1.68-.489 5.2 5.2 0 0 0 1.771-1.414l.896 1.833a1.97 1.97 0 0 1-.905 2.632m-3.697-7.565a4.2 4.2 0 0 1 1.977-.429l.704.02-.213-.671a3 3 0 0 0-.164-.409l-.975-1.995A2.967 2.967 0 1 0 6.785 8.8l.975 1.995q.172.35.416.633l.459.534.353-.609a4.2 4.2 0 0 1 1.801-1.675m-2.21.516-.895-1.833a1.968 1.968 0 1 1 3.536-1.728l.81 1.658a5.2 5.2 0 0 0-1.68.489 5.2 5.2 0 0 0-1.771 1.414m3.151 1.965a3 3 0 0 0 1.02-.818l.755-.95-1.205.142a2.97 2.97 0 0 0-1.975 1.1l-.755.95 1.205-.142c.324-.039.646-.132.955-.282" /> <path - android:fillColor="#000000" + android:fillColor="?attr/pointerIconVectorFill" android:pathData="M16.449 11.622a4.2 4.2 0 0 0-1.555-1.728l-.234-.146-.001-.276a4.2 4.2 0 0 0-.431-1.838l-.975-1.995a4.232 4.232 0 1 0-7.604 3.716l.975 1.995a4.2 4.2 0 0 0 1.555 1.729l.234.146.001.276c.002.617.141 1.244.431 1.838l.975 1.995a4.232 4.232 0 1 0 7.604-3.716zm-7.814.34-.459-.534a3 3 0 0 1-.416-.633L6.785 8.8a2.967 2.967 0 1 1 5.332-2.606l.975 1.995q.098.202.164.409l.214.672-.704-.02a4.2 4.2 0 0 0-1.977.429 4.2 4.2 0 0 0-1.801 1.675zm1.689-.33a2.97 2.97 0 0 1 1.975-1.1l1.205-.142-.755.95a2.95 2.95 0 0 1-1.02.818 3 3 0 0 1-.955.281l-1.204.143zm4.601 6.51a2.967 2.967 0 0 1-3.969-1.363l-.975-1.995a3 3 0 0 1-.164-.409l-.213-.671.704.02a4.2 4.2 0 0 0 1.977-.429 4.2 4.2 0 0 0 1.801-1.675l.353-.609.459.534q.245.284.416.633l.975 1.995a2.97 2.97 0 0 1-1.364 3.969" /> </vector>
\ No newline at end of file diff --git a/core/res/res/drawable/pointer_all_scroll_vector.xml b/core/res/res/drawable/pointer_all_scroll_vector.xml index 1692e5e62a46..45ad98c1b57c 100644 --- a/core/res/res/drawable/pointer_all_scroll_vector.xml +++ b/core/res/res/drawable/pointer_all_scroll_vector.xml @@ -21,7 +21,7 @@ android:viewportHeight="24"> <path android:pathData="M12.93 4.54a1.06 1.06 0 0 0-1.85 0L9.32 7.6c-.4.71.1 1.6.92 1.6h.82v1.86H9.2v-.84c0-.82-.88-1.33-1.6-.93l-3.06 1.76c-.7.41-.7 1.44 0 1.85l3.07 1.76c.7.4 1.6-.1 1.6-.93v-.79h1.86v1.87h-.82c-.81 0-1.33.88-.92 1.6l1.76 3.06c.4.71 1.44.71 1.85 0l1.75-3.07c.41-.7-.1-1.6-.92-1.6h-.82v-1.86h1.86v.8c0 .81.89 1.32 1.6.92l3.07-1.76c.7-.41.7-1.44 0-1.85L16.4 9.3c-.71-.4-1.6.1-1.6.93v.84h-1.86V9.2h.82c.82 0 1.33-.89.92-1.6l-1.75-3.06z" - android:fillColor="#000000"/> + android:fillColor="?attr/pointerIconVectorFill"/> <path android:pathData="M12 4c.36 0 .72.18.93.54l1.75 3.06c.41.71-.1 1.6-.92 1.6h-.82v1.86h1.86v-.84a1.07 1.07 0 0 1 1.6-.92l3.06 1.75c.72.41.72 1.44 0 1.85l-3.06 1.76a1.07 1.07 0 0 1-1.6-.92v-.8h-1.86v1.87h.82c.82 0 1.33.88.92 1.6l-1.75 3.06a1.07 1.07 0 0 1-1.85 0L9.32 16.4c-.4-.7.1-1.6.93-1.6h.81v-1.86H9.2v.8a1.07 1.07 0 0 1-1.6.92L4.54 12.9a1.06 1.06 0 0 1 0-1.85L7.6 9.3a1.07 1.07 0 0 1 1.6.92v.85h1.86V9.2h-.82c-.81 0-1.33-.89-.92-1.6l1.76-3.06c.2-.36.56-.54.92-.54m0-1c-.74 0-1.41.39-1.79 1.04L8.45 7.1c-.18.33-.28.7-.27 1.05h-.05c-.36 0-.71.1-1.03.28l-3.06 1.76a2.05 2.05 0 0 0 0 3.58l3.06 1.75c.32.18.67.28 1.03.28h.05c-.01.38.08.76.28 1.1l1.75 3.07c.38.65 1.05 1.03 1.8 1.03s1.41-.38 1.78-1.03l1.76-3.07c.2-.34.3-.72.28-1.1h.04c.36 0 .71-.1 1.03-.28l3.06-1.75a2.07 2.07 0 0 0 0-3.58L16.9 8.43a2.07 2.07 0 0 0-1.03-.28h-.04c0-.36-.09-.72-.28-1.05L13.8 4.04A2.04 2.04 0 0 0 12 3z" android:fillColor="#FFFFFF"/> diff --git a/core/res/res/drawable/pointer_arrow_vector.xml b/core/res/res/drawable/pointer_arrow_vector.xml index 562f0c05f662..2614170f6994 100644 --- a/core/res/res/drawable/pointer_arrow_vector.xml +++ b/core/res/res/drawable/pointer_arrow_vector.xml @@ -21,7 +21,7 @@ android:viewportHeight="24"> <path android:pathData="M16.34 11.18 6.77 4.02a1.78 1.78 0 0 0-1.88-.17c-.63.31-1 .91-1 1.6l.01 11.96c0 .9.6 1.46 1.15 1.67a1.74 1.74 0 0 0 1.98-.45l2.96-3.19c.3-.32.7-.52 1.13-.56l4.33-.47a1.8 1.8 0 0 0 .89-3.23z" - android:fillColor="#000000"/> + android:fillColor="?attr/pointerIconVectorFill"/> <path android:pathData="M16.94 10.38 7.37 3.22a2.77 2.77 0 0 0-2.93-.27 2.75 2.75 0 0 0-1.55 2.51l.01 11.95a2.78 2.78 0 0 0 2.82 2.8c.77 0 1.5-.32 2.03-.9l2.97-3.19a.8.8 0 0 1 .5-.25l4.34-.46a2.76 2.76 0 0 0 2.4-2.05 2.8 2.8 0 0 0-1.02-2.98zM17 13.1a1.77 1.77 0 0 1-1.55 1.31l-4.33.47a1.8 1.8 0 0 0-1.13.56l-2.97 3.2c-.4.42-.86.57-1.3.57-.24 0-.48-.05-.68-.13a1.77 1.77 0 0 1-1.14-1.67V5.46a1.81 1.81 0 0 1 1.8-1.8c.38 0 .75.11 1.07.36l9.57 7.16c.72.54.81 1.35.66 1.92z" android:fillColor="#FFFFFF"/> diff --git a/core/res/res/drawable/pointer_cell_vector.xml b/core/res/res/drawable/pointer_cell_vector.xml index 044a4f4014cb..cead1c4185b4 100644 --- a/core/res/res/drawable/pointer_cell_vector.xml +++ b/core/res/res/drawable/pointer_cell_vector.xml @@ -22,6 +22,6 @@ android:fillColor="#FFFFFF" android:pathData="M19 9.667h-4.668V5a2 2 0 0 0-2-2h-.667a2 2 0 0 0-2 2v4.667H5a2 2 0 0 0-2 2v.667a2 2 0 0 0 2 2h4.665V19a2 2 0 0 0 2 2h.667a2 2 0 0 0 2-2v-4.666H19a2 2 0 0 0 2-2v-.667a2 2 0 0 0-2-2m1 2.667a1 1 0 0 1-1 1h-5.668V19a1 1 0 0 1-1 1h-.667a1 1 0 0 1-1-1v-5.666H5a1 1 0 0 1-1-1v-.667a1 1 0 0 1 1-1h5.665V5a1 1 0 0 1 1-1h.667a1 1 0 0 1 1 1v5.667H19a1 1 0 0 1 1 1z" /> <path - android:fillColor="#000000" + android:fillColor="?attr/pointerIconVectorFill" android:pathData="M19 10.667h-5.668V5a1 1 0 0 0-1-1h-.667a1 1 0 0 0-1 1v5.667H5a1 1 0 0 0-1 1v.667a1 1 0 0 0 1 1h5.665V19a1 1 0 0 0 1 1h.667a1 1 0 0 0 1-1v-5.666H19a1 1 0 0 0 1-1v-.667a1 1 0 0 0-1-1" /> </vector>
\ No newline at end of file diff --git a/core/res/res/drawable/pointer_context_menu_vector.xml b/core/res/res/drawable/pointer_context_menu_vector.xml index 8e954d290619..fb2af431ffb6 100644 --- a/core/res/res/drawable/pointer_context_menu_vector.xml +++ b/core/res/res/drawable/pointer_context_menu_vector.xml @@ -26,9 +26,9 @@ android:fillColor="#FFFFFF" android:pathData="M16.938 10.38 7.372 3.216a2.77 2.77 0 0 0-2.931-.262A2.75 2.75 0 0 0 2.894 5.46l.009 11.951a2.785 2.785 0 0 0 1.776 2.604c.33.129.691.197 1.044.197a2.75 2.75 0 0 0 2.031-.897l2.969-3.193a.8.8 0 0 1 .5-.25l4.336-.467c1.397-.15 2.157-1.153 2.401-2.041a2.785 2.785 0 0 0-1.022-2.984m.058 2.718c-.157.571-.645 1.216-1.544 1.312l-4.335.467a1.8 1.8 0 0 0-1.126.563l-2.97 3.193a1.74 1.74 0 0 1-1.298.578 1.9 1.9 0 0 1-.678-.128c-.551-.217-1.141-.771-1.142-1.674l-.009-11.95c0-.697.371-1.299.994-1.611.262-.131.538-.196.813-.196.377 0 .75.123 1.072.365l9.566 7.163c.723.542.814 1.346.657 1.918" /> <path - android:fillColor="#000000" + android:fillColor="?attr/pointerIconVectorFill" android:pathData="M16.339 11.18 6.773 4.017a1.78 1.78 0 0 0-1.072-.365c-.274 0-.551.065-.813.196a1.77 1.77 0 0 0-.994 1.611l.009 11.951c0 .903.59 1.457 1.142 1.674.2.078.433.128.678.128.434 0 .906-.155 1.298-.578l2.97-3.193a1.8 1.8 0 0 1 1.126-.563l4.335-.467c.899-.097 1.387-.741 1.544-1.312.157-.573.066-1.377-.657-1.919" /> <path - android:fillColor="#000000" + android:fillColor="?attr/pointerIconVectorFill" android:pathData="M19.475 3.461h-2.66c-.37 0-.67.3-.67.67v2.66c0 .37.3.67.67.67h2.66c.37 0 .67-.3.67-.67v-2.66a.67.67 0 0 0-.67-.67m-.3 3.062h-2.067a.3.3 0 1 1 0-.6h2.067a.3.3 0 1 1 0 .6m0-.868h-2.067a.3.3 0 1 1 0-.6h2.067a.3.3 0 1 1 0 .6m0-.885h-2.067a.3.3 0 1 1 0-.6h2.067a.3.3 0 1 1 0 .6" /> </vector>
\ No newline at end of file diff --git a/core/res/res/drawable/pointer_copy_vector.xml b/core/res/res/drawable/pointer_copy_vector.xml index b1e8995269a7..3f138685e6eb 100644 --- a/core/res/res/drawable/pointer_copy_vector.xml +++ b/core/res/res/drawable/pointer_copy_vector.xml @@ -19,8 +19,8 @@ android:viewportHeight="24" android:viewportWidth="24"> <group> - <path android:fillColor="#FFFFFF" android:pathData="M17.5 2c-2.104 0-3.861 1.457-4.351 3.41A4.5 4.5 0 0 0 13 6.5c0 .344.047.675.12.997-.062-.002-.122-.009-.185-.009q-.225 0-.446.018V6.484a1 1 0 0 0-2 0v1.57a5.7 5.7 0 0 0-.997.625V7.583a1 1 0 0 0-2 0v4.205l-.697-.713c-.482-.494-1.265-.494-1.747 0s-.482 1.294 0 1.787l3.847 3.936q.056.057.117.106a5.58 5.58 0 0 0 3.922 1.613c3.045 0 5.563-2.469 5.563-5.514q0-.192-.013-.38v-1.739a4.4 4.4 0 0 0 1-.37C20.969 9.778 22 8.265 22 6.5 22 4.019 19.981 2 17.5 2m1.985 7.364a3.6 3.6 0 0 1-1 .478A3.5 3.5 0 0 1 17.5 10a3.5 3.5 0 0 1-3.486-3.358C14.012 6.594 14 6.549 14 6.5c0-.328.06-.639.145-.941C14.559 4.088 15.898 3 17.5 3 19.43 3 21 4.57 21 6.5a3.47 3.47 0 0 1-1.515 2.864" /> - <path android:fillColor="#FFFFFF" android:pathData="M19.299 6H18V4.7a.5.5 0 0 0-1 0V6h-1.301a.5.5 0 0 0 0 1H17v1.3a.5.5 0 0 0 1 0V7h1.299a.5.5 0 0 0 0-1" /> + <path android:fillColor="?attr/pointerIconVectorFillInverse" android:pathData="M17.5 2c-2.104 0-3.861 1.457-4.351 3.41A4.5 4.5 0 0 0 13 6.5c0 .344.047.675.12.997-.062-.002-.122-.009-.185-.009q-.225 0-.446.018V6.484a1 1 0 0 0-2 0v1.57a5.7 5.7 0 0 0-.997.625V7.583a1 1 0 0 0-2 0v4.205l-.697-.713c-.482-.494-1.265-.494-1.747 0s-.482 1.294 0 1.787l3.847 3.936q.056.057.117.106a5.58 5.58 0 0 0 3.922 1.613c3.045 0 5.563-2.469 5.563-5.514q0-.192-.013-.38v-1.739a4.4 4.4 0 0 0 1-.37C20.969 9.778 22 8.265 22 6.5 22 4.019 19.981 2 17.5 2m1.985 7.364a3.6 3.6 0 0 1-1 .478A3.5 3.5 0 0 1 17.5 10a3.5 3.5 0 0 1-3.486-3.358C14.012 6.594 14 6.549 14 6.5c0-.328.06-.639.145-.941C14.559 4.088 15.898 3 17.5 3 19.43 3 21 4.57 21 6.5a3.47 3.47 0 0 1-1.515 2.864" /> + <path android:fillColor="?attr/pointerIconVectorFillInverse" android:pathData="M19.299 6H18V4.7a.5.5 0 0 0-1 0V6h-1.301a.5.5 0 0 0 0 1H17v1.3a.5.5 0 0 0 1 0V7h1.299a.5.5 0 0 0 0-1" /> </group> <path android:fillColor="#000000" diff --git a/core/res/res/drawable/pointer_crosshair_vector.xml b/core/res/res/drawable/pointer_crosshair_vector.xml index b2e7e8a68615..8a50d1bdd497 100644 --- a/core/res/res/drawable/pointer_crosshair_vector.xml +++ b/core/res/res/drawable/pointer_crosshair_vector.xml @@ -23,6 +23,6 @@ android:pathData="M19.25 10.25h-5.5v-5.5a1.75 1.75 0 0 0-3.5 0v5.5h-5.5a1.75 1.75 0 0 0 0 3.5h5.5v5.5a1.75 1.75 0 0 0 3.5 0v-5.5h5.5a1.75 1.75 0 0 0 0-3.5m0 2.5h-6.5v6.5a.75.75 0 0 1-1.5 0v-6.5h-6.5a.75.75 0 0 1 0-1.5h6.5v-6.5a.75.75 0 0 1 1.5 0v6.5h6.5a.75.75 0 0 1 0 1.5" /> <path android:fillType="evenOdd" - android:fillColor="#000000" + android:fillColor="?attr/pointerIconVectorFill" android:pathData="M19.25 11.25h-6.5v-6.5a.75.75 0 0 0-1.5 0v6.5h-6.5a.75.75 0 0 0 0 1.5h6.5v6.5a.75.75 0 0 0 1.5 0v-6.5h6.5a.75.75 0 0 0 0-1.5" /> </vector>
\ No newline at end of file diff --git a/core/res/res/drawable/pointer_grab_vector.xml b/core/res/res/drawable/pointer_grab_vector.xml index 7d9f048bbbb6..48c01ceb6588 100644 --- a/core/res/res/drawable/pointer_grab_vector.xml +++ b/core/res/res/drawable/pointer_grab_vector.xml @@ -22,6 +22,6 @@ android:fillColor="#000000" android:pathData="M20.442 7.562a2 2 0 0 0-2-2c-.366 0-.705.106-1 .277V4.686a2 2 0 0 0-2-2 2 2 0 0 0-1.004.279 1.995 1.995 0 0 0-3.986-.06 2 2 0 0 0-1.006-.28 2 2 0 0 0-2 2v6.501l-.247-.253a2.216 2.216 0 0 0-3.178 0 2.286 2.286 0 0 0 0 3.186l5.106 5.224q.063.061.131.118l-.001.001a6.58 6.58 0 0 0 4.624 1.901c3.587 0 6.565-2.906 6.565-6.516q0-.105-.004-.21m-6.561 5.727a5.58 5.58 0 0 1-3.922-1.613 1 1 0 0 1-.117-.106l-5.106-5.224a1.286 1.286 0 0 1 0-1.788 1.215 1.215 0 0 1 1.747 0l1.962 2.008V4.625a1 1 0 0 1 2 0v5.833q.463-.362.996-.623V3a1 1 0 0 1 2 0v6.29a6 6 0 0 1 1 .011V4.686a1 1 0 0 1 2 0v5.21c.357.185.693.408 1 .663V7.562a1 1 0 0 1 2 0v7.019h.001-.001q.004.104.004.207c.001 3.046-2.518 5.516-5.564 5.516" /> <path - android:fillColor="#FFFFFF" + android:fillColor="?attr/pointerIconVectorFillInverse" android:pathData="M19.442 14.581V7.562a1 1 0 0 0-2 0v2.997a5.7 5.7 0 0 0-1-.663v-5.21a1 1 0 0 0-2 0v4.615a5.5 5.5 0 0 0-1-.011V3a1 1 0 0 0-2 0v6.835a5.6 5.6 0 0 0-.996.623V4.625a1 1 0 0 0-2 0v8.955l-1.962-2.008a1.215 1.215 0 0 0-1.747 0 1.286 1.286 0 0 0 0 1.788l5.106 5.224q.056.057.117.106a5.58 5.58 0 0 0 3.922 1.613c3.046 0 5.565-2.469 5.565-5.516z" /> </vector>
\ No newline at end of file diff --git a/core/res/res/drawable/pointer_grabbing_vector.xml b/core/res/res/drawable/pointer_grabbing_vector.xml index 9c9610366b6d..ad9f86c1848c 100644 --- a/core/res/res/drawable/pointer_grabbing_vector.xml +++ b/core/res/res/drawable/pointer_grabbing_vector.xml @@ -22,6 +22,6 @@ android:fillColor="#000000" android:pathData="M19.485 12.622V8.508a2 2 0 0 0-3.12-1.657 1.993 1.993 0 0 0-2.99-1.006 1.99 1.99 0 0 0-1.886-1.361c-.903 0-1.658.603-1.906 1.425a2 2 0 0 0-3.09 1.674v2.206a2.2 2.2 0 0 0-2.159.586 2.285 2.285 0 0 0 0 3.185l3.847 3.936q.063.061.13.118l-.001.001a6.58 6.58 0 0 0 4.624 1.902c3.586 0 6.563-2.905 6.563-6.514a5 5 0 0 0-.012-.381m-6.55 5.895a5.58 5.58 0 0 1-3.922-1.613 1 1 0 0 1-.117-.106l-3.847-3.936c-.482-.494-.482-1.294 0-1.787s1.265-.494 1.747 0l.697.713V7.583a1 1 0 0 1 2 0v1.096q.463-.364.997-.625v-1.57a1 1 0 0 1 2 0v1.022a5.5 5.5 0 0 1 .996.009v-.007a1 1 0 0 1 2 0v.599q.537.277 1 .66v-.259a1 1 0 0 1 2 0v4.115q.013.189.013.38c-.001 3.045-2.518 5.514-5.564 5.514" /> <path - android:fillColor="#FFFFFF" + android:fillColor="?attr/pointerIconVectorFillInverse" android:pathData="M18.485 12.622V8.508a1 1 0 0 0-2 0v.259a5.6 5.6 0 0 0-1-.66v-.599a1 1 0 0 0-2 0v.008a5.6 5.6 0 0 0-.996-.009V6.484a1 1 0 0 0-2 0v1.57a5.7 5.7 0 0 0-.997.625V7.583a1 1 0 0 0-2 0v4.205l-.697-.713c-.482-.494-1.265-.494-1.747 0s-.482 1.294 0 1.787l3.847 3.936q.056.057.117.106a5.58 5.58 0 0 0 3.922 1.613c3.045 0 5.563-2.469 5.563-5.514a5 5 0 0 0-.012-.381" /> </vector>
\ No newline at end of file diff --git a/core/res/res/drawable/pointer_hand_vector.xml b/core/res/res/drawable/pointer_hand_vector.xml index 79792f840c85..a06dc08b8b3f 100644 --- a/core/res/res/drawable/pointer_hand_vector.xml +++ b/core/res/res/drawable/pointer_hand_vector.xml @@ -22,6 +22,6 @@ android:fillColor="#000000" android:pathData="M20.492 15.197v-4.198A1.995 1.995 0 0 0 18.5 9.001c-.413 0-.797.126-1.115.342a1.99 1.99 0 0 0-1.873-1.341c-.411 0-.792.125-1.109.339a1.99 1.99 0 0 0-1.879-1.361c-.363 0-.699.105-.992.275V3.998A1.99 1.99 0 0 0 9.542 2c-1.1 0-1.992.895-1.992 1.998v7.831l-.242-.249a2.2 2.2 0 0 0-3.164 0 2.29 2.29 0 0 0 0 3.183l5.084 5.219q.063.061.13.118l-.001.001A6.54 6.54 0 0 0 13.963 22c3.572 0 6.537-2.903 6.537-6.509q0-.148-.008-.294m-6.529 5.804a5.55 5.55 0 0 1-3.906-1.611 1 1 0 0 1-.117-.106l-5.084-5.219a1.286 1.286 0 0 1 0-1.786 1.21 1.21 0 0 1 1.74 0l1.95 2.002V3.998c0-.552.446-.999.996-.999s.996.447.996.999v7.17l.011-.007a.495.495 0 0 0 .989-.037V8.939a.992.992 0 0 1 1.984.039v.796l-.007 1.386a.5.5 0 0 0 .495.502h.003a.5.5 0 0 0 .498-.497l.006-1.157h.001V10a.997.997 0 1 1 1.991 0v.601l.004.003v1.02q.001.107.042.199a.5.5 0 0 0 .153.187l.031.021a.5.5 0 0 0 .231.083c.014.001.026.008.04.008a.5.5 0 0 0 .498-.5v-.642a.996.996 0 0 1 .993-.98c.55 0 .996.447.996.999v4.199a6 6 0 0 1 .008.293c-.001 3.043-2.509 5.51-5.542 5.51" /> <path - android:fillColor="#FFFFFF" + android:fillColor="?attr/pointerIconVectorFillInverse" android:pathData="M19.496 10.999A.997.997 0 0 0 18.5 10a.995.995 0 0 0-.992.98v.644a.5.5 0 0 1-.498.5c-.014 0-.026-.007-.04-.008a.493.493 0 0 1-.457-.491v-1.02l-.004-.003V10c0-.552-.446-.999-.996-.999s-.996.447-.996.999v.008h-.001l-.005 1.003-.001.154a.5.5 0 0 1-.498.497h-.003a.5.5 0 0 1-.495-.502l.001-.159.006-1.227v-.796a.997.997 0 0 0-.996-.999.993.993 0 0 0-.988.96v2.185a.496.496 0 0 1-.989.037l-.011.007v-7.17a.997.997 0 0 0-.996-.999.997.997 0 0 0-.996.999V14.28l-1.95-2.002a1.21 1.21 0 0 0-1.74 0 1.286 1.286 0 0 0 0 1.786l5.084 5.219q.056.057.117.106A5.54 5.54 0 0 0 13.962 21c3.033 0 5.541-2.467 5.541-5.51a6 6 0 0 0-.008-.293z" /> </vector>
\ No newline at end of file diff --git a/core/res/res/drawable/pointer_handwriting_vector.xml b/core/res/res/drawable/pointer_handwriting_vector.xml index 09f3e31473dd..849759291101 100644 --- a/core/res/res/drawable/pointer_handwriting_vector.xml +++ b/core/res/res/drawable/pointer_handwriting_vector.xml @@ -23,6 +23,6 @@ <path android:fillColor="#FFFFFF" android:pathData="m16.431 7.64-6.29 6.29 1.43 1.43 6.29-6.29-1.42-1.43z" /> </group> <path - android:fillColor="#000000" + android:fillColor="?attr/pointerIconVectorFill" android:pathData="M5 4c-.55 0-1 .45-1 1v14c0 .55.45 1 1 1s1-.45 1-1V5c0-.55-.45-1-1-1m14.41 3.51-1.42-1.42c-.39-.39-.9-.59-1.41-.59h-.01c-.51 0-1.02.2-1.41.59L8 13.25v4.25h4.25l7.16-7.16c.78-.78.78-2.05 0-2.83m-7.839 7.85-1.43-1.43 6.29-6.29h.01l1.42 1.43z" /> </vector>
\ No newline at end of file diff --git a/core/res/res/drawable/pointer_help_vector.xml b/core/res/res/drawable/pointer_help_vector.xml index 6b7fd9f99a26..07970fbdd67a 100644 --- a/core/res/res/drawable/pointer_help_vector.xml +++ b/core/res/res/drawable/pointer_help_vector.xml @@ -19,12 +19,12 @@ android:viewportHeight="24" android:viewportWidth="24"> <path - android:fillColor="#000000" + android:fillColor="?attr/pointerIconVectorFill" android:pathData="M16.339 11.18 6.773 4.017a1.78 1.78 0 0 0-1.072-.365c-.274 0-.551.065-.813.196a1.77 1.77 0 0 0-.994 1.611l.009 11.951c0 .903.59 1.457 1.142 1.674.2.078.433.128.678.128.434 0 .906-.155 1.298-.578l2.97-3.193a1.8 1.8 0 0 1 1.126-.563l4.335-.467c.899-.097 1.387-.741 1.544-1.312.157-.573.066-1.377-.657-1.919" /> <path android:fillColor="#FFFFFF" android:pathData="M16.94 10.38 7.37 3.22a2.77 2.77 0 0 0-2.93-.27A2.75 2.75 0 0 0 2.9 5.46l.01 11.95a2.79 2.79 0 0 0 2.82 2.8c.78 0 1.5-.32 2.03-.9l2.97-3.19a.8.8 0 0 1 .5-.25l4.34-.46a2.76 2.76 0 0 0 2.4-2.05 2.8 2.8 0 0 0-1.02-2.98zM17 13.1a1.77 1.77 0 0 1-1.55 1.31l-4.33.47a1.8 1.8 0 0 0-1.13.56l-2.97 3.2c-.4.42-.86.57-1.3.57-.24 0-.48-.05-.68-.13a1.77 1.77 0 0 1-1.14-1.67V5.46a1.81 1.81 0 0 1 1.8-1.8c.38 0 .75.11 1.07.36l9.57 7.16c.72.54.81 1.35.66 1.92zm2.64-10.83a2.5 2.5 0 0 0-1.84-.72 3 3 0 0 0-2.83 1.93l-.39.94.96.37.86.32.12.05-.02.03c-.22.4-.3.82-.3 1.33v.94a1.56 1.56 0 0 0 .4 1.47 1.54 1.54 0 0 0 2.24.01 1.55 1.55 0 0 0 .28-1.84v-.52c0-.1.02-.17.03-.25l.16-.15c.32-.25.6-.56.78-.93.18-.37.26-.76.26-1.16 0-.68-.21-1.32-.7-1.82zm-1.5 5.96a.55.55 0 0 1-.82 0 .56.56 0 0 1-.17-.4c0-.16.06-.3.17-.4a.55.55 0 0 1 .41-.18c.15 0 .28.06.4.17a.55.55 0 0 1 0 .81zm1.05-3.42c-.1.22-.28.42-.52.6-.26.22-.42.42-.47.6-.05.18-.08.37-.08.57l-.93-.06c0-.38.07-.62.19-.86.13-.24.3-.46.54-.66.17-.13.3-.28.4-.43s.14-.3.14-.46c0-.2-.08-.37-.22-.5s-.31-.17-.52-.17c-.2 0-.39.06-.56.18-.17.13-.3.31-.4.56l-.87-.33a2.03 2.03 0 0 1 1.91-1.3c.48 0 .86.14 1.13.42.28.28.41.65.41 1.12 0 .26-.05.5-.15.72z" /> <path - android:fillColor="#000000" + android:fillColor="?attr/pointerIconVectorFill" android:pathData="M17.73 7.254a.55.55 0 0 0-.407.169.55.55 0 0 0-.169.407q0 .225.169.401a.55.55 0 0 0 .808 0 .56.56 0 0 0 .175-.413.53.53 0 0 0-.175-.394.56.56 0 0 0-.401-.17m1.202-4.288q-.413-.42-1.126-.419-.651 0-1.164.357a2.1 2.1 0 0 0-.751.945l.864.326q.15-.363.407-.551a.93.93 0 0 1 .557-.188q.313 0 .526.182c.213.182.213.286.213.495q0 .226-.144.457a1.4 1.4 0 0 1-.394.432q-.35.3-.538.657c-.125.238-.187.485-.187.86l.926.06q0-.3.081-.57t.469-.595q.363-.276.519-.601t.156-.726q-.002-.701-.414-1.121" /> </vector>
\ No newline at end of file diff --git a/core/res/res/drawable/pointer_horizontal_double_arrow_vector.xml b/core/res/res/drawable/pointer_horizontal_double_arrow_vector.xml index d1aea9eacf3f..32c56b6aa098 100644 --- a/core/res/res/drawable/pointer_horizontal_double_arrow_vector.xml +++ b/core/res/res/drawable/pointer_horizontal_double_arrow_vector.xml @@ -22,6 +22,6 @@ android:fillColor="#FFFFFF" android:pathData="m19.963 10.185-3.065-1.758c-1.327-.761-2.96.14-3.072 1.633h-3.651c-.113-1.492-1.746-2.394-3.072-1.633l-3.065 1.758c-1.383.793-1.383 2.786 0 3.579l3.065 1.758c1.311.752 2.918-.12 3.065-1.581h3.666c.147 1.46 1.754 2.333 3.065 1.581l3.065-1.758c1.382-.793 1.382-2.786-.001-3.579m-.498 2.712L16.4 14.655a1.065 1.065 0 0 1-1.596-.922v-.791H9.195v.791c0 .818-.886 1.33-1.596.922l-3.065-1.758a1.063 1.063 0 0 1 0-1.845l3.065-1.758a1.065 1.065 0 0 1 1.596.922v.843h5.609v-.843c0-.818.886-1.33 1.596-.922l3.065 1.758a1.063 1.063 0 0 1 0 1.845" /> <path - android:fillColor="#000000" + android:fillColor="?attr/pointerIconVectorFill" android:pathData="M19.465 11.052 16.4 9.294a1.065 1.065 0 0 0-1.596.922v.843H9.195v-.843c0-.818-.886-1.33-1.596-.922l-3.065 1.758a1.063 1.063 0 0 0 0 1.845l3.065 1.758a1.065 1.065 0 0 0 1.596-.922v-.791h5.609v.791c0 .818.886 1.33 1.596.922l3.065-1.758a1.063 1.063 0 0 0 0-1.845" /> </vector>
\ No newline at end of file diff --git a/core/res/res/drawable/pointer_nodrop_vector.xml b/core/res/res/drawable/pointer_nodrop_vector.xml index 3a38babd12d2..6108e9681fa5 100644 --- a/core/res/res/drawable/pointer_nodrop_vector.xml +++ b/core/res/res/drawable/pointer_nodrop_vector.xml @@ -19,8 +19,8 @@ android:viewportHeight="24" android:viewportWidth="24"> <group> - <path android:fillColor="#FFFFFF" android:pathData="M17.5 1.953c-2.108 0-3.869 1.449-4.382 3.398a4.5 4.5 0 0 0-.165 1.148c0 .343.045.674.117.995-.045-.001-.09-.007-.135-.007q-.225 0-.446.018V6.484a1 1 0 0 0-2 0v1.57a5.7 5.7 0 0 0-.997.625V7.583a1 1 0 0 0-2 0v4.205l-.697-.713c-.482-.494-1.265-.494-1.747 0s-.482 1.294 0 1.787l3.847 3.936q.056.057.117.106a5.58 5.58 0 0 0 3.922 1.613c3.045 0 5.563-2.469 5.563-5.514q0-.192-.013-.38v-1.69a4.5 4.5 0 0 0 1-.366c1.51-.739 2.562-2.275 2.562-4.066A4.55 4.55 0 0 0 17.5 1.953m0 8.047C15.57 10 14 8.43 14 6.5S15.57 3 17.5 3 21 4.57 21 6.5 19.43 10 17.5 10" /> - <path android:fillColor="#FFFFFF" android:pathData="M17.5 4c-.493 0-.95.148-1.337.395l3.442 3.442C19.852 7.45 20 6.993 20 6.5 20 5.121 18.879 4 17.5 4M15 6.5C15 7.879 16.121 9 17.5 9c.525 0 1.011-.164 1.413-.441l-3.472-3.472A2.5 2.5 0 0 0 15 6.5" /> + <path android:fillColor="?attr/pointerIconVectorFillInverse" android:pathData="M17.5 1.953c-2.108 0-3.869 1.449-4.382 3.398a4.5 4.5 0 0 0-.165 1.148c0 .343.045.674.117.995-.045-.001-.09-.007-.135-.007q-.225 0-.446.018V6.484a1 1 0 0 0-2 0v1.57a5.7 5.7 0 0 0-.997.625V7.583a1 1 0 0 0-2 0v4.205l-.697-.713c-.482-.494-1.265-.494-1.747 0s-.482 1.294 0 1.787l3.847 3.936q.056.057.117.106a5.58 5.58 0 0 0 3.922 1.613c3.045 0 5.563-2.469 5.563-5.514q0-.192-.013-.38v-1.69a4.5 4.5 0 0 0 1-.366c1.51-.739 2.562-2.275 2.562-4.066A4.55 4.55 0 0 0 17.5 1.953m0 8.047C15.57 10 14 8.43 14 6.5S15.57 3 17.5 3 21 4.57 21 6.5 19.43 10 17.5 10" /> + <path android:fillColor="?attr/pointerIconVectorFillInverse" android:pathData="M17.5 4c-.493 0-.95.148-1.337.395l3.442 3.442C19.852 7.45 20 6.993 20 6.5 20 5.121 18.879 4 17.5 4M15 6.5C15 7.879 16.121 9 17.5 9c.525 0 1.011-.164 1.413-.441l-3.472-3.472A2.5 2.5 0 0 0 15 6.5" /> </group> <path android:fillColor="#000000" diff --git a/core/res/res/drawable/pointer_text_vector.xml b/core/res/res/drawable/pointer_text_vector.xml index 9e44f28f5779..a14727309674 100644 --- a/core/res/res/drawable/pointer_text_vector.xml +++ b/core/res/res/drawable/pointer_text_vector.xml @@ -19,7 +19,7 @@ android:viewportHeight="24" android:viewportWidth="24"> <path - android:fillColor="#000000" + android:fillColor="?attr/pointerIconVectorFill" android:pathData="M12 3c-.551 0-1 .448-1 1v14a1.001 1.001 0 0 0 2 0V4c0-.552-.449-1-1-1" /> <path android:fillColor="#FFFFFF" diff --git a/core/res/res/drawable/pointer_top_left_diagonal_double_arrow_vector.xml b/core/res/res/drawable/pointer_top_left_diagonal_double_arrow_vector.xml index e5d5301ce009..7f95207d9e82 100644 --- a/core/res/res/drawable/pointer_top_left_diagonal_double_arrow_vector.xml +++ b/core/res/res/drawable/pointer_top_left_diagonal_double_arrow_vector.xml @@ -23,6 +23,6 @@ android:pathData="m18.896 16.365-.924-3.41c-.398-1.467-2.169-1.985-3.305-1.035L12.08 9.333c.952-1.136.434-2.908-1.034-3.306l-3.41-.924c-1.539-.416-2.948.993-2.532 2.532l.924 3.41c.398 1.468 2.17 1.986 3.306 1.034l2.586 2.586c-.953 1.136-.435 2.91 1.033 3.307l3.41.924c1.54.417 2.949-.992 2.533-2.531m-2.27 1.566-3.41-.924a1.065 1.065 0 0 1-.476-1.781l.579-.579-3.966-3.966-.579.579a1.066 1.066 0 0 1-1.781-.476L6.07 7.373a1.063 1.063 0 0 1 1.304-1.304l3.41.924a1.065 1.065 0 0 1 .476 1.781l-.578.578 3.966 3.966.577-.577a1.066 1.066 0 0 1 1.781.477l.924 3.41a1.062 1.062 0 0 1-1.304 1.303" /> <path android:fillType="evenOdd" - android:fillColor="#000000" + android:fillColor="?attr/pointerIconVectorFill" android:pathData="M6.07 7.373a1.063 1.063 0 0 1 1.304-1.304l3.41.924a1.065 1.065 0 0 1 .476 1.781l-.578.578 3.966 3.966.577-.577a1.066 1.066 0 0 1 1.781.476l.924 3.41a1.063 1.063 0 0 1-1.304 1.304l-3.41-.924a1.065 1.065 0 0 1-.476-1.781l.579-.579-3.966-3.966-.579.579a1.066 1.066 0 0 1-1.781-.476z" /> </vector>
\ No newline at end of file diff --git a/core/res/res/drawable/pointer_top_right_diagonal_double_arrow_vector.xml b/core/res/res/drawable/pointer_top_right_diagonal_double_arrow_vector.xml index e6f7aafe2e6b..8a3371524429 100644 --- a/core/res/res/drawable/pointer_top_right_diagonal_double_arrow_vector.xml +++ b/core/res/res/drawable/pointer_top_right_diagonal_double_arrow_vector.xml @@ -22,6 +22,6 @@ android:fillColor="#FFFFFF" android:pathData="m16.365 5.104-3.41.924c-1.468.398-1.986 2.171-1.033 3.307l-2.586 2.586c-1.136-.952-2.909-.434-3.306 1.034l-.924 3.41c-.417 1.539.992 2.948 2.531 2.531l3.41-.924c1.468-.398 1.986-2.17 1.034-3.306l2.587-2.587c1.136.951 2.908.432 3.305-1.035l.924-3.41c.415-1.538-.994-2.947-2.532-2.53m1.565 2.269-.924 3.41a1.065 1.065 0 0 1-1.781.476l-.577-.577-3.966 3.966.578.578a1.066 1.066 0 0 1-.476 1.781l-3.41.924a1.063 1.063 0 0 1-1.304-1.304l.924-3.41a1.066 1.066 0 0 1 1.781-.477l.578.578 3.966-3.966-.579-.579a1.066 1.066 0 0 1 .476-1.781l3.41-.924a1.063 1.063 0 0 1 1.304 1.305" /> <path - android:fillColor="#000000" + android:fillColor="?attr/pointerIconVectorFill" android:pathData="m16.626 6.069-3.41.924a1.065 1.065 0 0 0-.476 1.781l.579.579-3.966 3.966-.579-.579a1.066 1.066 0 0 0-1.781.477l-.924 3.41a1.063 1.063 0 0 0 1.304 1.304l3.41-.924a1.065 1.065 0 0 0 .476-1.781l-.578-.578 3.966-3.966.577.577a1.066 1.066 0 0 0 1.781-.476l.924-3.41a1.062 1.062 0 0 0-1.303-1.304" /> </vector>
\ No newline at end of file diff --git a/core/res/res/drawable/pointer_vertical_double_arrow_vector.xml b/core/res/res/drawable/pointer_vertical_double_arrow_vector.xml index 6ffcfefead3d..889372c39433 100644 --- a/core/res/res/drawable/pointer_vertical_double_arrow_vector.xml +++ b/core/res/res/drawable/pointer_vertical_double_arrow_vector.xml @@ -22,6 +22,6 @@ android:fillColor="#FFFFFF" android:pathData="M13.945 13.829V10.17c1.476-.131 2.363-1.75 1.606-3.069l-1.758-3.065c-.793-1.383-2.786-1.383-3.579 0L8.455 7.102c-.757 1.319.131 2.939 1.607 3.069v3.658c-1.477.13-2.364 1.75-1.607 3.069l1.758 3.065c.793 1.383 2.786 1.383 3.579 0l1.758-3.065c.758-1.319-.129-2.938-1.605-3.069m.739 2.572-1.758 3.065a1.063 1.063 0 0 1-1.845 0l-1.758-3.065a1.065 1.065 0 0 1 .922-1.596h.818v-5.61h-.818c-.818 0-1.33-.886-.922-1.596l1.758-3.065a1.063 1.063 0 0 1 1.845 0l1.758 3.065a1.065 1.065 0 0 1-.922 1.596h-.817v5.609h.817c.817.001 1.329.886.922 1.597" /> <path - android:fillColor="#000000" + android:fillColor="?attr/pointerIconVectorFill" android:pathData="M13.761 14.805h-.817v-5.61h.817c.818 0 1.33-.886.922-1.596l-1.758-3.065a1.063 1.063 0 0 0-1.845 0L9.323 7.599c-.407.71.104 1.596.922 1.596h.818v5.609h-.818c-.818 0-1.33.886-.922 1.596l1.758 3.065a1.063 1.063 0 0 0 1.845 0l1.758-3.065a1.065 1.065 0 0 0-.923-1.595" /> </vector>
\ No newline at end of file diff --git a/core/res/res/drawable/pointer_vertical_text_vector.xml b/core/res/res/drawable/pointer_vertical_text_vector.xml index 72f40ccfc82b..9238f94fa808 100644 --- a/core/res/res/drawable/pointer_vertical_text_vector.xml +++ b/core/res/res/drawable/pointer_vertical_text_vector.xml @@ -19,7 +19,7 @@ android:viewportHeight="24" android:viewportWidth="24"> <path - android:fillColor="#000000" + android:fillColor="?attr/pointerIconVectorFill" android:pathData="M19 11H5a1 1 0 0 0 0 2h14a1 1 0 0 0 0-2" /> <path android:fillColor="#FFFFFF" diff --git a/core/res/res/drawable/pointer_zoom_in_vector.xml b/core/res/res/drawable/pointer_zoom_in_vector.xml index 89216662964b..a7f56c23d5fb 100644 --- a/core/res/res/drawable/pointer_zoom_in_vector.xml +++ b/core/res/res/drawable/pointer_zoom_in_vector.xml @@ -23,7 +23,7 @@ <path android:fillColor="#FFFFFF" android:pathData="M10.55 5a4.546 4.546 0 1 0 0 9.093 4.546 4.546 0 0 0 0-9.093m2.462 5h-2v2a.5.5 0 0 1-1 0v-2h-2a.5.5 0 0 1 0-1h2V7a.5.5 0 0 1 1 0v2h2a.5.5 0 0 1 0 1" /> </group> <group> - <path android:fillColor="#000000" android:pathData="m19.736 18.003-4.194-4.22a6.547 6.547 0 1 0-1.382 1.226l4.268 4.294a.923.923 0 0 0 1.308-1.3m-9.186-3.91A4.546 4.546 0 1 1 10.549 5a4.546 4.546 0 0 1 .001 9.093" /> - <path android:fillColor="#000000" android:pathData="M13.012 9h-2V7a.5.5 0 0 0-1 0v2h-2a.5.5 0 0 0 0 1h2v2a.5.5 0 0 0 1 0v-2h2a.5.5 0 0 0 0-1" /> + <path android:fillColor="?attr/pointerIconVectorFill" android:pathData="m19.736 18.003-4.194-4.22a6.547 6.547 0 1 0-1.382 1.226l4.268 4.294a.923.923 0 0 0 1.308-1.3m-9.186-3.91A4.546 4.546 0 1 1 10.549 5a4.546 4.546 0 0 1 .001 9.093" /> + <path android:fillColor="?attr/pointerIconVectorFill" android:pathData="M13.012 9h-2V7a.5.5 0 0 0-1 0v2h-2a.5.5 0 0 0 0 1h2v2a.5.5 0 0 0 1 0v-2h2a.5.5 0 0 0 0-1" /> </group> </vector>
\ No newline at end of file diff --git a/core/res/res/drawable/pointer_zoom_out_vector.xml b/core/res/res/drawable/pointer_zoom_out_vector.xml index 815ce0ebe9b4..e46b978df5b6 100644 --- a/core/res/res/drawable/pointer_zoom_out_vector.xml +++ b/core/res/res/drawable/pointer_zoom_out_vector.xml @@ -23,7 +23,7 @@ <path android:fillColor="#FFFFFF" android:pathData="M10.55 5a4.546 4.546 0 1 0 0 9.093 4.546 4.546 0 0 0 0-9.093m2.462 5h-5a.5.5 0 0 1 0-1h5a.5.5 0 0 1 0 1" /> </group> <group> - <path android:fillColor="#000000" android:pathData="m19.736 18.003-4.194-4.22a6.547 6.547 0 1 0-1.382 1.226l4.268 4.294a.923.923 0 0 0 1.308-1.3m-9.186-3.91A4.546 4.546 0 1 1 10.549 5a4.546 4.546 0 0 1 .001 9.093" /> - <path android:fillColor="#000000" android:pathData="M13.012 9h-5a.5.5 0 0 0 0 1h5a.5.5 0 0 0 0-1" /> + <path android:fillColor="?attr/pointerIconVectorFill" android:pathData="m19.736 18.003-4.194-4.22a6.547 6.547 0 1 0-1.382 1.226l4.268 4.294a.923.923 0 0 0 1.308-1.3m-9.186-3.91A4.546 4.546 0 1 1 10.549 5a4.546 4.546 0 0 1 .001 9.093" /> + <path android:fillColor="?attr/pointerIconVectorFill" android:pathData="M13.012 9h-5a.5.5 0 0 0 0 1h5a.5.5 0 0 0 0-1" /> </group> </vector>
\ No newline at end of file diff --git a/core/res/res/layout/floating_popup_container.xml b/core/res/res/layout/floating_popup_container.xml index 776a35d15ef0..96f0909eea10 100644 --- a/core/res/res/layout/floating_popup_container.xml +++ b/core/res/res/layout/floating_popup_container.xml @@ -24,4 +24,4 @@ android:elevation="@android:dimen/text_edit_floating_toolbar_elevation" android:focusable="true" android:focusableInTouchMode="true" - android:background="?attr/floatingToolbarPopupBackgroundDrawable"/> + android:background="@drawable/floating_popup_background"/> diff --git a/core/res/res/layout/floating_popup_menu_button.xml b/core/res/res/layout/floating_popup_menu_button.xml index e4c2a34b1af8..0b3861cad252 100644 --- a/core/res/res/layout/floating_popup_menu_button.xml +++ b/core/res/res/layout/floating_popup_menu_button.xml @@ -16,6 +16,7 @@ */ --> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:androidprv="http://schemas.android.com/apk/prv/res/android" android:layout_width="wrap_content" android:layout_height="wrap_content" android:orientation="horizontal" @@ -53,7 +54,7 @@ android:ellipsize="end" android:fontFamily="@*android:string/config_bodyFontFamily" android:textSize="@dimen/floating_toolbar_text_size" - android:textColor="?attr/floatingToolbarForegroundColor" + android:textColor="?androidprv:attr/materialColorOnSurface" android:background="@null" android:focusable="false" android:focusableInTouchMode="false" diff --git a/core/res/res/layout/floating_popup_overflow_button.xml b/core/res/res/layout/floating_popup_overflow_button.xml index 12e200088286..a51836b35057 100644 --- a/core/res/res/layout/floating_popup_overflow_button.xml +++ b/core/res/res/layout/floating_popup_overflow_button.xml @@ -16,6 +16,7 @@ */ --> <ImageButton xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:androidprv="http://schemas.android.com/apk/prv/res/android" android:id="@+id/overflow" android:layout_width="@dimen/floating_toolbar_menu_image_button_width" android:layout_height="@dimen/floating_toolbar_height" @@ -25,4 +26,4 @@ android:paddingBottom="@dimen/floating_toolbar_menu_image_button_vertical_padding" android:scaleType="centerInside" android:background="?attr/actionBarItemBackground" - android:tint="?attr/floatingToolbarForegroundColor" /> + android:tint="?androidprv:attr/materialColorOnSurface" /> diff --git a/core/res/res/layout/immersive_mode_cling.xml b/core/res/res/layout/immersive_mode_cling.xml index 9fd615dc96b7..2cde9e648276 100644 --- a/core/res/res/layout/immersive_mode_cling.xml +++ b/core/res/res/layout/immersive_mode_cling.xml @@ -14,79 +14,67 @@ limitations under the License. --> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:androidprv="http://schemas.android.com/apk/prv/res/android" + android:theme="@android:style/Theme.DeviceDefault.DayNight" android:layout_width="match_parent" android:layout_height="wrap_content" - android:background="?android:attr/colorAccent" + android:background="@android:drawable/immersive_cling_bg" android:gravity="center_vertical" - android:paddingBottom="24dp"> + android:padding="24dp"> - <FrameLayout - android:id="@+id/immersive_cling_chevron" - android:layout_width="76dp" - android:layout_height="76dp" - android:layout_marginTop="-24dp" - android:layout_centerHorizontal="true"> - - <ImageView - android:id="@+id/immersive_cling_back_bg_light" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:scaleType="center" - android:src="@drawable/immersive_cling_light_bg_circ" /> - - <ImageView - android:id="@+id/immersive_cling_back_bg" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:scaleType="center" - android:src="@drawable/immersive_cling_bg_circ" /> - - <ImageView - android:layout_width="match_parent" - android:layout_height="match_parent" - android:paddingTop="8dp" - android:scaleType="center" - android:src="@drawable/ic_expand_more_48dp" - android:tint="?android:attr/colorAccent"/> - </FrameLayout> + <!-- The top margin of this icon can be adjusted to push the content down to prevent overlapping + with the display cutout. --> + <ImageView + android:id="@+id/immersive_cling_icon" + android:layout_width="32dp" + android:layout_height="32dp" + android:layout_centerHorizontal="true" + android:scaleType="fitXY" + android:src="@drawable/ic_swipe_down" + android:tint="?android:attr/colorAccent" + android:tintMode="src_in" /> <TextView android:id="@+id/immersive_cling_title" android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_below="@id/immersive_cling_chevron" - android:paddingEnd="48dp" - android:paddingStart="48dp" - android:paddingTop="40dp" + android:layout_below="@id/immersive_cling_icon" + android:layout_marginTop="20dp" + android:gravity="center_horizontal" android:text="@string/immersive_cling_title" - android:textColor="@android:color/white" - android:textSize="24sp" /> + android:textColor="?androidprv:attr/materialColorOnSurface" + android:textSize="24sp" + android:fontFamily="google-sans" /> <TextView android:id="@+id/immersive_cling_description" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_below="@id/immersive_cling_title" - android:paddingEnd="48dp" - android:paddingStart="48dp" - android:paddingTop="12.6dp" + android:paddingTop="14dp" + android:gravity="center_horizontal" android:text="@string/immersive_cling_description" - android:textColor="@android:color/white" - android:textSize="16sp" /> + android:textColor="?androidprv:attr/materialColorOnSurfaceVariant" + android:textSize="14sp" + android:fontFamily="google-sans" /> <Button android:id="@+id/ok" - style="@style/Widget.Material.Button.Borderless" + style="@style/Widget.Material.Button.Borderless.Colored" + android:background="@drawable/immersive_cling_btn_bg" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentEnd="true" android:layout_below="@+id/immersive_cling_description" - android:layout_marginEnd="40dp" - android:layout_marginTop="18dp" - android:paddingEnd="8dp" - android:paddingStart="8dp" + android:layout_marginTop="24dp" + android:paddingStart="18dp" + android:paddingEnd="18dp" + android:minWidth="48dp" + android:minHeight="48dp" android:text="@string/immersive_cling_positive" - android:textColor="@android:color/white" - android:textSize="14sp" /> - + android:textColor="?androidprv:attr/materialColorOnPrimary" + android:textAllCaps="false" + android:textSize="14sp" + android:textFontWeight="500" + android:fontFamily="google-sans" /> </RelativeLayout> diff --git a/core/res/res/layout/subscription_item_layout.xml b/core/res/res/layout/subscription_item_layout.xml index aa835f4abf69..aa835f4abf69 100755..100644 --- a/core/res/res/layout/subscription_item_layout.xml +++ b/core/res/res/layout/subscription_item_layout.xml diff --git a/core/res/res/layout/text_edit_suggestion_container_material.xml b/core/res/res/layout/text_edit_suggestion_container_material.xml index 34e7bc8af8da..d6e1e9d61743 100644 --- a/core/res/res/layout/text_edit_suggestion_container_material.xml +++ b/core/res/res/layout/text_edit_suggestion_container_material.xml @@ -23,7 +23,7 @@ android:id="@+id/suggestionWindowContainer" android:layout_width="match_parent" android:layout_height="wrap_content" - android:background="?android:attr/floatingToolbarPopupBackgroundDrawable" + android:background="@drawable/floating_popup_background" android:elevation="@android:dimen/text_edit_floating_toolbar_elevation" android:layout_margin="@android:dimen/text_edit_floating_toolbar_margin" android:orientation="vertical" diff --git a/core/res/res/values-af/donottranslate-cldr.xml b/core/res/res/values-af/donottranslate-cldr.xml index c7f41b4c61e1..c7f41b4c61e1 100755..100644 --- a/core/res/res/values-af/donottranslate-cldr.xml +++ b/core/res/res/values-af/donottranslate-cldr.xml diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml index ca6a384bd96f..18425fe0163c 100644 --- a/core/res/res/values-af/strings.xml +++ b/core/res/res/values-af/strings.xml @@ -2407,6 +2407,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Privaat ruimte"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Kloon"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Gemeenskaplik"</string> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Privaat ruimte"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"Sensitiewe kennisgewinginhoud is versteek"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Appinhoud is weens sekuriteit van skermdeling verberg"</string> diff --git a/core/res/res/values-am/donottranslate-cldr.xml b/core/res/res/values-am/donottranslate-cldr.xml index 6afe07fb23ae..6afe07fb23ae 100755..100644 --- a/core/res/res/values-am/donottranslate-cldr.xml +++ b/core/res/res/values-am/donottranslate-cldr.xml diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml index 218b13ae3de7..965160e4b3a2 100644 --- a/core/res/res/values-am/strings.xml +++ b/core/res/res/values-am/strings.xml @@ -2407,6 +2407,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"የግል ቦታ"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"አባዛ"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"የጋራ"</string> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"የግል ቦታ"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"አደገኛ የማሳወቂያ ይዘት ተደብቋል"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"ለደኅንነት ሲባል የመተግበሪያ ይዘት ከማያ ገጽ ማጋራት ተደብቋል"</string> diff --git a/core/res/res/values-ar-rEG/donottranslate-cldr.xml b/core/res/res/values-ar-rEG/donottranslate-cldr.xml index 1be9ed8bc8b8..1be9ed8bc8b8 100755..100644 --- a/core/res/res/values-ar-rEG/donottranslate-cldr.xml +++ b/core/res/res/values-ar-rEG/donottranslate-cldr.xml diff --git a/core/res/res/values-ar/donottranslate-cldr.xml b/core/res/res/values-ar/donottranslate-cldr.xml index 1be9ed8bc8b8..1be9ed8bc8b8 100755..100644 --- a/core/res/res/values-ar/donottranslate-cldr.xml +++ b/core/res/res/values-ar/donottranslate-cldr.xml diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml index d0e28b7b243a..3e396e0ce86f 100644 --- a/core/res/res/values-ar/strings.xml +++ b/core/res/res/values-ar/strings.xml @@ -2411,6 +2411,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"المساحة الخاصة"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"نسخة طبق الأصل"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"ملف شخصي مشترك"</string> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"المساحة الخاصّة"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"تم إخفاء المحتوى الحساس في الإشعار"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"تم إخفاء محتوى التطبيق بعد تفعيل ميزة \"مشاركة الشاشة\" للحفاظ على أمانك"</string> diff --git a/core/res/res/values-as/strings.xml b/core/res/res/values-as/strings.xml index 42b0af9d4490..5f9f16a61945 100644 --- a/core/res/res/values-as/strings.xml +++ b/core/res/res/values-as/strings.xml @@ -1865,7 +1865,7 @@ <string name="mediasize_japanese_you4" msgid="5552111912684384833">"You4"</string> <string name="mediasize_japanese_l" msgid="1326765321473431817">"L"</string> <string name="mediasize_unknown_portrait" msgid="3817016220446495613">"অজ্ঞাত প\'ৰ্ট্ৰেইট"</string> - <string name="mediasize_unknown_landscape" msgid="1584741567225095325">"অজ্ঞাত লেণ্ডস্কেইপ"</string> + <string name="mediasize_unknown_landscape" msgid="1584741567225095325">"অজ্ঞাত লেণ্ডস্কে’প"</string> <string name="write_fail_reason_cancelled" msgid="2344081488493969190">"বাতিল কৰা হ’ল"</string> <string name="write_fail_reason_cannot_write" msgid="432118118378451508">"সমল লিখাত আসোঁৱাহ"</string> <string name="reason_unknown" msgid="5599739807581133337">"অজ্ঞাত"</string> @@ -2407,6 +2407,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"প্ৰাইভেট স্পে’চ"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"ক্ল’ন"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"সম্প্ৰদায়ৰ সৈতে জড়িত"</string> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"প্ৰাইভেট স্পে’চ"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"সংবেদনশীল জাননী লুকুওৱা হৈছে"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"সুৰক্ষাৰ বাবে এপৰ সমল স্ক্ৰীণ শ্বেয়াৰ কৰাৰ পৰা লুকুৱাই ৰখা হৈছে"</string> diff --git a/core/res/res/values-az/strings.xml b/core/res/res/values-az/strings.xml index 5633c06f6b92..c4327e79a45f 100644 --- a/core/res/res/values-az/strings.xml +++ b/core/res/res/values-az/strings.xml @@ -2407,6 +2407,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Məxfi sahə"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Klon"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Kommunal"</string> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Şəxsi sahə"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"Həssas bildiriş kontenti gizlədildi"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Güvənlik üçün tətbiq kontenti ekran paylaşımından gizlədildi"</string> diff --git a/core/res/res/values-b+sr+Latn/strings.xml b/core/res/res/values-b+sr+Latn/strings.xml index e38706ea866c..2b81e1cdce7c 100644 --- a/core/res/res/values-b+sr+Latn/strings.xml +++ b/core/res/res/values-b+sr+Latn/strings.xml @@ -2408,6 +2408,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Privatan prostor"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Klonirano"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Zajedničko"</string> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Privatan prostor"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"Osetljiv sadržaj obaveštenja je skriven"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Sadržaj aplikacije je skriven za deljenje sadržaja ekrana zbog bezbednosti"</string> diff --git a/core/res/res/values-be/strings.xml b/core/res/res/values-be/strings.xml index 25ae5a764360..7d1f6bee5816 100644 --- a/core/res/res/values-be/strings.xml +++ b/core/res/res/values-be/strings.xml @@ -2409,6 +2409,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Прыватная прастора"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Клон"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Супольны"</string> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Прыватная прастора"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"Канфідэнцыяльнае змесціва ў апавяшчэннях схавана"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Змесціва праграмы выключана з абагульвання экрана ў мэтах бяспекі"</string> diff --git a/core/res/res/values-bg/donottranslate-cldr.xml b/core/res/res/values-bg/donottranslate-cldr.xml index 7c5ba7cdf131..7c5ba7cdf131 100755..100644 --- a/core/res/res/values-bg/donottranslate-cldr.xml +++ b/core/res/res/values-bg/donottranslate-cldr.xml diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml index 6b388d8d6e6d..a2f03646bb66 100644 --- a/core/res/res/values-bg/strings.xml +++ b/core/res/res/values-bg/strings.xml @@ -2407,6 +2407,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Частно пространство"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Клониране"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Общи"</string> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Частно пространство"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"Деликатното съдържание в известието е скрито"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Съдържанието на приложението е скрито от функцията за споделяне на екрана от съображения за сигурност"</string> diff --git a/core/res/res/values-bn/strings.xml b/core/res/res/values-bn/strings.xml index 3f338cce08aa..e21b5c05965f 100644 --- a/core/res/res/values-bn/strings.xml +++ b/core/res/res/values-bn/strings.xml @@ -2407,6 +2407,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"প্রাইভেট স্পেস"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"ক্লোন"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"কমিউনাল"</string> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"প্রাইভেট স্পেস"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"সংবেদনশীল বিজ্ঞপ্তির কন্টেন্ট লুকানো আছে"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"নিরাপত্তার জন্য স্ক্রিন শেয়ার করা থেকে লুকানো অ্যাপের কন্টেন্ট"</string> diff --git a/core/res/res/values-bs/strings.xml b/core/res/res/values-bs/strings.xml index 0438302d762b..2f285f13f4e5 100644 --- a/core/res/res/values-bs/strings.xml +++ b/core/res/res/values-bs/strings.xml @@ -2408,6 +2408,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Privatni prostor"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Klon"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Opće"</string> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Privatni prostor"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"Sakriven je osjetljiv sadržaj obavještenja"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Sadržaj aplikacije je sakriven od dijeljenja ekrana radi sigurnosti"</string> diff --git a/core/res/res/values-ca/donottranslate-cldr.xml b/core/res/res/values-ca/donottranslate-cldr.xml index db2d1ec964a9..db2d1ec964a9 100755..100644 --- a/core/res/res/values-ca/donottranslate-cldr.xml +++ b/core/res/res/values-ca/donottranslate-cldr.xml diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml index 21f397b0e093..48ba090c65c3 100644 --- a/core/res/res/values-ca/strings.xml +++ b/core/res/res/values-ca/strings.xml @@ -2016,9 +2016,9 @@ <string name="app_streaming_blocked_message" product="tv" msgid="4003011766528814377">"En aquests moments, No s\'hi pot accedir des del teu <xliff:g id="DEVICE">%1$s</xliff:g>. Prova-ho al dispositiu Android TV."</string> <string name="app_streaming_blocked_message" product="tablet" msgid="4242053045964946062">"En aquests moments, No s\'hi pot accedir des del teu <xliff:g id="DEVICE">%1$s</xliff:g>. Prova-ho a la tauleta."</string> <string name="app_streaming_blocked_message" product="default" msgid="6159168735030739398">"No s\'hi pot accedir des del teu <xliff:g id="DEVICE">%1$s</xliff:g>. Prova-ho al telèfon."</string> - <string name="app_streaming_blocked_message_for_permission_request" product="tv" msgid="4706276040125072077">"Aquesta aplicació requereix permisos addicionals, però els permisos no es poden concedir en una sessió de reproducció en línia. Primer concedeix el permís al teu dispositiu Android TV."</string> - <string name="app_streaming_blocked_message_for_permission_request" product="tablet" msgid="1824604581465771629">"Aquesta aplicació requereix permisos addicionals, però els permisos no es poden concedir en una sessió de reproducció en línia. Primer concedeix el permís a la teva tauleta."</string> - <string name="app_streaming_blocked_message_for_permission_request" product="default" msgid="7755223160363292105">"Aquesta aplicació requereix permisos addicionals, però els permisos no es poden concedir en una sessió de reproducció en línia. Primer concedeix el permís al teu telèfon."</string> + <string name="app_streaming_blocked_message_for_permission_request" product="tv" msgid="4706276040125072077">"Aquesta aplicació requereix permisos addicionals, però els permisos no es poden concedir en una sessió d\'estríming. Primer concedeix el permís al teu dispositiu Android TV."</string> + <string name="app_streaming_blocked_message_for_permission_request" product="tablet" msgid="1824604581465771629">"Aquesta aplicació requereix permisos addicionals, però els permisos no es poden concedir en una sessió d\'estríming. Primer concedeix el permís a la teva tauleta."</string> + <string name="app_streaming_blocked_message_for_permission_request" product="default" msgid="7755223160363292105">"Aquesta aplicació requereix permisos addicionals, però els permisos no es poden concedir en una sessió d\'estríming. Primer concedeix el permís al teu telèfon."</string> <string name="app_streaming_blocked_message_for_fingerprint_dialog" product="tv" msgid="3470977315395784567">"Aquesta aplicació sol·licita seguretat addicional. Prova-ho al dispositiu Android TV."</string> <string name="app_streaming_blocked_message_for_fingerprint_dialog" product="tablet" msgid="698460091901465092">"Aquesta aplicació sol·licita seguretat addicional. Prova-ho a la tauleta."</string> <string name="app_streaming_blocked_message_for_fingerprint_dialog" product="default" msgid="8552691971910603907">"Aquesta aplicació sol·licita seguretat addicional. Prova-ho al telèfon."</string> @@ -2365,7 +2365,7 @@ <string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"No es pot accedir a la càmera del telèfon des del teu <xliff:g id="DEVICE">%1$s</xliff:g>"</string> <string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"No es pot accedir a la càmera de la tauleta des del teu <xliff:g id="DEVICE">%1$s</xliff:g>"</string> <string name="vdm_secure_window" msgid="161700398158812314">"No s\'hi pot accedir mentre s\'està reproduint en continu. Prova-ho al telèfon."</string> - <string name="vdm_pip_blocked" msgid="4036107522497281397">"No es pot veure el mode d\'imatge sobre imatge durant la reproducció en línia"</string> + <string name="vdm_pip_blocked" msgid="4036107522497281397">"No es pot veure el mode d\'imatge sobre imatge durant l\'estríming"</string> <string name="system_locale_title" msgid="711882686834677268">"Valor predeterminat del sistema"</string> <string name="default_card_name" msgid="9198284935962911468">"TARGETA <xliff:g id="CARDNUMBER">%d</xliff:g>"</string> <string name="permlab_companionProfileWatch" msgid="2457738382085872542">"Permís del perfil del rellotge perquè l\'aplicació complementària gestioni els rellotges"</string> @@ -2408,6 +2408,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Espai privat"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Clon"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Comunitari"</string> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Espai privat"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"S\'ha amagat contingut sensible de les notificacions"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Contingut de l\'aplicació amagat de la compartició de pantalla per motius de seguretat"</string> diff --git a/core/res/res/values-cs/donottranslate-cldr.xml b/core/res/res/values-cs/donottranslate-cldr.xml index 51f7e38e1e6a..51f7e38e1e6a 100755..100644 --- a/core/res/res/values-cs/donottranslate-cldr.xml +++ b/core/res/res/values-cs/donottranslate-cldr.xml diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml index 4013a034a5c3..64ccaf540411 100644 --- a/core/res/res/values-cs/strings.xml +++ b/core/res/res/values-cs/strings.xml @@ -2409,6 +2409,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Soukromý prostor"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Klon"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Komunální"</string> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Soukromý prostor"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"Obsah citlivých oznámení je skrytý"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Obsah aplikace je z bezpečnostních důvodů při sdílení obrazovky skryt"</string> diff --git a/core/res/res/values-da/donottranslate-cldr.xml b/core/res/res/values-da/donottranslate-cldr.xml index af352577905e..af352577905e 100755..100644 --- a/core/res/res/values-da/donottranslate-cldr.xml +++ b/core/res/res/values-da/donottranslate-cldr.xml diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml index 5226d562384c..a499afc01987 100644 --- a/core/res/res/values-da/strings.xml +++ b/core/res/res/values-da/strings.xml @@ -2407,6 +2407,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Privat område"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Klon"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Fælles"</string> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Privat område"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"Følsomt indhold i notifikationen er skjult"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Af sikkerhedsmæssige årsager vises appindhold ikke ved skærmdeling"</string> diff --git a/core/res/res/values-de/donottranslate-cldr.xml b/core/res/res/values-de/donottranslate-cldr.xml index d641e10e2b9b..d641e10e2b9b 100755..100644 --- a/core/res/res/values-de/donottranslate-cldr.xml +++ b/core/res/res/values-de/donottranslate-cldr.xml diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml index 949150ac18c4..464a5373bcf1 100644 --- a/core/res/res/values-de/strings.xml +++ b/core/res/res/values-de/strings.xml @@ -2407,6 +2407,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Vertrauliches Profil"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Klon"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Gemeinsam genutzt"</string> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Vertrauliches Profil"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"Vertrauliche Benachrichtigungsinhalte ausgeblendet"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"App-Inhalte werden aus Sicherheitsgründen bei der Bildschirmfreigabe ausgeblendet"</string> diff --git a/core/res/res/values-el/donottranslate-cldr.xml b/core/res/res/values-el/donottranslate-cldr.xml index c7f41b4c61e1..c7f41b4c61e1 100755..100644 --- a/core/res/res/values-el/donottranslate-cldr.xml +++ b/core/res/res/values-el/donottranslate-cldr.xml diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml index 11dac6c4e16b..98ce03c16c69 100644 --- a/core/res/res/values-el/strings.xml +++ b/core/res/res/values-el/strings.xml @@ -2407,6 +2407,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Ιδιωτικός χώρος"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Κλώνος"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Κοινόχρηστο"</string> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Ιδιωτικός χώρος"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"Έγινε απόκρυψη της ειδοποίησης ευαίσθητου περιεχομένου"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Για λόγους ασφάλειας, έγινε απόκρυψη του περιεχομένου της εφαρμογής από την κοινή χρήση οθόνης"</string> diff --git a/core/res/res/values-en-rAU/donottranslate-cldr.xml b/core/res/res/values-en-rAU/donottranslate-cldr.xml index 69c3aea0d84e..69c3aea0d84e 100755..100644 --- a/core/res/res/values-en-rAU/donottranslate-cldr.xml +++ b/core/res/res/values-en-rAU/donottranslate-cldr.xml diff --git a/core/res/res/values-en-rAU/strings.xml b/core/res/res/values-en-rAU/strings.xml index 1effe7c8f7d1..1d2fc4d5e89c 100644 --- a/core/res/res/values-en-rAU/strings.xml +++ b/core/res/res/values-en-rAU/strings.xml @@ -2407,6 +2407,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Private space"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Clone"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Communal"</string> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Private space"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"Sensitive notification content hidden"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"App content hidden from screen share for security"</string> diff --git a/core/res/res/values-en-rCA/donottranslate-cldr.xml b/core/res/res/values-en-rCA/donottranslate-cldr.xml index 57b80dfc157d..57b80dfc157d 100755..100644 --- a/core/res/res/values-en-rCA/donottranslate-cldr.xml +++ b/core/res/res/values-en-rCA/donottranslate-cldr.xml diff --git a/core/res/res/values-en-rCA/strings.xml b/core/res/res/values-en-rCA/strings.xml index 4abc5816a744..c03bb3cb53e3 100644 --- a/core/res/res/values-en-rCA/strings.xml +++ b/core/res/res/values-en-rCA/strings.xml @@ -2407,6 +2407,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Private space"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Clone"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Communal"</string> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Private space"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"Sensitive notification content hidden"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"App content hidden from screen share for security"</string> diff --git a/core/res/res/values-en-rGB/donottranslate-cldr.xml b/core/res/res/values-en-rGB/donottranslate-cldr.xml index db438f22208f..db438f22208f 100755..100644 --- a/core/res/res/values-en-rGB/donottranslate-cldr.xml +++ b/core/res/res/values-en-rGB/donottranslate-cldr.xml diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml index 425d88ce49ea..436d7aeafea4 100644 --- a/core/res/res/values-en-rGB/strings.xml +++ b/core/res/res/values-en-rGB/strings.xml @@ -2407,6 +2407,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Private space"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Clone"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Communal"</string> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Private space"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"Sensitive notification content hidden"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"App content hidden from screen share for security"</string> diff --git a/core/res/res/values-en-rIE/donottranslate-cldr.xml b/core/res/res/values-en-rIE/donottranslate-cldr.xml index db438f22208f..db438f22208f 100755..100644 --- a/core/res/res/values-en-rIE/donottranslate-cldr.xml +++ b/core/res/res/values-en-rIE/donottranslate-cldr.xml diff --git a/core/res/res/values-en-rIN/donottranslate-cldr.xml b/core/res/res/values-en-rIN/donottranslate-cldr.xml index 84157fe10a69..84157fe10a69 100755..100644 --- a/core/res/res/values-en-rIN/donottranslate-cldr.xml +++ b/core/res/res/values-en-rIN/donottranslate-cldr.xml diff --git a/core/res/res/values-en-rIN/strings.xml b/core/res/res/values-en-rIN/strings.xml index 56ff14aa994c..d34ed3f6e01e 100644 --- a/core/res/res/values-en-rIN/strings.xml +++ b/core/res/res/values-en-rIN/strings.xml @@ -2407,6 +2407,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Private space"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Clone"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Communal"</string> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Private space"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"Sensitive notification content hidden"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"App content hidden from screen share for security"</string> diff --git a/core/res/res/values-en-rNZ/donottranslate-cldr.xml b/core/res/res/values-en-rNZ/donottranslate-cldr.xml index 4e9bec6c7417..4e9bec6c7417 100755..100644 --- a/core/res/res/values-en-rNZ/donottranslate-cldr.xml +++ b/core/res/res/values-en-rNZ/donottranslate-cldr.xml diff --git a/core/res/res/values-en-rUS/donottranslate-cldr.xml b/core/res/res/values-en-rUS/donottranslate-cldr.xml index a8e2b2bc3246..a8e2b2bc3246 100755..100644 --- a/core/res/res/values-en-rUS/donottranslate-cldr.xml +++ b/core/res/res/values-en-rUS/donottranslate-cldr.xml diff --git a/core/res/res/values-en-rXC/strings.xml b/core/res/res/values-en-rXC/strings.xml index e599c0355c8b..c2c107c65b5f 100644 --- a/core/res/res/values-en-rXC/strings.xml +++ b/core/res/res/values-en-rXC/strings.xml @@ -2407,6 +2407,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Private space"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Clone"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Communal"</string> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Private space"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"Sensitive notification content hidden"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"App content hidden from screen share for security"</string> diff --git a/core/res/res/values-en-rZA/donottranslate-cldr.xml b/core/res/res/values-en-rZA/donottranslate-cldr.xml index a4a530884b11..a4a530884b11 100755..100644 --- a/core/res/res/values-en-rZA/donottranslate-cldr.xml +++ b/core/res/res/values-en-rZA/donottranslate-cldr.xml diff --git a/core/res/res/values-es-rCO/donottranslate-cldr.xml b/core/res/res/values-es-rCO/donottranslate-cldr.xml index db438f22208f..db438f22208f 100755..100644 --- a/core/res/res/values-es-rCO/donottranslate-cldr.xml +++ b/core/res/res/values-es-rCO/donottranslate-cldr.xml diff --git a/core/res/res/values-es-rCR/donottranslate-cldr.xml b/core/res/res/values-es-rCR/donottranslate-cldr.xml index db438f22208f..db438f22208f 100755..100644 --- a/core/res/res/values-es-rCR/donottranslate-cldr.xml +++ b/core/res/res/values-es-rCR/donottranslate-cldr.xml diff --git a/core/res/res/values-es-rEC/donottranslate-cldr.xml b/core/res/res/values-es-rEC/donottranslate-cldr.xml index db438f22208f..db438f22208f 100755..100644 --- a/core/res/res/values-es-rEC/donottranslate-cldr.xml +++ b/core/res/res/values-es-rEC/donottranslate-cldr.xml diff --git a/core/res/res/values-es-rGT/donottranslate-cldr.xml b/core/res/res/values-es-rGT/donottranslate-cldr.xml index db438f22208f..db438f22208f 100755..100644 --- a/core/res/res/values-es-rGT/donottranslate-cldr.xml +++ b/core/res/res/values-es-rGT/donottranslate-cldr.xml diff --git a/core/res/res/values-es-rHN/donottranslate-cldr.xml b/core/res/res/values-es-rHN/donottranslate-cldr.xml index db438f22208f..db438f22208f 100755..100644 --- a/core/res/res/values-es-rHN/donottranslate-cldr.xml +++ b/core/res/res/values-es-rHN/donottranslate-cldr.xml diff --git a/core/res/res/values-es-rMX/donottranslate-cldr.xml b/core/res/res/values-es-rMX/donottranslate-cldr.xml index db438f22208f..db438f22208f 100755..100644 --- a/core/res/res/values-es-rMX/donottranslate-cldr.xml +++ b/core/res/res/values-es-rMX/donottranslate-cldr.xml diff --git a/core/res/res/values-es-rNI/donottranslate-cldr.xml b/core/res/res/values-es-rNI/donottranslate-cldr.xml index db438f22208f..db438f22208f 100755..100644 --- a/core/res/res/values-es-rNI/donottranslate-cldr.xml +++ b/core/res/res/values-es-rNI/donottranslate-cldr.xml diff --git a/core/res/res/values-es-rPA/donottranslate-cldr.xml b/core/res/res/values-es-rPA/donottranslate-cldr.xml index db438f22208f..db438f22208f 100755..100644 --- a/core/res/res/values-es-rPA/donottranslate-cldr.xml +++ b/core/res/res/values-es-rPA/donottranslate-cldr.xml diff --git a/core/res/res/values-es-rPE/donottranslate-cldr.xml b/core/res/res/values-es-rPE/donottranslate-cldr.xml index db438f22208f..db438f22208f 100755..100644 --- a/core/res/res/values-es-rPE/donottranslate-cldr.xml +++ b/core/res/res/values-es-rPE/donottranslate-cldr.xml diff --git a/core/res/res/values-es-rSV/donottranslate-cldr.xml b/core/res/res/values-es-rSV/donottranslate-cldr.xml index db438f22208f..db438f22208f 100755..100644 --- a/core/res/res/values-es-rSV/donottranslate-cldr.xml +++ b/core/res/res/values-es-rSV/donottranslate-cldr.xml diff --git a/core/res/res/values-es-rUS/donottranslate-cldr.xml b/core/res/res/values-es-rUS/donottranslate-cldr.xml index 8adac31fc3dd..8adac31fc3dd 100755..100644 --- a/core/res/res/values-es-rUS/donottranslate-cldr.xml +++ b/core/res/res/values-es-rUS/donottranslate-cldr.xml diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml index f5859bf78846..df7deacdf10d 100644 --- a/core/res/res/values-es-rUS/strings.xml +++ b/core/res/res/values-es-rUS/strings.xml @@ -2408,6 +2408,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Espacio privado"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Clon"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Compartido"</string> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Espacio privado"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"Se ocultó contenido sensible de la notificación"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Se ocultó el contenido de la app durante el uso compartido de la pantalla por motivos de seguridad"</string> diff --git a/core/res/res/values-es/donottranslate-cldr.xml b/core/res/res/values-es/donottranslate-cldr.xml index ca16aa0abca0..ca16aa0abca0 100755..100644 --- a/core/res/res/values-es/donottranslate-cldr.xml +++ b/core/res/res/values-es/donottranslate-cldr.xml diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml index d8b1f59a721d..e22430ff072d 100644 --- a/core/res/res/values-es/strings.xml +++ b/core/res/res/values-es/strings.xml @@ -273,7 +273,7 @@ <string name="bugreport_option_full_title" msgid="7681035745950045690">"Informe completo"</string> <string name="bugreport_option_full_summary" msgid="1975130009258435885">"Utiliza esta opción para que la interferencia del sistema sea mínima cuando el dispositivo no responda o funcione demasiado lento, o bien cuando necesites todas las secciones del informe. No permite introducir más detalles ni hacer más capturas de pantalla."</string> <string name="bugreport_countdown" msgid="6418620521782120755">"{count,plural, =1{La captura de pantalla para el informe de errores se hará en # segundo.}many{La captura de pantalla para el informe de errores se hará en # segundos.}other{La captura de pantalla para el informe de errores se hará en # segundos.}}"</string> - <string name="bugreport_screenshot_success_toast" msgid="7986095104151473745">"Se ha hecho la captura de pantalla con el informe de errores"</string> + <string name="bugreport_screenshot_success_toast" msgid="7986095104151473745">"Captura de pantalla generada con el informe de errores"</string> <string name="bugreport_screenshot_failure_toast" msgid="6736320861311294294">"No se ha podido hacer la captura de pantalla con el informe de errores"</string> <string name="global_action_toggle_silent_mode" msgid="8464352592860372188">"Modo Silencio"</string> <string name="global_action_silent_mode_on_status" msgid="2371892537738632013">"El sonido está desactivado. Activar"</string> @@ -717,7 +717,7 @@ <string name="face_acquired_too_right" msgid="6245286514593540859">"Mueve el teléfono hacia la izquierda"</string> <string name="face_acquired_too_left" msgid="9201762240918405486">"Mueve el teléfono hacia la derecha"</string> <string name="face_acquired_poor_gaze" msgid="4427153558773628020">"Mira de forma más directa al dispositivo."</string> - <string name="face_acquired_not_detected" msgid="1057966913397548150">"No se puede detectar tu cara. Sujeta el teléfono a la altura de los ojos."</string> + <string name="face_acquired_not_detected" msgid="1057966913397548150">"No se detecta tu cara. Sujeta el teléfono a la altura de los ojos."</string> <string name="face_acquired_too_much_motion" msgid="8199691445085189528">"El teléfono se mueve demasiado. Mantenlo quieto."</string> <string name="face_acquired_recalibrate" msgid="8724013080976469746">"Vuelve a registrar tu cara."</string> <string name="face_acquired_too_different" msgid="4505278456634706967">"Cara no reconocida. Inténtalo de nuevo."</string> @@ -2408,6 +2408,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Espacio privado"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Clon"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Común"</string> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Espacio privado"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"Contenido sensible de la notificación oculto"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Contenido de la aplicación oculto en pantalla compartida por motivos de seguridad"</string> diff --git a/core/res/res/values-et/strings.xml b/core/res/res/values-et/strings.xml index 77e665aa87a9..979079d05762 100644 --- a/core/res/res/values-et/strings.xml +++ b/core/res/res/values-et/strings.xml @@ -2407,6 +2407,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Privaatne ruum"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Kloon"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Ühine"</string> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Privaatne ruum"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"Märguande delikaatne sisu peideti"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Rakenduse sisu on ekraani jagamises turvalisuse huvides peidetud"</string> diff --git a/core/res/res/values-eu/strings.xml b/core/res/res/values-eu/strings.xml index 9bf2a4d0380e..06855f5539ee 100644 --- a/core/res/res/values-eu/strings.xml +++ b/core/res/res/values-eu/strings.xml @@ -190,7 +190,7 @@ <string name="contentServiceTooManyDeletesNotificationDesc" msgid="4562226280528716090">"<xliff:g id="CONTENT_TYPE">%s</xliff:g> gehiegi ezabatzen saiatu zara."</string> <string name="low_memory" product="tablet" msgid="5557552311566179924">"Tabletaren memoria beteta dago. Tokia egiteko, ezabatu fitxategi batzuk."</string> <string name="low_memory" product="watch" msgid="3479447988234030194">"Erlojuaren memoria beteta dago. Tokia egiteko, ezabatu fitxategi batzuk."</string> - <string name="low_memory" product="tv" msgid="6663680413790323318">"Android TV gailuaren memoria beteta dago. Tokia egiteko, ezabatu fitxategi batzuk."</string> + <string name="low_memory" product="tv" msgid="6663680413790323318">"Android TV gailuko biltegia beteta dago. Tokia egiteko, ezabatu fitxategi batzuk."</string> <string name="low_memory" product="default" msgid="2539532364144025569">"Telefonoaren memoria beteta dago. Tokia egiteko, ezabatu fitxategi batzuk."</string> <string name="ssl_ca_cert_warning" msgid="7233573909730048571">"{count,plural, =1{Autoritate ziurtagiri-emaile bat dago instalatuta}other{Autoritate ziurtagiri-emaile bat baino gehiago daude instalatuta}}"</string> <string name="ssl_ca_cert_noti_by_unknown" msgid="4961102218216815242">"Hirugarren alderdi ezezagun baten arabera"</string> @@ -2407,6 +2407,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Eremu pribatua"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Klona"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Partekatua"</string> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Eremu pribatua"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"Jakinarazpenaren kontuzko edukia ezkutatu da"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Aplikazioko edukia ezkutatu egin da pantaila partekatzeko eginbidetik, segurtasuna bermatzeko"</string> diff --git a/core/res/res/values-fa/donottranslate-cldr.xml b/core/res/res/values-fa/donottranslate-cldr.xml index 2821cd5d90c4..2821cd5d90c4 100755..100644 --- a/core/res/res/values-fa/donottranslate-cldr.xml +++ b/core/res/res/values-fa/donottranslate-cldr.xml diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml index 308260f0019d..b0e6e6ecd0a5 100644 --- a/core/res/res/values-fa/strings.xml +++ b/core/res/res/values-fa/strings.xml @@ -2407,6 +2407,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"فضای خصوصی"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"همسانهسازی"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"همگانی"</string> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"فضای خصوصی"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"محتوای اعلان حساس پنهان شده است"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"بهدلایل امنیتی، محتوای برنامه از دید همرسانی صفحهنمایش پنهان شد"</string> diff --git a/core/res/res/values-fi-rFI/donottranslate-cldr.xml b/core/res/res/values-fi-rFI/donottranslate-cldr.xml index eab4957946d0..eab4957946d0 100755..100644 --- a/core/res/res/values-fi-rFI/donottranslate-cldr.xml +++ b/core/res/res/values-fi-rFI/donottranslate-cldr.xml diff --git a/core/res/res/values-fi/donottranslate-cldr.xml b/core/res/res/values-fi/donottranslate-cldr.xml index eab4957946d0..eab4957946d0 100755..100644 --- a/core/res/res/values-fi/donottranslate-cldr.xml +++ b/core/res/res/values-fi/donottranslate-cldr.xml diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml index db579f223781..7e2fa9c7b14b 100644 --- a/core/res/res/values-fi/strings.xml +++ b/core/res/res/values-fi/strings.xml @@ -2407,6 +2407,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Yksityinen tila"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Klooni"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Yhteinen"</string> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Yksityinen tila"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"Arkaluontoisen ilmoituksen sisältö piilotettu"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Sovelluksen sisältö piilotettu näytön jakamiselta turvallisuussyistä"</string> diff --git a/core/res/res/values-fr-rCA/strings.xml b/core/res/res/values-fr-rCA/strings.xml index 8888005be1d3..a21aefb2169e 100644 --- a/core/res/res/values-fr-rCA/strings.xml +++ b/core/res/res/values-fr-rCA/strings.xml @@ -717,7 +717,7 @@ <string name="face_acquired_too_right" msgid="6245286514593540859">"Déplacez le téléphone vers la gauche"</string> <string name="face_acquired_too_left" msgid="9201762240918405486">"Déplacez le téléphone vers la droite"</string> <string name="face_acquired_poor_gaze" msgid="4427153558773628020">"Veuillez regarder plus directement votre appareil."</string> - <string name="face_acquired_not_detected" msgid="1057966913397548150">"Visage non détecté. Tenez votre téléphone à hauteur des yeux."</string> + <string name="face_acquired_not_detected" msgid="1057966913397548150">"Visage non détecté. Tenez le téléphone au niveau des yeux."</string> <string name="face_acquired_too_much_motion" msgid="8199691445085189528">"Trop de mouvement. Tenez le téléphone immobile."</string> <string name="face_acquired_recalibrate" msgid="8724013080976469746">"Veuillez inscrire votre visage à nouveau."</string> <string name="face_acquired_too_different" msgid="4505278456634706967">"Visage non reconnu. Réessayez."</string> @@ -1434,7 +1434,7 @@ <string name="select_keyboard_layout_notification_message" msgid="8835158247369158154">"Touchez pour sélectionner la langue et la configuration du clavier"</string> <string name="fast_scroll_alphabet" msgid="8854435958703888376">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string> <string name="fast_scroll_numeric_alphabet" msgid="2529539945421557329">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string> - <string name="alert_windows_notification_channel_group_name" msgid="6063891141815714246">"Afficher par-dessus les autres applications"</string> + <string name="alert_windows_notification_channel_group_name" msgid="6063891141815714246">"Afficher par-dessus les autres applis"</string> <string name="alert_windows_notification_channel_name" msgid="3437528564303192620">"<xliff:g id="NAME">%s</xliff:g> affiche du contenu par-dessus d\'autres applications"</string> <string name="alert_windows_notification_title" msgid="6331662751095228536">"<xliff:g id="NAME">%s</xliff:g> aff. contenu par-dessus applis"</string> <string name="alert_windows_notification_message" msgid="6538171456970725333">"Si vous ne voulez pas que <xliff:g id="NAME">%s</xliff:g> utilise cette fonctionnalités, touchez l\'écran pour ouvrir les paramètres, puis désactivez-la."</string> @@ -1910,7 +1910,7 @@ <string name="confirm_battery_saver" msgid="5247976246208245754">"OK"</string> <string name="battery_saver_description_with_learn_more" msgid="5444908404021316250">"Le mode Économiseur de pile active le thème sombre et limite ou désactive l\'activité en arrière-plan, certains effets visuels, certaines fonctionnalités et certaines connexions réseau."</string> <string name="battery_saver_description" msgid="8518809702138617167">"Le mode Économiseur de pile active le thème sombre et limite ou désactive l\'activité en arrière-plan, certains effets visuels, certaines fonctionnalités et certaines connexions réseau."</string> - <string name="data_saver_description" msgid="4995164271550590517">"Pour aider à diminuer l\'utilisation des données, la fonctionnalité Économiseur de données empêche certaines applications d\'envoyer ou de recevoir des données en arrière-plan. Une application que vous utilisez actuellement peut accéder à des données, mais peut le faire moins souvent. Cela peut signifier, par exemple, que les images ne s\'affichent pas jusqu\'à ce que vous les touchiez."</string> + <string name="data_saver_description" msgid="4995164271550590517">"Pour aider à diminuer l\'utilisation des données, la fonctionnalité Économiseur de données empêche certaines applis d\'envoyer ou de recevoir des données en arrière-plan. Une appli que vous utilisez actuellement peut accéder à des données, mais peut le faire moins souvent. Cela peut signifier, par exemple, que les images ne s\'affichent pas jusqu\'à ce que vous les touchiez."</string> <string name="data_saver_enable_title" msgid="7080620065745260137">"Activer l\'économiseur de données?"</string> <string name="data_saver_enable_button" msgid="4399405762586419726">"Activer"</string> <string name="zen_mode_duration_minutes_summary" msgid="4555514757230849789">"{count,plural, =1{Pendant une minute (jusqu\'à {formattedTime})}one{Pendant # minute (jusqu\'à {formattedTime})}many{Pendant # minutes (jusqu\'à {formattedTime})}other{Pendant # minutes (jusqu\'à {formattedTime})}}"</string> @@ -2408,6 +2408,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Espace privé"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Clone"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Commun"</string> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Espace privé"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"Le contenu confidentiel de la notification est masqué"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Le contenu de l\'application est masqué du Partage d\'écran par mesure de sécurité"</string> diff --git a/core/res/res/values-fr/donottranslate-cldr.xml b/core/res/res/values-fr/donottranslate-cldr.xml index 2d6a109fd9f4..2d6a109fd9f4 100755..100644 --- a/core/res/res/values-fr/donottranslate-cldr.xml +++ b/core/res/res/values-fr/donottranslate-cldr.xml diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml index 4c2835d4f1cb..486124e310b4 100644 --- a/core/res/res/values-fr/strings.xml +++ b/core/res/res/values-fr/strings.xml @@ -1910,7 +1910,7 @@ <string name="confirm_battery_saver" msgid="5247976246208245754">"OK"</string> <string name="battery_saver_description_with_learn_more" msgid="5444908404021316250">"L\'économiseur de batterie active le thème sombre et limite ou désactive l\'activité en arrière-plan ainsi que certains effets visuels, fonctionnalités et connexions réseau."</string> <string name="battery_saver_description" msgid="8518809702138617167">"L\'économiseur de batterie active le thème sombre et limite ou désactive les activités en arrière-plan ainsi que certains effets visuels, fonctionnalités et connexions réseau."</string> - <string name="data_saver_description" msgid="4995164271550590517">"Pour réduire la consommation des données, l\'Économiseur de données empêche certaines applis d\'envoyer ou de recevoir des données en arrière-plan. Les applis que vous utiliserez pourront toujours accéder aux données, mais le feront moins fréquemment. Par exemple, les images pourront ne pas s\'afficher tant que vous n\'aurez pas appuyé dessus."</string> + <string name="data_saver_description" msgid="4995164271550590517">"Pour réduire la consommation des données, l\'Économiseur de données empêche certaines applis d\'envoyer ou de recevoir des données en arrière-plan. Les applis que vous utiliserez pourront toujours accéder aux données, mais le feront moins fréquemment. Par exemple, il est possible que les images ne s\'afficheront pas tant que vous n\'aurez pas appuyé dessus."</string> <string name="data_saver_enable_title" msgid="7080620065745260137">"Activer l\'Économiseur de données ?"</string> <string name="data_saver_enable_button" msgid="4399405762586419726">"Activer"</string> <string name="zen_mode_duration_minutes_summary" msgid="4555514757230849789">"{count,plural, =1{Pendant 1 minute (jusqu\'à {formattedTime})}one{Pendant # minute (jusqu\'à {formattedTime})}many{Pendant # minutes (jusqu\'à {formattedTime})}other{Pendant # minutes (jusqu\'à {formattedTime})}}"</string> @@ -2408,6 +2408,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Espace privé"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Clone"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Commun"</string> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Espace privé"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"Le contenu sensible de la notification a été masqué"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Le contenu de l\'appli est masqué lors du partage d\'écran pour des raisons de sécurité"</string> diff --git a/core/res/res/values-gl/strings.xml b/core/res/res/values-gl/strings.xml index cb8e8046db81..9994a3e74a50 100644 --- a/core/res/res/values-gl/strings.xml +++ b/core/res/res/values-gl/strings.xml @@ -329,7 +329,7 @@ <string name="permgroupdesc_storage" msgid="5378659041354582769">"acceder a ficheiros no teu dispositivo"</string> <string name="permgrouplab_readMediaAural" msgid="1858331312624942053">"Música e audio"</string> <string name="permgroupdesc_readMediaAural" msgid="7565467343667089595">"acceder a música e audio do dispositivo"</string> - <string name="permgrouplab_readMediaVisual" msgid="4724874717811908660">"fotos e vídeos"</string> + <string name="permgrouplab_readMediaVisual" msgid="4724874717811908660">"Fotos e vídeos"</string> <string name="permgroupdesc_readMediaVisual" msgid="4080463241903508688">"acceder a fotos e vídeos do dispositivo"</string> <string name="permgrouplab_microphone" msgid="2480597427667420076">"Micrófono"</string> <string name="permgroupdesc_microphone" msgid="1047786732792487722">"gravar audio"</string> @@ -346,7 +346,7 @@ <string name="permgrouplab_sensors" msgid="9134046949784064495">"Sensores corporais"</string> <string name="permgroupdesc_sensors" msgid="2610631290633747752">"acceder aos datos dos sensores sobre as túas constantes vitais"</string> <string name="permgrouplab_notifications" msgid="5472972361980668884">"Notificacións"</string> - <string name="permgroupdesc_notifications" msgid="4608679556801506580">"mostra notificacións"</string> + <string name="permgroupdesc_notifications" msgid="4608679556801506580">"mostrar notificacións"</string> <string name="capability_title_canRetrieveWindowContent" msgid="7554282892101587296">"Recuperar contido da ventá"</string> <string name="capability_desc_canRetrieveWindowContent" msgid="6195610527625237661">"Inspecciona o contido dunha ventá coa que estás interactuando."</string> <string name="capability_title_canRequestTouchExploration" msgid="327598364696316213">"Activar a exploración táctil"</string> @@ -2407,6 +2407,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Espazo privado"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Clonado"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Compartido"</string> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Espazo privado"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"Contido confidencial da notificación oculto"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Por motivos de seguranza, ocultouse o contido da aplicación para que no se mostre na pantalla compartida"</string> diff --git a/core/res/res/values-gu/strings.xml b/core/res/res/values-gu/strings.xml index 55032c9301af..3206242c0e47 100644 --- a/core/res/res/values-gu/strings.xml +++ b/core/res/res/values-gu/strings.xml @@ -2407,6 +2407,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"ખાનગી સ્પેસ"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"ક્લોન"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"કૉમ્યુનલ"</string> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"ખાનગી સ્પેસ"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"સંવેદનશીલ માહિતીવાળા નોટિફિકેશનનું કન્ટેન્ટ છુપાવ્યું"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"સુરક્ષા માટે સ્ક્રીન શેર કરતી વખતે ઍપનું કન્ટેન્ટ છુપાવેલું છે"</string> diff --git a/core/res/res/values-hi-rIN/donottranslate-cldr.xml b/core/res/res/values-hi-rIN/donottranslate-cldr.xml index b1dc879fb60f..b1dc879fb60f 100755..100644 --- a/core/res/res/values-hi-rIN/donottranslate-cldr.xml +++ b/core/res/res/values-hi-rIN/donottranslate-cldr.xml diff --git a/core/res/res/values-hi/donottranslate-cldr.xml b/core/res/res/values-hi/donottranslate-cldr.xml index b1dc879fb60f..b1dc879fb60f 100755..100644 --- a/core/res/res/values-hi/donottranslate-cldr.xml +++ b/core/res/res/values-hi/donottranslate-cldr.xml diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml index e47715ab25f2..f7ae13e66984 100644 --- a/core/res/res/values-hi/strings.xml +++ b/core/res/res/values-hi/strings.xml @@ -2407,6 +2407,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"प्राइवेट स्पेस"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"क्लोन"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"कम्यूनिटी"</string> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"प्राइवेट स्पेस"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"संवेदनशील जानकारी वाली सूचना का कॉन्टेंट छिपा है"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"स्क्रीन शेयर करने के दौरान सुरक्षा के लिए, ऐप्लिकेशन का कॉन्टेंट छिपाया गया"</string> diff --git a/core/res/res/values-hr-rHR/donottranslate-cldr.xml b/core/res/res/values-hr-rHR/donottranslate-cldr.xml index ca21a47ecc46..ca21a47ecc46 100755..100644 --- a/core/res/res/values-hr-rHR/donottranslate-cldr.xml +++ b/core/res/res/values-hr-rHR/donottranslate-cldr.xml diff --git a/core/res/res/values-hr/donottranslate-cldr.xml b/core/res/res/values-hr/donottranslate-cldr.xml index ca21a47ecc46..ca21a47ecc46 100755..100644 --- a/core/res/res/values-hr/donottranslate-cldr.xml +++ b/core/res/res/values-hr/donottranslate-cldr.xml diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml index 0b9b662397f2..f6f7e8b27ba3 100644 --- a/core/res/res/values-hr/strings.xml +++ b/core/res/res/values-hr/strings.xml @@ -1413,7 +1413,7 @@ <string name="test_harness_mode_notification_title" msgid="2282785860014142511">"Omogućen je način testnog okvira"</string> <string name="test_harness_mode_notification_message" msgid="3039123743127958420">"Vratite na tvorničke postavke da biste onemogućili način testnog okvira."</string> <string name="console_running_notification_title" msgid="6087888939261635904">"Serijska konzola omogućena"</string> - <string name="console_running_notification_message" msgid="7892751888125174039">"Izvedba je otežana. Provjerite početni program za pokretanje da biste onemogućili konzolu."</string> + <string name="console_running_notification_message" msgid="7892751888125174039">"Izvedba je otežana. Provjerite pokretač operativnog sustava da biste onemogućili konzolu."</string> <string name="mte_override_notification_title" msgid="4731115381962792944">"Omogućen je eksperimentalni MTE"</string> <string name="mte_override_notification_message" msgid="2441170442725738942">"To može utjecati na izvedbu i stabilnost. Ponovno pokrenite da biste onemogućili. Ako je omogućeno pomoću arm64.memtag.bootctl, prethodno postavite na \"none\"."</string> <string name="usb_contaminant_detected_title" msgid="4359048603069159678">"Tekućina ili prljavština u USB priključku"</string> @@ -2408,6 +2408,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Privatni prostor"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Klon"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Zajedničko"</string> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Privatni prostor"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"Skriven je osjetljiv sadržaj obavijesti"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Sadržaj aplikacije sakriven je od dijeljenja zaslona radi sigurnosti"</string> diff --git a/core/res/res/values-hu-rHU/donottranslate-cldr.xml b/core/res/res/values-hu-rHU/donottranslate-cldr.xml index fd2fe920651c..fd2fe920651c 100755..100644 --- a/core/res/res/values-hu-rHU/donottranslate-cldr.xml +++ b/core/res/res/values-hu-rHU/donottranslate-cldr.xml diff --git a/core/res/res/values-hu/donottranslate-cldr.xml b/core/res/res/values-hu/donottranslate-cldr.xml index 3f60be7c1287..3f60be7c1287 100755..100644 --- a/core/res/res/values-hu/donottranslate-cldr.xml +++ b/core/res/res/values-hu/donottranslate-cldr.xml diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml index de6777e8bce7..51626166db1b 100644 --- a/core/res/res/values-hu/strings.xml +++ b/core/res/res/values-hu/strings.xml @@ -2407,6 +2407,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Privát terület"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Klón"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Közös"</string> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Privát terület"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"Bizalmas értesítéstartalom elrejtve"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"A biztonság érdekében a képernyőmegosztástól elrejtett alkalmazástartalom"</string> diff --git a/core/res/res/values-hy/strings.xml b/core/res/res/values-hy/strings.xml index 58331c7a1b3b..f2b6932b1300 100644 --- a/core/res/res/values-hy/strings.xml +++ b/core/res/res/values-hy/strings.xml @@ -2407,6 +2407,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Մասնավոր տարածք"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Կլոն"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Ընդհանուր"</string> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Մասնավոր տարածք"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"Ծանուցման զգայուն բովանդակությունը թաքցված է"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Անվտանգության նկատառումներից ելնելով՝ հավելվածի բովանդակությունը թաքցվել է էկրանի ցուցադրումից"</string> diff --git a/core/res/res/values-in-rID/donottranslate-cldr.xml b/core/res/res/values-in-rID/donottranslate-cldr.xml index 823b41fc579b..823b41fc579b 100755..100644 --- a/core/res/res/values-in-rID/donottranslate-cldr.xml +++ b/core/res/res/values-in-rID/donottranslate-cldr.xml diff --git a/core/res/res/values-in/donottranslate-cldr.xml b/core/res/res/values-in/donottranslate-cldr.xml index 35a84eb48433..35a84eb48433 100755..100644 --- a/core/res/res/values-in/donottranslate-cldr.xml +++ b/core/res/res/values-in/donottranslate-cldr.xml diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml index eeaf2b6514fa..d1a20f34c6d7 100644 --- a/core/res/res/values-in/strings.xml +++ b/core/res/res/values-in/strings.xml @@ -2407,6 +2407,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Ruang privasi"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Clone"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Umum"</string> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Ruang privasi"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"Konten notifikasi sensitif disembunyikan"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Konten aplikasi disembunyikan dari berbagi layar untuk alasan keamanan"</string> diff --git a/core/res/res/values-is/strings.xml b/core/res/res/values-is/strings.xml index 7e68a06cc9f8..c8da94810664 100644 --- a/core/res/res/values-is/strings.xml +++ b/core/res/res/values-is/strings.xml @@ -2407,6 +2407,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Leynirými"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Afrit"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Sameiginlegt"</string> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Leynirými"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"Viðkvæmt tilkynningaefni falið"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Efni forrits falið í skjádeilingu af öryggisástæðum"</string> diff --git a/core/res/res/values-it/donottranslate-cldr.xml b/core/res/res/values-it/donottranslate-cldr.xml index 95ba6dcd87c4..95ba6dcd87c4 100755..100644 --- a/core/res/res/values-it/donottranslate-cldr.xml +++ b/core/res/res/values-it/donottranslate-cldr.xml diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml index 037e148cd60a..403c522e24ca 100644 --- a/core/res/res/values-it/strings.xml +++ b/core/res/res/values-it/strings.xml @@ -202,10 +202,8 @@ <string name="work_profile_deleted_description_dpm_wipe" msgid="2477244968924647232">"Il tuo profilo di lavoro non è più disponibile sul dispositivo"</string> <string name="work_profile_deleted_reason_maximum_password_failure" msgid="1080323158315663167">"Troppi tentativi di inserimento della password"</string> <string name="device_ownership_relinquished" msgid="4080886992183195724">"L\'amministratore ha abbandonato il dispositivo per uso personale"</string> - <!-- no translation found for private_space_deleted_by_admin (1484365588862066939) --> - <skip /> - <!-- no translation found for private_space_deleted_by_admin_details (7007781735201818689) --> - <skip /> + <string name="private_space_deleted_by_admin" msgid="1484365588862066939">"Spazio privato rimosso"</string> + <string name="private_space_deleted_by_admin_details" msgid="7007781735201818689">"La tua organizzazione non consente spazi privati su questo dispositivo gestito."</string> <string name="network_logging_notification_title" msgid="554983187553845004">"Il dispositivo è gestito"</string> <string name="network_logging_notification_text" msgid="1327373071132562512">"Questo dispositivo è gestito dalla tua organizzazione, che potrebbe monitorare il traffico di rete. Tocca per i dettagli."</string> <string name="location_changed_notification_title" msgid="3620158742816699316">"Le app possono accedere alla tua posizione"</string> @@ -2039,7 +2037,7 @@ <string name="usb_mtp_launch_notification_description" msgid="6942535713629852684">"Tocca per visualizzare i file"</string> <string name="pin_target" msgid="8036028973110156895">"Fissa"</string> <string name="pin_specific_target" msgid="7824671240625957415">"Blocca <xliff:g id="LABEL">%1$s</xliff:g>"</string> - <string name="unpin_target" msgid="3963318576590204447">"Sgancia"</string> + <string name="unpin_target" msgid="3963318576590204447">"Sblocca"</string> <string name="unpin_specific_target" msgid="3859828252160908146">"Sblocca <xliff:g id="LABEL">%1$s</xliff:g>"</string> <string name="app_info" msgid="6113278084877079851">"Informazioni app"</string> <string name="negative_duration" msgid="1938335096972945232">"−<xliff:g id="TIME">%1$s</xliff:g>"</string> @@ -2410,6 +2408,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Spazio privato"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Clone"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Condiviso"</string> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Spazio privato"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"Contenuti sensibili della notifica nascosti"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Contenuti dell\'app nascosti dalla condivisione schermo per sicurezza"</string> diff --git a/core/res/res/values-iw/donottranslate-cldr.xml b/core/res/res/values-iw/donottranslate-cldr.xml index a9015d0a1a9f..a9015d0a1a9f 100755..100644 --- a/core/res/res/values-iw/donottranslate-cldr.xml +++ b/core/res/res/values-iw/donottranslate-cldr.xml diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml index a1e72da42cae..21e9293a7e4f 100644 --- a/core/res/res/values-iw/strings.xml +++ b/core/res/res/values-iw/strings.xml @@ -2039,7 +2039,7 @@ <string name="pin_specific_target" msgid="7824671240625957415">"הצמדה של <xliff:g id="LABEL">%1$s</xliff:g>"</string> <string name="unpin_target" msgid="3963318576590204447">"ביטול הצמדה"</string> <string name="unpin_specific_target" msgid="3859828252160908146">"ביטול ההצמדה של <xliff:g id="LABEL">%1$s</xliff:g>"</string> - <string name="app_info" msgid="6113278084877079851">"פרטי אפליקציה"</string> + <string name="app_info" msgid="6113278084877079851">"פרטי האפליקציה"</string> <string name="negative_duration" msgid="1938335096972945232">"−<xliff:g id="TIME">%1$s</xliff:g>"</string> <string name="demo_starting_message" msgid="6577581216125805905">"תהליך ההדגמה מתחיל…"</string> <string name="demo_restarting_message" msgid="1160053183701746766">"מתבצע איפוס של המכשיר…"</string> @@ -2408,6 +2408,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"המרחב הפרטי"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"שכפול"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"שיתופי"</string> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"המרחב הפרטי"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"יש תוכן רגיש בהתראה שהוסתר"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"תוכן האפליקציה מוסתר משיתוף המסך מטעמי אבטחה"</string> diff --git a/core/res/res/values-ja/donottranslate-cldr.xml b/core/res/res/values-ja/donottranslate-cldr.xml index a3c1f647c9bd..a3c1f647c9bd 100755..100644 --- a/core/res/res/values-ja/donottranslate-cldr.xml +++ b/core/res/res/values-ja/donottranslate-cldr.xml diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml index 04e47a782e12..149f3cd02f54 100644 --- a/core/res/res/values-ja/strings.xml +++ b/core/res/res/values-ja/strings.xml @@ -716,7 +716,7 @@ <string name="face_acquired_too_right" msgid="6245286514593540859">"スマートフォンを左に動かしてください"</string> <string name="face_acquired_too_left" msgid="9201762240918405486">"スマートフォンを右に動かしてください"</string> <string name="face_acquired_poor_gaze" msgid="4427153558773628020">"もっとまっすぐデバイスに顔を向けてください。"</string> - <string name="face_acquired_not_detected" msgid="1057966913397548150">"顔を確認できません。スマートフォンを目の高さに合わせます。"</string> + <string name="face_acquired_not_detected" msgid="1057966913397548150">"顔を確認できません。目の高さに合わせてください"</string> <string name="face_acquired_too_much_motion" msgid="8199691445085189528">"あまり動かさないでください。安定させてください。"</string> <string name="face_acquired_recalibrate" msgid="8724013080976469746">"顔を登録し直してください。"</string> <string name="face_acquired_too_different" msgid="4505278456634706967">"顔を認識できません。もう一度お試しください。"</string> @@ -2407,6 +2407,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"プライベート スペース"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"複製"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"共用"</string> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"プライベート スペース"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"プライベートな通知内容は表示されません"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"セキュリティ上、画面共有ではアプリの内容は非表示となります"</string> diff --git a/core/res/res/values-ka/strings.xml b/core/res/res/values-ka/strings.xml index 4f87855446d6..ee1e89d8a40f 100644 --- a/core/res/res/values-ka/strings.xml +++ b/core/res/res/values-ka/strings.xml @@ -161,7 +161,7 @@ <string name="scNullCipherIssueEncryptedTitle" msgid="234717016411824969">"დაკავშირებულია დაშიფრულ ქსელთან <xliff:g id="NETWORK_NAME">%1$s</xliff:g>"</string> <string name="scNullCipherIssueEncryptedSummary" msgid="8577510708842150475">"<xliff:g id="NETWORK_NAME">%1$s</xliff:g> SIM-ბარათის კავშირი ახლა ბევრად უსაფრთხო გახდა"</string> <string name="scNullCipherIssueNonEncryptedTitle" msgid="3978071464929453915">"დაკავშირებულია დაუშიფრავ ქსელთან"</string> - <string name="scNullCipherIssueNonEncryptedSummaryNotification" msgid="7386936934128110388">"ზარები, შეტყობინებები და მონაცემები ამჟამად უფრო მოწყვლადია თქვენი <xliff:g id="NETWORK_NAME">%1$s</xliff:g> SIM-ბარათით სარგებლობისას"</string> + <string name="scNullCipherIssueNonEncryptedSummaryNotification" msgid="7386936934128110388">"ზარები, შეტყობინებები/მონაცემები უფრო მოწყვლადია <xliff:g id="NETWORK_NAME">%1$s</xliff:g> SIM-ბარათის გამოყენებისას"</string> <string name="scNullCipherIssueNonEncryptedSummary" msgid="5093428974513703253">"ზარები, შეტყობინებები და მონაცემები ამჟამად უფრო მოწყვლადია <xliff:g id="NETWORK_NAME">%1$s</xliff:g> SIM-ბარათით სარგებლობისას.\n\nკავშირის ხელახლა დაშიფვრის შემთხვევაში კიდევ ერთ შეტყობინებას მიიღებთ."</string> <string name="scNullCipherIssueActionSettings" msgid="5888857706424639946">"მობილური ქსელის უსაფრთოების პარამეტრები"</string> <string name="scNullCipherIssueActionLearnMore" msgid="7896642417214757769">"შეიტყვეთ მეტი"</string> @@ -2407,6 +2407,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"კერძო სივრცე"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"კლონის შექმნა"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"საერთო"</string> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"კერძო სივრცე"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"სენსიტიური შეტყობინების კონტენტი დამალულია"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"ეკრანის გაზიარებიდან აპის კონტენტი დამალულია უსაფრთხოების მიზნით"</string> diff --git a/core/res/res/values-kk/strings.xml b/core/res/res/values-kk/strings.xml index f36d9fca2a35..63457e000bce 100644 --- a/core/res/res/values-kk/strings.xml +++ b/core/res/res/values-kk/strings.xml @@ -2407,6 +2407,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Құпия кеңістік"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Клон"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Жалпы"</string> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Құпия кеңістік"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"Хабарландырудың құпия контенті жасырылған."</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Қауіпсіздік мақсатында қолданба контенті экранды көрсету кезінде жасырылды."</string> diff --git a/core/res/res/values-km/strings.xml b/core/res/res/values-km/strings.xml index 195eb133c872..7294d4b0f3fd 100644 --- a/core/res/res/values-km/strings.xml +++ b/core/res/res/values-km/strings.xml @@ -2407,6 +2407,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"លំហឯកជន"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"ក្លូន"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"ទូទៅ"</string> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"លំហឯកជន"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"បានលាក់ខ្លឹមសារជូនដំណឹងដែលមានលក្ខណៈរសើប"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"បានលាក់ខ្លឹមសារកម្មវិធីពីការបង្ហាញអេក្រង់ដើម្បីសុវត្ថិភាព"</string> diff --git a/core/res/res/values-kn/strings.xml b/core/res/res/values-kn/strings.xml index 073e44885f6c..3d7c7823f4d6 100644 --- a/core/res/res/values-kn/strings.xml +++ b/core/res/res/values-kn/strings.xml @@ -2407,6 +2407,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"ಪ್ರೈವೆಟ್ ಸ್ಪೇಸ್"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"ಕ್ಲೋನ್"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"ಸಮುದಾಯ"</string> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"ಪ್ರೈವೆಟ್ ಸ್ಪೇಸ್"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"ಸೂಕ್ಷ್ಮ ನೋಟಿಫಿಕೇಶನ್ ಕಂಟೆಂಟ್ ಅನ್ನು ಮರೆಮಾಡಲಾಗಿದೆ"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"ಭದ್ರತೆಗಾಗಿ ಸ್ಕ್ರೀನ್ ಹಂಚಿಕೊಳ್ಳುವಿಕೆಯಲ್ಲಿ ಆ್ಯಪ್ ಕಂಟೆಂಟ್ ಅನ್ನು ಮರೆಮಾಡಲಾಗಿದೆ"</string> diff --git a/core/res/res/values-ko/donottranslate-cldr.xml b/core/res/res/values-ko/donottranslate-cldr.xml index 626a480e40a2..626a480e40a2 100755..100644 --- a/core/res/res/values-ko/donottranslate-cldr.xml +++ b/core/res/res/values-ko/donottranslate-cldr.xml diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml index bbe8aef4bc70..d815fe8f82bd 100644 --- a/core/res/res/values-ko/strings.xml +++ b/core/res/res/values-ko/strings.xml @@ -2407,6 +2407,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"비공개 스페이스"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"클론"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"공동"</string> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"비공개 스페이스"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"민감한 알림 콘텐츠 숨김"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"보안을 위해 화면 공유에서 앱 콘텐츠가 숨겨집니다."</string> diff --git a/core/res/res/values-ky/strings.xml b/core/res/res/values-ky/strings.xml index 9686a141d528..3cdf3cad862a 100644 --- a/core/res/res/values-ky/strings.xml +++ b/core/res/res/values-ky/strings.xml @@ -2407,6 +2407,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Жеке мейкиндик"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Клон"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Жалпы"</string> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Жеке мейкиндик"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"Купуя билдирменин мазмуну жашырылган"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Коопсуздук үчүн колдонмодогу контент бөлүшүлгөн экрандан жашырылды"</string> diff --git a/core/res/res/values-land/dimens.xml b/core/res/res/values-land/dimens.xml index f58c4b0dbe7e..c0916bc8a1e1 100644 --- a/core/res/res/values-land/dimens.xml +++ b/core/res/res/values-land/dimens.xml @@ -69,7 +69,7 @@ <dimen name="timepicker_left_side_width">250dip</dimen> <!-- width of ImmersiveModeConfirmation (-1 for match_parent) --> - <dimen name="immersive_mode_cling_width">380dp</dimen> + <dimen name="immersive_mode_cling_width">500dp</dimen> <!-- Floating toolbar dimensions --> <dimen name="floating_toolbar_preferred_width">544dp</dimen> diff --git a/core/res/res/values-lo/strings.xml b/core/res/res/values-lo/strings.xml index b6bd4b2dec51..80e09a6b4a1e 100644 --- a/core/res/res/values-lo/strings.xml +++ b/core/res/res/values-lo/strings.xml @@ -2407,6 +2407,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"ພື້ນທີ່ສ່ວນບຸກຄົນ"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"ໂຄລນ"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"ສ່ວນກາງ"</string> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"ພື້ນທີ່ສ່ວນບຸກຄົນ"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"ເນື້ອຫາການແຈ້ງເຕືອນທີ່ລະອຽດອ່ອນເຊື່ອງຢູ່"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"ເນື້ອຫາແອັບຖືກເຊື່ອງໄວ້ຈາກການແບ່ງປັນໜ້າຈໍເພື່ອຄວາມປອດໄພ"</string> diff --git a/core/res/res/values-lt-rLT/donottranslate-cldr.xml b/core/res/res/values-lt-rLT/donottranslate-cldr.xml index ba4a326d9b46..ba4a326d9b46 100755..100644 --- a/core/res/res/values-lt-rLT/donottranslate-cldr.xml +++ b/core/res/res/values-lt-rLT/donottranslate-cldr.xml diff --git a/core/res/res/values-lt/donottranslate-cldr.xml b/core/res/res/values-lt/donottranslate-cldr.xml index cdca7b6e2eb3..cdca7b6e2eb3 100755..100644 --- a/core/res/res/values-lt/donottranslate-cldr.xml +++ b/core/res/res/values-lt/donottranslate-cldr.xml diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml index 2b4f370ffc45..b2372414614a 100644 --- a/core/res/res/values-lt/strings.xml +++ b/core/res/res/values-lt/strings.xml @@ -2409,6 +2409,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Privati erdvė"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Klonuoti"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Bendruomenės"</string> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Privati erdvė"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"Neskelbtinos informacijos pranešimo turinys paslėptas"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Programos turinys paslėptas bendrinant ekraną saugumo sumetimais"</string> diff --git a/core/res/res/values-lv-rLV/donottranslate-cldr.xml b/core/res/res/values-lv-rLV/donottranslate-cldr.xml index 3bed6cdb27c3..3bed6cdb27c3 100755..100644 --- a/core/res/res/values-lv-rLV/donottranslate-cldr.xml +++ b/core/res/res/values-lv-rLV/donottranslate-cldr.xml diff --git a/core/res/res/values-lv/donottranslate-cldr.xml b/core/res/res/values-lv/donottranslate-cldr.xml index 7ecdc318f841..7ecdc318f841 100755..100644 --- a/core/res/res/values-lv/donottranslate-cldr.xml +++ b/core/res/res/values-lv/donottranslate-cldr.xml diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml index 3669f041f877..cc902cd9ab40 100644 --- a/core/res/res/values-lv/strings.xml +++ b/core/res/res/values-lv/strings.xml @@ -2408,6 +2408,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Privātā telpa"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Klons"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Kopīgs"</string> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Privātā telpa"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"Sensitīvs paziņojuma saturs ir paslēpts"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Drošības nolūkos lietotnes saturs kopīgotajā ekrānā ir paslēpts"</string> diff --git a/core/res/res/values-mcc204-mnc04/config.xml b/core/res/res/values-mcc204-mnc04/config.xml index 4a3bf22d61db..4a3bf22d61db 100755..100644 --- a/core/res/res/values-mcc204-mnc04/config.xml +++ b/core/res/res/values-mcc204-mnc04/config.xml diff --git a/core/res/res/values-mcc310-mnc004/config.xml b/core/res/res/values-mcc310-mnc004/config.xml index a328c49cbc71..a328c49cbc71 100755..100644 --- a/core/res/res/values-mcc310-mnc004/config.xml +++ b/core/res/res/values-mcc310-mnc004/config.xml diff --git a/core/res/res/values-mcc311-mnc480/config.xml b/core/res/res/values-mcc311-mnc480/config.xml index db2f8d01f93a..db2f8d01f93a 100755..100644 --- a/core/res/res/values-mcc311-mnc480/config.xml +++ b/core/res/res/values-mcc311-mnc480/config.xml diff --git a/core/res/res/values-mk/strings.xml b/core/res/res/values-mk/strings.xml index 8902619d341d..b61b817dcf9c 100644 --- a/core/res/res/values-mk/strings.xml +++ b/core/res/res/values-mk/strings.xml @@ -2407,6 +2407,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Приватен простор"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Клониран профил"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Профил на заедницата"</string> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Приватен простор"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"Содржината на чувствителните известувања е скриена"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Од безбедносни причини, содржините на апликацијата се скриени од споделувањето екран"</string> diff --git a/core/res/res/values-ml/strings.xml b/core/res/res/values-ml/strings.xml index 005d7cb5da4f..9906470edbcf 100644 --- a/core/res/res/values-ml/strings.xml +++ b/core/res/res/values-ml/strings.xml @@ -2407,6 +2407,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"സ്വകാര്യ സ്പേസ്"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"ക്ലോൺ ചെയ്യുക"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"കമ്മ്യൂണൽ"</string> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"സ്വകാര്യ സ്പേസ്"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"സൂക്ഷ്മമായി കൈകാര്യം ചെയ്യേണ്ട അറിയിപ്പ് ഉള്ളടക്കം മറച്ചിരിക്കുന്നു"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"ആപ്പ് ഉള്ളടക്കം, അതിന്റെ സുരക്ഷയ്ക്കായി സ്ക്രീൻ പങ്കിടലിൽ നിന്ന് മറച്ചിരിക്കുന്നു"</string> diff --git a/core/res/res/values-mn/strings.xml b/core/res/res/values-mn/strings.xml index ddbeb35cdf31..22f8f15ae8da 100644 --- a/core/res/res/values-mn/strings.xml +++ b/core/res/res/values-mn/strings.xml @@ -81,10 +81,10 @@ <string name="RestrictedOnAllVoiceTitle" msgid="3982069078579103087">"Дуут үйлчилгээ эсвэл яаралтай дуудлага алга"</string> <string name="RestrictedStateContent" msgid="7693575344608618926">"Үйлчилгээг таны оператор компани түр хугацаанд унтраасан"</string> <string name="RestrictedStateContentMsimTemplate" msgid="5228235722511044687">"<xliff:g id="SIMNUMBER">%d</xliff:g> SIM-н оператор компаниас түр унтраасан"</string> - <string name="NetworkPreferenceSwitchTitle" msgid="1008329951315753038">"Мобайл сүлжээнд холбогдох боломжгүй байна"</string> + <string name="NetworkPreferenceSwitchTitle" msgid="1008329951315753038">"Хөдөлгөөнт холбооны сүлжээнд холбогдох боломжгүй байна"</string> <string name="NetworkPreferenceSwitchSummary" msgid="2086506181486324860">"Сонгосон сүлжээг өөрчлөхөөр оролдоно уу. Өөрчлөхийн тулд товшино уу."</string> <string name="EmergencyCallWarningTitle" msgid="1615688002899152860">"Яаралтай дуудлага хийх боломжгүй"</string> - <string name="EmergencyCallWarningSummary" msgid="9102799172089265268">"Яаралтай дуудлагуудад мобайл сүлжээ шаардлагатай"</string> + <string name="EmergencyCallWarningSummary" msgid="9102799172089265268">"Яаралтай дуудлагуудад хөдөлгөөнт холбооны сүлжээ шаардлагатай"</string> <string name="notification_channel_network_alert" msgid="4788053066033851841">"Сануулга"</string> <string name="notification_channel_call_forward" msgid="8230490317314272406">"Дуудлага шилжүүлэх"</string> <string name="notification_channel_emergency_callback" msgid="54074839059123159">"Яаралтай дуудлага хийх горим"</string> @@ -143,7 +143,7 @@ <string name="wfcSpnFormat_wifi_call" msgid="434016592539090004">"Wi-Fi дуудлага"</string> <string name="wifi_calling_off_summary" msgid="5626710010766902560">"Идэвхгүй"</string> <string name="wfc_mode_wifi_preferred_summary" msgid="1035175836270943089">"Wi-Fi-р залгах"</string> - <string name="wfc_mode_cellular_preferred_summary" msgid="4958965609212575619">"Мобайл сүлжээгээр дуудлага хийх"</string> + <string name="wfc_mode_cellular_preferred_summary" msgid="4958965609212575619">"Хөдөлгөөнт холбооны сүлжээгээр дуудлага хийх"</string> <string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Зөвхөн Wi-Fi"</string> <!-- no translation found for crossSimFormat_spn (9125246077491634262) --> <skip /> @@ -153,7 +153,7 @@ <string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> <xliff:g id="TIME_DELAY">{2}</xliff:g> секундын дараа"</string> <string name="cfTemplateRegistered" msgid="5619930473441550596">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: дамжуулагдаагүй"</string> <string name="cfTemplateRegisteredTime" msgid="5222794399642525045">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: дамжуулагдаагүй"</string> - <string name="scCellularNetworkSecurityTitle" msgid="7752521808690294384">"Мобайл сүлжээний аюулгүй байдал"</string> + <string name="scCellularNetworkSecurityTitle" msgid="7752521808690294384">"Хөдөлгөөнт холбооны сүлжээний аюулгүй байдал"</string> <string name="scCellularNetworkSecuritySummary" msgid="7042036754550545005">"Шифрлэлт, шифрлэгдээгүй сүлжээний мэдэгдэл"</string> <string name="scIdentifierDisclosureIssueTitle" msgid="2898888825129970328">"Төхөөрөмжийн ID-д хандсан"</string> <string name="scIdentifierDisclosureIssueSummaryNotification" msgid="3699930821270580416">"<xliff:g id="DISCLOSURE_TIME">%1$s</xliff:g>-д таныг <xliff:g id="DISCLOSURE_NETWORK">%2$s</xliff:g> SIM-ээ ашиглаж байхад ойролцоох сүлжээ таны төхөөрөмжийн цор ганц дугаарыг (IMSI эсвэл IMEI) бүртгэсэн."</string> @@ -163,7 +163,7 @@ <string name="scNullCipherIssueNonEncryptedTitle" msgid="3978071464929453915">"Шифрлэгдээгүй сүлжээнд холбогдсон"</string> <string name="scNullCipherIssueNonEncryptedSummaryNotification" msgid="7386936934128110388">"Таныг <xliff:g id="NETWORK_NAME">%1$s</xliff:g> SIM-ээ ашиглаж байхад дуудлага, мессеж, өгөгдөл одоогоор илүү эмзэг байна."</string> <string name="scNullCipherIssueNonEncryptedSummary" msgid="5093428974513703253">"Таныг <xliff:g id="NETWORK_NAME">%1$s</xliff:g> SIM-ээ ашиглаж байхад дуудлага, мессеж, өгөгдөл одоогоор илүү эмзэг байна.\n\nТаны холболтыг дахин шифрлэсэн үед та өөр мэдэгдэл авна."</string> - <string name="scNullCipherIssueActionSettings" msgid="5888857706424639946">"Мобайл сүлжээний аюулгүй байдлын тохиргоо"</string> + <string name="scNullCipherIssueActionSettings" msgid="5888857706424639946">"Хөдөлгөөнт холбооны сүлжээний аюулгүй байдлын тохиргоо"</string> <string name="scNullCipherIssueActionLearnMore" msgid="7896642417214757769">"Нэмэлт мэдээлэл авах"</string> <string name="scNullCipherIssueActionGotIt" msgid="8747796640866585787">"Ойлголоо"</string> <string name="fcComplete" msgid="1080909484660507044">"Онцлог код дуусав."</string> @@ -1338,7 +1338,7 @@ <skip /> <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g>-д интернэтийн хандалт алга"</string> <string name="wifi_no_internet_detailed" msgid="634938444133558942">"Сонголт хийхийн тулд товшино уу"</string> - <string name="mobile_no_internet" msgid="4014455157529909781">"Мобайл сүлжээнд интернэт хандалт байхгүй байна"</string> + <string name="mobile_no_internet" msgid="4014455157529909781">"Хөдөлгөөнт холбооны сүлжээнд интернэт хандалт байхгүй байна"</string> <string name="other_networks_no_internet" msgid="6698711684200067033">"Сүлжээнд интернэт хандалт байхгүй байна"</string> <string name="private_dns_broken_detailed" msgid="3709388271074611847">"Хувийн DNS серверт хандах боломжгүй байна"</string> <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> зарим үйлчилгээнд хандах боломжгүй байна"</string> @@ -1371,10 +1371,10 @@ <string name="sms_short_code_confirm_always_allow" msgid="2223014893129755950">"Байнга зөвшөөрөх"</string> <string name="sms_short_code_confirm_never_allow" msgid="2688828813521652079">"Хэзээ ч зөвшөөрөхгүй"</string> <string name="sim_removed_title" msgid="1349026474932481037">"SIM-г салгасан"</string> - <string name="sim_removed_message" msgid="8469588437451533845">"Таныг хүчинтэй SIM-р дахин эхлүүлэх хүртэл мобайл сүлжээ боломжгүй байх болно."</string> + <string name="sim_removed_message" msgid="8469588437451533845">"Таныг хүчинтэй SIM-р дахин эхлүүлэх хүртэл хөдөлгөөнт холбооны сүлжээ боломжгүй байх болно."</string> <string name="sim_done_button" msgid="6464250841528410598">"Дуусгах"</string> <string name="sim_added_title" msgid="2976783426741012468">"SIM нэмсэн"</string> - <string name="sim_added_message" msgid="6602906609509958680">"Мобайл сүлжээнд хандах бол төхөөрөмжөө дахин асаан уу."</string> + <string name="sim_added_message" msgid="6602906609509958680">"Хөдөлгөөнт холбооны сүлжээнд хандах бол төхөөрөмжөө дахин асаан уу."</string> <string name="sim_restart_button" msgid="8481803851341190038">"Дахин эхлүүлэх"</string> <string name="install_carrier_app_notification_title" msgid="5712723402213090102">"Мобайл үйлчилгээг идэвхжүүлэх"</string> <string name="install_carrier_app_notification_text" msgid="2781317581274192728">"Шинэ SIM-ээ идэвхжүүлэхийн тулд үүрэн холбооны компанийн аппыг татаж авна уу"</string> @@ -2407,6 +2407,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Хаалттай орон зай"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Клон"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Нийтийн"</string> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Хаалттай орон зай"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"Эмзэг мэдэгдлийн контентыг нуусан"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Аюулгүй байдлын улмаас аппын контентыг дэлгэц хуваалцахаас нуусан"</string> diff --git a/core/res/res/values-mr/strings.xml b/core/res/res/values-mr/strings.xml index 77a58aff3157..36d0bba7e17f 100644 --- a/core/res/res/values-mr/strings.xml +++ b/core/res/res/values-mr/strings.xml @@ -2407,6 +2407,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"खाजगी स्पेस"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"क्लोन"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"सामुदायिक"</string> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"खाजगी स्पेस"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"संवेदनशील नोटिफिकेशनचा आशय लपवलेला आहे"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"स्क्रीन शेअर करताना सुरक्षेसाठी अॅपमधील आशय लपवला आहे"</string> diff --git a/core/res/res/values-ms/strings.xml b/core/res/res/values-ms/strings.xml index 96691f60b151..c215f62e7aa1 100644 --- a/core/res/res/values-ms/strings.xml +++ b/core/res/res/values-ms/strings.xml @@ -2407,6 +2407,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Ruang persendirian"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Klon"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Umum"</string> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Ruang privasi"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"Kandungan pemberitahuan yang sensitif disembunyikan"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Kandungan apl disembunyikan daripada perkongsian skrin untuk keselamatan"</string> diff --git a/core/res/res/values-my/strings.xml b/core/res/res/values-my/strings.xml index c1b782236a65..f030ef2ad024 100644 --- a/core/res/res/values-my/strings.xml +++ b/core/res/res/values-my/strings.xml @@ -2407,6 +2407,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"သီးသန့်နေရာ"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"ပုံတူပွားရန်"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"အများသုံး"</string> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"သီးသန့်နေရာ"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"သတိထားရမည့် အကြောင်းကြားချက်ပါ အချက်အလက်ကို ဖျောက်ထားသည်"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"အက်ပ်အကြောင်းအရာသည် လုံခြုံရေးအတွက် မျက်နှာပြင် မျှဝေခြင်းမှ ဖျောက်ထားသည်"</string> diff --git a/core/res/res/values-nb/donottranslate-cldr.xml b/core/res/res/values-nb/donottranslate-cldr.xml index 17aea0ec48b3..17aea0ec48b3 100755..100644 --- a/core/res/res/values-nb/donottranslate-cldr.xml +++ b/core/res/res/values-nb/donottranslate-cldr.xml diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml index 23f675e73f78..116c586cb628 100644 --- a/core/res/res/values-nb/strings.xml +++ b/core/res/res/values-nb/strings.xml @@ -342,7 +342,7 @@ <string name="permgrouplab_calllog" msgid="7926834372073550288">"Samtalelogger"</string> <string name="permgroupdesc_calllog" msgid="2026996642917801803">"lese og skrive samtaleloggen"</string> <string name="permgrouplab_phone" msgid="570318944091926620">"Telefon"</string> - <string name="permgroupdesc_phone" msgid="270048070781478204">"ring og administrer anrop"</string> + <string name="permgroupdesc_phone" msgid="270048070781478204">"ringe og administrere anrop"</string> <string name="permgrouplab_sensors" msgid="9134046949784064495">"Kroppssensorer"</string> <string name="permgroupdesc_sensors" msgid="2610631290633747752">"få tilgang til sensordata om de vitale tegnene dine"</string> <string name="permgrouplab_notifications" msgid="5472972361980668884">"Varsler"</string> @@ -2407,6 +2407,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Privat område"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Klon"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Felles"</string> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Privat område"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"Sensitivt varselinnhold er skjult"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Av sikkerhetsgrunner er appinnholdet skjult for skjermdelingen"</string> diff --git a/core/res/res/values-ne/strings.xml b/core/res/res/values-ne/strings.xml index 99dd0e4c2c9f..a202d4c4d81c 100644 --- a/core/res/res/values-ne/strings.xml +++ b/core/res/res/values-ne/strings.xml @@ -1930,7 +1930,7 @@ <string name="zen_mode_feature_name" msgid="3785547207263754500">"अवरोध नपुर्याउँनुहोस्"</string> <string name="zen_mode_downtime_feature_name" msgid="5886005761431427128">"डाउनटाइम"</string> <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_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_trigger_description" msgid="5714956693073007111">"<xliff:g id="APP_NAME">%1$s</xliff:g> ले व्यवस्थापन गरेको"</string> @@ -2407,6 +2407,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"निजी स्पेस"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"क्लोन"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"सामुदायिक"</string> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"निजी स्पेस"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"संवेदनशील सूचनासम्बन्धी सामग्री लुकाइएको छ"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"स्क्रिन सेयर गर्दा सुरक्षाका लागि एपमा भएको सामग्री लुकाइएको छ"</string> diff --git a/core/res/res/values-nl/donottranslate-cldr.xml b/core/res/res/values-nl/donottranslate-cldr.xml index 35a84eb48433..35a84eb48433 100755..100644 --- a/core/res/res/values-nl/donottranslate-cldr.xml +++ b/core/res/res/values-nl/donottranslate-cldr.xml diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml index 0b2c507b644f..51d895987fea 100644 --- a/core/res/res/values-nl/strings.xml +++ b/core/res/res/values-nl/strings.xml @@ -2407,6 +2407,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Privégedeelte"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Kloon"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Gemeenschappelijk"</string> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Privégedeelte"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"Content van gevoelige meldingen verborgen"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"App-content verborgen voor scherm delen vanwege beveiligingsrisico\'s"</string> diff --git a/core/res/res/values-or/strings.xml b/core/res/res/values-or/strings.xml index f3cd70e83db1..90ab620fb710 100644 --- a/core/res/res/values-or/strings.xml +++ b/core/res/res/values-or/strings.xml @@ -1751,7 +1751,7 @@ <string name="color_correction_feature_name" msgid="7975133554160979214">"ରଙ୍ଗ ସଂଶୋଧନ"</string> <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"ଏକ-ହାତ ମୋଡ୍"</string> <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"ଅତ୍ୟଧିକ ଡିମ"</string> - <string name="hearing_aids_feature_name" msgid="1125892105105852542">"ହିଅରିଂ ଡିଭାଇସଗୁଡ଼ିକ"</string> + <string name="hearing_aids_feature_name" msgid="1125892105105852542">"ଶ୍ରବଣ ଡିଭାଇସଗୁଡ଼ିକ"</string> <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"ଭଲ୍ୟୁମ୍ କୀ\'ଗୁଡ଼ିକୁ ଧରି ରଖାଯାଇଛି। <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ଚାଲୁ ହୋଇଛି।"</string> <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"ଭଲ୍ୟୁମ୍ କୀ\'ଗୁଡ଼ିକୁ ଧରି ରଖାଯାଇଛି। <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ବନ୍ଦ ହୋଇଛି।"</string> <string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"ଭଲ୍ୟୁମ କୀ\'ଗୁଡ଼ିକୁ ରିଲିଜ କରନ୍ତୁ। <xliff:g id="SERVICE_NAME">%1$s</xliff:g>କୁ ଚାଲୁ କରିବା ପାଇଁ ଉଭୟ ଭଲ୍ୟୁମ କୀ\'କୁ ପୁଣି 3 ସେକେଣ୍ଡ ପାଇଁ ଦବାଇ ଧରି ରଖନ୍ତୁ।"</string> @@ -2407,6 +2407,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"ପ୍ରାଇଭେଟ ସ୍ପେସ"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"କ୍ଲୋନ"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"କମ୍ୟୁନାଲ"</string> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"ପ୍ରାଇଭେଟ ସ୍ପେସ"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"ସମ୍ୱେଦନଶୀଳ ବିଜ୍ଞପ୍ତି ବିଷୟବସ୍ତୁକୁ ଲୁଚାଯାଇଛି"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"ସୁରକ୍ଷା ପାଇଁ ସ୍କ୍ରିନ ସେୟାରରୁ ଆପ ବିଷୟବସ୍ତୁକୁ ଲୁଚାଯାଇଛି"</string> diff --git a/core/res/res/values-pa/strings.xml b/core/res/res/values-pa/strings.xml index 60d8737405da..de91ea7ec24a 100644 --- a/core/res/res/values-pa/strings.xml +++ b/core/res/res/values-pa/strings.xml @@ -2407,6 +2407,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"ਪ੍ਰਾਈਵੇਟ ਸਪੇਸ"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"ਕਲੋਨ"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"ਭਾਈਚਾਰਕ"</string> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"ਪ੍ਰਾਈਵੇਟ ਸਪੇਸ"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"ਲੁਕੀ ਹੋਈ ਸੰਵੇਦਨਸ਼ੀਲ ਸੂਚਨਾ ਸਮੱਗਰੀ"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"ਐਪ ਸਮੱਗਰੀ ਨੂੰ ਸੁਰੱਖਿਆ ਲਈ ਸਕ੍ਰੀਨ ਸਾਂਝਾਕਰਨ ਤੋਂ ਲੁਕਾਇਆ ਗਿਆ ਹੈ"</string> diff --git a/core/res/res/values-pl/donottranslate-cldr.xml b/core/res/res/values-pl/donottranslate-cldr.xml index 3f341b801485..3f341b801485 100755..100644 --- a/core/res/res/values-pl/donottranslate-cldr.xml +++ b/core/res/res/values-pl/donottranslate-cldr.xml diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml index ec5fee76d73d..d9a95b898356 100644 --- a/core/res/res/values-pl/strings.xml +++ b/core/res/res/values-pl/strings.xml @@ -2409,6 +2409,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Przestrzeń prywatna"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Klon"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Wspólny"</string> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Przestrzeń prywatna"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"Treść poufnego powiadomienia została ukryta"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Ze względów bezpieczeństwa zawartość aplikacji jest niewidoczna podczas udostępniania ekranu"</string> diff --git a/core/res/res/values-pt-rBR/strings.xml b/core/res/res/values-pt-rBR/strings.xml index 7067a89b2621..cd3a7c55bed8 100644 --- a/core/res/res/values-pt-rBR/strings.xml +++ b/core/res/res/values-pt-rBR/strings.xml @@ -1910,7 +1910,7 @@ <string name="confirm_battery_saver" msgid="5247976246208245754">"OK"</string> <string name="battery_saver_description_with_learn_more" msgid="5444908404021316250">"A Economia de bateria ativa o tema escuro e limita ou desativa atividades em segundo plano, alguns efeitos visuais, recursos específicos e algumas conexões de rede."</string> <string name="battery_saver_description" msgid="8518809702138617167">"A Economia de bateria ativa o tema escuro e limita ou desativa atividades em segundo plano, alguns efeitos visuais, recursos específicos e algumas conexões de rede."</string> - <string name="data_saver_description" msgid="4995164271550590517">"Para ajudar a reduzir o uso de dados, a Economia de dados impede que alguns apps enviem ou recebam dados em segundo plano. Um app que você está usando no momento pode acessar dados, mas com menos frequência. Isso pode fazer com que imagens não apareçam até você tocar nelas."</string> + <string name="data_saver_description" msgid="4995164271550590517">"Para ajudar a reduzir o uso de dados, a Economia de dados impede que alguns apps enviem ou recebam dados em segundo plano. Um app que você esteja usando em determinado momento pode acessar dados, mas com menos frequência. Isso pode fazer com que imagens não apareçam até você tocar nelas."</string> <string name="data_saver_enable_title" msgid="7080620065745260137">"Ativar a Economia de dados?"</string> <string name="data_saver_enable_button" msgid="4399405762586419726">"Ativar"</string> <string name="zen_mode_duration_minutes_summary" msgid="4555514757230849789">"{count,plural, =1{Por um minuto (até {formattedTime})}one{Por # minuto (até {formattedTime})}many{Por # minutos (até {formattedTime})}other{Por # minutos (até {formattedTime})}}"</string> @@ -2408,6 +2408,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Espaço privado"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Clone"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Público"</string> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Espaço privado"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"Conteúdo de notificação sensível oculto"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Conteúdo do app oculto no compartilhamento de tela por motivos de segurança"</string> diff --git a/core/res/res/values-pt-rPT/donottranslate-cldr.xml b/core/res/res/values-pt-rPT/donottranslate-cldr.xml index 6355432c03f9..6355432c03f9 100755..100644 --- a/core/res/res/values-pt-rPT/donottranslate-cldr.xml +++ b/core/res/res/values-pt-rPT/donottranslate-cldr.xml diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml index 6632f85b30fd..d8fe4fe8c2e1 100644 --- a/core/res/res/values-pt-rPT/strings.xml +++ b/core/res/res/values-pt-rPT/strings.xml @@ -733,7 +733,7 @@ <skip /> <string name="face_acquired_recalibrate_alt" msgid="5702674220280332115">"Não é possível criar o seu modelo de rosto. Tente novamente."</string> <string name="face_acquired_dark_glasses_detected_alt" msgid="4052123776406041972">"Óculos escuros detetados. O seu rosto tem de estar completamente visível."</string> - <string name="face_acquired_mouth_covering_detected_alt" msgid="1122294982850589766">"Máscara detetada. Todo o rosto tem de estar visível"</string> + <string name="face_acquired_mouth_covering_detected_alt" msgid="1122294982850589766">"Cobertura facial detetada. O seu rosto tem de estar completamente visível."</string> <string-array name="face_acquired_vendor"> </string-array> <string name="face_error_hw_not_available" msgid="5085202213036026288">"Não pode validar o rosto. Hardware não disponível."</string> @@ -1292,7 +1292,7 @@ <string name="android_upgrading_complete" msgid="409800058018374746">"A concluir o arranque."</string> <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"Premiu o botão ligar/desligar. Geralmente, esta ação desliga o ecrã.\n\nExperimente tocar levemente ao configurar a sua impressão digital."</string> <string name="fp_power_button_enrollment_title" msgid="6976841690455338563">"Para terminar, desligue o ecrã"</string> - <string name="fp_power_button_enrollment_button_text" msgid="3199783266386029200">"Desligar"</string> + <string name="fp_power_button_enrollment_button_text" msgid="3199783266386029200">"Desativar"</string> <string name="fp_power_button_bp_title" msgid="5585506104526820067">"Continuar a validar a impressão digital?"</string> <string name="fp_power_button_bp_message" msgid="2983163038168903393">"Premiu o botão ligar/desligar. Geralmente, esta ação desliga o ecrã.\n\nExperimente tocar levemente para validar a sua impressão digital."</string> <string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"Desligar ecrã"</string> @@ -1434,11 +1434,11 @@ <string name="select_keyboard_layout_notification_message" msgid="8835158247369158154">"Toque para selecionar o idioma e o esquema"</string> <string name="fast_scroll_alphabet" msgid="8854435958703888376">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string> <string name="fast_scroll_numeric_alphabet" msgid="2529539945421557329">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string> - <string name="alert_windows_notification_channel_group_name" msgid="6063891141815714246">"Sobrepor a outras apps"</string> + <string name="alert_windows_notification_channel_group_name" msgid="6063891141815714246">"Sobreposição a outras apps"</string> <string name="alert_windows_notification_channel_name" msgid="3437528564303192620">"A app <xliff:g id="NAME">%s</xliff:g> sobrepõe-se a outras aplicações"</string> <string name="alert_windows_notification_title" msgid="6331662751095228536">"O <xliff:g id="NAME">%s</xliff:g> sobrepõe-se a outras app"</string> <string name="alert_windows_notification_message" msgid="6538171456970725333">"Se não quer que a app <xliff:g id="NAME">%s</xliff:g> utilize esta funcionalidade, toque para abrir as definições e desative-a."</string> - <string name="alert_windows_notification_turn_off_action" msgid="7805857234839123780">"Desligar"</string> + <string name="alert_windows_notification_turn_off_action" msgid="7805857234839123780">"Desativar"</string> <string name="ext_media_checking_notification_title" msgid="8299199995416510094">"A verificar o <xliff:g id="NAME">%s</xliff:g>…"</string> <string name="ext_media_checking_notification_message" msgid="2231566971425375542">"A rever o conteúdo atual…"</string> <string name="ext_media_checking_notification_message" product="tv" msgid="7986154434946021415">"A analisar o armazenamento de multimédia"</string> @@ -2408,6 +2408,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Espaço privado"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Clone"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Comum"</string> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Espaço privado"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"Conteúdo das notificações sensíveis ocultado"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Conteúdo da app ocultado da partilha de ecrã por motivos de segurança"</string> diff --git a/core/res/res/values-pt/donottranslate-cldr.xml b/core/res/res/values-pt/donottranslate-cldr.xml index c97b337b8897..c97b337b8897 100755..100644 --- a/core/res/res/values-pt/donottranslate-cldr.xml +++ b/core/res/res/values-pt/donottranslate-cldr.xml diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml index 7067a89b2621..cd3a7c55bed8 100644 --- a/core/res/res/values-pt/strings.xml +++ b/core/res/res/values-pt/strings.xml @@ -1910,7 +1910,7 @@ <string name="confirm_battery_saver" msgid="5247976246208245754">"OK"</string> <string name="battery_saver_description_with_learn_more" msgid="5444908404021316250">"A Economia de bateria ativa o tema escuro e limita ou desativa atividades em segundo plano, alguns efeitos visuais, recursos específicos e algumas conexões de rede."</string> <string name="battery_saver_description" msgid="8518809702138617167">"A Economia de bateria ativa o tema escuro e limita ou desativa atividades em segundo plano, alguns efeitos visuais, recursos específicos e algumas conexões de rede."</string> - <string name="data_saver_description" msgid="4995164271550590517">"Para ajudar a reduzir o uso de dados, a Economia de dados impede que alguns apps enviem ou recebam dados em segundo plano. Um app que você está usando no momento pode acessar dados, mas com menos frequência. Isso pode fazer com que imagens não apareçam até você tocar nelas."</string> + <string name="data_saver_description" msgid="4995164271550590517">"Para ajudar a reduzir o uso de dados, a Economia de dados impede que alguns apps enviem ou recebam dados em segundo plano. Um app que você esteja usando em determinado momento pode acessar dados, mas com menos frequência. Isso pode fazer com que imagens não apareçam até você tocar nelas."</string> <string name="data_saver_enable_title" msgid="7080620065745260137">"Ativar a Economia de dados?"</string> <string name="data_saver_enable_button" msgid="4399405762586419726">"Ativar"</string> <string name="zen_mode_duration_minutes_summary" msgid="4555514757230849789">"{count,plural, =1{Por um minuto (até {formattedTime})}one{Por # minuto (até {formattedTime})}many{Por # minutos (até {formattedTime})}other{Por # minutos (até {formattedTime})}}"</string> @@ -2408,6 +2408,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Espaço privado"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Clone"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Público"</string> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Espaço privado"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"Conteúdo de notificação sensível oculto"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Conteúdo do app oculto no compartilhamento de tela por motivos de segurança"</string> diff --git a/core/res/res/values-ro-rRO/donottranslate-cldr.xml b/core/res/res/values-ro-rRO/donottranslate-cldr.xml index c874dcf26a76..c874dcf26a76 100755..100644 --- a/core/res/res/values-ro-rRO/donottranslate-cldr.xml +++ b/core/res/res/values-ro-rRO/donottranslate-cldr.xml diff --git a/core/res/res/values-ro/donottranslate-cldr.xml b/core/res/res/values-ro/donottranslate-cldr.xml index c874dcf26a76..c874dcf26a76 100755..100644 --- a/core/res/res/values-ro/donottranslate-cldr.xml +++ b/core/res/res/values-ro/donottranslate-cldr.xml diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml index bf82cd8cb5b7..d43556d47f86 100644 --- a/core/res/res/values-ro/strings.xml +++ b/core/res/res/values-ro/strings.xml @@ -2408,6 +2408,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Spațiu privat"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Clonă"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Comun"</string> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Spațiu privat"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"Conținutul sensibil din notificări a fost ascuns"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Conținutul aplicației este ascuns de permiterea accesului la ecran din motive de securitate"</string> diff --git a/core/res/res/values-ru/donottranslate-cldr.xml b/core/res/res/values-ru/donottranslate-cldr.xml index a36f13dacea4..a36f13dacea4 100755..100644 --- a/core/res/res/values-ru/donottranslate-cldr.xml +++ b/core/res/res/values-ru/donottranslate-cldr.xml diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml index c1900a86487a..16ddae82f144 100644 --- a/core/res/res/values-ru/strings.xml +++ b/core/res/res/values-ru/strings.xml @@ -2409,6 +2409,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Частное пространство"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Клонированный"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Совместный"</string> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Частное пространство"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"Конфиденциальная информация в уведомлении скрыта"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Содержимое приложения исключено из демонстрации экрана в целях безопасности."</string> diff --git a/core/res/res/values-si/strings.xml b/core/res/res/values-si/strings.xml index f216ce2da9fe..4f6e756d569e 100644 --- a/core/res/res/values-si/strings.xml +++ b/core/res/res/values-si/strings.xml @@ -2407,6 +2407,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"රහසිගත අවකාශය"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"ක්ලෝන කරන්න"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"වාර්ගික"</string> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"රහසිගත අවකාශය"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"සංවේදී දැනුම්දීම් අන්තර්ගතය සැඟවී ඇත"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"ආරක්ෂාව සඳහා යෙදුම් අන්තර්ගතය තිරය බෙදා ගැනීමෙන් සඟවා ඇත"</string> diff --git a/core/res/res/values-sk-rSK/donottranslate-cldr.xml b/core/res/res/values-sk-rSK/donottranslate-cldr.xml index 51f7e38e1e6a..51f7e38e1e6a 100755..100644 --- a/core/res/res/values-sk-rSK/donottranslate-cldr.xml +++ b/core/res/res/values-sk-rSK/donottranslate-cldr.xml diff --git a/core/res/res/values-sk/donottranslate-cldr.xml b/core/res/res/values-sk/donottranslate-cldr.xml index b30fe97965f3..b30fe97965f3 100755..100644 --- a/core/res/res/values-sk/donottranslate-cldr.xml +++ b/core/res/res/values-sk/donottranslate-cldr.xml diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml index 9c2a87537a4b..aeea7b688dbb 100644 --- a/core/res/res/values-sk/strings.xml +++ b/core/res/res/values-sk/strings.xml @@ -2040,7 +2040,7 @@ <string name="pin_specific_target" msgid="7824671240625957415">"Pripnúť <xliff:g id="LABEL">%1$s</xliff:g>"</string> <string name="unpin_target" msgid="3963318576590204447">"Uvoľniť"</string> <string name="unpin_specific_target" msgid="3859828252160908146">"Odopnúť <xliff:g id="LABEL">%1$s</xliff:g>"</string> - <string name="app_info" msgid="6113278084877079851">"Info o aplikácii"</string> + <string name="app_info" msgid="6113278084877079851">"Informácie o aplikácii"</string> <string name="negative_duration" msgid="1938335096972945232">"-<xliff:g id="TIME">%1$s</xliff:g>"</string> <string name="demo_starting_message" msgid="6577581216125805905">"Spúšťa sa ukážka…"</string> <string name="demo_restarting_message" msgid="1160053183701746766">"Resetuje sa zariadenie…"</string> @@ -2409,6 +2409,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Súkromný priestor"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Klon"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Spoločný"</string> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Súkromný priestor"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"Obsah citlivého upozornenia je skrytý"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Obsah aplikácie bol na účely zabezpečenia skrytý v zdieľaní obrazovky"</string> diff --git a/core/res/res/values-sl-rSI/donottranslate-cldr.xml b/core/res/res/values-sl-rSI/donottranslate-cldr.xml index 798f4c0f1863..798f4c0f1863 100755..100644 --- a/core/res/res/values-sl-rSI/donottranslate-cldr.xml +++ b/core/res/res/values-sl-rSI/donottranslate-cldr.xml diff --git a/core/res/res/values-sl/donottranslate-cldr.xml b/core/res/res/values-sl/donottranslate-cldr.xml index 92cd963bb18f..92cd963bb18f 100755..100644 --- a/core/res/res/values-sl/donottranslate-cldr.xml +++ b/core/res/res/values-sl/donottranslate-cldr.xml diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml index b3530e716b2f..d3ce57cdb28b 100644 --- a/core/res/res/values-sl/strings.xml +++ b/core/res/res/values-sl/strings.xml @@ -2409,6 +2409,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Zasebni prostor"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Klon"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Skupno"</string> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Zasebni prostor"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"Občutljiva vsebina obvestila je bila skrita"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Pri deljenju zaslona je vsebina aplikacije skrita zaradi varnosti"</string> diff --git a/core/res/res/values-sq/strings.xml b/core/res/res/values-sq/strings.xml index 5f9a6425e208..de0210013732 100644 --- a/core/res/res/values-sq/strings.xml +++ b/core/res/res/values-sq/strings.xml @@ -201,10 +201,8 @@ <string name="work_profile_deleted_description_dpm_wipe" msgid="2477244968924647232">"Profili yt i punës nuk është më i disponueshëm në këtë pajisje"</string> <string name="work_profile_deleted_reason_maximum_password_failure" msgid="1080323158315663167">"Shumë përpjekje për fjalëkalimin"</string> <string name="device_ownership_relinquished" msgid="4080886992183195724">"Administratori e refuzoi pajisjen për përdorim personal"</string> - <!-- no translation found for private_space_deleted_by_admin (1484365588862066939) --> - <skip /> - <!-- no translation found for private_space_deleted_by_admin_details (7007781735201818689) --> - <skip /> + <string name="private_space_deleted_by_admin" msgid="1484365588862066939">"Hapësira private u hoq"</string> + <string name="private_space_deleted_by_admin_details" msgid="7007781735201818689">"Organizata jote nuk i lejon hapësirat private në këtë pajisje të menaxhuar."</string> <string name="network_logging_notification_title" msgid="554983187553845004">"Pajisja është e menaxhuar"</string> <string name="network_logging_notification_text" msgid="1327373071132562512">"Organizata jote e menaxhon këtë pajisje dhe mund të monitorojë trafikun e rrjetit. Trokit për detaje."</string> <string name="location_changed_notification_title" msgid="3620158742816699316">"Aplikacionet mund të kenë qasje te vendndodhja jote"</string> @@ -2409,6 +2407,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Hapësira private"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Klon"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"I përbashkët"</string> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Hapësira private"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"Përmbajtjet delikate të njoftimeve janë fshehur"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Përmbajtja e aplikacionit është fshehur nga ndarja e ekranit për arsye sigurie"</string> diff --git a/core/res/res/values-sr-rRS/donottranslate-cldr.xml b/core/res/res/values-sr-rRS/donottranslate-cldr.xml index a0f4bc2497aa..a0f4bc2497aa 100755..100644 --- a/core/res/res/values-sr-rRS/donottranslate-cldr.xml +++ b/core/res/res/values-sr-rRS/donottranslate-cldr.xml diff --git a/core/res/res/values-sr/donottranslate-cldr.xml b/core/res/res/values-sr/donottranslate-cldr.xml index a0f4bc2497aa..a0f4bc2497aa 100755..100644 --- a/core/res/res/values-sr/donottranslate-cldr.xml +++ b/core/res/res/values-sr/donottranslate-cldr.xml diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml index 301fe244bbf1..f200ac16c798 100644 --- a/core/res/res/values-sr/strings.xml +++ b/core/res/res/values-sr/strings.xml @@ -2408,6 +2408,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Приватан простор"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Клонирано"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Заједничко"</string> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Приватан простор"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"Осетљив садржај обавештења је скривен"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Садржај апликације је скривен за дељење садржаја екрана због безбедности"</string> diff --git a/core/res/res/values-sv/donottranslate-cldr.xml b/core/res/res/values-sv/donottranslate-cldr.xml index 35a84eb48433..35a84eb48433 100755..100644 --- a/core/res/res/values-sv/donottranslate-cldr.xml +++ b/core/res/res/values-sv/donottranslate-cldr.xml diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml index b3c2063a6df7..589414da6291 100644 --- a/core/res/res/values-sv/strings.xml +++ b/core/res/res/values-sv/strings.xml @@ -2038,7 +2038,7 @@ <string name="pin_specific_target" msgid="7824671240625957415">"Fäst <xliff:g id="LABEL">%1$s</xliff:g>"</string> <string name="unpin_target" msgid="3963318576590204447">"Lossa"</string> <string name="unpin_specific_target" msgid="3859828252160908146">"Lossa <xliff:g id="LABEL">%1$s</xliff:g>"</string> - <string name="app_info" msgid="6113278084877079851">"Info om appen"</string> + <string name="app_info" msgid="6113278084877079851">"Appinformation"</string> <string name="negative_duration" msgid="1938335096972945232">"-<xliff:g id="TIME">%1$s</xliff:g>"</string> <string name="demo_starting_message" msgid="6577581216125805905">"Demo startas …"</string> <string name="demo_restarting_message" msgid="1160053183701746766">"Enheten återställs …"</string> @@ -2407,6 +2407,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Privat område"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Klona"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Allmän"</string> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Privat område"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"Känsligt aviseringsinnehåll dolt"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Av säkerhetsskäl döljs appinnehållet vid skärmdelning"</string> diff --git a/core/res/res/values-sw/donottranslate-cldr.xml b/core/res/res/values-sw/donottranslate-cldr.xml index 7313c71b4fca..7313c71b4fca 100755..100644 --- a/core/res/res/values-sw/donottranslate-cldr.xml +++ b/core/res/res/values-sw/donottranslate-cldr.xml diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml index f15796e5241b..4b50cb7366ef 100644 --- a/core/res/res/values-sw/strings.xml +++ b/core/res/res/values-sw/strings.xml @@ -2407,6 +2407,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Sehemu ya faragha"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Nakala"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Wasifu wa pamoja"</string> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Sehemu ya faragha"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"Maudhui nyeti kwenye arifa yamefichwa"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Maudhui ya programu yamefichwa ili yasionekane kwenye skrini ya pamoja kwa sababu za kiusalama"</string> diff --git a/core/res/res/values-sw600dp/dimens.xml b/core/res/res/values-sw600dp/dimens.xml index 4c70ea32bb5b..4aed94c3d8b0 100644 --- a/core/res/res/values-sw600dp/dimens.xml +++ b/core/res/res/values-sw600dp/dimens.xml @@ -112,7 +112,7 @@ <dimen name="keyguard_muliuser_selector_margin">12dp</dimen> <!-- width of ImmersiveModeConfirmation (-1 for match_parent) --> - <dimen name="immersive_mode_cling_width">380dp</dimen> + <dimen name="immersive_mode_cling_width">600dp</dimen> <dimen name="floating_toolbar_preferred_width">544dp</dimen> diff --git a/core/res/res/values-ta/strings.xml b/core/res/res/values-ta/strings.xml index f615965b721b..6ad8f59e0903 100644 --- a/core/res/res/values-ta/strings.xml +++ b/core/res/res/values-ta/strings.xml @@ -2407,6 +2407,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"ரகசிய இடம்"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"குளோன்"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"பொது"</string> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"ரகசிய இடம்"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"உணர்வுபூர்வமான அறிவிப்பு உள்ளடக்கம் மறைக்கப்பட்டது"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"பாதுகாப்பிற்காக, திரைப் பகிர்வில் இருந்து ஆப்ஸ் உள்ளடக்கம் மறைக்கப்பட்டுள்ளது"</string> @@ -2416,10 +2417,8 @@ <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"இது செயல்படும் விதம்"</string> <string name="unarchival_session_app_label" msgid="6811856981546348205">"நிலுவையிலுள்ளது..."</string> <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"கைரேகை அன்லாக் அம்சத்தை மீண்டும் அமையுங்கள்"</string> - <!-- no translation found for fingerprint_dangling_notification_msg_1 (8517140433796229725) --> - <skip /> - <!-- no translation found for fingerprint_dangling_notification_msg_2 (7578829498452127613) --> - <skip /> + <string name="fingerprint_dangling_notification_msg_1" msgid="8517140433796229725">"<xliff:g id="FINGERPRINT">%s</xliff:g> சரியாகச் செயல்படவில்லை என்பதால் அது நீக்கப்பட்டது"</string> + <string name="fingerprint_dangling_notification_msg_2" msgid="7578829498452127613">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> மற்றும் <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> சரியாகச் செயல்படவில்லை என்பதால் அவை நீக்கப்பட்டன"</string> <string name="fingerprint_dangling_notification_msg_all_deleted_1" msgid="2927018569542316055">"<xliff:g id="FINGERPRINT">%s</xliff:g> சரியாகச் செயல்படவில்லை என்பதால் அது நீக்கபட்டது. கைரேகை மூலம் உங்கள் மொபைலை அன்லாக் செய்ய அதை மீண்டும் அமையுங்கள்."</string> <string name="fingerprint_dangling_notification_msg_all_deleted_2" msgid="6897989352716156176">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> மற்றும் <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> சரியாகச் செயல்படவில்லை என்பதால் அவை நீக்கப்பட்டன. கைரேகை மூலம் உங்கள் மொபைலை அன்லாக் செய்ய அவற்றை மீண்டும் அமையுங்கள்."</string> <string name="face_dangling_notification_title" msgid="947852541060975473">"\'முகம் காட்டித் திறத்தல்\' அம்சத்தை மீண்டும் அமையுங்கள்"</string> diff --git a/core/res/res/values-te/strings.xml b/core/res/res/values-te/strings.xml index a84f6eb13d7e..91f9ef200a5c 100644 --- a/core/res/res/values-te/strings.xml +++ b/core/res/res/values-te/strings.xml @@ -1301,7 +1301,7 @@ <string name="heavy_weight_switcher_title" msgid="3861984210040100886">"గేమ్ను ఎంచుకోండి"</string> <string name="heavy_weight_switcher_text" msgid="6814316627367160126">"మెరుగైన పనితీరు పొందడానికి, ఈ గేమ్లలో ఒకసారికి ఒక్కటి మాత్రమే తెరవగలరు."</string> <string name="old_app_action" msgid="725331621042848590">"<xliff:g id="OLD_APP">%1$s</xliff:g>కి తిరిగి వెళ్లు"</string> - <string name="new_app_action" msgid="547772182913269801">"<xliff:g id="NEW_APP">%1$s</xliff:g>ని తెరువు"</string> + <string name="new_app_action" msgid="547772182913269801">"<xliff:g id="NEW_APP">%1$s</xliff:g>ను తెరవండి"</string> <string name="new_app_description" msgid="1958903080400806644">"<xliff:g id="OLD_APP">%1$s</xliff:g> సేవ్ చేయకుండానే మూసివేయబడుతుంది"</string> <string name="dump_heap_notification" msgid="5316644945404825032">"<xliff:g id="PROC">%1$s</xliff:g> మెమరీ పరిమితిని మించిపోయింది"</string> <string name="dump_heap_ready_notification" msgid="2302452262927390268">"<xliff:g id="PROC">%1$s</xliff:g> హీప్ డంప్ సిద్ధంగా ఉంది"</string> @@ -1909,7 +1909,7 @@ <string name="confirm_battery_saver" msgid="5247976246208245754">"సరే"</string> <string name="battery_saver_description_with_learn_more" msgid="5444908404021316250">"బ్యాటరీ సేవర్ ముదురు రంగు రూపాన్ని ఆన్ చేసి, బ్యాక్గ్రౌండ్ యాక్టివిటీ, కొన్ని విజువల్ ఎఫెక్ట్లు, నిర్దిష్ట ఫీచర్లు, ఇంకా కొన్ని నెట్వర్క్ కనెక్షన్లను పరిమితం చేస్తుంది లేదా ఆఫ్ చేస్తుంది."</string> <string name="battery_saver_description" msgid="8518809702138617167">"బ్యాటరీ సేవర్ ముదురు రంగు రూపాన్ని ఆన్ చేసి, బ్యాక్గ్రౌండ్ యాక్టివిటీ, కొన్ని విజువల్ ఎఫెక్ట్లు, నిర్దిష్ట ఫీచర్లు, ఇంకా కొన్ని నెట్వర్క్ కనెక్షన్లను పరిమితం చేస్తుంది లేదా ఆఫ్ చేస్తుంది."</string> - <string name="data_saver_description" msgid="4995164271550590517">"డేటా వినియోగాన్ని తగ్గించడంలో డేటా సేవర్ సహాయకరంగా ఉంటుంది. బ్యాక్గ్రౌండ్లో కొన్ని యాప్లు డేటాను పంపకుండా లేదా స్వీకరించకుండా నిరోధిస్తుంది. మీరు ప్రస్తుతం ఉపయోగిస్తోన్న యాప్, డేటాను యాక్సెస్ చేయగలదు. కానీ తక్కువ సార్లు మాత్రమే అలా చేయవచ్చు. ఉదాహరణకు, మీరు నొక్కే వరకు ఇమేజ్లు ప్రదర్శించబడవు."</string> + <string name="data_saver_description" msgid="4995164271550590517">"డేటా వినియోగాన్ని తగ్గించడంలో డేటా సేవర్ సహాయకరంగా ఉంటుంది. బ్యాక్గ్రౌండ్లో కొన్ని యాప్లు డేటాను పంపకుండా లేదా స్వీకరించకుండా నిరోధిస్తుంది. మీరు ప్రస్తుతం ఉపయోగిస్తోన్న యాప్, డేటాను యాక్సెస్ చేయగలదు. కానీ తక్కువ సార్లు మాత్రమే అలా చేయవచ్చు. ఉదాహరణకు, మీరు ఇమేజ్లపై ట్యాప్ చేసే వరకు అవి కనిపించవు."</string> <string name="data_saver_enable_title" msgid="7080620065745260137">"డేటా సేవర్ను ఆన్ చేయాలా?"</string> <string name="data_saver_enable_button" msgid="4399405762586419726">"ఆన్ చేయి"</string> <string name="zen_mode_duration_minutes_summary" msgid="4555514757230849789">"{count,plural, =1{ఒక నిమిషానికి ({formattedTime} వరకు)}other{# నిమిషాలకు ({formattedTime} వరకు)}}"</string> @@ -2407,6 +2407,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"ప్రైవేట్ స్పేస్"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"క్లోన్"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"కమ్యూనల్"</string> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"ప్రైవేట్ స్పేస్"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"గోప్యమైన నోటిఫికేషన్ కంటెంట్ దాచబడింది"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"సెక్యూరిటీ కోసం స్క్రీన్ షేర్ నుండి యాప్ కంటెంట్ దాచబడింది"</string> diff --git a/core/res/res/values-th-rTH/donottranslate-cldr.xml b/core/res/res/values-th-rTH/donottranslate-cldr.xml index 867a58e38263..867a58e38263 100755..100644 --- a/core/res/res/values-th-rTH/donottranslate-cldr.xml +++ b/core/res/res/values-th-rTH/donottranslate-cldr.xml diff --git a/core/res/res/values-th/donottranslate-cldr.xml b/core/res/res/values-th/donottranslate-cldr.xml index 867a58e38263..867a58e38263 100755..100644 --- a/core/res/res/values-th/donottranslate-cldr.xml +++ b/core/res/res/values-th/donottranslate-cldr.xml diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml index e34b526ba940..eb102fc79966 100644 --- a/core/res/res/values-th/strings.xml +++ b/core/res/res/values-th/strings.xml @@ -2407,6 +2407,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"พื้นที่ส่วนตัว"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"โคลน"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"ส่วนกลาง"</string> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"พื้นที่ส่วนตัว"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"เนื้อหาการแจ้งเตือนที่ละเอียดอ่อนซ่อนอยู่"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"ซ่อนเนื้อหาแอปจากการแชร์หน้าจอเพื่อความปลอดภัย"</string> diff --git a/core/res/res/values-tl/donottranslate-cldr.xml b/core/res/res/values-tl/donottranslate-cldr.xml index 7313c71b4fca..7313c71b4fca 100755..100644 --- a/core/res/res/values-tl/donottranslate-cldr.xml +++ b/core/res/res/values-tl/donottranslate-cldr.xml diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml index 4288460e6e97..de499d346524 100644 --- a/core/res/res/values-tl/strings.xml +++ b/core/res/res/values-tl/strings.xml @@ -2407,6 +2407,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Pribadong space"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Clone"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Communal"</string> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Pribadong space"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"Nakatago ang content ng sensitibong notification"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Nakatago ang content ng app mula sa pagbabahagi ng screen para sa seguridad"</string> diff --git a/core/res/res/values-tr/donottranslate-cldr.xml b/core/res/res/values-tr/donottranslate-cldr.xml index 0577faba5fef..0577faba5fef 100755..100644 --- a/core/res/res/values-tr/donottranslate-cldr.xml +++ b/core/res/res/values-tr/donottranslate-cldr.xml diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml index 906ccbc2ddc7..7dab2360da04 100644 --- a/core/res/res/values-tr/strings.xml +++ b/core/res/res/values-tr/strings.xml @@ -2407,6 +2407,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Özel alan"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Klon"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Paylaşılan"</string> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Özel alan"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"Hassas bildirim içerikleri gizlendi"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Uygulama içerikleri, güvenlik nedeniyle ekran paylaşımında gizlendi"</string> diff --git a/core/res/res/values-uk-rUA/donottranslate-cldr.xml b/core/res/res/values-uk-rUA/donottranslate-cldr.xml index 75025d888dc2..75025d888dc2 100755..100644 --- a/core/res/res/values-uk-rUA/donottranslate-cldr.xml +++ b/core/res/res/values-uk-rUA/donottranslate-cldr.xml diff --git a/core/res/res/values-uk/donottranslate-cldr.xml b/core/res/res/values-uk/donottranslate-cldr.xml index 1c25b4d51872..1c25b4d51872 100755..100644 --- a/core/res/res/values-uk/donottranslate-cldr.xml +++ b/core/res/res/values-uk/donottranslate-cldr.xml diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml index cc9df2e09efc..22c72727537b 100644 --- a/core/res/res/values-uk/strings.xml +++ b/core/res/res/values-uk/strings.xml @@ -2409,6 +2409,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Приватний простір"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Копія профілю"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Спільний профіль"</string> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Приватний простір"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"Чутливий вміст сповіщення приховано"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"З міркувань безпеки вміст додатка приховано під час показу екрана"</string> diff --git a/core/res/res/values-ur/strings.xml b/core/res/res/values-ur/strings.xml index c0be641c84c7..0d878c9d1346 100644 --- a/core/res/res/values-ur/strings.xml +++ b/core/res/res/values-ur/strings.xml @@ -2407,6 +2407,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"پرائیویٹ اسپیس"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"کلون"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"کمیونل"</string> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"پرائیویٹ اسپیس"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"حساس اطلاعی مواد چھپا ہوا ہے"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"سیکیورٹی کے مد نظر ایپ کا مواد اسکرین کے اشتراک سے چھپا ہوا ہے"</string> diff --git a/core/res/res/values-uz/strings.xml b/core/res/res/values-uz/strings.xml index 517176b33a02..fc5d8ee32520 100644 --- a/core/res/res/values-uz/strings.xml +++ b/core/res/res/values-uz/strings.xml @@ -2407,6 +2407,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Maxfiy makon"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Nusxalash"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Umumiy"</string> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Maxfiy makon"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"Bildirishnomadagi maxfiy axborot berkitildi"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Ekran namoyishida xavfsizlik maqsadida ilova kontenti berkitildi"</string> diff --git a/core/res/res/values-vi-rVN/donottranslate-cldr.xml b/core/res/res/values-vi-rVN/donottranslate-cldr.xml index 8f5cf3bf6a06..8f5cf3bf6a06 100755..100644 --- a/core/res/res/values-vi-rVN/donottranslate-cldr.xml +++ b/core/res/res/values-vi-rVN/donottranslate-cldr.xml diff --git a/core/res/res/values-vi/donottranslate-cldr.xml b/core/res/res/values-vi/donottranslate-cldr.xml index 8f5cf3bf6a06..8f5cf3bf6a06 100755..100644 --- a/core/res/res/values-vi/donottranslate-cldr.xml +++ b/core/res/res/values-vi/donottranslate-cldr.xml diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml index e6db9dd4c680..4619be37f2ad 100644 --- a/core/res/res/values-vi/strings.xml +++ b/core/res/res/values-vi/strings.xml @@ -306,7 +306,7 @@ <string name="notification_channel_foreground_service" msgid="7102189948158885178">"Các ứng dụng tiêu thụ pin"</string> <string name="notification_channel_accessibility_magnification" msgid="1707913872219798098">"Phóng to"</string> <string name="notification_channel_accessibility_security_policy" msgid="1727787021725251912">"Việc sử dụng tính năng hỗ trợ tiếp cận"</string> - <string name="notification_channel_display" msgid="6905032605735615090">"Màn hình"</string> + <string name="notification_channel_display" msgid="6905032605735615090">"Hiển thị"</string> <string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g> đang sử dụng pin"</string> <string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g> ứng dụng đang sử dụng pin"</string> <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"Nhấn để biết chi tiết về mức sử dụng dữ liệu và pin"</string> @@ -1098,8 +1098,8 @@ <string name="js_dialog_before_unload_negative_button" msgid="3873765747622415310">"Ở lại trang này"</string> <string name="js_dialog_before_unload" msgid="7213364985774778744">"<xliff:g id="MESSAGE">%s</xliff:g>\n\nBạn có chắc chắn muốn điều hướng khỏi trang này không?"</string> <string name="autofill_window_title" msgid="4379134104008111961">"Tự động điền với <xliff:g id="SERVICENAME">%1$s</xliff:g>"</string> - <string name="permlab_setAlarm" msgid="1158001610254173567">"đặt báo thức"</string> - <string name="permdesc_setAlarm" msgid="2185033720060109640">"Cho phép ứng dụng đặt báo thức trong ứng dụng đồng hồ báo thức được cài đặt. Một số ứng dụng đồng hồ báo thức có thể không thực thi tính năng này."</string> + <string name="permlab_setAlarm" msgid="1158001610254173567">"đặt chuông báo"</string> + <string name="permdesc_setAlarm" msgid="2185033720060109640">"Cho phép ứng dụng đặt chuông báo trong ứng dụng đồng hồ được cài đặt. Một số ứng dụng đồng hồ có thể không có năng này."</string> <string name="permlab_addVoicemail" msgid="4770245808840814471">"thêm thư thoại"</string> <string name="permdesc_addVoicemail" msgid="5470312139820074324">"Cho phép ứng dụng thêm thông báo vào hộp thư thoại đến của bạn."</string> <string name="pasted_from_clipboard" msgid="7355790625710831847">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> đã dán dữ liệu từ bảng nhớ tạm của bạn"</string> @@ -1329,7 +1329,7 @@ <string name="ringtone_default_with_actual" msgid="2709686194556159773">"Mặc định (<xliff:g id="ACTUAL_RINGTONE">%1$s</xliff:g>)"</string> <string name="ringtone_silent" msgid="397111123930141876">"Không"</string> <string name="ringtone_picker_title" msgid="667342618626068253">"Nhạc chuông"</string> - <string name="ringtone_picker_title_alarm" msgid="7438934548339024767">"Âm thanh báo thức"</string> + <string name="ringtone_picker_title_alarm" msgid="7438934548339024767">"Âm thanh chuông báo"</string> <string name="ringtone_picker_title_notification" msgid="6387191794719608122">"Âm thanh thông báo"</string> <string name="ringtone_unknown" msgid="5059495249862816475">"Không xác định"</string> <string name="wifi_available_sign_in" msgid="381054692557675237">"Đăng nhập vào mạng Wi-Fi"</string> @@ -2407,6 +2407,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Không gian riêng tư"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Nhân bản"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Dùng chung"</string> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Không gian riêng tư"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"Đã ẩn nội dung thông báo nhạy cảm"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Nội dung ứng dụng bị ẩn khỏi tính năng chia sẻ màn hình vì lý do bảo mật"</string> diff --git a/core/res/res/values-w204dp-round-watch/dimens_material.xml b/core/res/res/values-w204dp-round-watch/dimens_material.xml new file mode 100644 index 000000000000..c07d5c47e2e1 --- /dev/null +++ b/core/res/res/values-w204dp-round-watch/dimens_material.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> + <dimen name="screen_percentage_05">10.2dp</dimen> + <dimen name="screen_percentage_10">20.4dp</dimen> + <dimen name="screen_percentage_15">30.6dp</dimen> +</resources> diff --git a/core/res/res/values-zh-rCN/donottranslate-cldr.xml b/core/res/res/values-zh-rCN/donottranslate-cldr.xml index b3f009ef0780..b3f009ef0780 100755..100644 --- a/core/res/res/values-zh-rCN/donottranslate-cldr.xml +++ b/core/res/res/values-zh-rCN/donottranslate-cldr.xml diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml index 4b0494004dd7..a6c62dc826ce 100644 --- a/core/res/res/values-zh-rCN/strings.xml +++ b/core/res/res/values-zh-rCN/strings.xml @@ -581,7 +581,7 @@ <string name="permlab_changeTetherState" msgid="9079611809931863861">"更改网络共享连接"</string> <string name="permdesc_changeTetherState" msgid="3025129606422533085">"允许应用更改绑定网络连接的状态。"</string> <string name="permlab_accessWifiState" msgid="5552488500317911052">"查看WLAN连接"</string> - <string name="permdesc_accessWifiState" msgid="6913641669259483363">"允许该应用查看WLAN网络的相关信息,例如是否启用了WLAN以及连接的WLAN设备的名称。"</string> + <string name="permdesc_accessWifiState" msgid="6913641669259483363">"允许该应用查看 WLAN 网络的相关信息,例如是否启用了 WLAN 以及连接的 WLAN 设备的名称。"</string> <string name="permlab_changeWifiState" msgid="7947824109713181554">"连接WLAN网络和断开连接"</string> <string name="permdesc_changeWifiState" msgid="7170350070554505384">"允许该应用与WLAN接入点建立和断开连接,以及更改WLAN网络的设备配置。"</string> <string name="permlab_changeWifiMulticastState" msgid="285626875870754696">"允许接收WLAN多播"</string> @@ -2407,6 +2407,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"私密空间"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"克隆"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"共用"</string> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"私密空间"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"已隐藏敏感通知内容"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"为安全起见而在屏幕共享画面中处于隐藏状态的应用内容"</string> diff --git a/core/res/res/values-zh-rHK/strings.xml b/core/res/res/values-zh-rHK/strings.xml index 1aa9c72c8cb9..f9b10a2dac52 100644 --- a/core/res/res/values-zh-rHK/strings.xml +++ b/core/res/res/values-zh-rHK/strings.xml @@ -2407,6 +2407,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"私人空間"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"複製"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"共用"</string> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"私人空間"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"已隱藏敏感通知內容"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"為安全起見,應用程式內容已從分享螢幕畫面隱藏"</string> diff --git a/core/res/res/values-zh-rTW/donottranslate-cldr.xml b/core/res/res/values-zh-rTW/donottranslate-cldr.xml index 3e2b33dc87ee..3e2b33dc87ee 100755..100644 --- a/core/res/res/values-zh-rTW/donottranslate-cldr.xml +++ b/core/res/res/values-zh-rTW/donottranslate-cldr.xml diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml index 1c83ee6d7465..4dce59ada110 100644 --- a/core/res/res/values-zh-rTW/strings.xml +++ b/core/res/res/values-zh-rTW/strings.xml @@ -1909,7 +1909,7 @@ <string name="confirm_battery_saver" msgid="5247976246208245754">"確定"</string> <string name="battery_saver_description_with_learn_more" msgid="5444908404021316250">"省電模式會開啟深色主題,並限制或關閉背景活動、某些視覺效果、特定功能和部分網路連線。"</string> <string name="battery_saver_description" msgid="8518809702138617167">"省電模式會開啟深色主題,並限制或關閉背景活動、某些視覺效果、特定功能和部分網路連線。"</string> - <string name="data_saver_description" msgid="4995164271550590517">"「數據節省模式」可防止部分應用程式在背景收發資料,以節省數據用量。目前使用中的單一應用程式仍可存取資料,但存取頻率可能會變低。舉例來說,圖片可能要等到你輕觸後才會顯示。"</string> + <string name="data_saver_description" msgid="4995164271550590517">"「數據節省模式」可防止部分應用程式在背景收發資料,以節省數據用量。你使用中的一個應用程式仍可存取資料,但存取頻率可能會變低。舉例來說,圖片可能要等到你輕觸後才會顯示。"</string> <string name="data_saver_enable_title" msgid="7080620065745260137">"要開啟數據節省模式嗎?"</string> <string name="data_saver_enable_button" msgid="4399405762586419726">"開啟"</string> <string name="zen_mode_duration_minutes_summary" msgid="4555514757230849789">"{count,plural, =1{1 分鐘 (直到 {formattedTime})}other{# 分鐘 (直到 {formattedTime})}}"</string> @@ -2407,6 +2407,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"私人空間"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"複製"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"共通"</string> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"私人空間"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"系統已隱藏含有私密資訊的通知內容"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"為安全起見,分享螢幕畫面未顯示應用程式內容"</string> diff --git a/core/res/res/values-zu/donottranslate-cldr.xml b/core/res/res/values-zu/donottranslate-cldr.xml index e2cf516a42da..e2cf516a42da 100755..100644 --- a/core/res/res/values-zu/donottranslate-cldr.xml +++ b/core/res/res/values-zu/donottranslate-cldr.xml diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml index 23754425047b..60cb31b4db5c 100644 --- a/core/res/res/values-zu/strings.xml +++ b/core/res/res/values-zu/strings.xml @@ -1270,7 +1270,7 @@ <string name="screen_compat_mode_scale" msgid="8627359598437527726">"Isilinganisi"</string> <string name="screen_compat_mode_show" msgid="5080361367584709857">"Bonisa njalo"</string> <string name="screen_compat_mode_hint" msgid="4032272159093750908">"Yenza kuphinde kusebenze kuzilungiselelo Zesistimue > Izinhlelo zokusebenza > Okulayishiwe."</string> - <string name="unsupported_display_size_message" msgid="7265211375269394699">"I-<xliff:g id="APP_NAME">%1$s</xliff:g> ayisekeli isilungiselelo sosayizi sokubonisa samanje futhi ingasebenza ngokungalindelekile."</string> + <string name="unsupported_display_size_message" msgid="7265211375269394699">"I-<xliff:g id="APP_NAME">%1$s</xliff:g> ayisekeli isethingi sosayizi sokubonisa samanje futhi ingasebenza ngokungalindelekile."</string> <string name="unsupported_display_size_show" msgid="980129850974919375">"Bonisa njalo"</string> <string name="unsupported_compile_sdk_message" msgid="7326293500707890537">"I-<xliff:g id="APP_NAME">%1$s</xliff:g> yakhiwe kunguqulo engahambisani ye-Android OS futhi ingaziphatha ngokungalindelekile. Inguqulo ebuyekeziwe yohlelo lokusebenza ingatholakala."</string> <string name="unsupported_compile_sdk_show" msgid="1601210057960312248">"Bonisa njalo"</string> @@ -2407,6 +2407,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Indawo engasese"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Yenza i-Clone"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Okomphakathi"</string> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Indawo engasese"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"Okuqukethwe kwesaziso esizwelayo kufihliwe"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Okuqukethwe kwe-app kufihliwe kusuka ekwabelaneni kwesikrini ngokuvikelwa"</string> diff --git a/core/res/res/values/arrays.xml b/core/res/res/values/arrays.xml index 575573cb0ffb..df5cbb1fbc92 100644 --- a/core/res/res/values/arrays.xml +++ b/core/res/res/values/arrays.xml @@ -37,8 +37,7 @@ <item>@drawable/fastscroll_label_right_material</item> <item>@drawable/fastscroll_thumb_material</item> <item>@drawable/fastscroll_track_material</item> - <item>@drawable/floating_popup_background_dark</item> - <item>@drawable/floating_popup_background_light</item> + <item>@drawable/floating_popup_background</item> <item>@drawable/ic_ab_back_material</item> <item>@drawable/ic_ab_back_material_dark</item> <item>@drawable/ic_ab_back_material_light</item> diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml index 405324bf76af..9846b710300f 100644 --- a/core/res/res/values/attrs.xml +++ b/core/res/res/values/attrs.xml @@ -606,11 +606,9 @@ <!-- ============ --> <eat-comment /> <attr name="floatingToolbarCloseDrawable" format="reference" /> - <attr name="floatingToolbarForegroundColor" format="reference|color" /> <attr name="floatingToolbarItemBackgroundBorderlessDrawable" format="reference" /> <attr name="floatingToolbarItemBackgroundDrawable" format="reference" /> <attr name="floatingToolbarOpenDrawable" format="reference" /> - <attr name="floatingToolbarPopupBackgroundDrawable" format="reference" /> <attr name="floatingToolbarDividerColor" format="reference" /> <!-- ============ --> @@ -9702,6 +9700,12 @@ <attr name="hotSpotY" format="dimension" /> </declare-styleable> + <!-- @hide --> + <declare-styleable name="PointerIconVectorTheme"> + <attr name="pointerIconVectorFill" format="color" /> + <attr name="pointerIconVectorFillInverse" format="color" /> + </declare-styleable> + <declare-styleable name="Storage"> <!-- path to mount point for the storage. --> <attr name="mountPoint" format="string" /> diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml index 27b756d46b12..f94c8ab0350e 100644 --- a/core/res/res/values/attrs_manifest.xml +++ b/core/res/res/values/attrs_manifest.xml @@ -851,7 +851,12 @@ of the screen. <p>This attribute is supported by the <a href="{@docRoot}guide/topics/manifest/activity-element.html">{@code <activity>}</a> - element. --> + element. + <aside class="note"><b>Note:</b> Device manufacturers can configure devices to override + (ignore) this attribute to improve the layout of apps. See + <a href="{@docRoot}guide/practices/device-compatibility-mode"> + Device compatibility mode</a>. + </aside> --> <attr name="screenOrientation"> <!-- No preference specified: let the system decide the best orientation. This will either be the orientation selected @@ -1447,13 +1452,23 @@ no other apps in multi-window visible on screen (e.g. picture-in-picture) or on other displays. Therefore, this flag cannot be used to assure an exclusive resource access. - <p>NOTE: A task's root activity value is applied to all additional activities launched in + <p>A task's root activity value is applied to all additional activities launched in the task. That is if the root activity of a task is resizeable then the system will treat all other activities in the task as resizeable and will not if the root activity isn't resizeable. - <p>NOTE: The value of {@link android.R.attr#screenOrientation} is ignored for - resizeable activities when in multi-window mode before Android 12. --> + <aside class="note"><b>Note:</b> + <ul> + <li>On Android 11 (API level 30) and lower, the value of + {@link android.R.attr#screenOrientation} is ignored for resizeable activities + in multi-window mode. + <li>Device manufacturers can configure devices to override (ignore) this attribute + to force apps to resize. The override does not affect the app's support for + multi-window mode. See + <a href="{@docRoot}guide/practices/device-compatibility-mode"> + Device compatibility mode</a>. + </ul> + </aside> --> <attr name="resizeableActivity" format="boolean" /> <!-- Indicates that the activity specifically supports the picture-in-picture form of @@ -1470,27 +1485,37 @@ <!-- This value indicates the maximum aspect ratio the activity supports. If the app runs on a device with a wider aspect ratio, the system automatically letterboxes the app, leaving portions of the screen unused so the app can run at its specified maximum aspect ratio. - <p> - Maximum aspect ratio, expressed as (longer dimension / shorter dimension) in decimal + <p>Maximum aspect ratio, expressed as (longer dimension / shorter dimension) in decimal form. For example, if the maximum aspect ratio is 7:3, set value to 2.33. - <p> - Value needs to be greater or equal to 1.0, otherwise it is ignored. - <p> - NOTE: This attribute is ignored if the activity has - {@link android.R.attr#resizeableActivity} set to true. --> + <p>Value needs to be greater or equal to 1.0, otherwise it is ignored. + <aside class="note"><b>Note:</b> + <ul> + <li>This attribute is ignored if the activity has + {@link android.R.attr#resizeableActivity} set to {@code true}. + <li>Device manufacturers can configure devices to override (ignore) this attribute + to improve the layout of apps. See + <a href="{@docRoot}guide/practices/device-compatibility-mode"> + Device compatibility mode</a>. + </ul> + </aside> --> <attr name="maxAspectRatio" format="float" /> <!-- This value indicates the minimum aspect ratio the activity supports. If the app runs on a device with a narrower aspect ratio, the system automatically letterboxes the app, leaving portions of the screen unused so the app can run at its specified minimum aspect ratio. - <p> - Minimum aspect ratio, expressed as (longer dimension / shorter dimension) in decimal - form. For example, if the minimum aspect ratio is 4:3, set value to 1.33. - <p> - Value needs to be greater or equal to 1.0, otherwise it is ignored. - <p> - NOTE: This attribute is ignored if the activity has - {@link android.R.attr#resizeableActivity} set to true. --> + <p>Minimum aspect ratio, expressed as (longer dimension / shorter dimension) in decimal + form. For example, if the minimum aspect ratio is 4:3, set value to 1.33. + <p>Value needs to be greater or equal to 1.0, otherwise it is ignored. + <aside class="note"><b>Note:</b> + <ul> + <li>This attribute is ignored if the activity has + {@link android.R.attr#resizeableActivity} set to {@code true}. + <li>Device manufacturers can configure devices to override (ignore) this attribute + to improve the layout of apps. See + <a href="{@docRoot}guide/practices/device-compatibility-mode"> + Device compatibility mode</a>. + </ul> + </aside> --> <attr name="minAspectRatio" format="float" /> <!-- This value indicates how tasks rooted at this activity will behave in lockTask mode. diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 0706b32dfbb5..f43351a2e456 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -7030,6 +7030,13 @@ <!-- Name of the starting activity for DisplayCompat host. specific to automotive.--> <string name="config_defaultDisplayCompatHostActivity" translatable="false"></string> + <!-- Name of the starting activity for launch on private display. specific to automotive.--> + <string name="config_defaultLaunchOnPrivateDisplayRouterActivity" translatable="false"></string> + + <!-- Allowlisted activities for launch on a private display. specific to automotive.--> + <!--TODO(b/343733988): Remove this allowlisting when GMS is ready with the allowlisting mechanism.--> + <string-array name="config_defaultAllowlistLaunchOnPrivateDisplayPackages"></string-array> + <!-- Whether to use file hashes cache in watchlist--> <bool name="config_watchlistUseFileHashesCache">false</bool> @@ -7069,4 +7076,8 @@ <!-- Whether the system uses auto-suspend mode. --> <bool name="config_useAutoSuspend">true</bool> + + <!-- Whether to show GAIA education screen during account login of private space setup. + OEM/Partner can explicitly opt to disable the screen. --> + <bool name="config_enableGaiaEducationInPrivateSpace">true</bool> </resources> diff --git a/core/res/res/values/config_telephony.xml b/core/res/res/values/config_telephony.xml index dcda5d8669a4..fba95a5e41b4 100644 --- a/core/res/res/values/config_telephony.xml +++ b/core/res/res/values/config_telephony.xml @@ -409,4 +409,10 @@ <bool name="config_force_phone_globals_creation">false</bool> <java-symbol type="bool" name="config_force_phone_globals_creation" /> + <!-- Boolean indicating whether to enable persistent logging via DropBoxManager. + Used in persisting SOS/emergency related log messages. + --> + <bool name="config_dropboxmanager_persistent_logging_enabled">false</bool> + <java-symbol type="bool" name="config_dropboxmanager_persistent_logging_enabled" /> + </resources> diff --git a/core/res/res/values/donottranslate-cldr.xml b/core/res/res/values/donottranslate-cldr.xml index a8e2b2bc3246..a8e2b2bc3246 100755..100644 --- a/core/res/res/values/donottranslate-cldr.xml +++ b/core/res/res/values/donottranslate-cldr.xml diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 87141c790f0c..b6e8383a0094 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -5125,7 +5125,7 @@ <string name="immersive_cling_title">Viewing full screen</string> <!-- Cling help message description when hiding the navigation bar entering immersive mode [CHAR LIMIT=none] --> - <string name="immersive_cling_description">To exit, swipe down from the top.</string> + <string name="immersive_cling_description">To exit, swipe down from the top of your screen</string> <!-- Cling help message confirmation button when hiding the navigation bar entering immersive mode [CHAR LIMIT=30] --> <string name="immersive_cling_positive">Got it</string> @@ -6473,6 +6473,9 @@ ul.</string> <!-- Accessibility label for clone profile user type [CHAR LIMIT=30] --> <string name="accessibility_label_communal_profile">Communal</string> + <!-- Label for private space biometric prompt logo description [CHAR LIMIT=30] --> + <string name="private_space_biometric_prompt_title">Private space</string> + <!-- Notification message used when a notification's normal message contains sensitive information [CHAR_LIMIT=NOTIF_BODY] --> <string name="redacted_notification_message">Sensitive notification content hidden</string> <!-- Notification action title used instead of a notification's normal title sensitive [CHAR_LIMIT=NOTIF_BODY] --> diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml index a46dc045269d..50c3b1a93d75 100644 --- a/core/res/res/values/styles.xml +++ b/core/res/res/values/styles.xml @@ -1497,6 +1497,36 @@ please see styles_device_defaults.xml. </style> <!-- @hide --> + <style name="PointerIconVectorStyleFillBlack"> + <item name="pointerIconVectorFill">@color/black</item> + <item name="pointerIconVectorFillInverse">@color/white</item> + </style> + + <!-- @hide --> + <style name="PointerIconVectorStyleFillGreen"> + <item name="pointerIconVectorFill">#6DD58C</item> + <item name="pointerIconVectorFillInverse">#6DD58C</item> + </style> + + <!-- @hide --> + <style name="PointerIconVectorStyleFillYellow"> + <item name="pointerIconVectorFill">#FDD663</item> + <item name="pointerIconVectorFillInverse">#FDD663</item> + </style> + + <!-- @hide --> + <style name="PointerIconVectorStyleFillPink"> + <item name="pointerIconVectorFill">#F2B8B5</item> + <item name="pointerIconVectorFillInverse">#F2B8B5</item> + </style> + + <!-- @hide --> + <style name="PointerIconVectorStyleFillBlue"> + <item name="pointerIconVectorFill">#8AB4F8</item> + <item name="pointerIconVectorFillInverse">#8AB4F8</item> + </style> + + <!-- @hide --> <style name="aerr_list_item" parent="Widget.Material.Light.Button.Borderless"> <item name="minHeight">?attr/listPreferredItemHeightSmall</item> <item name="textAppearance">?attr/textAppearanceListItemSmall</item> @@ -1690,4 +1720,12 @@ please see styles_device_defaults.xml. parent="@style/Theme.DeviceDefault.Resolver"> <item name="android:windowOptOutEdgeToEdgeEnforcement">true</item> </style> + + <!-- + TODO(b/309578419): Make activities go edge-to-edge properly and then remove this. + --> + <style name="GrantCredentialsPermissionActivity" + parent="@style/Theme.DeviceDefault.Light.DialogWhenLarge"> + <item name="android:windowOptOutEdgeToEdgeEnforcement">true</item> + </style> </resources> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index bb73934450c9..c16bd241a860 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -521,6 +521,7 @@ <java-symbol type="bool" name="config_preferKeepClearForFocus" /> <java-symbol type="bool" name="config_hibernationDeletesOatArtifactsEnabled"/> <java-symbol type="integer" name="config_defaultAnalogClockSecondsHandFps"/> + <java-symbol type="bool" name="config_enableGaiaEducationInPrivateSpace"/> <java-symbol type="color" name="tab_indicator_text_v4" /> @@ -1116,6 +1117,7 @@ <java-symbol type="string" name="accessibility_label_private_profile" /> <java-symbol type="string" name="accessibility_label_clone_profile" /> <java-symbol type="string" name="accessibility_label_communal_profile" /> + <java-symbol type="string" name="private_space_biometric_prompt_title" /> <java-symbol type="string" name="mediasize_unknown_portrait" /> <java-symbol type="string" name="mediasize_unknown_landscape" /> <java-symbol type="string" name="mediasize_iso_a0" /> @@ -1617,6 +1619,7 @@ <java-symbol type="layout" name="restrictions_pin_challenge" /> <java-symbol type="layout" name="restrictions_pin_setup" /> <java-symbol type="layout" name="immersive_mode_cling" /> + <java-symbol type="id" name="immersive_cling_icon" /> <java-symbol type="layout" name="user_switching_dialog" /> <java-symbol type="layout" name="common_tab_settings" /> <java-symbol type="layout" name="resolver_list_per_profile" /> @@ -1694,6 +1697,12 @@ <java-symbol type="style" name="Pointer" /> <java-symbol type="style" name="LargePointer" /> <java-symbol type="style" name="VectorPointer" /> + <java-symbol type="style" name="PointerIconVectorStyleFillBlack" /> + <java-symbol type="style" name="PointerIconVectorStyleFillGreen" /> + <java-symbol type="style" name="PointerIconVectorStyleFillYellow" /> + <java-symbol type="style" name="PointerIconVectorStyleFillPink" /> + <java-symbol type="style" name="PointerIconVectorStyleFillBlue" /> + <java-symbol type="attr" name="pointerIconVectorFill" /> <java-symbol type="style" name="TextAppearance.DeviceDefault.Notification.Title" /> <java-symbol type="style" name="TextAppearance.DeviceDefault.Notification.Info" /> diff --git a/core/res/res/values/themes.xml b/core/res/res/values/themes.xml index bdbf96b97c1e..c3d304dc35e1 100644 --- a/core/res/res/values/themes.xml +++ b/core/res/res/values/themes.xml @@ -391,11 +391,9 @@ please see themes_device_defaults.xml. <!-- Floating toolbar styles --> <item name="floatingToolbarCloseDrawable">@drawable/ic_ab_back_material_dark</item> - <item name="floatingToolbarForegroundColor">@color/foreground_material_dark</item> <item name="floatingToolbarItemBackgroundBorderlessDrawable">@drawable/item_background_borderless_material_dark</item> <item name="floatingToolbarItemBackgroundDrawable">@drawable/item_background_material_dark</item> <item name="floatingToolbarOpenDrawable">@drawable/ic_menu_moreoverflow_material_dark</item> - <item name="floatingToolbarPopupBackgroundDrawable">@drawable/floating_popup_background_dark</item> <item name="floatingToolbarDividerColor">@color/floating_popup_divider_dark</item> <!-- SearchView attributes --> @@ -579,11 +577,9 @@ please see themes_device_defaults.xml. <!-- Floating toolbar styles --> <item name="floatingToolbarCloseDrawable">@drawable/ic_ab_back_material_light</item> - <item name="floatingToolbarForegroundColor">@color/foreground_material_light</item> <item name="floatingToolbarItemBackgroundBorderlessDrawable">@drawable/item_background_borderless_material_light</item> <item name="floatingToolbarItemBackgroundDrawable">@drawable/item_background_material_light</item> <item name="floatingToolbarOpenDrawable">@drawable/ic_menu_moreoverflow_material_light</item> - <item name="floatingToolbarPopupBackgroundDrawable">@drawable/floating_popup_background_light</item> <item name="floatingToolbarDividerColor">@color/floating_popup_divider_light</item> <!-- Tooltip popup colors --> diff --git a/core/sysprop/Android.bp b/core/sysprop/Android.bp index 512a2eb20abb..ed827656d679 100644 --- a/core/sysprop/Android.bp +++ b/core/sysprop/Android.bp @@ -43,3 +43,10 @@ sysprop_library { property_owner: "Platform", api_packages: ["android.sysprop"], } + +sysprop_library { + name: "com.android.sysprop.view", + srcs: ["ViewProperties.sysprop"], + property_owner: "Platform", + api_packages: ["android.sysprop"], +} diff --git a/core/sysprop/ViewProperties.sysprop b/core/sysprop/ViewProperties.sysprop new file mode 100644 index 000000000000..e8016432efad --- /dev/null +++ b/core/sysprop/ViewProperties.sysprop @@ -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. + +module: "android.sysprop.ViewProperties" +owner: Platform + +# On low-end devices, the cost of calculating frame rate can +# have noticeable overhead. These devices don't benefit from +# reduced frame rate as much as they benefit from reduced +# work. By setting this to false, the device won't do any +# VRR frame rate calculation for Views. +prop { + api_name: "vrr_enabled" + type: Boolean + prop_name: "ro.view.vrr.enabled" + scope: Internal + access: Readonly +} diff --git a/core/tests/BroadcastRadioTests/src/android/hardware/radio/RadioManagerTest.java b/core/tests/BroadcastRadioTests/src/android/hardware/radio/RadioManagerTest.java index 4f9b2697ee89..4c3d4e3af99f 100644 --- a/core/tests/BroadcastRadioTests/src/android/hardware/radio/RadioManagerTest.java +++ b/core/tests/BroadcastRadioTests/src/android/hardware/radio/RadioManagerTest.java @@ -16,8 +16,6 @@ package android.hardware.radio; -import static com.google.common.truth.Truth.assertWithMessage; - import static org.junit.Assert.assertThrows; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; @@ -36,6 +34,8 @@ import android.os.RemoteException; import android.platform.test.flag.junit.SetFlagsRule; import android.util.ArrayMap; +import com.google.common.truth.Expect; + import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; @@ -155,6 +155,9 @@ public final class RadioManagerTest { private RadioManager mRadioManager; private final ApplicationInfo mApplicationInfo = new ApplicationInfo(); + @Rule + public final Expect mExpect = Expect.create(); + @Mock private IRadioService mRadioServiceMock; @Mock @@ -175,7 +178,7 @@ public final class RadioManagerTest { () -> new RadioManager.AmBandDescriptor(REGION, /* type= */ 100, AM_LOWER_LIMIT, AM_UPPER_LIMIT, AM_SPACING, STEREO_SUPPORTED)); - assertWithMessage("Unsupported band type exception") + mExpect.withMessage("Unsupported band type exception") .that(thrown).hasMessageThat().contains("Unsupported band"); } @@ -183,7 +186,7 @@ public final class RadioManagerTest { public void getType_forBandDescriptor() { RadioManager.BandDescriptor bandDescriptor = createAmBandDescriptor(); - assertWithMessage("AM Band Descriptor type") + mExpect.withMessage("AM Band Descriptor type") .that(bandDescriptor.getType()).isEqualTo(RadioManager.BAND_AM); } @@ -191,7 +194,7 @@ public final class RadioManagerTest { public void getRegion_forBandDescriptor() { RadioManager.BandDescriptor bandDescriptor = createFmBandDescriptor(); - assertWithMessage("FM Band Descriptor region") + mExpect.withMessage("FM Band Descriptor region") .that(bandDescriptor.getRegion()).isEqualTo(REGION); } @@ -199,7 +202,7 @@ public final class RadioManagerTest { public void getLowerLimit_forBandDescriptor() { RadioManager.BandDescriptor bandDescriptor = createFmBandDescriptor(); - assertWithMessage("FM Band Descriptor lower limit") + mExpect.withMessage("FM Band Descriptor lower limit") .that(bandDescriptor.getLowerLimit()).isEqualTo(FM_LOWER_LIMIT); } @@ -207,7 +210,7 @@ public final class RadioManagerTest { public void getUpperLimit_forBandDescriptor() { RadioManager.BandDescriptor bandDescriptor = createAmBandDescriptor(); - assertWithMessage("AM Band Descriptor upper limit") + mExpect.withMessage("AM Band Descriptor upper limit") .that(bandDescriptor.getUpperLimit()).isEqualTo(AM_UPPER_LIMIT); } @@ -215,7 +218,7 @@ public final class RadioManagerTest { public void getSpacing_forBandDescriptor() { RadioManager.BandDescriptor bandDescriptor = createAmBandDescriptor(); - assertWithMessage("AM Band Descriptor spacing") + mExpect.withMessage("AM Band Descriptor spacing") .that(bandDescriptor.getSpacing()).isEqualTo(AM_SPACING); } @@ -223,7 +226,7 @@ public final class RadioManagerTest { public void describeContents_forBandDescriptor() { RadioManager.BandDescriptor bandDescriptor = createFmBandDescriptor(); - assertWithMessage("Band Descriptor contents") + mExpect.withMessage("Band Descriptor contents") .that(bandDescriptor.describeContents()).isEqualTo(0); } @@ -237,7 +240,7 @@ public final class RadioManagerTest { RadioManager.BandDescriptor bandDescriptorFromParcel = RadioManager.BandDescriptor.CREATOR.createFromParcel(parcel); - assertWithMessage("Band Descriptor created from parcel") + mExpect.withMessage("Band Descriptor created from parcel") .that(bandDescriptorFromParcel).isEqualTo(bandDescriptor); } @@ -246,14 +249,14 @@ public final class RadioManagerTest { RadioManager.BandDescriptor[] bandDescriptors = RadioManager.BandDescriptor.CREATOR.newArray(CREATOR_ARRAY_SIZE); - assertWithMessage("Band Descriptors").that(bandDescriptors).hasLength(CREATOR_ARRAY_SIZE); + mExpect.withMessage("Band Descriptors").that(bandDescriptors).hasLength(CREATOR_ARRAY_SIZE); } @Test public void isAmBand_forAmBandDescriptor_returnsTrue() { RadioManager.BandDescriptor bandDescriptor = createAmBandDescriptor(); - assertWithMessage("Is AM Band Descriptor an AM band") + mExpect.withMessage("Is AM Band Descriptor an AM band") .that(bandDescriptor.isAmBand()).isTrue(); } @@ -261,43 +264,43 @@ public final class RadioManagerTest { public void isFmBand_forAmBandDescriptor_returnsFalse() { RadioManager.BandDescriptor bandDescriptor = createAmBandDescriptor(); - assertWithMessage("Is AM Band Descriptor an FM band") + mExpect.withMessage("Is AM Band Descriptor an FM band") .that(bandDescriptor.isFmBand()).isFalse(); } @Test public void isStereoSupported_forFmBandDescriptor() { - assertWithMessage("FM Band Descriptor stereo") + mExpect.withMessage("FM Band Descriptor stereo") .that(FM_BAND_DESCRIPTOR.isStereoSupported()).isEqualTo(STEREO_SUPPORTED); } @Test public void isRdsSupported_forFmBandDescriptor() { - assertWithMessage("FM Band Descriptor RDS or RBDS") + mExpect.withMessage("FM Band Descriptor RDS or RBDS") .that(FM_BAND_DESCRIPTOR.isRdsSupported()).isEqualTo(RDS_SUPPORTED); } @Test public void isTaSupported_forFmBandDescriptor() { - assertWithMessage("FM Band Descriptor traffic announcement") + mExpect.withMessage("FM Band Descriptor traffic announcement") .that(FM_BAND_DESCRIPTOR.isTaSupported()).isEqualTo(TA_SUPPORTED); } @Test public void isAfSupported_forFmBandDescriptor() { - assertWithMessage("FM Band Descriptor alternate frequency") + mExpect.withMessage("FM Band Descriptor alternate frequency") .that(FM_BAND_DESCRIPTOR.isAfSupported()).isEqualTo(AF_SUPPORTED); } @Test public void isEaSupported_forFmBandDescriptor() { - assertWithMessage("FM Band Descriptor emergency announcement") + mExpect.withMessage("FM Band Descriptor emergency announcement") .that(FM_BAND_DESCRIPTOR.isEaSupported()).isEqualTo(EA_SUPPORTED); } @Test public void describeContents_forFmBandDescriptor() { - assertWithMessage("FM Band Descriptor contents") + mExpect.withMessage("FM Band Descriptor contents") .that(FM_BAND_DESCRIPTOR.describeContents()).isEqualTo(0); } @@ -310,7 +313,7 @@ public final class RadioManagerTest { RadioManager.FmBandDescriptor fmBandDescriptorFromParcel = RadioManager.FmBandDescriptor.CREATOR.createFromParcel(parcel); - assertWithMessage("FM Band Descriptor created from parcel") + mExpect.withMessage("FM Band Descriptor created from parcel") .that(fmBandDescriptorFromParcel).isEqualTo(FM_BAND_DESCRIPTOR); } @@ -319,19 +322,19 @@ public final class RadioManagerTest { RadioManager.FmBandDescriptor[] fmBandDescriptors = RadioManager.FmBandDescriptor.CREATOR.newArray(CREATOR_ARRAY_SIZE); - assertWithMessage("FM Band Descriptors") + mExpect.withMessage("FM Band Descriptors") .that(fmBandDescriptors).hasLength(CREATOR_ARRAY_SIZE); } @Test public void isStereoSupported_forAmBandDescriptor() { - assertWithMessage("AM Band Descriptor stereo") + mExpect.withMessage("AM Band Descriptor stereo") .that(AM_BAND_DESCRIPTOR.isStereoSupported()).isEqualTo(STEREO_SUPPORTED); } @Test public void describeContents_forAmBandDescriptor() { - assertWithMessage("AM Band Descriptor contents") + mExpect.withMessage("AM Band Descriptor contents") .that(AM_BAND_DESCRIPTOR.describeContents()).isEqualTo(0); } @@ -344,7 +347,7 @@ public final class RadioManagerTest { RadioManager.AmBandDescriptor amBandDescriptorFromParcel = RadioManager.AmBandDescriptor.CREATOR.createFromParcel(parcel); - assertWithMessage("FM Band Descriptor created from parcel") + mExpect.withMessage("FM Band Descriptor created from parcel") .that(amBandDescriptorFromParcel).isEqualTo(AM_BAND_DESCRIPTOR); } @@ -353,7 +356,7 @@ public final class RadioManagerTest { RadioManager.AmBandDescriptor[] amBandDescriptors = RadioManager.AmBandDescriptor.CREATOR.newArray(CREATOR_ARRAY_SIZE); - assertWithMessage("AM Band Descriptors") + mExpect.withMessage("AM Band Descriptors") .that(amBandDescriptors).hasLength(CREATOR_ARRAY_SIZE); } @@ -361,7 +364,7 @@ public final class RadioManagerTest { public void equals_withSameFmBandDescriptors_returnsTrue() { RadioManager.FmBandDescriptor fmBandDescriptorCompared = createFmBandDescriptor(); - assertWithMessage("The same FM Band Descriptor") + mExpect.withMessage("The same FM Band Descriptor") .that(FM_BAND_DESCRIPTOR).isEqualTo(fmBandDescriptorCompared); } @@ -369,19 +372,19 @@ public final class RadioManagerTest { public void equals_withSameAmBandDescriptors_returnsTrue() { RadioManager.AmBandDescriptor amBandDescriptorCompared = createAmBandDescriptor(); - assertWithMessage("The same AM Band Descriptor") + mExpect.withMessage("The same AM Band Descriptor") .that(AM_BAND_DESCRIPTOR).isEqualTo(amBandDescriptorCompared); } @Test public void equals_withAmBandDescriptorsAndOtherTypeObject() { - assertWithMessage("AM Band Descriptor") + mExpect.withMessage("AM Band Descriptor") .that(AM_BAND_DESCRIPTOR).isNotEqualTo(FM_BAND_DESCRIPTOR); } @Test public void equals_withFmBandDescriptorsAndOtherTypeObject() { - assertWithMessage("FM Band Descriptor") + mExpect.withMessage("FM Band Descriptor") .that(FM_BAND_DESCRIPTOR).isNotEqualTo(AM_BAND_DESCRIPTOR); } @@ -391,7 +394,7 @@ public final class RadioManagerTest { new RadioManager.AmBandDescriptor(REGION, RadioManager.BAND_AM, AM_LOWER_LIMIT, AM_UPPER_LIMIT + AM_SPACING, AM_SPACING, STEREO_SUPPORTED); - assertWithMessage("AM Band Descriptor of different upper limit") + mExpect.withMessage("AM Band Descriptor of different upper limit") .that(AM_BAND_DESCRIPTOR).isNotEqualTo(amBandDescriptorCompared); } @@ -401,7 +404,7 @@ public final class RadioManagerTest { new RadioManager.AmBandDescriptor(REGION, RadioManager.BAND_AM, AM_LOWER_LIMIT, AM_UPPER_LIMIT, AM_SPACING, !STEREO_SUPPORTED); - assertWithMessage("AM Band Descriptor of different stereo support values") + mExpect.withMessage("AM Band Descriptor of different stereo support values") .that(AM_BAND_DESCRIPTOR).isNotEqualTo(amBandDescriptorCompared); } @@ -411,7 +414,7 @@ public final class RadioManagerTest { REGION, RadioManager.BAND_FM, FM_LOWER_LIMIT, FM_UPPER_LIMIT, FM_SPACING * 2, STEREO_SUPPORTED, RDS_SUPPORTED, TA_SUPPORTED, AF_SUPPORTED, EA_SUPPORTED); - assertWithMessage("FM Band Descriptors of different support limit values") + mExpect.withMessage("FM Band Descriptors of different support limit values") .that(FM_BAND_DESCRIPTOR).isNotEqualTo(fmBandDescriptorCompared); } @@ -421,7 +424,7 @@ public final class RadioManagerTest { REGION + 1, RadioManager.BAND_AM_HD, AM_LOWER_LIMIT, AM_UPPER_LIMIT, AM_SPACING, STEREO_SUPPORTED, RDS_SUPPORTED, TA_SUPPORTED, AF_SUPPORTED, EA_SUPPORTED); - assertWithMessage("FM Band Descriptors of different region values") + mExpect.withMessage("FM Band Descriptors of different region values") .that(FM_BAND_DESCRIPTOR).isNotEqualTo(fmBandDescriptorCompared); } @@ -431,7 +434,7 @@ public final class RadioManagerTest { REGION, RadioManager.BAND_FM, FM_LOWER_LIMIT, FM_UPPER_LIMIT, FM_SPACING, !STEREO_SUPPORTED, RDS_SUPPORTED, TA_SUPPORTED, AF_SUPPORTED, EA_SUPPORTED); - assertWithMessage("FM Band Descriptors of different stereo support values") + mExpect.withMessage("FM Band Descriptors of different stereo support values") .that(fmBandDescriptorCompared).isNotEqualTo(FM_BAND_DESCRIPTOR); } @@ -441,7 +444,7 @@ public final class RadioManagerTest { REGION, RadioManager.BAND_FM, FM_LOWER_LIMIT, FM_UPPER_LIMIT, FM_SPACING, STEREO_SUPPORTED, !RDS_SUPPORTED, TA_SUPPORTED, AF_SUPPORTED, EA_SUPPORTED); - assertWithMessage("FM Band Descriptors of different rds support values") + mExpect.withMessage("FM Band Descriptors of different rds support values") .that(fmBandDescriptorCompared).isNotEqualTo(FM_BAND_DESCRIPTOR); } @@ -451,7 +454,7 @@ public final class RadioManagerTest { REGION, RadioManager.BAND_FM, FM_LOWER_LIMIT, FM_UPPER_LIMIT, FM_SPACING, STEREO_SUPPORTED, RDS_SUPPORTED, !TA_SUPPORTED, AF_SUPPORTED, EA_SUPPORTED); - assertWithMessage("FM Band Descriptors of different ta support values") + mExpect.withMessage("FM Band Descriptors of different ta support values") .that(fmBandDescriptorCompared).isNotEqualTo(FM_BAND_DESCRIPTOR); } @@ -461,7 +464,7 @@ public final class RadioManagerTest { REGION, RadioManager.BAND_FM, FM_LOWER_LIMIT, FM_UPPER_LIMIT, FM_SPACING, STEREO_SUPPORTED, RDS_SUPPORTED, TA_SUPPORTED, !AF_SUPPORTED, EA_SUPPORTED); - assertWithMessage("FM Band Descriptors of different af support values") + mExpect.withMessage("FM Band Descriptors of different af support values") .that(fmBandDescriptorCompared).isNotEqualTo(FM_BAND_DESCRIPTOR); } @@ -471,7 +474,7 @@ public final class RadioManagerTest { REGION, RadioManager.BAND_FM, FM_LOWER_LIMIT, FM_UPPER_LIMIT, FM_SPACING, STEREO_SUPPORTED, RDS_SUPPORTED, TA_SUPPORTED, AF_SUPPORTED, !EA_SUPPORTED); - assertWithMessage("FM Band Descriptors of different ea support values") + mExpect.withMessage("FM Band Descriptors of different ea support values") .that(fmBandDescriptorCompared).isNotEqualTo(FM_BAND_DESCRIPTOR); } @@ -479,7 +482,7 @@ public final class RadioManagerTest { public void hashCode_withSameFmBandDescriptors_equals() { RadioManager.FmBandDescriptor fmBandDescriptorCompared = createFmBandDescriptor(); - assertWithMessage("Hash code of the same FM Band Descriptor") + mExpect.withMessage("Hash code of the same FM Band Descriptor") .that(fmBandDescriptorCompared.hashCode()).isEqualTo(FM_BAND_DESCRIPTOR.hashCode()); } @@ -487,7 +490,7 @@ public final class RadioManagerTest { public void hashCode_withSameAmBandDescriptors_equals() { RadioManager.AmBandDescriptor amBandDescriptorCompared = createAmBandDescriptor(); - assertWithMessage("Hash code of the same AM Band Descriptor") + mExpect.withMessage("Hash code of the same AM Band Descriptor") .that(amBandDescriptorCompared.hashCode()).isEqualTo(AM_BAND_DESCRIPTOR.hashCode()); } @@ -497,7 +500,7 @@ public final class RadioManagerTest { REGION, RadioManager.BAND_FM, FM_LOWER_LIMIT, FM_UPPER_LIMIT, FM_SPACING, STEREO_SUPPORTED, RDS_SUPPORTED, TA_SUPPORTED, !AF_SUPPORTED, EA_SUPPORTED); - assertWithMessage("Hash code of FM Band Descriptor of different spacing") + mExpect.withMessage("Hash code of FM Band Descriptor of different spacing") .that(fmBandDescriptorCompared.hashCode()) .isNotEqualTo(FM_BAND_DESCRIPTOR.hashCode()); } @@ -508,7 +511,7 @@ public final class RadioManagerTest { new RadioManager.AmBandDescriptor(REGION, RadioManager.BAND_AM, AM_LOWER_LIMIT, AM_UPPER_LIMIT, AM_SPACING * 2, STEREO_SUPPORTED); - assertWithMessage("Hash code of AM Band Descriptor of different spacing") + mExpect.withMessage("Hash code of AM Band Descriptor of different spacing") .that(amBandDescriptorCompared.hashCode()) .isNotEqualTo(AM_BAND_DESCRIPTOR.hashCode()); } @@ -517,7 +520,7 @@ public final class RadioManagerTest { public void getType_forBandConfig() { RadioManager.BandConfig fmBandConfig = createFmBandConfig(); - assertWithMessage("FM Band Config type") + mExpect.withMessage("FM Band Config type") .that(fmBandConfig.getType()).isEqualTo(RadioManager.BAND_FM); } @@ -525,7 +528,7 @@ public final class RadioManagerTest { public void getRegion_forBandConfig() { RadioManager.BandConfig amBandConfig = createAmBandConfig(); - assertWithMessage("AM Band Config region") + mExpect.withMessage("AM Band Config region") .that(amBandConfig.getRegion()).isEqualTo(REGION); } @@ -533,7 +536,7 @@ public final class RadioManagerTest { public void getLowerLimit_forBandConfig() { RadioManager.BandConfig amBandConfig = createAmBandConfig(); - assertWithMessage("AM Band Config lower limit") + mExpect.withMessage("AM Band Config lower limit") .that(amBandConfig.getLowerLimit()).isEqualTo(AM_LOWER_LIMIT); } @@ -541,7 +544,7 @@ public final class RadioManagerTest { public void getUpperLimit_forBandConfig() { RadioManager.BandConfig fmBandConfig = createFmBandConfig(); - assertWithMessage("FM Band Config upper limit") + mExpect.withMessage("FM Band Config upper limit") .that(fmBandConfig.getUpperLimit()).isEqualTo(FM_UPPER_LIMIT); } @@ -549,7 +552,7 @@ public final class RadioManagerTest { public void getSpacing_forBandConfig() { RadioManager.BandConfig fmBandConfig = createFmBandConfig(); - assertWithMessage("FM Band Config spacing") + mExpect.withMessage("FM Band Config spacing") .that(fmBandConfig.getSpacing()).isEqualTo(FM_SPACING); } @@ -557,7 +560,7 @@ public final class RadioManagerTest { public void describeContents_forBandConfig() { RadioManager.BandConfig bandConfig = createFmBandConfig(); - assertWithMessage("FM Band Config contents") + mExpect.withMessage("FM Band Config contents") .that(bandConfig.describeContents()).isEqualTo(0); } @@ -571,7 +574,7 @@ public final class RadioManagerTest { RadioManager.BandConfig bandConfigFromParcel = RadioManager.BandConfig.CREATOR.createFromParcel(parcel); - assertWithMessage("Band Config created from parcel") + mExpect.withMessage("Band Config created from parcel") .that(bandConfigFromParcel).isEqualTo(bandConfig); } @@ -580,42 +583,42 @@ public final class RadioManagerTest { RadioManager.BandConfig[] bandConfigs = RadioManager.BandConfig.CREATOR.newArray(CREATOR_ARRAY_SIZE); - assertWithMessage("Band Configs").that(bandConfigs).hasLength(CREATOR_ARRAY_SIZE); + mExpect.withMessage("Band Configs").that(bandConfigs).hasLength(CREATOR_ARRAY_SIZE); } @Test public void getStereo_forFmBandConfig() { - assertWithMessage("FM Band Config stereo") + mExpect.withMessage("FM Band Config stereo") .that(FM_BAND_CONFIG.getStereo()).isEqualTo(STEREO_SUPPORTED); } @Test public void getRds_forFmBandConfig() { - assertWithMessage("FM Band Config RDS or RBDS") + mExpect.withMessage("FM Band Config RDS or RBDS") .that(FM_BAND_CONFIG.getRds()).isEqualTo(RDS_SUPPORTED); } @Test public void getTa_forFmBandConfig() { - assertWithMessage("FM Band Config traffic announcement") + mExpect.withMessage("FM Band Config traffic announcement") .that(FM_BAND_CONFIG.getTa()).isEqualTo(TA_SUPPORTED); } @Test public void getAf_forFmBandConfig() { - assertWithMessage("FM Band Config alternate frequency") + mExpect.withMessage("FM Band Config alternate frequency") .that(FM_BAND_CONFIG.getAf()).isEqualTo(AF_SUPPORTED); } @Test public void getEa_forFmBandConfig() { - assertWithMessage("FM Band Config emergency Announcement") + mExpect.withMessage("FM Band Config emergency Announcement") .that(FM_BAND_CONFIG.getEa()).isEqualTo(EA_SUPPORTED); } @Test public void describeContents_forFmBandConfig() { - assertWithMessage("FM Band Config contents") + mExpect.withMessage("FM Band Config contents") .that(FM_BAND_CONFIG.describeContents()).isEqualTo(0); } @@ -628,7 +631,7 @@ public final class RadioManagerTest { RadioManager.FmBandConfig fmBandConfigFromParcel = RadioManager.FmBandConfig.CREATOR.createFromParcel(parcel); - assertWithMessage("FM Band Config created from parcel") + mExpect.withMessage("FM Band Config created from parcel") .that(fmBandConfigFromParcel).isEqualTo(FM_BAND_CONFIG); } @@ -637,18 +640,18 @@ public final class RadioManagerTest { RadioManager.FmBandConfig[] fmBandConfigs = RadioManager.FmBandConfig.CREATOR.newArray(CREATOR_ARRAY_SIZE); - assertWithMessage("FM Band Configs").that(fmBandConfigs).hasLength(CREATOR_ARRAY_SIZE); + mExpect.withMessage("FM Band Configs").that(fmBandConfigs).hasLength(CREATOR_ARRAY_SIZE); } @Test public void getStereo_forAmBandConfig() { - assertWithMessage("AM Band Config stereo") + mExpect.withMessage("AM Band Config stereo") .that(AM_BAND_CONFIG.getStereo()).isEqualTo(STEREO_SUPPORTED); } @Test public void describeContents_forAmBandConfig() { - assertWithMessage("AM Band Config contents") + mExpect.withMessage("AM Band Config contents") .that(AM_BAND_CONFIG.describeContents()).isEqualTo(0); } @@ -661,7 +664,7 @@ public final class RadioManagerTest { RadioManager.AmBandConfig amBandConfigFromParcel = RadioManager.AmBandConfig.CREATOR.createFromParcel(parcel); - assertWithMessage("AM Band Config created from parcel") + mExpect.withMessage("AM Band Config created from parcel") .that(amBandConfigFromParcel).isEqualTo(AM_BAND_CONFIG); } @@ -670,7 +673,7 @@ public final class RadioManagerTest { RadioManager.AmBandConfig[] amBandConfigs = RadioManager.AmBandConfig.CREATOR.newArray(CREATOR_ARRAY_SIZE); - assertWithMessage("AM Band Configs").that(amBandConfigs).hasLength(CREATOR_ARRAY_SIZE); + mExpect.withMessage("AM Band Configs").that(amBandConfigs).hasLength(CREATOR_ARRAY_SIZE); } @Test @@ -679,7 +682,7 @@ public final class RadioManagerTest { new RadioManager.FmBandConfig.Builder(FM_BAND_CONFIG); RadioManager.FmBandConfig fmBandConfigCompared = builder.build(); - assertWithMessage("The same FM Band Config") + mExpect.withMessage("The same FM Band Config") .that(FM_BAND_CONFIG).isEqualTo(fmBandConfigCompared); } @@ -690,7 +693,7 @@ public final class RadioManagerTest { AM_LOWER_LIMIT, AM_UPPER_LIMIT, AM_SPACING, STEREO_SUPPORTED, RDS_SUPPORTED, TA_SUPPORTED, AF_SUPPORTED, EA_SUPPORTED)); - assertWithMessage("FM Band Config of different regions") + mExpect.withMessage("FM Band Config of different regions") .that(FM_BAND_CONFIG).isNotEqualTo(fmBandConfigCompared); } @@ -701,7 +704,7 @@ public final class RadioManagerTest { FM_UPPER_LIMIT, FM_SPACING, !STEREO_SUPPORTED, RDS_SUPPORTED, TA_SUPPORTED, AF_SUPPORTED, EA_SUPPORTED)); - assertWithMessage("FM Band Config with different stereo support values") + mExpect.withMessage("FM Band Config with different stereo support values") .that(fmBandConfigCompared).isNotEqualTo(FM_BAND_CONFIG); } @@ -712,7 +715,7 @@ public final class RadioManagerTest { FM_UPPER_LIMIT, FM_SPACING, STEREO_SUPPORTED, !RDS_SUPPORTED, TA_SUPPORTED, AF_SUPPORTED, EA_SUPPORTED)); - assertWithMessage("FM Band Config with different RDS support values") + mExpect.withMessage("FM Band Config with different RDS support values") .that(fmBandConfigCompared).isNotEqualTo(FM_BAND_CONFIG); } @@ -723,7 +726,7 @@ public final class RadioManagerTest { FM_UPPER_LIMIT, FM_SPACING, STEREO_SUPPORTED, RDS_SUPPORTED, !TA_SUPPORTED, AF_SUPPORTED, EA_SUPPORTED)); - assertWithMessage("FM Band Configs with different ta values") + mExpect.withMessage("FM Band Configs with different ta values") .that(fmBandConfigCompared).isNotEqualTo(FM_BAND_CONFIG); } @@ -734,7 +737,7 @@ public final class RadioManagerTest { .setTa(TA_SUPPORTED).setAf(!AF_SUPPORTED).setEa(EA_SUPPORTED); RadioManager.FmBandConfig fmBandConfigCompared = builder.build(); - assertWithMessage("FM Band Config of different af support value") + mExpect.withMessage("FM Band Config of different af support value") .that(FM_BAND_CONFIG).isNotEqualTo(fmBandConfigCompared); } @@ -745,19 +748,19 @@ public final class RadioManagerTest { FM_UPPER_LIMIT, FM_SPACING, STEREO_SUPPORTED, RDS_SUPPORTED, TA_SUPPORTED, AF_SUPPORTED, !EA_SUPPORTED)); - assertWithMessage("FM Band Configs with different ea support values") + mExpect.withMessage("FM Band Configs with different ea support values") .that(fmBandConfigCompared).isNotEqualTo(FM_BAND_CONFIG); } @Test public void equals_withAmBandConfigsAndOtherTypeObject() { - assertWithMessage("AM Band Config") + mExpect.withMessage("AM Band Config") .that(AM_BAND_CONFIG).isNotEqualTo(FM_BAND_CONFIG); } @Test public void equals_withFmBandConfigsAndOtherTypeObject() { - assertWithMessage("FM Band Config") + mExpect.withMessage("FM Band Config") .that(FM_BAND_CONFIG).isNotEqualTo(AM_BAND_CONFIG); } @@ -767,7 +770,7 @@ public final class RadioManagerTest { new RadioManager.AmBandConfig.Builder(AM_BAND_CONFIG); RadioManager.AmBandConfig amBandConfigCompared = builder.build(); - assertWithMessage("The same AM Band Config") + mExpect.withMessage("The same AM Band Config") .that(AM_BAND_CONFIG).isEqualTo(amBandConfigCompared); } @@ -777,7 +780,7 @@ public final class RadioManagerTest { new RadioManager.AmBandDescriptor(REGION, RadioManager.BAND_AM_HD, AM_LOWER_LIMIT, AM_UPPER_LIMIT, AM_SPACING, STEREO_SUPPORTED)); - assertWithMessage("AM Band Config of different type") + mExpect.withMessage("AM Band Config of different type") .that(AM_BAND_CONFIG).isNotEqualTo(amBandConfigCompared); } @@ -787,7 +790,7 @@ public final class RadioManagerTest { createAmBandDescriptor()).setStereo(!STEREO_SUPPORTED); RadioManager.AmBandConfig amBandConfigFromBuilder = builder.build(); - assertWithMessage("AM Band Config of different stereo value") + mExpect.withMessage("AM Band Config of different stereo value") .that(AM_BAND_CONFIG).isNotEqualTo(amBandConfigFromBuilder); } @@ -795,7 +798,7 @@ public final class RadioManagerTest { public void hashCode_withSameFmBandConfigs_equals() { RadioManager.FmBandConfig fmBandConfigCompared = createFmBandConfig(); - assertWithMessage("Hash code of the same FM Band Config") + mExpect.withMessage("Hash code of the same FM Band Config") .that(FM_BAND_CONFIG.hashCode()).isEqualTo(fmBandConfigCompared.hashCode()); } @@ -803,7 +806,7 @@ public final class RadioManagerTest { public void hashCode_withSameAmBandConfigs_equals() { RadioManager.AmBandConfig amBandConfigCompared = createAmBandConfig(); - assertWithMessage("Hash code of the same AM Band Config") + mExpect.withMessage("Hash code of the same AM Band Config") .that(amBandConfigCompared.hashCode()).isEqualTo(AM_BAND_CONFIG.hashCode()); } @@ -814,7 +817,7 @@ public final class RadioManagerTest { FM_UPPER_LIMIT, FM_SPACING, STEREO_SUPPORTED, RDS_SUPPORTED, TA_SUPPORTED, AF_SUPPORTED, EA_SUPPORTED)); - assertWithMessage("Hash code of FM Band Config with different type") + mExpect.withMessage("Hash code of FM Band Config with different type") .that(fmBandConfigCompared.hashCode()).isNotEqualTo(FM_BAND_CONFIG.hashCode()); } @@ -824,87 +827,87 @@ public final class RadioManagerTest { new RadioManager.AmBandDescriptor(REGION, RadioManager.BAND_AM, AM_LOWER_LIMIT, AM_UPPER_LIMIT, AM_SPACING, !STEREO_SUPPORTED)); - assertWithMessage("Hash code of AM Band Config with different stereo support") + mExpect.withMessage("Hash code of AM Band Config with different stereo support") .that(amBandConfigCompared.hashCode()).isNotEqualTo(AM_BAND_CONFIG.hashCode()); } @Test public void getId_forModuleProperties() { - assertWithMessage("Properties id") + mExpect.withMessage("Properties id") .that(AMFM_PROPERTIES.getId()).isEqualTo(PROPERTIES_ID); } @Test public void getServiceName_forModuleProperties() { - assertWithMessage("Properties service name") + mExpect.withMessage("Properties service name") .that(AMFM_PROPERTIES.getServiceName()).isEqualTo(SERVICE_NAME); } @Test public void getClassId_forModuleProperties() { - assertWithMessage("Properties class ID") + mExpect.withMessage("Properties class ID") .that(AMFM_PROPERTIES.getClassId()).isEqualTo(CLASS_ID); } @Test public void getImplementor_forModuleProperties() { - assertWithMessage("Properties implementor") + mExpect.withMessage("Properties implementor") .that(AMFM_PROPERTIES.getImplementor()).isEqualTo(IMPLEMENTOR); } @Test public void getProduct_forModuleProperties() { - assertWithMessage("Properties product") + mExpect.withMessage("Properties product") .that(AMFM_PROPERTIES.getProduct()).isEqualTo(PRODUCT); } @Test public void getVersion_forModuleProperties() { - assertWithMessage("Properties version") + mExpect.withMessage("Properties version") .that(AMFM_PROPERTIES.getVersion()).isEqualTo(VERSION); } @Test public void getSerial_forModuleProperties() { - assertWithMessage("Serial properties") + mExpect.withMessage("Serial properties") .that(AMFM_PROPERTIES.getSerial()).isEqualTo(SERIAL); } @Test public void getNumTuners_forModuleProperties() { - assertWithMessage("Number of tuners in properties") + mExpect.withMessage("Number of tuners in properties") .that(AMFM_PROPERTIES.getNumTuners()).isEqualTo(NUM_TUNERS); } @Test public void getNumAudioSources_forModuleProperties() { - assertWithMessage("Number of audio sources in properties") + mExpect.withMessage("Number of audio sources in properties") .that(AMFM_PROPERTIES.getNumAudioSources()).isEqualTo(NUM_AUDIO_SOURCES); } @Test public void isInitializationRequired_forModuleProperties() { - assertWithMessage("Initialization required in properties") + mExpect.withMessage("Initialization required in properties") .that(AMFM_PROPERTIES.isInitializationRequired()) .isEqualTo(IS_INITIALIZATION_REQUIRED); } @Test public void isCaptureSupported_forModuleProperties() { - assertWithMessage("Capture support in properties") + mExpect.withMessage("Capture support in properties") .that(AMFM_PROPERTIES.isCaptureSupported()).isEqualTo(IS_CAPTURE_SUPPORTED); } @Test public void isBackgroundScanningSupported_forModuleProperties() { - assertWithMessage("Background scan support in properties") + mExpect.withMessage("Background scan support in properties") .that(AMFM_PROPERTIES.isBackgroundScanningSupported()) .isEqualTo(IS_BG_SCAN_SUPPORTED); } @Test public void isProgramTypeSupported_withSupportedType_forModuleProperties() { - assertWithMessage("AM/FM frequency type radio support in properties") + mExpect.withMessage("AM/FM frequency type radio support in properties") .that(AMFM_PROPERTIES.isProgramTypeSupported( ProgramSelector.IDENTIFIER_TYPE_AMFM_FREQUENCY)) .isTrue(); @@ -912,28 +915,28 @@ public final class RadioManagerTest { @Test public void isProgramTypeSupported_withNonSupportedType_forModuleProperties() { - assertWithMessage("DAB frequency type radio support in properties") + mExpect.withMessage("DAB frequency type radio support in properties") .that(AMFM_PROPERTIES.isProgramTypeSupported( ProgramSelector.IDENTIFIER_TYPE_DAB_FREQUENCY)).isFalse(); } @Test public void isProgramIdentifierSupported_withSupportedIdentifier_forModuleProperties() { - assertWithMessage("AM/FM frequency identifier radio support in properties") + mExpect.withMessage("AM/FM frequency identifier radio support in properties") .that(AMFM_PROPERTIES.isProgramIdentifierSupported( ProgramSelector.IDENTIFIER_TYPE_AMFM_FREQUENCY)).isTrue(); } @Test public void isProgramIdentifierSupported_withNonSupportedIdentifier_forModuleProperties() { - assertWithMessage("DAB frequency identifier radio support in properties") + mExpect.withMessage("DAB frequency identifier radio support in properties") .that(AMFM_PROPERTIES.isProgramIdentifierSupported( ProgramSelector.IDENTIFIER_TYPE_DAB_FREQUENCY)).isFalse(); } @Test public void getDabFrequencyTable_forModulePropertiesInitializedWithNullTable() { - assertWithMessage("Properties DAB frequency table") + mExpect.withMessage("Properties DAB frequency table") .that(AMFM_PROPERTIES.getDabFrequencyTable()).isNull(); } @@ -941,32 +944,32 @@ public final class RadioManagerTest { public void getDabFrequencyTable_forModulePropertiesInitializedWithEmptyTable() { RadioManager.ModuleProperties properties = createAmFmProperties(new ArrayMap<>()); - assertWithMessage("Properties DAB frequency table") + mExpect.withMessage("Properties DAB frequency table") .that(properties.getDabFrequencyTable()).isNull(); } @Test public void getVendorInfo_forModuleProperties() { - assertWithMessage("Properties vendor info") + mExpect.withMessage("Properties vendor info") .that(AMFM_PROPERTIES.getVendorInfo()).isEmpty(); } @Test public void getBands_forModuleProperties() { - assertWithMessage("Properties bands") + mExpect.withMessage("Properties bands") .that(AMFM_PROPERTIES.getBands()).asList() .containsExactly(AM_BAND_DESCRIPTOR, FM_BAND_DESCRIPTOR); } @Test public void describeContents_forModuleProperties() { - assertWithMessage("Module properties contents") + mExpect.withMessage("Module properties contents") .that(AMFM_PROPERTIES.describeContents()).isEqualTo(0); } @Test public void toString_forModuleProperties() { - assertWithMessage("Module properties string").that(AMFM_PROPERTIES.toString()) + mExpect.withMessage("Module properties string").that(AMFM_PROPERTIES.toString()) .contains(AM_BAND_DESCRIPTOR.toString() + ", " + FM_BAND_DESCRIPTOR.toString()); } @@ -979,7 +982,7 @@ public final class RadioManagerTest { RadioManager.ModuleProperties modulePropertiesFromParcel = RadioManager.ModuleProperties.CREATOR.createFromParcel(parcel); - assertWithMessage("Module properties created from parcel") + mExpect.withMessage("Module properties created from parcel") .that(modulePropertiesFromParcel).isEqualTo(AMFM_PROPERTIES); } @@ -994,7 +997,7 @@ public final class RadioManagerTest { RadioManager.ModuleProperties modulePropertiesFromParcel = RadioManager.ModuleProperties.CREATOR.createFromParcel(parcel); - assertWithMessage("Module properties created from parcel") + mExpect.withMessage("Module properties created from parcel") .that(modulePropertiesFromParcel).isEqualTo(propertiesToParcel); } @@ -1003,7 +1006,7 @@ public final class RadioManagerTest { RadioManager.ModuleProperties propertiesCompared = createAmFmProperties(/* dabFrequencyTable= */ null); - assertWithMessage("The same module properties") + mExpect.withMessage("The same module properties") .that(AMFM_PROPERTIES).isEqualTo(propertiesCompared); } @@ -1016,7 +1019,7 @@ public final class RadioManagerTest { SUPPORTED_PROGRAM_TYPES, SUPPORTED_IDENTIFIERS_TYPES, Map.of("5A", 174928), /* vendorInfo= */ null); - assertWithMessage("Module properties of different id") + mExpect.withMessage("Module properties of different id") .that(AMFM_PROPERTIES).isNotEqualTo(propertiesDab); } @@ -1025,7 +1028,7 @@ public final class RadioManagerTest { RadioManager.ModuleProperties propertiesCompared = createAmFmProperties(/* dabFrequencyTable= */ null); - assertWithMessage("Hash code of the same module properties") + mExpect.withMessage("Hash code of the same module properties") .that(propertiesCompared.hashCode()).isEqualTo(AMFM_PROPERTIES.hashCode()); } @@ -1034,86 +1037,86 @@ public final class RadioManagerTest { RadioManager.ModuleProperties[] modulePropertiesArray = RadioManager.ModuleProperties.CREATOR.newArray(CREATOR_ARRAY_SIZE); - assertWithMessage("Module properties array") + mExpect.withMessage("Module properties array") .that(modulePropertiesArray).hasLength(CREATOR_ARRAY_SIZE); } @Test public void getSelector_forProgramInfo() { - assertWithMessage("Selector of DAB program info") + mExpect.withMessage("Selector of DAB program info") .that(DAB_PROGRAM_INFO.getSelector()).isEqualTo(DAB_SELECTOR); } @Test public void getLogicallyTunedTo_forProgramInfo() { - assertWithMessage("Identifier logically tuned to in DAB program info") + mExpect.withMessage("Identifier logically tuned to in DAB program info") .that(DAB_PROGRAM_INFO.getLogicallyTunedTo()).isEqualTo(DAB_SID_EXT_IDENTIFIER); } @Test public void getPhysicallyTunedTo_forProgramInfo() { - assertWithMessage("Identifier physically tuned to DAB program info") + mExpect.withMessage("Identifier physically tuned to DAB program info") .that(DAB_PROGRAM_INFO.getPhysicallyTunedTo()).isEqualTo(DAB_FREQUENCY_IDENTIFIER); } @Test public void getRelatedContent_forProgramInfo() { - assertWithMessage("DAB program info contents") + mExpect.withMessage("DAB program info contents") .that(DAB_PROGRAM_INFO.getRelatedContent()) .containsExactly(DAB_SID_EXT_IDENTIFIER_RELATED); } @Test public void getChannel_forProgramInfo() { - assertWithMessage("Main channel of DAB program info") + mExpect.withMessage("Main channel of DAB program info") .that(DAB_PROGRAM_INFO.getChannel()).isEqualTo(0); } @Test public void getSubChannel_forProgramInfo() { - assertWithMessage("Sub channel of DAB program info") + mExpect.withMessage("Sub channel of DAB program info") .that(DAB_PROGRAM_INFO.getSubChannel()).isEqualTo(0); } @Test public void isTuned_forProgramInfo() { - assertWithMessage("Tuned status of DAB program info") + mExpect.withMessage("Tuned status of DAB program info") .that(DAB_PROGRAM_INFO.isTuned()).isTrue(); } @Test public void isStereo_forProgramInfo() { - assertWithMessage("Stereo support in DAB program info") + mExpect.withMessage("Stereo support in DAB program info") .that(DAB_PROGRAM_INFO.isStereo()).isTrue(); } @Test public void isDigital_forProgramInfo() { - assertWithMessage("Digital DAB program info") + mExpect.withMessage("Digital DAB program info") .that(DAB_PROGRAM_INFO.isDigital()).isTrue(); } @Test public void isLive_forProgramInfo() { - assertWithMessage("Live status of DAB program info") + mExpect.withMessage("Live status of DAB program info") .that(DAB_PROGRAM_INFO.isLive()).isTrue(); } @Test public void isMuted_forProgramInfo() { - assertWithMessage("Muted status of DAB program info") + mExpect.withMessage("Muted status of DAB program info") .that(DAB_PROGRAM_INFO.isMuted()).isFalse(); } @Test public void isTrafficProgram_forProgramInfo() { - assertWithMessage("Traffic program support in DAB program info") + mExpect.withMessage("Traffic program support in DAB program info") .that(DAB_PROGRAM_INFO.isTrafficProgram()).isFalse(); } @Test public void isTrafficAnnouncementActive_forProgramInfo() { - assertWithMessage("Active traffic announcement for DAB program info") + mExpect.withMessage("Active traffic announcement for DAB program info") .that(DAB_PROGRAM_INFO.isTrafficAnnouncementActive()).isFalse(); } @@ -1121,7 +1124,7 @@ public final class RadioManagerTest { public void isSignalAcquired_forProgramInfo() { mSetFlagsRule.enableFlags(Flags.FLAG_HD_RADIO_IMPROVED); - assertWithMessage("Signal acquisition status for HD program info") + mExpect.withMessage("Signal acquisition status for HD program info") .that(HD_PROGRAM_INFO.isSignalAcquired()).isTrue(); } @@ -1129,7 +1132,7 @@ public final class RadioManagerTest { public void isHdSisAvailable_forProgramInfo() { mSetFlagsRule.enableFlags(Flags.FLAG_HD_RADIO_IMPROVED); - assertWithMessage("SIS information acquisition status for HD program") + mExpect.withMessage("SIS information acquisition status for HD program") .that(HD_PROGRAM_INFO.isHdSisAvailable()).isTrue(); } @@ -1137,31 +1140,31 @@ public final class RadioManagerTest { public void isHdAudioAvailable_forProgramInfo() { mSetFlagsRule.enableFlags(Flags.FLAG_HD_RADIO_IMPROVED); - assertWithMessage("Audio acquisition status for HD program") + mExpect.withMessage("Audio acquisition status for HD program") .that(HD_PROGRAM_INFO.isHdAudioAvailable()).isFalse(); } @Test public void getSignalStrength_forProgramInfo() { - assertWithMessage("Signal strength of DAB program info") + mExpect.withMessage("Signal strength of DAB program info") .that(DAB_PROGRAM_INFO.getSignalStrength()).isEqualTo(SIGNAL_QUALITY); } @Test public void getMetadata_forProgramInfo() { - assertWithMessage("Metadata of DAB program info") + mExpect.withMessage("Metadata of DAB program info") .that(DAB_PROGRAM_INFO.getMetadata()).isEqualTo(METADATA); } @Test public void getVendorInfo_forProgramInfo() { - assertWithMessage("Vendor info of DAB program info") + mExpect.withMessage("Vendor info of DAB program info") .that(DAB_PROGRAM_INFO.getVendorInfo()).isEmpty(); } @Test public void describeContents_forProgramInfo() { - assertWithMessage("Program info contents") + mExpect.withMessage("Program info contents") .that(DAB_PROGRAM_INFO.describeContents()).isEqualTo(0); } @@ -1170,7 +1173,7 @@ public final class RadioManagerTest { RadioManager.ProgramInfo[] programInfoArray = RadioManager.ProgramInfo.CREATOR.newArray(CREATOR_ARRAY_SIZE); - assertWithMessage("Program infos").that(programInfoArray).hasLength(CREATOR_ARRAY_SIZE); + mExpect.withMessage("Program infos").that(programInfoArray).hasLength(CREATOR_ARRAY_SIZE); } @Test @@ -1182,7 +1185,7 @@ public final class RadioManagerTest { RadioManager.ProgramInfo programInfoFromParcel = RadioManager.ProgramInfo.CREATOR.createFromParcel(parcel); - assertWithMessage("Program info created from parcel") + mExpect.withMessage("Program info created from parcel") .that(programInfoFromParcel).isEqualTo(DAB_PROGRAM_INFO); } @@ -1190,7 +1193,7 @@ public final class RadioManagerTest { public void equals_withSameProgramInfo_returnsTrue() { RadioManager.ProgramInfo dabProgramInfoCompared = createDabProgramInfo(DAB_SELECTOR); - assertWithMessage("The same program info") + mExpect.withMessage("The same program info") .that(dabProgramInfoCompared).isEqualTo(DAB_PROGRAM_INFO); } @@ -1202,7 +1205,7 @@ public final class RadioManagerTest { /* vendorIds= */ null); RadioManager.ProgramInfo dabProgramInfoCompared = createDabProgramInfo(dabSelectorCompared); - assertWithMessage("Program info with different secondary id selectors") + mExpect.withMessage("Program info with different secondary id selectors") .that(DAB_PROGRAM_INFO).isNotEqualTo(dabProgramInfoCompared); } @@ -1213,7 +1216,7 @@ public final class RadioManagerTest { mRadioManager.listModules(modules); - assertWithMessage("Modules in radio manager") + mExpect.withMessage("Modules in radio manager") .that(modules).containsExactly(AMFM_PROPERTIES); } @@ -1221,7 +1224,7 @@ public final class RadioManagerTest { public void listModules_forRadioManagerWithNullListAsInput_fails() throws Exception { createRadioManager(); - assertWithMessage("Status when listing module with empty list input") + mExpect.withMessage("Status when listing module with empty list input") .that(mRadioManager.listModules(null)).isEqualTo(RadioManager.STATUS_BAD_VALUE); } @@ -1231,7 +1234,7 @@ public final class RadioManagerTest { when(mRadioServiceMock.listModules()).thenReturn(null); List<RadioManager.ModuleProperties> modules = new ArrayList<>(); - assertWithMessage("Status for listing module when getting null list from HAL client") + mExpect.withMessage("Status for listing module when getting null list from HAL client") .that(mRadioManager.listModules(modules)).isEqualTo(RadioManager.STATUS_ERROR); } @@ -1241,7 +1244,7 @@ public final class RadioManagerTest { when(mRadioServiceMock.listModules()).thenThrow(new RemoteException()); List<RadioManager.ModuleProperties> modules = new ArrayList<>(); - assertWithMessage("Status for listing module when HAL client service is dead") + mExpect.withMessage("Status for listing module when HAL client service is dead") .that(mRadioManager.listModules(modules)) .isEqualTo(RadioManager.STATUS_DEAD_OBJECT); } @@ -1267,7 +1270,21 @@ public final class RadioManagerTest { RadioTuner nullTuner = mRadioManager.openTuner(/* moduleId= */ 0, FM_BAND_CONFIG, /* withAudio= */ true, mCallbackMock, /* handler= */ null); - assertWithMessage("Radio tuner when service is dead").that(nullTuner).isNull(); + mExpect.withMessage("Radio tuner when service is dead").that(nullTuner).isNull(); + } + + @Test + public void openTuner_withNullCallback() throws Exception { + createRadioManager(); + int moduleId = 0; + boolean withAudio = true; + + IllegalArgumentException thrown = assertThrows(IllegalArgumentException.class, + () -> mRadioManager.openTuner(moduleId, FM_BAND_CONFIG, withAudio, + /* callback= */ null, /* handler= */ null)); + + mExpect.withMessage("Null tuner callback exception").that(thrown) + .hasMessageThat().contains("callback must not be empty"); } @Test @@ -1323,7 +1340,7 @@ public final class RadioManagerTest { RuntimeException thrown = assertThrows(RuntimeException.class, () -> mRadioManager.addAnnouncementListener(enableTypeSet, mEventListener)); - assertWithMessage("Exception for adding announcement listener with dead service") + mExpect.withMessage("Exception for adding announcement listener with dead service") .that(thrown).hasMessageThat().contains(exceptionMessage); } diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/AnnouncementAggregatorTest.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/AnnouncementAggregatorTest.java index 0e0dbecb7df3..2bf0aa3bfaa4 100644 --- a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/AnnouncementAggregatorTest.java +++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/AnnouncementAggregatorTest.java @@ -16,11 +16,11 @@ package com.android.server.broadcastradio.aidl; -import static com.google.common.truth.Truth.assertWithMessage; - import static org.junit.Assert.assertThrows; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; @@ -33,7 +33,10 @@ import android.hardware.radio.ICloseHandle; import android.os.IBinder; import android.os.RemoteException; +import com.google.common.truth.Expect; + import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; @@ -54,6 +57,9 @@ public final class AnnouncementAggregatorTest { private AnnouncementAggregator mAnnouncementAggregator; private IBinder.DeathRecipient mDeathRecipient; + @Rule + public final Expect mExpect = Expect.create(); + @Mock private IAnnouncementListener mListenerMock; @Mock @@ -75,6 +81,18 @@ public final class AnnouncementAggregatorTest { } @Test + public void constructor_withBinderDied() throws Exception { + RemoteException remoteException = new RemoteException("Binder is died"); + doThrow(remoteException).when(mBinderMock).linkToDeath(any(), anyInt()); + + RuntimeException thrown = assertThrows(RuntimeException.class, () -> + new AnnouncementAggregator(mListenerMock, mLock)); + + mExpect.withMessage("Exception for dead binder").that(thrown).hasMessageThat() + .contains(remoteException.getMessage()); + } + + @Test public void onListUpdated_withOneModuleWatcher() throws Exception { ArgumentCaptor<IAnnouncementListener> moduleWatcherCaptor = ArgumentCaptor.forClass(IAnnouncementListener.class); @@ -103,7 +121,7 @@ public final class AnnouncementAggregatorTest { moduleWatcherCaptor.getValue().onListUpdated(Arrays.asList(mAnnouncementMocks[index])); verify(mListenerMock, times(index + 1)).onListUpdated(announcementsCaptor.capture()); - assertWithMessage("Number of announcements %s after %s announcements were updated", + mExpect.withMessage("Number of announcements %s after %s announcements were updated", announcementsCaptor.getValue(), index + 1) .that(announcementsCaptor.getValue().size()).isEqualTo(index + 1); } @@ -131,7 +149,7 @@ public final class AnnouncementAggregatorTest { () -> mAnnouncementAggregator.watchModule(mRadioModuleMocks[0], TEST_ENABLED_TYPES)); - assertWithMessage("Exception for watching module after aggregator has been closed") + mExpect.withMessage("Exception for watching module after aggregator has been closed") .that(thrown).hasMessageThat() .contains("announcement aggregator has already been closed"); } diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/ConversionUtilsTest.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/ConversionUtilsTest.java index 8d9fad999624..42501c1bf461 100644 --- a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/ConversionUtilsTest.java +++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/ConversionUtilsTest.java @@ -27,6 +27,7 @@ import android.hardware.broadcastradio.ConfigFlag; import android.hardware.broadcastradio.DabTableEntry; import android.hardware.broadcastradio.IdentifierType; import android.hardware.broadcastradio.Metadata; +import android.hardware.broadcastradio.ProgramFilter; import android.hardware.broadcastradio.ProgramIdentifier; import android.hardware.broadcastradio.ProgramInfo; import android.hardware.broadcastradio.Properties; @@ -41,6 +42,7 @@ import android.hardware.radio.RadioMetadata; import android.hardware.radio.UniqueProgramIdentifier; import android.os.ServiceSpecificException; import android.platform.test.flag.junit.SetFlagsRule; +import android.util.ArraySet; import com.android.dx.mockito.inline.extended.StaticMockitoSessionBuilder; import com.android.server.broadcastradio.ExtendedRadioMockitoTestCase; @@ -93,6 +95,11 @@ public final class ConversionUtilsTest extends ExtendedRadioMockitoTestCase { private static final long TEST_HD_LOCATION_VALUE = 0x4E647007665CF6L; private static final long TEST_VENDOR_ID_VALUE = 9_901; + private static final ProgramSelector.Identifier TEST_INVALID_ID = + new ProgramSelector.Identifier(ProgramSelector.IDENTIFIER_TYPE_INVALID, 1); + private static final ProgramIdentifier TEST_HAL_INVALID_ID = + AidlTestUtils.makeHalIdentifier(IdentifierType.INVALID, 1); + private static final ProgramSelector.Identifier TEST_DAB_SID_EXT_ID = new ProgramSelector.Identifier( ProgramSelector.IDENTIFIER_TYPE_DAB_DMB_SID_EXT, TEST_DAB_DMB_SID_EXT_VALUE); @@ -139,7 +146,7 @@ public final class ConversionUtilsTest extends ExtendedRadioMockitoTestCase { private static final int TEST_ANNOUNCEMENT_FREQUENCY = FM_LOWER_LIMIT + FM_SPACING; private static final RadioManager.ModuleProperties MODULE_PROPERTIES = - convertToModuleProperties(); + createModuleProperties(); private static final Announcement ANNOUNCEMENT = ConversionUtils.announcementFromHalAnnouncement( AidlTestUtils.makeAnnouncement(TEST_ENABLED_TYPE, TEST_ANNOUNCEMENT_FREQUENCY)); @@ -291,6 +298,37 @@ public final class ConversionUtilsTest extends ExtendedRadioMockitoTestCase { } @Test + public void propertiesFromHalProperties_withoutAmFmAndDabConfigs() { + RadioManager.ModuleProperties properties = createModuleProperties(/* amFmConfig= */ null, + new DabTableEntry[]{}); + + expect.withMessage("Empty AM/FM config") + .that(properties.getBands()).asList().isEmpty(); + expect.withMessage("Empty DAB config") + .that(properties.getDabFrequencyTable()).isNull(); + } + + @Test + public void propertiesFromHalProperties_withInvalidBand() { + AmFmRegionConfig amFmRegionConfig = new AmFmRegionConfig(); + amFmRegionConfig.ranges = new AmFmBandRange[]{createAmFmBandRange(/* lowerBound= */ 50000, + /* upperBound= */ 60000, /* spacing= */ 10), + createAmFmBandRange(FM_LOWER_LIMIT, FM_UPPER_LIMIT, FM_SPACING)}; + + RadioManager.ModuleProperties properties = createModuleProperties(amFmRegionConfig, + new DabTableEntry[]{}); + + RadioManager.BandDescriptor[] bands = properties.getBands(); + expect.withMessage("Band descriptors").that(bands).hasLength(1); + expect.withMessage("FM band frequency lower limit") + .that(bands[0].getLowerLimit()).isEqualTo(FM_LOWER_LIMIT); + expect.withMessage("FM band frequency upper limit") + .that(bands[0].getUpperLimit()).isEqualTo(FM_UPPER_LIMIT); + expect.withMessage("FM band frequency spacing") + .that(bands[0].getSpacing()).isEqualTo(FM_SPACING); + } + + @Test public void identifierToHalProgramIdentifier_withDabId() { ProgramIdentifier halDabId = ConversionUtils.identifierToHalProgramIdentifier(TEST_DAB_SID_EXT_ID); @@ -358,6 +396,13 @@ public final class ConversionUtilsTest extends ExtendedRadioMockitoTestCase { } @Test + public void identifierFromHalProgramIdentifier_withInvalidIdentifier() { + expect.withMessage("Identifier converted from invalid HAL identifier") + .that(ConversionUtils.identifierFromHalProgramIdentifier(TEST_HAL_INVALID_ID)) + .isNull(); + } + + @Test public void programSelectorToHalProgramSelector_withValidSelector() { android.hardware.broadcastradio.ProgramSelector halDabSelector = ConversionUtils.programSelectorToHalProgramSelector(TEST_DAB_SELECTOR); @@ -370,6 +415,23 @@ public final class ConversionUtilsTest extends ExtendedRadioMockitoTestCase { } @Test + public void programSelectorToHalProgramSelector_withInvalidSecondaryId() { + ProgramSelector dabSelector = new ProgramSelector(ProgramSelector.PROGRAM_TYPE_DAB, + TEST_DAB_SID_EXT_ID, new ProgramSelector.Identifier[]{TEST_INVALID_ID, + TEST_DAB_FREQUENCY_ID, TEST_DAB_ENSEMBLE_ID}, /* vendorIds= */ null); + + android.hardware.broadcastradio.ProgramSelector halDabSelector = + ConversionUtils.programSelectorToHalProgramSelector(dabSelector); + + expect.withMessage("Primary identifier of converted HAL DAB selector with invalid " + + "secondary id").that(halDabSelector.primaryId) + .isEqualTo(TEST_HAL_DAB_SID_EXT_ID); + expect.withMessage("Secondary identifiers of converted HAL DAB selector with " + + "invalid secondary id").that(halDabSelector.secondaryIds).asList() + .containsExactly(TEST_HAL_DAB_FREQUENCY_ID, TEST_HAL_DAB_ENSEMBLE_ID); + } + + @Test public void programSelectorFromHalProgramSelector_withValidSelector() { android.hardware.broadcastradio.ProgramSelector halDabSelector = AidlTestUtils.makeHalSelector(TEST_HAL_DAB_SID_EXT_ID, new ProgramIdentifier[]{ @@ -386,6 +448,33 @@ public final class ConversionUtilsTest extends ExtendedRadioMockitoTestCase { } @Test + public void programSelectorFromHalProgramSelector_withInvalidSelector() { + android.hardware.broadcastradio.ProgramSelector invalidSelector = + AidlTestUtils.makeHalSelector(TEST_HAL_INVALID_ID, new ProgramIdentifier[]{}); + + expect.withMessage("Selector converted from invalid HAL selector") + .that(ConversionUtils.programSelectorFromHalProgramSelector(invalidSelector)) + .isNull(); + } + + @Test + public void programSelectorFromHalProgramSelector_withInvalidSecondaryId() { + android.hardware.broadcastradio.ProgramSelector halDabSelector = + AidlTestUtils.makeHalSelector(TEST_HAL_DAB_SID_EXT_ID, new ProgramIdentifier[]{ + TEST_HAL_INVALID_ID, TEST_HAL_DAB_ENSEMBLE_ID, TEST_HAL_DAB_FREQUENCY_ID}); + + ProgramSelector dabSelector = + ConversionUtils.programSelectorFromHalProgramSelector(halDabSelector); + + expect.withMessage("Primary identifier of converted DAB selector with invalid " + + "secondary id").that(dabSelector.getPrimaryId()) + .isEqualTo(TEST_DAB_SID_EXT_ID); + expect.withMessage("Secondary identifiers of converted DAB selector with invalid " + + "secondary id").that(dabSelector.getSecondaryIds()).asList() + .containsExactly(TEST_DAB_FREQUENCY_ID, TEST_DAB_ENSEMBLE_ID); + } + + @Test public void programInfoFromHalProgramInfo_withValidProgramInfo() { android.hardware.broadcastradio.ProgramSelector halDabSelector = AidlTestUtils.makeHalSelector(TEST_HAL_DAB_SID_EXT_ID, new ProgramIdentifier[]{ @@ -622,11 +711,47 @@ public final class ConversionUtilsTest extends ExtendedRadioMockitoTestCase { .isEqualTo(TEST_ALBUM_ART); } - private static RadioManager.ModuleProperties convertToModuleProperties() { + @Test + public void getBands_withInvalidFrequency() { + expect.withMessage("Band for invalid frequency") + .that(Utils.getBand(/* freq= */ 110000)).isEqualTo(Utils.FrequencyBand.UNKNOWN); + } + + @Test + public void filterToHalProgramFilter_withNullFilter() { + ProgramFilter filter = ConversionUtils.filterToHalProgramFilter(null); + + expect.withMessage("Filter identifier types").that(filter.identifierTypes) + .asList().isEmpty(); + expect.withMessage("Filter identifiers").that(filter.identifiers).asList() + .isEmpty(); + } + + @Test + public void filterToHalProgramFilter_withInvalidIdentifier() { + Set<ProgramSelector.Identifier> identifiers = + new ArraySet<ProgramSelector.Identifier>(2); + identifiers.add(TEST_INVALID_ID); + identifiers.add(TEST_DAB_SID_EXT_ID); + ProgramList.Filter filter = new ProgramList.Filter(/* identifierTypes */ new ArraySet<>(), + identifiers, /* includeCategories= */ true, /* excludeModifications= */ false); + ProgramFilter halFilter = ConversionUtils.filterToHalProgramFilter(filter); + + expect.withMessage("Filter identifiers with invalid ones removed") + .that(halFilter.identifiers).asList().containsExactly( + ConversionUtils.identifierToHalProgramIdentifier(TEST_DAB_SID_EXT_ID)); + } + + private static RadioManager.ModuleProperties createModuleProperties() { AmFmRegionConfig amFmConfig = createAmFmRegionConfig(); DabTableEntry[] dabTableEntries = new DabTableEntry[]{ createDabTableEntry(DAB_ENTRY_LABEL_1, DAB_ENTRY_FREQUENCY_1), createDabTableEntry(DAB_ENTRY_LABEL_2, DAB_ENTRY_FREQUENCY_2)}; + return createModuleProperties(amFmConfig, dabTableEntries); + } + + private static RadioManager.ModuleProperties createModuleProperties( + AmFmRegionConfig amFmConfig, DabTableEntry[] dabTableEntries) { Properties properties = createHalProperties(); return ConversionUtils.propertiesFromHalProperties(TEST_ID, TEST_SERVICE_NAME, properties, diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/ProgramInfoCacheTest.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/ProgramInfoCacheTest.java index ce27bc177769..d64fcaf865f2 100644 --- a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/ProgramInfoCacheTest.java +++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/ProgramInfoCacheTest.java @@ -440,6 +440,29 @@ public class ProgramInfoCacheTest { TEST_DAB_UNIQUE_ID_ALTERNATIVE); } + @Test + public void filterAndApplyChunkInternal_withInvalidProgramInfoAndIdentifiers() + throws RemoteException { + ProgramInfoCache cache = new ProgramInfoCache(/* filter= */ null, + /* complete= */ false, TEST_FM_INFO, TEST_RDS_INFO, TEST_DAB_INFO); + ProgramInfo[] halModified = new android.hardware.broadcastradio.ProgramInfo[1]; + halModified[0] = AidlTestUtils.makeHalProgramInfo( + ConversionUtils.programSelectorToHalProgramSelector(TEST_DAB_SELECTOR_ALTERNATIVE), + ConversionUtils.identifierToHalProgramIdentifier(TEST_DAB_FREQUENCY_ID_ALTERNATIVE), + ConversionUtils.identifierToHalProgramIdentifier(TEST_DAB_FREQUENCY_ID_ALTERNATIVE), + TEST_SIGNAL_QUALITY); + ProgramIdentifier[] halRemoved = new android.hardware.broadcastradio.ProgramIdentifier[1]; + halRemoved[0] = new android.hardware.broadcastradio.ProgramIdentifier(); + ProgramListChunk halChunk = AidlTestUtils.makeHalChunk(/* purge= */ false, + /* complete= */ true, halModified, halRemoved); + + List<ProgramList.Chunk> programListChunks = cache.filterAndApplyChunkInternal(halChunk, + TEST_MAX_NUM_MODIFIED_PER_CHUNK, TEST_MAX_NUM_REMOVED_PER_CHUNK); + + expect.withMessage("Program list chunk applied with invalid program and identifiers") + .that(programListChunks).isEmpty(); + } + private void verifyChunkListPurge(List<ProgramList.Chunk> chunks, boolean purge) { if (chunks.isEmpty()) { return; diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/RadioModuleTest.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/RadioModuleTest.java index 10ac05d66c62..a952bde956d8 100644 --- a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/RadioModuleTest.java +++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/RadioModuleTest.java @@ -16,13 +16,12 @@ package com.android.server.broadcastradio.aidl; -import static com.google.common.truth.Truth.assertWithMessage; - import static org.junit.Assert.assertThrows; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -32,9 +31,13 @@ import android.hardware.radio.Announcement; import android.hardware.radio.IAnnouncementListener; import android.hardware.radio.ICloseHandle; import android.hardware.radio.RadioManager; +import android.os.ParcelableException; import android.os.RemoteException; +import com.google.common.truth.Expect; + import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; @@ -50,6 +53,9 @@ public final class RadioModuleTest { private static final RadioManager.ModuleProperties TEST_MODULE_PROPERTIES = AidlTestUtils.makeDefaultModuleProperties(); + @Rule + public final Expect mExpect = Expect.create(); + // Mocks @Mock private IBroadcastRadio mBroadcastRadioMock; @@ -77,13 +83,13 @@ public final class RadioModuleTest { @Test public void getService() { - assertWithMessage("Service of radio module") + mExpect.withMessage("Service of radio module") .that(mRadioModule.getService()).isEqualTo(mBroadcastRadioMock); } @Test public void getProperties() { - assertWithMessage("Module properties of radio module") + mExpect.withMessage("Module properties of radio module") .that(mRadioModule.getProperties()).isEqualTo(TEST_MODULE_PROPERTIES); } @@ -93,7 +99,7 @@ public final class RadioModuleTest { Bitmap imageTest = mRadioModule.getImage(imageId); - assertWithMessage("Image from radio module").that(imageTest).isNull(); + mExpect.withMessage("Image from radio module").that(imageTest).isNull(); } @Test @@ -104,7 +110,7 @@ public final class RadioModuleTest { mRadioModule.getImage(invalidImageId); }); - assertWithMessage("Exception for getting image with invalid ID") + mExpect.withMessage("Exception for getting image with invalid ID") .that(thrown).hasMessageThat().contains("Image ID is missing"); } @@ -117,6 +123,18 @@ public final class RadioModuleTest { } @Test + public void addAnnouncementListener_whenHalThrowsRemoteException() throws Exception { + doThrow(new RuntimeException("HAL service died")).when(mBroadcastRadioMock) + .registerAnnouncementListener(any(), any()); + + ParcelableException thrown = assertThrows(ParcelableException.class, () -> + mRadioModule.addAnnouncementListener(mListenerMock, new int[]{TEST_ENABLED_TYPE})); + + mExpect.withMessage("Exception for adding announcement listener when HAL service died") + .that(thrown).hasMessageThat().contains("unknown error"); + } + + @Test public void onListUpdate_forAnnouncementListener() throws Exception { android.hardware.broadcastradio.Announcement halAnnouncement = AidlTestUtils.makeAnnouncement(TEST_ENABLED_TYPE, /* selectorFreq= */ 96300); diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/TunerSessionTest.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/TunerSessionTest.java index 755bcdb7df20..4ded91d03579 100644 --- a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/TunerSessionTest.java +++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/TunerSessionTest.java @@ -421,6 +421,19 @@ public final class TunerSessionTest extends ExtendedRadioMockitoTestCase { } @Test + public void tune_withClosedTuner_fails() throws Exception { + openAidlClients(/* numClients= */ 1); + ProgramSelector sel = AidlTestUtils.makeFmSelector(AM_FM_FREQUENCY_LIST[1]); + mTunerSessions[0].close(); + + IllegalStateException thrown = assertThrows(IllegalStateException.class, + () -> mTunerSessions[0].tune(sel)); + + expect.withMessage("Exception for tuning on closed tuner").that(thrown).hasMessageThat() + .contains("Tuner is closed"); + } + + @Test public void step_withDirectionUp() throws Exception { long initFreq = AM_FM_FREQUENCY_LIST[1]; ProgramSelector initialSel = AidlTestUtils.makeFmSelector(initFreq); @@ -1149,6 +1162,20 @@ public final class TunerSessionTest extends ExtendedRadioMockitoTestCase { } @Test + public void onCurrentProgramInfoChanged_withLowerSdkVersion_doesNotInvokesCallback() + throws Exception { + doReturn(false).when(() -> CompatChanges.isChangeEnabled( + eq(ConversionUtils.RADIO_U_VERSION_REQUIRED), anyInt())); + openAidlClients(/* numClients= */ 1); + + mHalTunerCallback.onCurrentProgramInfoChanged( + AidlTestUtils.programInfoToHalProgramInfo(TEST_DAB_INFO)); + + verify(mAidlTunerCallbackMocks[0], after(CALLBACK_TIMEOUT_MS).never()) + .onCurrentProgramInfoChanged(any()); + } + + @Test public void onTuneFailed_forTunerCallback() throws Exception { int numSessions = 3; openAidlClients(numSessions); @@ -1165,6 +1192,20 @@ public final class TunerSessionTest extends ExtendedRadioMockitoTestCase { } @Test + public void onTuneFailed_withLowerSdkVersion_doesNotInvokesCallback() + throws Exception { + doReturn(false).when(() -> CompatChanges.isChangeEnabled( + eq(ConversionUtils.RADIO_U_VERSION_REQUIRED), anyInt())); + openAidlClients(/* numClients= */ 1); + + mHalTunerCallback.onTuneFailed(Result.CANCELED, + ConversionUtils.programSelectorToHalProgramSelector(TEST_DAB_SELECTOR)); + + verify(mAidlTunerCallbackMocks[0], after(CALLBACK_TIMEOUT_MS).never()) + .onTuneFailed(anyInt(), any()); + } + + @Test public void onAntennaStateChange_forTunerCallback() throws Exception { int numSessions = 3; openAidlClients(numSessions); @@ -1231,6 +1272,36 @@ public final class TunerSessionTest extends ExtendedRadioMockitoTestCase { } } + @Test + public void openSession_withNonNullAntennaState() throws Exception { + boolean antennaConnected = false; + android.hardware.radio.ITunerCallback callback = + mock(android.hardware.radio.ITunerCallback.class); + openAidlClients(/* numClients= */ 1); + mHalTunerCallback.onAntennaStateChange(antennaConnected); + verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT).onAntennaState(antennaConnected); + + mRadioModule.openSession(callback); + + verify(callback, CALLBACK_TIMEOUT).onAntennaState(antennaConnected); + } + + @Test + public void openSession_withNonNullCurrentProgramInfo() throws Exception { + openAidlClients(/* numClients= */ 1); + ProgramSelector initialSel = AidlTestUtils.makeFmSelector(AM_FM_FREQUENCY_LIST[1]); + RadioManager.ProgramInfo tuneInfo = AidlTestUtils.makeProgramInfo(initialSel, + SIGNAL_QUALITY); + mTunerSessions[0].tune(initialSel); + verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT).onCurrentProgramInfoChanged(tuneInfo); + android.hardware.radio.ITunerCallback callback = + mock(android.hardware.radio.ITunerCallback.class); + + mRadioModule.openSession(callback); + + verify(callback, CALLBACK_TIMEOUT).onCurrentProgramInfoChanged(tuneInfo); + } + private void openAidlClients(int numClients) throws Exception { mAidlTunerCallbackMocks = new android.hardware.radio.ITunerCallback[numClients]; mTunerSessions = new TunerSession[numClients]; diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/AnnouncementAggregatorHidlTest.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/AnnouncementAggregatorHidlTest.java index 5e99b28b62b1..8e0abff07944 100644 --- a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/AnnouncementAggregatorHidlTest.java +++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/AnnouncementAggregatorHidlTest.java @@ -16,11 +16,11 @@ package com.android.server.broadcastradio.hal2; -import static com.google.common.truth.Truth.assertWithMessage; - import static org.junit.Assert.assertThrows; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; @@ -33,7 +33,10 @@ import android.hardware.radio.ICloseHandle; import android.os.IBinder; import android.os.RemoteException; +import com.google.common.truth.Expect; + import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; @@ -55,6 +58,9 @@ public final class AnnouncementAggregatorHidlTest { private AnnouncementAggregator mAnnouncementAggregator; private IBinder.DeathRecipient mDeathRecipient; + @Rule + public final Expect mExpect = Expect.create(); + @Mock private IAnnouncementListener mListenerMock; @Mock @@ -76,6 +82,19 @@ public final class AnnouncementAggregatorHidlTest { } @Test + public void constructor_withBinderDied() throws Exception { + RemoteException remoteException = new RemoteException("Binder is died"); + doThrow(remoteException).when(mBinderMock).linkToDeath(any(), anyInt()); + + RuntimeException thrown = assertThrows(RuntimeException.class, + () -> new com.android.server.broadcastradio.aidl.AnnouncementAggregator( + mListenerMock, mLock)); + + mExpect.withMessage("Exception for dead binder").that(thrown).hasMessageThat() + .contains(remoteException.getMessage()); + } + + @Test public void onListUpdated_withOneModuleWatcher() throws Exception { ArgumentCaptor<IAnnouncementListener> moduleWatcherCaptor = ArgumentCaptor.forClass(IAnnouncementListener.class); @@ -104,7 +123,7 @@ public final class AnnouncementAggregatorHidlTest { moduleWatcherCaptor.getValue().onListUpdated(Arrays.asList(mAnnouncementMocks[index])); verify(mListenerMock, times(index + 1)).onListUpdated(announcementsCaptor.capture()); - assertWithMessage("Number of announcements %s after %s announcements were updated", + mExpect.withMessage("Number of announcements %s after %s announcements were updated", announcementsCaptor.getValue(), index + 1) .that(announcementsCaptor.getValue().size()).isEqualTo(index + 1); } @@ -132,7 +151,7 @@ public final class AnnouncementAggregatorHidlTest { () -> mAnnouncementAggregator.watchModule(mRadioModuleMocks[0], TEST_ENABLED_TYPES)); - assertWithMessage("Exception for watching module after aggregator has been closed") + mExpect.withMessage("Exception for watching module after aggregator has been closed") .that(thrown).hasMessageThat() .contains("announcement aggregator has already been closed"); } diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/ConvertTest.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/ConvertTest.java index 3de4f5d7dcd9..4cb012ca27db 100644 --- a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/ConvertTest.java +++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/ConvertTest.java @@ -21,7 +21,6 @@ import android.hardware.broadcastradio.V2_0.AmFmRegionConfig; import android.hardware.broadcastradio.V2_0.DabTableEntry; import android.hardware.broadcastradio.V2_0.IdentifierType; import android.hardware.broadcastradio.V2_0.Properties; -import android.hardware.broadcastradio.V2_0.VendorKeyValue; import android.hardware.radio.Announcement; import android.hardware.radio.ProgramSelector; import android.hardware.radio.RadioManager; @@ -149,6 +148,26 @@ public final class ConvertTest { } @Test + public void propertiesFromHalProperties_withInvalidBand() { + AmFmRegionConfig amFmRegionConfig = new AmFmRegionConfig(); + amFmRegionConfig.ranges = new ArrayList<>(Arrays.asList(createAmFmBandRange( + /* lowerBound= */ 50000, /* upperBound= */ 60000, /* spacing= */ 10), + createAmFmBandRange(FM_LOWER_LIMIT, FM_UPPER_LIMIT, FM_SPACING))); + + RadioManager.ModuleProperties properties = convertToModuleProperties(amFmRegionConfig, + new ArrayList<>()); + + RadioManager.BandDescriptor[] bands = properties.getBands(); + expect.withMessage("Band descriptors").that(bands).hasLength(1); + expect.withMessage("FM band frequency lower limit") + .that(bands[0].getLowerLimit()).isEqualTo(FM_LOWER_LIMIT); + expect.withMessage("FM band frequency upper limit") + .that(bands[0].getUpperLimit()).isEqualTo(FM_UPPER_LIMIT); + expect.withMessage("FM band frequency spacing") + .that(bands[0].getSpacing()).isEqualTo(FM_SPACING); + } + + @Test public void announcementFromHalAnnouncement_typesMatch() { expect.withMessage("Announcement type") .that(ANNOUNCEMENT.getType()).isEqualTo(TEST_ENABLED_TYPE); @@ -173,20 +192,31 @@ public final class ConvertTest { .that(ANNOUNCEMENT.getVendorInfo()).isEmpty(); } + @Test + public void getBands_withInvalidFrequency() { + expect.withMessage("Band for invalid frequency") + .that(Utils.getBand(/* freq= */ 110000)).isEqualTo(FrequencyBand.UNKNOWN); + } + private static RadioManager.ModuleProperties convertToModuleProperties() { AmFmRegionConfig amFmConfig = createAmFmRegionConfig(); List<DabTableEntry> dabTableEntries = Arrays.asList( createDabTableEntry(DAB_ENTRY_LABEL_1, DAB_ENTRY_FREQUENCY_1), createDabTableEntry(DAB_ENTRY_LABEL_2, DAB_ENTRY_FREQUENCY_2)); - Properties properties = createHalProperties(); + return convertToModuleProperties(amFmConfig, dabTableEntries); + } + + private static RadioManager.ModuleProperties convertToModuleProperties( + AmFmRegionConfig amFmConfig, List<DabTableEntry> dabTableEntries) { + Properties properties = createHalProperties(); return Convert.propertiesFromHal(TEST_ID, TEST_SERVICE_NAME, properties, amFmConfig, dabTableEntries); } private static AmFmRegionConfig createAmFmRegionConfig() { AmFmRegionConfig amFmRegionConfig = new AmFmRegionConfig(); - amFmRegionConfig.ranges = new ArrayList<AmFmBandRange>(Arrays.asList( + amFmRegionConfig.ranges = new ArrayList<>(Arrays.asList( createAmFmBandRange(FM_LOWER_LIMIT, FM_UPPER_LIMIT, FM_SPACING), createAmFmBandRange(AM_LOWER_LIMIT, AM_UPPER_LIMIT, AM_SPACING))); return amFmRegionConfig; @@ -216,7 +246,7 @@ public final class ConvertTest { halProperties.product = TEST_PRODUCT; halProperties.version = TEST_VERSION; halProperties.serial = TEST_SERIAL; - halProperties.vendorInfo = new ArrayList<VendorKeyValue>(Arrays.asList( + halProperties.vendorInfo = new ArrayList<>(Arrays.asList( TestUtils.makeVendorKeyValue(VENDOR_INFO_KEY_1, VENDOR_INFO_VALUE_1), TestUtils.makeVendorKeyValue(VENDOR_INFO_KEY_2, VENDOR_INFO_VALUE_2))); return halProperties; diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/ProgramInfoCacheTest.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/ProgramInfoCacheTest.java index 36a64303cddd..015e9c032fd0 100644 --- a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/ProgramInfoCacheTest.java +++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/ProgramInfoCacheTest.java @@ -17,6 +17,7 @@ package com.android.server.broadcastradio.hal2; import static org.junit.Assert.*; +import android.hardware.broadcastradio.V2_0.ProgramIdentifier; import android.hardware.broadcastradio.V2_0.ProgramListChunk; import android.hardware.radio.ProgramList; import android.hardware.radio.ProgramSelector; @@ -34,6 +35,7 @@ import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; +import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; @@ -261,6 +263,25 @@ public class ProgramInfoCacheTest extends ExtendedRadioMockitoTestCase { verifyChunkListRemoved(chunks, 1, TEST_DAB_UNIQUE_ID, TEST_VENDOR_UNIQUE_ID); } + @Test + public void filterAndApplyChunkInternal_withInvalidIdentifier() { + ProgramInfoCache cache = new ProgramInfoCache(/* filter= */ null, /* complete= */ false, + TEST_AM_FM_INFO, TEST_RDS_INFO, TEST_DAB_INFO, TEST_VENDOR_INFO); + ArrayList<ProgramIdentifier> halRemoved = new ArrayList<>(); + halRemoved.add(new ProgramIdentifier()); + ProgramListChunk halChunk = new ProgramListChunk(); + halChunk.complete = true; + halChunk.purge = false; + halChunk.modified = new ArrayList<>(); + halChunk.removed = halRemoved; + + List<ProgramList.Chunk> programListChunks = cache.filterAndApplyChunkInternal(halChunk, + /* maxNumModifiedPerChunk= */ 1, /* maxNumRemovedPerChunk= */ 1); + + expect.withMessage("Program list chunk applied with invalid identifier") + .that(programListChunks).isEmpty(); + } + // Verifies that: // - The first chunk's purge flag matches expectPurge. // - The last chunk's complete flag matches expectComplete. diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/TunerSessionHidlTest.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/TunerSessionHidlTest.java index 6edfa0294fdd..898ef577629c 100644 --- a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/TunerSessionHidlTest.java +++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/TunerSessionHidlTest.java @@ -29,8 +29,6 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.never; import static com.android.dx.mockito.inline.extended.ExtendedMockito.timeout; import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; -import static com.google.common.truth.Truth.assertWithMessage; - import static org.junit.Assert.assertThrows; import android.graphics.Bitmap; @@ -57,8 +55,11 @@ import com.android.dx.mockito.inline.extended.StaticMockitoSessionBuilder; import com.android.server.broadcastradio.ExtendedRadioMockitoTestCase; import com.android.server.broadcastradio.RadioServiceUserController; +import com.google.common.truth.Expect; + 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; @@ -98,6 +99,9 @@ public final class TunerSessionHidlTest extends ExtendedRadioMockitoTestCase { private ProgramInfo mHalCurrentInfo; private TunerSession[] mTunerSessions; + @Rule + public final Expect mExpect = Expect.create(); + @Mock private UserHandle mUserHandleMock; @Mock @@ -206,7 +210,7 @@ public final class TunerSessionHidlTest extends ExtendedRadioMockitoTestCase { openAidlClients(numSessions); for (int index = 0; index < numSessions; index++) { - assertWithMessage("Session of index %s close state", index) + mExpect.withMessage("Session of index %s close state", index) .that(mTunerSessions[index].isClosed()).isFalse(); } } @@ -238,7 +242,7 @@ public final class TunerSessionHidlTest extends ExtendedRadioMockitoTestCase { RadioManager.BandConfig config = mTunerSessions[0].getConfiguration(); - assertWithMessage("Session configuration").that(config) + mExpect.withMessage("Session configuration").that(config) .isEqualTo(FM_BAND_CONFIG); } @@ -248,7 +252,7 @@ public final class TunerSessionHidlTest extends ExtendedRadioMockitoTestCase { mTunerSessions[0].setMuted(/* mute= */ false); - assertWithMessage("Session mute state after setting unmuted") + mExpect.withMessage("Session mute state after setting unmuted") .that(mTunerSessions[0].isMuted()).isFalse(); } @@ -258,7 +262,7 @@ public final class TunerSessionHidlTest extends ExtendedRadioMockitoTestCase { mTunerSessions[0].setMuted(/* mute= */ true); - assertWithMessage("Session mute state after setting muted") + mExpect.withMessage("Session mute state after setting muted") .that(mTunerSessions[0].isMuted()).isTrue(); } @@ -268,7 +272,7 @@ public final class TunerSessionHidlTest extends ExtendedRadioMockitoTestCase { mTunerSessions[0].close(); - assertWithMessage("Close state of broadcast radio service session") + mExpect.withMessage("Close state of broadcast radio service session") .that(mTunerSessions[0].isClosed()).isTrue(); } @@ -282,11 +286,11 @@ public final class TunerSessionHidlTest extends ExtendedRadioMockitoTestCase { for (int index = 0; index < numSessions; index++) { if (index == closeIdx) { - assertWithMessage( + mExpect.withMessage( "Close state of broadcast radio service session of index %s", index) .that(mTunerSessions[index].isClosed()).isTrue(); } else { - assertWithMessage( + mExpect.withMessage( "Close state of broadcast radio service session of index %s", index) .that(mTunerSessions[index].isClosed()).isFalse(); } @@ -301,7 +305,21 @@ public final class TunerSessionHidlTest extends ExtendedRadioMockitoTestCase { mTunerSessions[0].close(errorCode); verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT).onError(errorCode); - assertWithMessage("Close state of broadcast radio service session") + mExpect.withMessage("Close state of broadcast radio service session") + .that(mTunerSessions[0].isClosed()).isTrue(); + } + + @Test + public void close_forMultipleTimes() throws Exception { + openAidlClients(/* numClients= */ 1); + int errorCode = RadioTuner.ERROR_SERVER_DIED; + mTunerSessions[0].close(errorCode); + verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT).onError(errorCode); + + mTunerSessions[0].close(errorCode); + + verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT).onError(errorCode); + mExpect.withMessage("State of closing broadcast radio service session twice") .that(mTunerSessions[0].isClosed()).isTrue(); } @@ -315,7 +333,7 @@ public final class TunerSessionHidlTest extends ExtendedRadioMockitoTestCase { for (int index = 0; index < numSessions; index++) { verify(mAidlTunerCallbackMocks[index], CALLBACK_TIMEOUT).onError(errorCode); - assertWithMessage("Close state of broadcast radio service session of index %s", index) + mExpect.withMessage("Close state of broadcast radio service session of index %s", index) .that(mTunerSessions[index].isClosed()).isTrue(); } } @@ -365,7 +383,7 @@ public final class TunerSessionHidlTest extends ExtendedRadioMockitoTestCase { UnsupportedOperationException thrown = assertThrows(UnsupportedOperationException.class, () -> mTunerSessions[0].tune(unsupportedSelector)); - assertWithMessage("Exception for tuning on unsupported program selector") + mExpect.withMessage("Exception for tuning on unsupported program selector") .that(thrown).hasMessageThat().contains("tune: NOT_SUPPORTED"); } @@ -393,11 +411,24 @@ public final class TunerSessionHidlTest extends ExtendedRadioMockitoTestCase { mTunerSessions[0].tune(sel); }); - assertWithMessage("Unknown error HAL exception when tuning") + mExpect.withMessage("Unknown error HAL exception when tuning") .that(thrown).hasMessageThat().contains(Result.toString(Result.UNKNOWN_ERROR)); } @Test + public void tune_withClosedTuner_fails() throws Exception { + openAidlClients(/* numClients= */ 1); + ProgramSelector sel = TestUtils.makeFmSelector(AM_FM_FREQUENCY_LIST[1]); + mTunerSessions[0].close(); + + IllegalStateException thrown = assertThrows(IllegalStateException.class, + () -> mTunerSessions[0].tune(sel)); + + mExpect.withMessage("Exception for tuning on closed tuner").that(thrown).hasMessageThat() + .contains("Tuner is closed"); + } + + @Test public void step_withDirectionUp() throws Exception { long initFreq = AM_FM_FREQUENCY_LIST[1]; ProgramSelector initialSel = TestUtils.makeFmSelector(initFreq); @@ -454,7 +485,7 @@ public final class TunerSessionHidlTest extends ExtendedRadioMockitoTestCase { mTunerSessions[0].step(/* directionDown= */ true, /* skipSubChannel= */ false); }); - assertWithMessage("Exception for stepping when HAL is in invalid state") + mExpect.withMessage("Exception for stepping when HAL is in invalid state") .that(thrown).hasMessageThat().contains(Result.toString(Result.INVALID_STATE)); } @@ -533,7 +564,7 @@ public final class TunerSessionHidlTest extends ExtendedRadioMockitoTestCase { mTunerSessions[0].seek(/* directionDown= */ true, /* skipSubChannel= */ false); }); - assertWithMessage("Internal error HAL exception when seeking") + mExpect.withMessage("Internal error HAL exception when seeking") .that(thrown).hasMessageThat().contains(Result.toString(Result.INTERNAL_ERROR)); } @@ -566,7 +597,7 @@ public final class TunerSessionHidlTest extends ExtendedRadioMockitoTestCase { mTunerSessions[0].cancel(); }); - assertWithMessage("Exception for canceling when HAL throws remote exception") + mExpect.withMessage("Exception for canceling when HAL throws remote exception") .that(thrown).hasMessageThat().contains(exceptionMessage); } @@ -579,7 +610,7 @@ public final class TunerSessionHidlTest extends ExtendedRadioMockitoTestCase { mTunerSessions[0].getImage(imageId); }); - assertWithMessage("Get image exception") + mExpect.withMessage("Get image exception") .that(thrown).hasMessageThat().contains("Image ID is missing"); } @@ -590,7 +621,7 @@ public final class TunerSessionHidlTest extends ExtendedRadioMockitoTestCase { Bitmap imageTest = mTunerSessions[0].getImage(imageId); - assertWithMessage("Null image").that(imageTest).isEqualTo(null); + mExpect.withMessage("Null image").that(imageTest).isEqualTo(null); } @Test @@ -603,7 +634,7 @@ public final class TunerSessionHidlTest extends ExtendedRadioMockitoTestCase { mTunerSessions[0].getImage(/* id= */ 1); }); - assertWithMessage("Exception for getting image when HAL throws remote exception") + mExpect.withMessage("Exception for getting image when HAL throws remote exception") .that(thrown).hasMessageThat().contains(exceptionMessage); } @@ -649,7 +680,7 @@ public final class TunerSessionHidlTest extends ExtendedRadioMockitoTestCase { mTunerSessions[0].startProgramListUpdates(/* filter= */ null); }); - assertWithMessage("Unknown error HAL exception when updating program list") + mExpect.withMessage("Unknown error HAL exception when updating program list") .that(thrown).hasMessageThat().contains(Result.toString(Result.UNKNOWN_ERROR)); } @@ -686,7 +717,7 @@ public final class TunerSessionHidlTest extends ExtendedRadioMockitoTestCase { boolean isSupported = mTunerSessions[0].isConfigFlagSupported(flag); verify(mHalTunerSessionMock).isConfigFlagSet(eq(flag), any()); - assertWithMessage("Config flag %s is supported", flag).that(isSupported).isFalse(); + mExpect.withMessage("Config flag %s is supported", flag).that(isSupported).isFalse(); } @Test @@ -697,7 +728,7 @@ public final class TunerSessionHidlTest extends ExtendedRadioMockitoTestCase { boolean isSupported = mTunerSessions[0].isConfigFlagSupported(flag); verify(mHalTunerSessionMock).isConfigFlagSet(eq(flag), any()); - assertWithMessage("Config flag %s is supported", flag).that(isSupported).isTrue(); + mExpect.withMessage("Config flag %s is supported", flag).that(isSupported).isTrue(); } @Test @@ -709,7 +740,7 @@ public final class TunerSessionHidlTest extends ExtendedRadioMockitoTestCase { mTunerSessions[0].setConfigFlag(flag, /* value= */ true); }); - assertWithMessage("Exception for setting unsupported flag %s", flag) + mExpect.withMessage("Exception for setting unsupported flag %s", flag) .that(thrown).hasMessageThat().contains("setConfigFlag: NOT_SUPPORTED"); } @@ -755,7 +786,7 @@ public final class TunerSessionHidlTest extends ExtendedRadioMockitoTestCase { mTunerSessions[0].isConfigFlagSet(flag); }); - assertWithMessage("Exception for checking if unsupported flag %s is set", flag) + mExpect.withMessage("Exception for checking if unsupported flag %s is set", flag) .that(thrown).hasMessageThat().contains("isConfigFlagSet: NOT_SUPPORTED"); } @@ -768,7 +799,7 @@ public final class TunerSessionHidlTest extends ExtendedRadioMockitoTestCase { boolean isSet = mTunerSessions[0].isConfigFlagSet(flag); - assertWithMessage("Config flag %s is set", flag) + mExpect.withMessage("Config flag %s is set", flag) .that(isSet).isEqualTo(expectedConfigFlagValue); } @@ -782,7 +813,7 @@ public final class TunerSessionHidlTest extends ExtendedRadioMockitoTestCase { mTunerSessions[0].isConfigFlagSet(flag); }); - assertWithMessage("Exception for checking config flag when HAL throws remote exception") + mExpect.withMessage("Exception for checking config flag when HAL throws remote exception") .that(thrown).hasMessageThat().contains("Failed to check flag"); } @@ -822,7 +853,7 @@ public final class TunerSessionHidlTest extends ExtendedRadioMockitoTestCase { mTunerSessions[0].setParameters(parametersSet); }); - assertWithMessage("Exception for setting parameters when HAL throws remote exception") + mExpect.withMessage("Exception for setting parameters when HAL throws remote exception") .that(thrown).hasMessageThat().contains(exceptionMessage); } @@ -848,7 +879,7 @@ public final class TunerSessionHidlTest extends ExtendedRadioMockitoTestCase { mTunerSessions[0].getParameters(parameterKeys); }); - assertWithMessage("Exception for getting parameters when HAL throws remote exception") + mExpect.withMessage("Exception for getting parameters when HAL throws remote exception") .that(thrown).hasMessageThat().contains(exceptionMessage); } @@ -894,6 +925,36 @@ public final class TunerSessionHidlTest extends ExtendedRadioMockitoTestCase { } } + @Test + public void openSession_withNonNullAntennaState() throws Exception { + boolean antennaConnected = false; + android.hardware.radio.ITunerCallback callback = + mock(android.hardware.radio.ITunerCallback.class); + openAidlClients(/* numClients= */ 1); + mHalTunerCallback.onAntennaStateChange(antennaConnected); + verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT).onAntennaState(antennaConnected); + + mRadioModule.openSession(callback); + + verify(callback, CALLBACK_TIMEOUT).onAntennaState(antennaConnected); + } + + @Test + public void openSession_withNonNullCurrentProgramInfo() throws Exception { + openAidlClients(/* numClients= */ 1); + ProgramSelector initialSel = TestUtils.makeFmSelector(AM_FM_FREQUENCY_LIST[1]); + RadioManager.ProgramInfo tuneInfo = TestUtils.makeProgramInfo(initialSel, + SIGNAL_QUALITY); + mTunerSessions[0].tune(initialSel); + verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT).onCurrentProgramInfoChanged(tuneInfo); + android.hardware.radio.ITunerCallback callback = + mock(android.hardware.radio.ITunerCallback.class); + + mRadioModule.openSession(callback); + + verify(callback, CALLBACK_TIMEOUT).onCurrentProgramInfoChanged(tuneInfo); + } + private void openAidlClients(int numClients) throws Exception { mAidlTunerCallbackMocks = new android.hardware.radio.ITunerCallback[numClients]; mTunerSessions = new TunerSession[numClients]; diff --git a/core/tests/coretests/Android.bp b/core/tests/coretests/Android.bp index 0e855af1a494..41696dfa782e 100644 --- a/core/tests/coretests/Android.bp +++ b/core/tests/coretests/Android.bp @@ -85,6 +85,7 @@ android_test { "kotlin-test", "mockito-target-minus-junit4", "androidx.test.uiautomator_uiautomator", + "platform-parametric-runner-lib", "platform-test-annotations", "platform-compat-test-rules", "truth", diff --git a/core/tests/coretests/src/android/app/NotificationTest.java b/core/tests/coretests/src/android/app/NotificationTest.java index 30ec940bda8e..b64eeca04101 100644 --- a/core/tests/coretests/src/android/app/NotificationTest.java +++ b/core/tests/coretests/src/android/app/NotificationTest.java @@ -703,10 +703,10 @@ public class NotificationTest { hugeIcon).build(); Bitmap smallNotificationIcon = notification.getSmallIcon().getBitmap(); - assertThat(smallNotificationIcon.getWidth()).isEqualTo( + assertThat((float) smallNotificationIcon.getWidth()).isWithin(3f).of( mContext.getResources().getDimensionPixelSize( R.dimen.notification_small_icon_size)); - assertThat(smallNotificationIcon.getHeight()).isEqualTo( + assertThat((float) smallNotificationIcon.getHeight()).isWithin(3f).of( mContext.getResources().getDimensionPixelSize( R.dimen.notification_small_icon_size)); } @@ -730,23 +730,23 @@ public class NotificationTest { Notification notification = new Notification.Builder(mContext, "Channel").setStyle( style).build(); - int targetSize = mContext.getResources().getDimensionPixelSize( + float targetSize = mContext.getResources().getDimensionPixelSize( ActivityManager.isLowRamDeviceStatic() ? R.dimen.notification_person_icon_max_size_low_ram : R.dimen.notification_person_icon_max_size); Bitmap personIcon = style.getUser().getIcon().getBitmap(); - assertThat(personIcon.getWidth()).isEqualTo(targetSize); - assertThat(personIcon.getHeight()).isEqualTo(targetSize); + assertThat((float) personIcon.getWidth()).isWithin(3f).of(targetSize); + assertThat((float) personIcon.getHeight()).isWithin(3f).of(targetSize); Bitmap avatarIcon = style.getMessages().get(0).getSenderPerson().getIcon().getBitmap(); - assertThat(avatarIcon.getWidth()).isEqualTo(targetSize); - assertThat(avatarIcon.getHeight()).isEqualTo(targetSize); + assertThat((float) avatarIcon.getWidth()).isWithin(3f).of(targetSize); + assertThat((float) avatarIcon.getHeight()).isWithin(3f).of(targetSize); Bitmap historicAvatarIcon = style.getHistoricMessages().get( 0).getSenderPerson().getIcon().getBitmap(); - assertThat(historicAvatarIcon.getWidth()).isEqualTo(targetSize); - assertThat(historicAvatarIcon.getHeight()).isEqualTo(targetSize); + assertThat((float) historicAvatarIcon.getWidth()).isWithin(3f).of(targetSize); + assertThat((float) historicAvatarIcon.getHeight()).isWithin(3f).of(targetSize); } @Test @@ -760,10 +760,10 @@ public class NotificationTest { style).build(); Bitmap shortcutIcon = style.getShortcutIcon().getBitmap(); - assertThat(shortcutIcon.getWidth()).isEqualTo( + assertThat((float) shortcutIcon.getWidth()).isWithin(3f).of( mContext.getResources().getDimensionPixelSize( R.dimen.notification_small_icon_size)); - assertThat(shortcutIcon.getHeight()).isEqualTo( + assertThat((float) shortcutIcon.getHeight()).isWithin(3f).of( mContext.getResources().getDimensionPixelSize( R.dimen.notification_small_icon_size)); } diff --git a/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java b/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java index 584fe16d00ac..32e611cf0a1e 100644 --- a/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java +++ b/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java @@ -21,6 +21,8 @@ import static android.app.servertransaction.TestUtils.mergedConfig; import static android.app.servertransaction.TestUtils.referrerIntentList; import static android.app.servertransaction.TestUtils.resultInfoList; +import static com.android.window.flags.Flags.FLAG_DISABLE_OBJECT_POOL; + import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNotSame; @@ -39,19 +41,27 @@ import android.os.Bundle; import android.os.IBinder; import android.os.PersistableBundle; import android.platform.test.annotations.Presubmit; +import android.platform.test.flag.junit.FlagsParameterization; +import android.platform.test.flag.junit.SetFlagsRule; import android.window.ActivityWindowInfo; import androidx.test.filters.SmallTest; -import androidx.test.runner.AndroidJUnit4; + +import com.android.window.flags.Flags; 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.List; import java.util.function.Supplier; +import platform.test.runner.parameterized.ParameterizedAndroidJunit4; +import platform.test.runner.parameterized.Parameters; + /** * Tests for {@link ObjectPool}. * @@ -61,16 +71,28 @@ import java.util.function.Supplier; * <p>This test class is a part of Window Manager Service tests and specified in * {@link com.android.server.wm.test.filters.FrameworksTestsFilter}. */ -@RunWith(AndroidJUnit4.class) +@RunWith(ParameterizedAndroidJunit4.class) @SmallTest @Presubmit public class ObjectPoolTests { + @Parameters(name = "{0}") + public static List<FlagsParameterization> getParams() { + return FlagsParameterization.allCombinationsOf(FLAG_DISABLE_OBJECT_POOL); + } + + @Rule + public SetFlagsRule mSetFlagsRule; + @Mock private IApplicationThread mApplicationThread; @Mock private IBinder mActivityToken; + public ObjectPoolTests(FlagsParameterization flags) { + mSetFlagsRule = new SetFlagsRule(flags); + } + @Before public void setup() { MockitoAnnotations.initMocks(this); @@ -199,12 +221,20 @@ public class ObjectPoolTests { item.recycle(); final ObjectPoolItem item2 = obtain.get(); - assertSame(item, item2); + if (Flags.disableObjectPool()) { + assertNotSame(item, item2); // Different instance. + } else { + assertSame(item, item2); + } // Create new object when the pool is empty. final ObjectPoolItem item3 = obtain.get(); assertNotSame(item, item3); + if (Flags.disableObjectPool()) { + // Skip recycle if flag enabled, compare unnecessary. + return; + } assertEquals(item, item3); // Reset fields after recycle. diff --git a/core/tests/coretests/src/android/view/InsetsControllerTest.java b/core/tests/coretests/src/android/view/InsetsControllerTest.java index f05390dff5cd..ae7f465baa87 100644 --- a/core/tests/coretests/src/android/view/InsetsControllerTest.java +++ b/core/tests/coretests/src/android/view/InsetsControllerTest.java @@ -32,6 +32,7 @@ import static android.view.WindowInsets.Type.ime; import static android.view.WindowInsets.Type.navigationBars; import static android.view.WindowInsets.Type.statusBars; import static android.view.WindowInsets.Type.systemBars; +import static android.view.WindowInsetsAnimation.Callback.DISPATCH_MODE_STOP; import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION; @@ -52,6 +53,9 @@ import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; +import static java.util.concurrent.TimeUnit.SECONDS; + +import android.annotation.NonNull; import android.content.Context; import android.graphics.Insets; import android.graphics.Point; @@ -80,6 +84,8 @@ import org.mockito.ArgumentCaptor; import org.mockito.InOrder; import org.mockito.Mockito; +import java.util.ArrayList; +import java.util.List; import java.util.concurrent.CountDownLatch; /** @@ -668,30 +674,58 @@ public class InsetsControllerTest { } @Test - public void testResizeAnimation_withFlagAnimateResizing() { + public void testResizeAnimation_withFlagAnimateResizing() throws InterruptedException { + final int id = ID_NAVIGATION_BAR; + final @InsetsType int type = navigationBars(); + final int fromInsetsHeight = 50; + final int toInsetsHeight = 60; + final ArrayList<WindowInsets> progressList = new ArrayList<>(); + final CountDownLatch animationEndLatch = new CountDownLatch(1); InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> { - final int id = ID_NAVIGATION_BAR; - final @InsetsType int type = navigationBars(); final InsetsState state1 = new InsetsState(); state1.getOrCreateSource(id, type) .setVisible(true) - .setFrame(0, 0, 500, 50) + .setFrame(0, 0, 500, fromInsetsHeight) .setFlags(FLAG_ANIMATE_RESIZING, FLAG_ANIMATE_RESIZING); final InsetsState state2 = new InsetsState(state1, true /* copySources */); - state2.peekSource(id).setFrame(0, 0, 500, 60); + state2.peekSource(id).setFrame(0, 0, 500, toInsetsHeight); // New insets source won't cause the resize animation. mController.onStateChanged(state1); assertEquals("There must not be resize animation.", ANIMATION_TYPE_NONE, mController.getAnimationType(type)); + mViewRoot.getView().setWindowInsetsAnimationCallback( + new WindowInsetsAnimation.Callback(DISPATCH_MODE_STOP) { + @Override + public WindowInsets onProgress( + @NonNull WindowInsets insets, + @NonNull List<WindowInsetsAnimation> runningAnimations) { + progressList.add(insets); + return insets; + } + + @Override + public void onEnd(@NonNull WindowInsetsAnimation animation) { + animationEndLatch.countDown(); + } + }); + // Changing frame of the source with FLAG_ANIMATE_RESIZING will cause the resize // animation. mController.onStateChanged(state2); assertEquals("There must be resize animation.", ANIMATION_TYPE_RESIZE, mController.getAnimationType(type)); + + mViewRoot.getView().getViewTreeObserver().dispatchOnPreDraw(); }); InstrumentationRegistry.getInstrumentation().waitForIdleSync(); + + assertTrue("Animation must be ended.", animationEndLatch.await(3, SECONDS)); + assertEquals("The first insets height must be the same as `fromInsetsHeight`", + fromInsetsHeight, progressList.get(0).getInsets(type).top); + assertEquals("The last insets height must be the same as `toInsetsHeight`", + toInsetsHeight, progressList.get(progressList.size() - 1).getInsets(type).top); } @Test diff --git a/core/tests/coretests/src/android/view/ViewFrameRateTest.java b/core/tests/coretests/src/android/view/ViewFrameRateTest.java index abe9c8e337bb..169300a6b81c 100644 --- a/core/tests/coretests/src/android/view/ViewFrameRateTest.java +++ b/core/tests/coretests/src/android/view/ViewFrameRateTest.java @@ -45,6 +45,7 @@ import android.platform.test.annotations.LargeTest; import android.platform.test.annotations.RequiresFlagsEnabled; import android.platform.test.flag.junit.CheckFlagsRule; import android.platform.test.flag.junit.DeviceFlagsValueProvider; +import android.sysprop.ViewProperties; import android.util.DisplayMetrics; import android.widget.FrameLayout; import android.widget.ProgressBar; @@ -101,6 +102,9 @@ public class ViewFrameRateTest { @RequiresFlagsEnabled({FLAG_VIEW_VELOCITY_API, FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY}) public void frameRateChangesWhenContentMoves() throws Throwable { + if (!ViewProperties.vrr_enabled().orElse(true)) { + return; + } waitForFrameRateCategoryToSettle(); mActivityRule.runOnUiThread(() -> { mMovingView.offsetLeftAndRight(100); @@ -126,7 +130,10 @@ public class ViewFrameRateTest { @Test @RequiresFlagsEnabled(FLAG_VIEW_VELOCITY_API) - public void touchBoostDisable() throws Throwable { + public void frameBoostDisable() throws Throwable { + if (!ViewProperties.vrr_enabled().orElse(true)) { + return; + } mActivityRule.runOnUiThread(() -> { long now = SystemClock.uptimeMillis(); MotionEvent down = MotionEvent.obtain( @@ -146,6 +153,7 @@ public class ViewFrameRateTest { mActivityRule.runOnUiThread(() -> { assertFalse(mViewRoot.getIsTouchBoosting()); + assertFalse(mViewRoot.getIsFrameRateBoosting()); }); } @@ -154,6 +162,9 @@ public class ViewFrameRateTest { FLAG_TOOLKIT_FRAME_RATE_VELOCITY_MAPPING_READ_ONLY, FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY}) public void lowVelocity60() throws Throwable { + if (!ViewProperties.vrr_enabled().orElse(true)) { + return; + } mActivityRule.runOnUiThread(() -> { ViewGroup.LayoutParams layoutParams = mMovingView.getLayoutParams(); layoutParams.width = ViewGroup.LayoutParams.MATCH_PARENT; @@ -174,6 +185,9 @@ public class ViewFrameRateTest { FLAG_TOOLKIT_FRAME_RATE_VELOCITY_MAPPING_READ_ONLY, FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY}) public void velocityWithChildMovement() throws Throwable { + if (!ViewProperties.vrr_enabled().orElse(true)) { + return; + } FrameLayout frameLayout = new FrameLayout(mActivity); mActivityRule.runOnUiThread(() -> { ViewGroup.LayoutParams fullSize = new ViewGroup.LayoutParams( @@ -200,6 +214,9 @@ public class ViewFrameRateTest { FLAG_TOOLKIT_FRAME_RATE_VELOCITY_MAPPING_READ_ONLY, FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY}) public void highVelocity120() throws Throwable { + if (!ViewProperties.vrr_enabled().orElse(true)) { + return; + } mActivityRule.runOnUiThread(() -> { ViewGroup.LayoutParams layoutParams = mMovingView.getLayoutParams(); layoutParams.width = ViewGroup.LayoutParams.MATCH_PARENT; @@ -221,6 +238,9 @@ public class ViewFrameRateTest { @RequiresFlagsEnabled({FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY, FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY}) public void noVelocityUsesCategorySmall() throws Throwable { + if (!ViewProperties.vrr_enabled().orElse(true)) { + return; + } final CountDownLatch drawLatch1 = new CountDownLatch(1); mActivityRule.runOnUiThread(() -> { DisplayMetrics displayMetrics = mActivity.getResources().getDisplayMetrics(); @@ -258,6 +278,9 @@ public class ViewFrameRateTest { @RequiresFlagsEnabled({FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY, FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY}) public void noVelocityUsesCategoryNarrowWidth() throws Throwable { + if (!ViewProperties.vrr_enabled().orElse(true)) { + return; + } final CountDownLatch drawLatch1 = new CountDownLatch(1); mActivityRule.runOnUiThread(() -> { DisplayMetrics displayMetrics = mActivity.getResources().getDisplayMetrics(); @@ -294,6 +317,9 @@ public class ViewFrameRateTest { @RequiresFlagsEnabled({FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY, FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY}) public void noVelocityUsesCategoryNarrowHeight() throws Throwable { + if (!ViewProperties.vrr_enabled().orElse(true)) { + return; + } final CountDownLatch drawLatch1 = new CountDownLatch(1); mActivityRule.runOnUiThread(() -> { DisplayMetrics displayMetrics = mActivity.getResources().getDisplayMetrics(); @@ -330,6 +356,9 @@ public class ViewFrameRateTest { @RequiresFlagsEnabled({FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY, FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY}) public void noVelocityUsesCategoryLargeWidth() throws Throwable { + if (!ViewProperties.vrr_enabled().orElse(true)) { + return; + } final CountDownLatch drawLatch1 = new CountDownLatch(1); mActivityRule.runOnUiThread(() -> { DisplayMetrics displayMetrics = mActivity.getResources().getDisplayMetrics(); @@ -366,6 +395,9 @@ public class ViewFrameRateTest { @RequiresFlagsEnabled({FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY, FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY}) public void noVelocityUsesCategoryLargeHeight() throws Throwable { + if (!ViewProperties.vrr_enabled().orElse(true)) { + return; + } final CountDownLatch drawLatch1 = new CountDownLatch(1); mActivityRule.runOnUiThread(() -> { DisplayMetrics displayMetrics = mActivity.getResources().getDisplayMetrics(); @@ -402,6 +434,9 @@ public class ViewFrameRateTest { @RequiresFlagsEnabled({FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY, FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY}) public void defaultNormal() throws Throwable { + if (!ViewProperties.vrr_enabled().orElse(true)) { + return; + } mActivityRule.runOnUiThread(() -> { View parent = (View) mMovingView.getParent(); ViewGroup.LayoutParams layoutParams = mMovingView.getLayoutParams(); @@ -426,6 +461,9 @@ public class ViewFrameRateTest { FLAG_TOOLKIT_FRAME_RATE_VELOCITY_MAPPING_READ_ONLY }) public void frameRateAndCategory() throws Throwable { + if (!ViewProperties.vrr_enabled().orElse(true)) { + return; + } mMovingView.setRequestedFrameRate(View.REQUESTED_FRAME_RATE_CATEGORY_NO_PREFERENCE); waitForFrameRateCategoryToSettle(); mActivityRule.runOnUiThread(() -> { @@ -446,6 +484,9 @@ public class ViewFrameRateTest { FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY }) public void willNotDrawUsesCategory() throws Throwable { + if (!ViewProperties.vrr_enabled().orElse(true)) { + return; + } mActivityRule.runOnUiThread(() -> { mMovingView.setWillNotDraw(true); mMovingView.setRequestedFrameRate(View.REQUESTED_FRAME_RATE_CATEGORY_LOW); @@ -479,6 +520,9 @@ public class ViewFrameRateTest { @RequiresFlagsEnabled({FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY, FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY}) public void intermittentDoubleInvalidate() throws Throwable { + if (!ViewProperties.vrr_enabled().orElse(true)) { + return; + } View parent = (View) mMovingView.getParent(); mActivityRule.runOnUiThread(() -> { parent.setWillNotDraw(false); @@ -498,12 +542,9 @@ public class ViewFrameRateTest { toolkitFrameRateDefaultNormalReadOnly() ? FRAME_RATE_CATEGORY_NORMAL : FRAME_RATE_CATEGORY_HIGH; } else { - // intermittent - // Even though this is not a small View, step 3 is triggered by this flag, which - // brings intermittent to LOW - expectedCategory = toolkitFrameRateBySizeReadOnly() - ? FRAME_RATE_CATEGORY_LOW - : FRAME_RATE_CATEGORY_NORMAL; + // intermittent. + // The expected category is normal. + expectedCategory = FRAME_RATE_CATEGORY_NORMAL; } mActivityRule.runOnUiThread(() -> { mMovingView.invalidate(); @@ -528,6 +569,9 @@ public class ViewFrameRateTest { FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY }) public void sameFrameMotion() throws Throwable { + if (!ViewProperties.vrr_enabled().orElse(true)) { + return; + } mMovingView.setRequestedFrameRate(View.REQUESTED_FRAME_RATE_CATEGORY_NO_PREFERENCE); waitForFrameRateCategoryToSettle(); @@ -551,6 +595,9 @@ public class ViewFrameRateTest { FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY }) public void frameRateReset() throws Throwable { + if (!ViewProperties.vrr_enabled().orElse(true)) { + return; + } mMovingView.setRequestedFrameRate(120f); waitForFrameRateCategoryToSettle(); mActivityRule.runOnUiThread(() -> mMovingView.setVisibility(View.INVISIBLE)); @@ -572,6 +619,9 @@ public class ViewFrameRateTest { FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY }) public void frameRateResetWithInvalidations() throws Throwable { + if (!ViewProperties.vrr_enabled().orElse(true)) { + return; + } mMovingView.setRequestedFrameRate(120f); waitForFrameRateCategoryToSettle(); mMovingView.setRequestedFrameRate(View.REQUESTED_FRAME_RATE_CATEGORY_NORMAL); @@ -592,6 +642,9 @@ public class ViewFrameRateTest { FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY }) public void testQuickTouchBoost() throws Throwable { + if (!ViewProperties.vrr_enabled().orElse(true)) { + return; + } mActivityRule.runOnUiThread(() -> { mMovingView.setRequestedFrameRate(View.REQUESTED_FRAME_RATE_CATEGORY_LOW); ViewGroup.LayoutParams layoutParams = mMovingView.getLayoutParams(); @@ -632,6 +685,9 @@ public class ViewFrameRateTest { com.android.graphics.surfaceflinger.flags.Flags.FLAG_VRR_BUGFIX_24Q4 }) public void idleDetected() throws Throwable { + if (!ViewProperties.vrr_enabled().orElse(true)) { + return; + } waitForFrameRateCategoryToSettle(); mActivityRule.runOnUiThread(() -> { mMovingView.setRequestedFrameRate(View.REQUESTED_FRAME_RATE_CATEGORY_HIGH); @@ -656,6 +712,9 @@ public class ViewFrameRateTest { com.android.graphics.surfaceflinger.flags.Flags.FLAG_VRR_BUGFIX_24Q4 }) public void vectorDrawableFrameRate() throws Throwable { + if (!ViewProperties.vrr_enabled().orElse(true)) { + return; + } final ProgressBar[] progressBars = new ProgressBar[3]; final ViewGroup[] parents = new ViewGroup[1]; mActivityRule.runOnUiThread(() -> { @@ -713,6 +772,9 @@ public class ViewFrameRateTest { com.android.graphics.surfaceflinger.flags.Flags.FLAG_VRR_BUGFIX_24Q4 }) public void renderNodeAnimatorFrameRateCanceled() throws Throwable { + if (!ViewProperties.vrr_enabled().orElse(true)) { + return; + } mMovingView.setRequestedFrameRate(View.REQUESTED_FRAME_RATE_CATEGORY_NO_PREFERENCE); waitForFrameRateCategoryToSettle(); @@ -750,6 +812,9 @@ public class ViewFrameRateTest { com.android.graphics.surfaceflinger.flags.Flags.FLAG_VRR_BUGFIX_24Q4 }) public void renderNodeAnimatorFrameRateRemoved() throws Throwable { + if (!ViewProperties.vrr_enabled().orElse(true)) { + return; + } mMovingView.setRequestedFrameRate(View.REQUESTED_FRAME_RATE_CATEGORY_NO_PREFERENCE); waitForFrameRateCategoryToSettle(); diff --git a/core/tests/coretests/src/android/view/ViewRootImplTest.java b/core/tests/coretests/src/android/view/ViewRootImplTest.java index 94e187aed698..06cb0eea811b 100644 --- a/core/tests/coretests/src/android/view/ViewRootImplTest.java +++ b/core/tests/coretests/src/android/view/ViewRootImplTest.java @@ -72,6 +72,7 @@ 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.sysprop.ViewProperties; import android.util.DisplayMetrics; import android.util.Log; import android.view.WindowInsets.Side; @@ -503,6 +504,9 @@ public class ViewRootImplTest { @RequiresFlagsEnabled({FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY, FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY}) public void votePreferredFrameRate_getDefaultValues() { + if (!ViewProperties.vrr_enabled().orElse(true)) { + return; + } ViewRootImpl viewRootImpl = new ViewRootImpl(sContext, sContext.getDisplayNoVerify()); assertEquals(FRAME_RATE_CATEGORY_DEFAULT, @@ -521,6 +525,9 @@ public class ViewRootImplTest { FLAG_TOOLKIT_FRAME_RATE_BY_SIZE_READ_ONLY, FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY}) public void votePreferredFrameRate_voteFrameRateCategory_visibility_bySize() throws Throwable { + if (!ViewProperties.vrr_enabled().orElse(true)) { + return; + } mView = new View(sContext); attachViewToWindow(mView); mViewRootImpl = mView.getViewRootImpl(); @@ -558,6 +565,9 @@ public class ViewRootImplTest { FLAG_TOOLKIT_FRAME_RATE_BY_SIZE_READ_ONLY, FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY}) public void votePreferredFrameRate_voteFrameRateCategory_smallSize_bySize() throws Throwable { + if (!ViewProperties.vrr_enabled().orElse(true)) { + return; + } mView = new View(sContext); WindowManager.LayoutParams wmlp = new WindowManager.LayoutParams(TYPE_APPLICATION_OVERLAY); wmlp.token = new Binder(); // Set a fake token to bypass 'is your activity running' check @@ -590,6 +600,9 @@ public class ViewRootImplTest { FLAG_TOOLKIT_FRAME_RATE_BY_SIZE_READ_ONLY, FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY}) public void votePreferredFrameRate_voteFrameRateCategory_normalSize_bySize() throws Throwable { + if (!ViewProperties.vrr_enabled().orElse(true)) { + return; + } mView = new View(sContext); WindowManager.LayoutParams wmlp = new WindowManager.LayoutParams(TYPE_APPLICATION_OVERLAY); wmlp.token = new Binder(); // Set a fake token to bypass 'is your activity running' check @@ -627,6 +640,9 @@ public class ViewRootImplTest { FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY}) public void votePreferredFrameRate_voteFrameRateCategory_visibility_defaultHigh() throws Throwable { + if (!ViewProperties.vrr_enabled().orElse(true)) { + return; + } mView = new View(sContext); WindowManager.LayoutParams wmlp = new WindowManager.LayoutParams(TYPE_APPLICATION_OVERLAY); wmlp.token = new Binder(); // Set a fake token to bypass 'is your activity running' check @@ -688,6 +704,9 @@ public class ViewRootImplTest { FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY}) public void votePreferredFrameRate_voteFrameRateCategory_smallSize_defaultHigh() throws Throwable { + if (!ViewProperties.vrr_enabled().orElse(true)) { + return; + } mView = new View(sContext); WindowManager.LayoutParams wmlp = new WindowManager.LayoutParams(TYPE_APPLICATION_OVERLAY); wmlp.token = new Binder(); // Set a fake token to bypass 'is your activity running' check @@ -723,6 +742,9 @@ public class ViewRootImplTest { FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY}) public void votePreferredFrameRate_voteFrameRateCategory_normalSize_defaultHigh() throws Throwable { + if (!ViewProperties.vrr_enabled().orElse(true)) { + return; + } mView = new View(sContext); WindowManager.LayoutParams wmlp = new WindowManager.LayoutParams(TYPE_APPLICATION_OVERLAY); wmlp.token = new Binder(); // Set a fake token to bypass 'is your activity running' check @@ -758,6 +780,9 @@ public class ViewRootImplTest { @RequiresFlagsEnabled({FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY, FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY}) public void votePreferredFrameRate_voteFrameRateCategory_aggregate() { + if (!ViewProperties.vrr_enabled().orElse(true)) { + return; + } mView = new View(sContext); attachViewToWindow(mView); mViewRootImpl = mView.getViewRootImpl(); @@ -804,6 +829,9 @@ public class ViewRootImplTest { @RequiresFlagsEnabled({FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY, FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY}) public void votePreferredFrameRate_voteFrameRate_aggregate() { + if (!ViewProperties.vrr_enabled().orElse(true)) { + return; + } mView = new View(sContext); attachViewToWindow(mView); mViewRootImpl = mView.getViewRootImpl(); @@ -876,6 +904,9 @@ public class ViewRootImplTest { FLAG_TOOLKIT_FRAME_RATE_FUNCTION_ENABLING_READ_ONLY, FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY}) public void votePreferredFrameRate_voteFrameRate_category() throws Throwable { + if (!ViewProperties.vrr_enabled().orElse(true)) { + return; + } mView = new View(sContext); attachViewToWindow(mView); sInstrumentation.waitForIdleSync(); @@ -930,6 +961,9 @@ public class ViewRootImplTest { FLAG_TOOLKIT_FRAME_RATE_FUNCTION_ENABLING_READ_ONLY, FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY}) public void votePreferredFrameRate_voteFrameRateCategory_velocityToHigh() throws Throwable { + if (!ViewProperties.vrr_enabled().orElse(true)) { + return; + } mView = new View(sContext); WindowManager.LayoutParams wmlp = new WindowManager.LayoutParams(TYPE_APPLICATION_OVERLAY); wmlp.token = new Binder(); // Set a fake token to bypass 'is your activity running' check @@ -973,6 +1007,9 @@ public class ViewRootImplTest { @RequiresFlagsEnabled({FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY, FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY}) public void votePreferredFrameRate_insetsAnimation() { + if (!ViewProperties.vrr_enabled().orElse(true)) { + return; + } mView = new View(sContext); WindowManager.LayoutParams wmlp = new WindowManager.LayoutParams(TYPE_APPLICATION_OVERLAY); wmlp.token = new Binder(); // Set a fake token to bypass 'is your activity running' check @@ -1010,6 +1047,9 @@ public class ViewRootImplTest { @RequiresFlagsEnabled({FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY, FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY}) public void votePreferredFrameRate_frameRateBoostOnTouch() { + if (!ViewProperties.vrr_enabled().orElse(true)) { + return; + } mView = new View(sContext); attachViewToWindow(mView); sInstrumentation.waitForIdleSync(); @@ -1043,6 +1083,9 @@ public class ViewRootImplTest { @RequiresFlagsEnabled({FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY, FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY}) public void votePreferredFrameRate_voteFrameRateTimeOut() throws InterruptedException { + if (!ViewProperties.vrr_enabled().orElse(true)) { + return; + } final long delay = 200L; mView = new View(sContext); @@ -1082,6 +1125,9 @@ public class ViewRootImplTest { FLAG_TOOLKIT_FRAME_RATE_FUNCTION_ENABLING_READ_ONLY, FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY}) public void votePreferredFrameRate_voteFrameRateOnly() throws Throwable { + if (!ViewProperties.vrr_enabled().orElse(true)) { + return; + } mView = new View(sContext); float frameRate = 20; attachViewToWindow(mView); @@ -1133,6 +1179,9 @@ public class ViewRootImplTest { FLAG_TOOLKIT_FRAME_RATE_FUNCTION_ENABLING_READ_ONLY, FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY}) public void votePreferredFrameRate_infrequentLayer_defaultHigh() throws Throwable { + if (!ViewProperties.vrr_enabled().orElse(true)) { + return; + } final long delay = 200L; mView = new View(sContext); @@ -1175,11 +1224,8 @@ public class ViewRootImplTest { // Infrequent update Thread.sleep(delay); - // Even though this is not a small View, step 3 is triggered by this flag, which - // brings intermittent to LOW - int intermittentExpected = toolkitFrameRateBySizeReadOnly() - ? FRAME_RATE_CATEGORY_LOW - : FRAME_RATE_CATEGORY_NORMAL; + // The expected category is normal for intermittent. + int intermittentExpected = FRAME_RATE_CATEGORY_NORMAL; sInstrumentation.runOnMainSync(() -> { mView.invalidate(); @@ -1211,6 +1257,9 @@ public class ViewRootImplTest { @RequiresFlagsEnabled({FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY, FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY}) public void votePreferredFrameRate_isFrameRatePowerSavingsBalanced() { + if (!ViewProperties.vrr_enabled().orElse(true)) { + return; + } mView = new View(sContext); attachViewToWindow(mView); sInstrumentation.waitForIdleSync(); @@ -1245,6 +1294,9 @@ public class ViewRootImplTest { FLAG_TOOLKIT_FRAME_RATE_FUNCTION_ENABLING_READ_ONLY, FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY}) public void votePreferredFrameRate_applyTextureViewHeuristic() throws Throwable { + if (!ViewProperties.vrr_enabled().orElse(true)) { + return; + } final long delay = 30L; mView = new TextureView(sContext); @@ -1289,6 +1341,9 @@ public class ViewRootImplTest { @Test @RequiresFlagsEnabled(FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY) public void votePreferredFrameRate_velocityVotedAfterOnDraw() throws Throwable { + if (!ViewProperties.vrr_enabled().orElse(true)) { + return; + } mView = new View(sContext); double delta = 0.1; float pixelsPerSecond = 1000_000; diff --git a/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java b/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java index 5a6824bf0d7e..b5c264c4ae5e 100644 --- a/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java +++ b/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java @@ -146,10 +146,6 @@ public class AccessibilityServiceConnectionImpl extends IAccessibilityServiceCon public void setMagnificationCallbackEnabled(int displayId, boolean enabled) {} - public boolean isMagnificationSystemUIConnected() { - return false; - } - public boolean setSoftKeyboardShowMode(int showMode) { return false; } diff --git a/core/tests/coretests/src/android/widget/RemoteViewsTest.java b/core/tests/coretests/src/android/widget/RemoteViewsTest.java index 15c90474c017..c8ea3742b3aa 100644 --- a/core/tests/coretests/src/android/widget/RemoteViewsTest.java +++ b/core/tests/coretests/src/android/widget/RemoteViewsTest.java @@ -34,6 +34,9 @@ import android.app.PendingIntent; import android.appwidget.AppWidgetHostView; import android.content.Context; import android.content.Intent; +import android.graphics.Bitmap; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.Drawable; import android.graphics.drawable.Icon; import android.net.Uri; import android.os.AsyncTask; @@ -834,33 +837,6 @@ public class RemoteViewsTest { } @Test - public void visitUris_intents() { - RemoteViews views = new RemoteViews(mPackage, R.layout.remote_views_test); - - Uri fillIntentUri = Uri.parse("content://intent/fill"); - views.setOnCheckedChangeResponse( - R.id.layout, - RemoteViews.RemoteResponse.fromFillInIntent(new Intent("action", fillIntentUri))); - - Uri pendingIntentUri = Uri.parse("content://intent/pending"); - PendingIntent pendingIntent = getPendingIntentWithUri(pendingIntentUri); - views.setOnClickResponse( - R.id.layout, - RemoteViews.RemoteResponse.fromPendingIntent(pendingIntent)); - - Consumer<Uri> visitor = (Consumer<Uri>) spy(Consumer.class); - views.visitUris(visitor); - verify(visitor, times(1)).accept(eq(fillIntentUri)); - verify(visitor, times(1)).accept(eq(pendingIntentUri)); - } - - private PendingIntent getPendingIntentWithUri(Uri uri) { - return PendingIntent.getActivity(mContext, 0, - new Intent("action", uri), - PendingIntent.FLAG_IMMUTABLE); - } - - @Test public void layoutInflaterFactory_nothingSet_returnsNull() { final RemoteViews rv = new RemoteViews(mPackage, R.layout.remote_views_test); assertNull(rv.getLayoutInflaterFactory()); diff --git a/core/tests/coretests/src/android/window/WindowOnBackInvokedDispatcherTest.java b/core/tests/coretests/src/android/window/WindowOnBackInvokedDispatcherTest.java index 1aada40ab8e9..b0190a5c8218 100644 --- a/core/tests/coretests/src/android/window/WindowOnBackInvokedDispatcherTest.java +++ b/core/tests/coretests/src/android/window/WindowOnBackInvokedDispatcherTest.java @@ -28,6 +28,7 @@ import static org.mockito.Mockito.atLeast; import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.never; +import static org.mockito.Mockito.timeout; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; @@ -111,7 +112,7 @@ public class WindowOnBackInvokedDispatcherTest { doReturn(mApplicationInfo).when(mContext).getApplicationInfo(); mDispatcher = new WindowOnBackInvokedDispatcher(mContext, Looper.getMainLooper()); - mDispatcher.attachToWindow(mWindowSession, mWindow, null, mImeBackAnimationController); + mDispatcher.attachToWindow(mWindowSession, mWindow, mImeBackAnimationController); } private void waitForIdle() { @@ -368,7 +369,7 @@ public class WindowOnBackInvokedDispatcherTest { callbackInfo.getCallback().onBackInvoked(); waitForIdle(); - verify(mCallback1).onBackInvoked(); + verify(mCallback1, timeout(/*millis*/ 1000)).onBackInvoked(); verify(mCallback1, never()).onBackCancelled(); } @@ -454,26 +455,25 @@ public class WindowOnBackInvokedDispatcherTest { @Test public void registerImeCallbacks_onBackInvokedCallbackEnabled() throws RemoteException { - verifyImeCallackRegistrations(); - } - - @Test - public void registerImeCallbacks_onBackInvokedCallbackDisabled() throws RemoteException { - doReturn(false).when(mApplicationInfo).isOnBackInvokedCallbackEnabled(); - verifyImeCallackRegistrations(); - } - - private void verifyImeCallackRegistrations() throws RemoteException { - // verify default callback is replaced with ImeBackAnimationController - mDispatcher.registerOnBackInvokedCallbackUnchecked(mDefaultImeCallback, PRIORITY_DEFAULT); + mDispatcher.registerOnBackInvokedCallback(PRIORITY_DEFAULT, mDefaultImeCallback); assertCallbacksSize(/* default */ 1, /* overlay */ 0); assertSetCallbackInfo(); assertTopCallback(mImeBackAnimationController); - // verify regular ime callback is successfully registered - mDispatcher.registerOnBackInvokedCallbackUnchecked(mImeCallback, PRIORITY_DEFAULT); + mDispatcher.registerOnBackInvokedCallback(PRIORITY_DEFAULT, mImeCallback); assertCallbacksSize(/* default */ 2, /* overlay */ 0); assertSetCallbackInfo(); assertTopCallback(mImeCallback); } + + @Test + public void registerImeCallbacks_legacyBack() throws RemoteException { + doReturn(false).when(mApplicationInfo).isOnBackInvokedCallbackEnabled(); + + mDispatcher.registerOnBackInvokedCallback(PRIORITY_DEFAULT, mDefaultImeCallback); + assertNoSetCallbackInfo(); + + mDispatcher.registerOnBackInvokedCallback(PRIORITY_DEFAULT, mImeCallback); + assertNoSetCallbackInfo(); + } } diff --git a/core/tests/coretests/src/com/android/internal/app/NoOpResolverComparatorTest.java b/core/tests/coretests/src/com/android/internal/app/NoOpResolverComparatorTest.java new file mode 100644 index 000000000000..22c319cd4d08 --- /dev/null +++ b/core/tests/coretests/src/com/android/internal/app/NoOpResolverComparatorTest.java @@ -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.internal.app; + +import static com.google.common.truth.Truth.assertThat; + +import android.content.Intent; +import android.os.UserHandle; + +import androidx.test.InstrumentationRegistry; +import androidx.test.runner.AndroidJUnit4; + +import com.android.internal.app.ResolverActivity.ResolvedComponentInfo; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** Unit tests for the behavior of {@link NoOpResolverComparator}. */ +@RunWith(AndroidJUnit4.class) +public class NoOpResolverComparatorTest { + + private static final UserHandle PERSONAL_USER_HANDLE = InstrumentationRegistry + .getInstrumentation().getTargetContext().getUser(); + + public final ResolvedComponentInfo resolution1 = + ResolverDataProvider.createResolvedComponentInfo(1, PERSONAL_USER_HANDLE); + public final ResolvedComponentInfo resolution2 = + ResolverDataProvider.createResolvedComponentInfo(2, PERSONAL_USER_HANDLE); + public final ResolvedComponentInfo resolution3 = + ResolverDataProvider.createResolvedComponentInfo(3, PERSONAL_USER_HANDLE); + public final ResolvedComponentInfo resolution4 = + ResolverDataProvider.createResolvedComponentInfo(4, PERSONAL_USER_HANDLE); + + private NoOpResolverComparator mComparator; + + @Before + public void setUp() { + mComparator = new NoOpResolverComparator( + InstrumentationRegistry.getInstrumentation().getTargetContext(), + new Intent(), + List.of(PERSONAL_USER_HANDLE)); + } + + @Test + public void testKnownItemsSortInOriginalOrder() { + List<ResolvedComponentInfo> originalOrder = List.of(resolution1, resolution2, resolution3); + mComparator.doCompute(originalOrder); + + List<ResolvedComponentInfo> queryOrder = new ArrayList<>( + List.of(resolution2, resolution3, resolution1)); + + Collections.sort(queryOrder, mComparator); + assertThat(queryOrder).isEqualTo(originalOrder); + } + + @Test + public void testUnknownItemsSortAfterKnownItems() { + List<ResolvedComponentInfo> originalOrder = List.of(resolution1, resolution2); + mComparator.doCompute(originalOrder); + + // Query includes the unknown `resolution4`. + List<ResolvedComponentInfo> queryOrder = new ArrayList<>( + List.of(resolution2, resolution4, resolution1)); + Collections.sort(queryOrder, mComparator); + + assertThat(queryOrder).isEqualTo(List.of(resolution1, resolution2, resolution4)); + } + + @Test + public void testKnownItemsGetNonZeroScoresInOrder() { + List<ResolvedComponentInfo> originalOrder = List.of(resolution1, resolution2); + mComparator.doCompute(originalOrder); + + float score1 = mComparator.getScore(resolution1.getResolveInfoAt(0)); + float score2 = mComparator.getScore(resolution2.getResolveInfoAt(0)); + + assertThat(score1).isEqualTo(1.0f); + assertThat(score2).isLessThan(score1); + assertThat(score2).isGreaterThan(0.0f); + } + + @Test + public void testUnknownItemsGetZeroScore() { + List<ResolvedComponentInfo> originalOrder = List.of(resolution1, resolution2); + mComparator.doCompute(originalOrder); + + assertThat(mComparator.getScore(resolution3.getResolveInfoAt(0))).isEqualTo(0.0f); + } +} diff --git a/core/tests/coretests/src/com/android/internal/statusbar/RegisterStatusBarResultTest.java b/core/tests/coretests/src/com/android/internal/statusbar/RegisterStatusBarResultTest.java index f79ba28d946f..af2a2bbe2893 100644 --- a/core/tests/coretests/src/com/android/internal/statusbar/RegisterStatusBarResultTest.java +++ b/core/tests/coretests/src/com/android/internal/statusbar/RegisterStatusBarResultTest.java @@ -48,7 +48,8 @@ public class RegisterStatusBarResultTest { final String dumyIconKey = "dummyIcon1"; final ArrayMap<String, StatusBarIcon> iconMap = new ArrayMap<>(); iconMap.put(dumyIconKey, new StatusBarIcon("com.android.internal.statusbar.test", - UserHandle.of(100), 123, 1, 2, "dummyIconDescription")); + UserHandle.of(100), 123, 1, 2, "dummyIconDescription", + StatusBarIcon.Type.SystemIcon)); final LetterboxDetails letterboxDetails = new LetterboxDetails( /* letterboxInnerBounds= */ new Rect(1, 2, 3, 4), /* letterboxFullBounds= */ new Rect(5, 6, 7, 8), diff --git a/core/tests/coretests/src/com/android/internal/statusbar/StatusBarIconTest.java b/core/tests/coretests/src/com/android/internal/statusbar/StatusBarIconTest.java index fe552a08f629..a895378eaaf9 100644 --- a/core/tests/coretests/src/com/android/internal/statusbar/StatusBarIconTest.java +++ b/core/tests/coretests/src/com/android/internal/statusbar/StatusBarIconTest.java @@ -44,7 +44,8 @@ public class StatusBarIconTest { final int dummyIconNumber = 2; final CharSequence dummyIconContentDescription = "dummyIcon"; final StatusBarIcon original = new StatusBarIcon(dummyIconPackageName, dummyUserHandle, - dummyIconId, dummyIconLevel, dummyIconNumber, dummyIconContentDescription); + dummyIconId, dummyIconLevel, dummyIconNumber, dummyIconContentDescription, + StatusBarIcon.Type.SystemIcon); final StatusBarIcon copy = clone(original); diff --git a/core/tests/coretests/src/com/android/internal/widget/ActionBarOverlayLayoutTest.java b/core/tests/coretests/src/com/android/internal/widget/ActionBarOverlayLayoutTest.java index 1dbb7758bf39..2b8adcbe0656 100644 --- a/core/tests/coretests/src/com/android/internal/widget/ActionBarOverlayLayoutTest.java +++ b/core/tests/coretests/src/com/android/internal/widget/ActionBarOverlayLayoutTest.java @@ -20,6 +20,7 @@ import static android.view.DisplayCutout.NO_CUTOUT; import static android.view.View.MeasureSpec.EXACTLY; import static android.view.View.MeasureSpec.makeMeasureSpec; import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; +import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT; import static org.hamcrest.CoreMatchers.nullValue; import static org.hamcrest.Matchers.is; @@ -69,6 +70,7 @@ public class ActionBarOverlayLayoutTest { private ViewGroup mContent; private ViewGroup mActionBarTop; + private ViewGroup mActionBarView; private Toolbar mToolbar; private FakeOnApplyWindowListener mContentInsetsListener; @@ -86,15 +88,22 @@ public class ActionBarOverlayLayoutTest { mContentInsetsListener = new FakeOnApplyWindowListener(); mContent.setOnApplyWindowInsetsListener(mContentInsetsListener); + // mActionBarView and mToolbar are supposed to be the same view. Here makes mToolbar a child + // of mActionBarView is to control the height of mActionBarView. In this way, the child + // views of mToolbar won't affect the measurement of mActionBarView or mActionBarTop. + mActionBarView = new FrameLayout(mContext); + mActionBarView.setLayoutParams(new ViewGroup.LayoutParams(MATCH_PARENT, 20)); + + mToolbar = new Toolbar(mContext); + mToolbar.setId(com.android.internal.R.id.action_bar); + mActionBarView.addView(mToolbar); + mActionBarTop = new ActionBarContainer(mContext); mActionBarTop.setId(com.android.internal.R.id.action_bar_container); - mActionBarTop.setLayoutParams(new ViewGroup.LayoutParams(MATCH_PARENT, 20)); + mActionBarTop.setLayoutParams(new ViewGroup.LayoutParams(MATCH_PARENT, WRAP_CONTENT)); + mActionBarTop.addView(mActionBarView); mLayout.addView(mActionBarTop); mLayout.setActionBarHeight(20); - - mToolbar = new Toolbar(mContext); - mToolbar.setId(com.android.internal.R.id.action_bar); - mActionBarTop.addView(mToolbar); } @Test diff --git a/core/tests/hdmitests/src/android/hardware/hdmi/HdmiDeviceInfoTest.java b/core/tests/hdmitests/src/android/hardware/hdmi/HdmiDeviceInfoTest.java index 5039fe8b6cc2..5039fe8b6cc2 100755..100644 --- a/core/tests/hdmitests/src/android/hardware/hdmi/HdmiDeviceInfoTest.java +++ b/core/tests/hdmitests/src/android/hardware/hdmi/HdmiDeviceInfoTest.java diff --git a/core/tests/hdmitests/src/android/hardware/hdmi/HdmiPortInfoTest.java b/core/tests/hdmitests/src/android/hardware/hdmi/HdmiPortInfoTest.java index fde112269003..fde112269003 100755..100644 --- a/core/tests/hdmitests/src/android/hardware/hdmi/HdmiPortInfoTest.java +++ b/core/tests/hdmitests/src/android/hardware/hdmi/HdmiPortInfoTest.java diff --git a/data/etc/Android.bp b/data/etc/Android.bp index 1410950966e9..050f9b5e264f 100644 --- a/data/etc/Android.bp +++ b/data/etc/Android.bp @@ -200,13 +200,3 @@ prebuilt_etc { src: "com.android.systemui.xml", filename_from_src: true, } - -filegroup { - name: "services.core.protolog.json", - srcs: ["services.core.protolog.json"], -} - -filegroup { - name: "file-core.protolog.pb", - srcs: ["core.protolog.pb"], -} diff --git a/data/etc/core.protolog.pb b/data/etc/core.protolog.pb Binary files differdeleted file mode 100644 index a105ba756d51..000000000000 --- a/data/etc/core.protolog.pb +++ /dev/null diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json deleted file mode 100644 index db68f9528151..000000000000 --- a/data/etc/services.core.protolog.json +++ /dev/null @@ -1,4870 +0,0 @@ -{ - "version": "2.0.0", - "messages": { - "7286191062634870297": { - "message": "Binding proc %s with config %s", - "level": "VERBOSE", - "group": "WM_DEBUG_CONFIGURATION", - "at": "com\/android\/server\/am\/ActivityManagerService.java" - }, - "-4921282642721622589": { - "message": "Report configuration: %s %s", - "level": "VERBOSE", - "group": "WM_DEBUG_CONFIGURATION", - "at": "com\/android\/server\/wm\/ActivityClientController.java" - }, - "-1597980207704427048": { - "message": "Frontmost changed immersion: %s", - "level": "DEBUG", - "group": "WM_DEBUG_IMMERSIVE", - "at": "com\/android\/server\/wm\/ActivityClientController.java" - }, - "-6509265758887333864": { - "message": "Can't report activity moved to display - client not running, activityRecord=%s, displayId=%d", - "level": "WARN", - "group": "WM_DEBUG_SWITCH", - "at": "com\/android\/server\/wm\/ActivityRecord.java" - }, - "-4183059578873561863": { - "message": "Reporting activity moved to display, activityRecord=%s, displayId=%d, config=%s", - "level": "VERBOSE", - "group": "WM_DEBUG_SWITCH", - "at": "com\/android\/server\/wm\/ActivityRecord.java" - }, - "7435279034964784633": { - "message": "Can't report activity configuration update - client not running, activityRecord=%s", - "level": "WARN", - "group": "WM_DEBUG_CONFIGURATION", - "at": "com\/android\/server\/wm\/ActivityRecord.java" - }, - "-7418876140361338495": { - "message": "Sending new config to %s, config: %s", - "level": "VERBOSE", - "group": "WM_DEBUG_CONFIGURATION", - "at": "com\/android\/server\/wm\/ActivityRecord.java" - }, - "-4284934398288119962": { - "message": "Can't report activity position update - client not running, activityRecord=%s", - "level": "WARN", - "group": "WM_DEBUG_STATES", - "at": "com\/android\/server\/wm\/ActivityRecord.java" - }, - "7244227111034368231": { - "message": "Sending position change to %s, onTop: %b", - "level": "VERBOSE", - "group": "WM_DEBUG_STATES", - "at": "com\/android\/server\/wm\/ActivityRecord.java" - }, - "338586566486930495": { - "message": "Checking theme of starting window: 0x%x", - "level": "VERBOSE", - "group": "WM_DEBUG_STARTING_WINDOW", - "at": "com\/android\/server\/wm\/ActivityRecord.java" - }, - "-2561793317091789573": { - "message": "Translucent=%s Floating=%s ShowWallpaper=%s Disable=%s", - "level": "VERBOSE", - "group": "WM_DEBUG_STARTING_WINDOW", - "at": "com\/android\/server\/wm\/ActivityRecord.java" - }, - "7269690012594027154": { - "message": "Creating SplashScreenStartingData", - "level": "VERBOSE", - "group": "WM_DEBUG_STARTING_WINDOW", - "at": "com\/android\/server\/wm\/ActivityRecord.java" - }, - "-3432060893368468911": { - "message": "Creating SnapshotStartingData", - "level": "VERBOSE", - "group": "WM_DEBUG_STARTING_WINDOW", - "at": "com\/android\/server\/wm\/ActivityRecord.java" - }, - "5659016061937922595": { - "message": "Add starting %s: startingData=%s", - "level": "VERBOSE", - "group": "WM_DEBUG_STARTING_WINDOW", - "at": "com\/android\/server\/wm\/ActivityRecord.java" - }, - "7506106334102501360": { - "message": "Added starting %s: startingWindow=%s startingView=%s", - "level": "VERBOSE", - "group": "WM_DEBUG_STARTING_WINDOW", - "at": "com\/android\/server\/wm\/ActivityRecord.java" - }, - "1048048288756547220": { - "message": "Surface returned was null: %s", - "level": "VERBOSE", - "group": "WM_DEBUG_STARTING_WINDOW", - "at": "com\/android\/server\/wm\/ActivityRecord.java" - }, - "-1298801500610545721": { - "message": "Cleaning splash screen token=%s", - "level": "VERBOSE", - "group": "WM_DEBUG_STARTING_WINDOW", - "at": "com\/android\/server\/wm\/ActivityRecord.java" - }, - "-1948849214526113495": { - "message": "Clearing startingData for token=%s", - "level": "VERBOSE", - "group": "WM_DEBUG_STARTING_WINDOW", - "at": "com\/android\/server\/wm\/ActivityRecord.java" - }, - "5545923784327902026": { - "message": "Schedule remove starting %s startingWindow=%s animate=%b Callers=%s", - "level": "VERBOSE", - "group": "WM_DEBUG_STARTING_WINDOW", - "at": "com\/android\/server\/wm\/ActivityRecord.java" - }, - "-5150982660941074218": { - "message": "startingWindow was set but startingSurface==null, couldn't remove", - "level": "VERBOSE", - "group": "WM_DEBUG_STARTING_WINDOW", - "at": "com\/android\/server\/wm\/ActivityRecord.java" - }, - "-2178757341169633804": { - "message": "Tried to remove starting window but startingWindow was null: %s", - "level": "VERBOSE", - "group": "WM_DEBUG_STARTING_WINDOW", - "at": "com\/android\/server\/wm\/ActivityRecord.java" - }, - "5521236266092347335": { - "message": "reparent: moving activity=%s to new task fragment in task=%d at %d", - "level": "INFO", - "group": "WM_DEBUG_ADD_REMOVE", - "at": "com\/android\/server\/wm\/ActivityRecord.java" - }, - "-9024836052864189016": { - "message": "moveFocusableActivityToTop: unfocusable activity=%s", - "level": "DEBUG", - "group": "WM_DEBUG_FOCUS", - "at": "com\/android\/server\/wm\/ActivityRecord.java" - }, - "134255351804410010": { - "message": "moveFocusableActivityToTop: already on top and focused, activity=%s", - "level": "DEBUG", - "group": "WM_DEBUG_FOCUS", - "at": "com\/android\/server\/wm\/ActivityRecord.java" - }, - "-1058622321669556178": { - "message": "moveFocusableActivityToTop: set focused, activity=%s", - "level": "DEBUG", - "group": "WM_DEBUG_FOCUS", - "at": "com\/android\/server\/wm\/ActivityRecord.java" - }, - "731006689098152100": { - "message": "moveFocusableActivityToTop: activity=%s", - "level": "DEBUG", - "group": "WM_DEBUG_FOCUS", - "at": "com\/android\/server\/wm\/ActivityRecord.java" - }, - "3707721620395081349": { - "message": "Finishing activity r=%s, result=%d, data=%s, reason=%s", - "level": "VERBOSE", - "group": "WM_DEBUG_STATES", - "at": "com\/android\/server\/wm\/ActivityRecord.java" - }, - "-3691592300155948194": { - "message": "Finish needs to pause: %s", - "level": "VERBOSE", - "group": "WM_DEBUG_STATES", - "at": "com\/android\/server\/wm\/ActivityRecord.java" - }, - "5813636479397543744": { - "message": "Finish waiting for pause of: %s", - "level": "VERBOSE", - "group": "WM_DEBUG_STATES", - "at": "com\/android\/server\/wm\/ActivityRecord.java" - }, - "-2989211291975863399": { - "message": "destroyIfPossible: r=%s destroy returned removed=%s", - "level": "DEBUG", - "group": "WM_DEBUG_CONTAINERS", - "at": "com\/android\/server\/wm\/ActivityRecord.java" - }, - "3169053633576517098": { - "message": "Enqueueing pending finish: %s", - "level": "VERBOSE", - "group": "WM_DEBUG_STATES", - "at": "com\/android\/server\/wm\/ActivityRecord.java" - }, - "9050478058743283018": { - "message": "activity %s already destroying, skipping request with reason:%s", - "level": "VERBOSE", - "group": "WM_DEBUG_STATES", - "at": "com\/android\/server\/wm\/ActivityRecord.java" - }, - "5672598223877126839": { - "message": "Moving to DESTROYING: %s (destroy requested)", - "level": "VERBOSE", - "group": "WM_DEBUG_STATES", - "at": "com\/android\/server\/wm\/ActivityRecord.java" - }, - "-1834399855266808961": { - "message": "Moving to DESTROYED: %s (destroy skipped)", - "level": "VERBOSE", - "group": "WM_DEBUG_STATES", - "at": "com\/android\/server\/wm\/ActivityRecord.java" - }, - "3282063745558462269": { - "message": "Moving to DESTROYED: %s (no app)", - "level": "VERBOSE", - "group": "WM_DEBUG_STATES", - "at": "com\/android\/server\/wm\/ActivityRecord.java" - }, - "8836546031252812807": { - "message": "Removing activity %s, reason= %s callers=%s", - "level": "INFO", - "group": "WM_DEBUG_ADD_REMOVE", - "at": "com\/android\/server\/wm\/ActivityRecord.java" - }, - "8348126473928520781": { - "message": "Moving to DESTROYED: %s (removed from history)", - "level": "VERBOSE", - "group": "WM_DEBUG_STATES", - "at": "com\/android\/server\/wm\/ActivityRecord.java" - }, - "-8001673213497887656": { - "message": "activityDestroyedLocked: r=%s", - "level": "DEBUG", - "group": "WM_DEBUG_CONTAINERS", - "at": "com\/android\/server\/wm\/ActivityRecord.java" - }, - "587363723665813898": { - "message": "Removing activity %s hasSavedState=%b stateNotNeeded=%s finishing=%b state=%s callers=%s", - "level": "INFO", - "group": "WM_DEBUG_ADD_REMOVE", - "at": "com\/android\/server\/wm\/ActivityRecord.java" - }, - "-1842512343787359105": { - "message": "Removing app token: %s", - "level": "VERBOSE", - "group": "WM_DEBUG_APP_TRANSITIONS", - "at": "com\/android\/server\/wm\/ActivityRecord.java" - }, - "5548174277852675449": { - "message": "Removing app %s delayed=%b animation=%s animating=%b", - "level": "VERBOSE", - "group": "WM_DEBUG_APP_TRANSITIONS", - "at": "com\/android\/server\/wm\/ActivityRecord.java" - }, - "-601582700132879947": { - "message": "removeAppToken: %s delayed=%b Callers=%s", - "level": "VERBOSE", - "group": "WM_DEBUG_ADD_REMOVE", - "at": "com\/android\/server\/wm\/ActivityRecord.java" - }, - "3478214322581157355": { - "message": "removeAppToken make exiting: %s", - "level": "VERBOSE", - "group": "WM_DEBUG_ADD_REMOVE", - "at": "com\/android\/server\/wm\/ActivityRecord.java" - }, - "-7226216420432530281": { - "message": "Removing focused app token:%s displayId=%d", - "level": "VERBOSE", - "group": "WM_DEBUG_FOCUS_LIGHT", - "at": "com\/android\/server\/wm\/ActivityRecord.java" - }, - "8361394136152947990": { - "message": "Moving existing starting %s from %s to %s", - "level": "VERBOSE", - "group": "WM_DEBUG_STARTING_WINDOW", - "at": "com\/android\/server\/wm\/ActivityRecord.java" - }, - "-3450064502566932331": { - "message": "Removing starting %s from %s", - "level": "VERBOSE", - "group": "WM_DEBUG_ADD_REMOVE", - "at": "com\/android\/server\/wm\/ActivityRecord.java" - }, - "8639603536400037285": { - "message": "Moving pending starting from %s to %s", - "level": "VERBOSE", - "group": "WM_DEBUG_STARTING_WINDOW", - "at": "com\/android\/server\/wm\/ActivityRecord.java" - }, - "-3452055378690362514": { - "message": "setAppVisibility(%s, visible=%b): %s visible=%b mVisibleRequested=%b Callers=%s", - "level": "VERBOSE", - "group": "WM_DEBUG_APP_TRANSITIONS", - "at": "com\/android\/server\/wm\/ActivityRecord.java" - }, - "1728033820691545386": { - "message": "No longer Stopped: %s", - "level": "VERBOSE", - "group": "WM_DEBUG_ADD_REMOVE", - "at": "com\/android\/server\/wm\/ActivityRecord.java" - }, - "5062176994575790703": { - "message": "TRANSIT_FLAG_OPEN_BEHIND, adding %s to mOpeningApps", - "level": "DEBUG", - "group": "WM_DEBUG_APP_TRANSITIONS", - "at": "com\/android\/server\/wm\/ActivityRecord.java" - }, - "-477271988506706928": { - "message": "commitVisibility: %s: visible=%b visibleRequested=%b, isInTransition=%b, runningAnimation=%b, caller=%s", - "level": "VERBOSE", - "group": "WM_DEBUG_APP_TRANSITIONS", - "at": "com\/android\/server\/wm\/ActivityRecord.java" - }, - "-6873410057142191118": { - "message": "State movement: %s from:%s to:%s reason:%s", - "level": "VERBOSE", - "group": "WM_DEBUG_STATES", - "at": "com\/android\/server\/wm\/ActivityRecord.java" - }, - "4437231720834282527": { - "message": "State unchanged from:%s", - "level": "VERBOSE", - "group": "WM_DEBUG_STATES", - "at": "com\/android\/server\/wm\/ActivityRecord.java" - }, - "926038819327785799": { - "message": "notifyAppResumed: wasStopped=%b %s", - "level": "VERBOSE", - "group": "WM_DEBUG_ADD_REMOVE", - "at": "com\/android\/server\/wm\/ActivityRecord.java" - }, - "1734586111478674085": { - "message": "Resumed activity; dropping state of: %s", - "level": "INFO", - "group": "WM_DEBUG_STATES", - "at": "com\/android\/server\/wm\/ActivityRecord.java" - }, - "-69666241054231397": { - "message": "Refreshed activity: %s", - "level": "INFO", - "group": "WM_DEBUG_STATES", - "at": "com\/android\/server\/wm\/ActivityRecord.java" - }, - "1256300416726217367": { - "message": "Activity paused: token=%s, timeout=%b", - "level": "VERBOSE", - "group": "WM_DEBUG_STATES", - "at": "com\/android\/server\/wm\/ActivityRecord.java" - }, - "6879640870754727133": { - "message": "Moving to PAUSED: %s %s", - "level": "VERBOSE", - "group": "WM_DEBUG_STATES", - "at": "com\/android\/server\/wm\/ActivityRecord.java" - }, - "2737811012914917932": { - "message": "Executing finish of failed to pause activity: %s", - "level": "VERBOSE", - "group": "WM_DEBUG_STATES", - "at": "com\/android\/server\/wm\/ActivityRecord.java" - }, - "-2566496855129705006": { - "message": "Waiting for pause to complete...", - "level": "VERBOSE", - "group": "WM_DEBUG_STATES", - "at": "com\/android\/server\/wm\/ActivityRecord.java" - }, - "7498807658620137882": { - "message": "no-history finish of %s", - "level": "DEBUG", - "group": "WM_DEBUG_STATES", - "at": "com\/android\/server\/wm\/ActivityRecord.java" - }, - "3207149655622038378": { - "message": "Not finishing noHistory %s on stop because we're just sleeping", - "level": "DEBUG", - "group": "WM_DEBUG_STATES", - "at": "com\/android\/server\/wm\/ActivityRecord.java" - }, - "-2530718588485487045": { - "message": "Moving to STOPPING: %s (stop requested)", - "level": "VERBOSE", - "group": "WM_DEBUG_STATES", - "at": "com\/android\/server\/wm\/ActivityRecord.java" - }, - "-8424334454318351870": { - "message": "Stop failed; moving to STOPPED: %s", - "level": "VERBOSE", - "group": "WM_DEBUG_STATES", - "at": "com\/android\/server\/wm\/ActivityRecord.java" - }, - "-4913512058893421188": { - "message": "Saving icicle of %s: %s", - "level": "INFO", - "group": "WM_DEBUG_STATES", - "at": "com\/android\/server\/wm\/ActivityRecord.java" - }, - "7613353074402340933": { - "message": "Moving to STOPPED: %s (stop complete)", - "level": "VERBOSE", - "group": "WM_DEBUG_STATES", - "at": "com\/android\/server\/wm\/ActivityRecord.java" - }, - "3981777934616509782": { - "message": "Scheduling idle now: forceIdle=%b immediate=%b", - "level": "VERBOSE", - "group": "WM_DEBUG_STATES", - "at": "com\/android\/server\/wm\/ActivityRecord.java" - }, - "1083992181663415298": { - "message": "Skipping set freeze of %s", - "level": "VERBOSE", - "group": "WM_DEBUG_ORIENTATION", - "at": "com\/android\/server\/wm\/ActivityRecord.java" - }, - "3713860954819212080": { - "message": "Set freezing of %s: visible=%b freezing=%b visibleRequested=%b. %s", - "level": "INFO", - "group": "WM_DEBUG_ORIENTATION", - "at": "com\/android\/server\/wm\/ActivityRecord.java" - }, - "7696002120820208745": { - "message": "Clear freezing of %s force=%b", - "level": "VERBOSE", - "group": "WM_DEBUG_ORIENTATION", - "at": "com\/android\/server\/wm\/ActivityRecord.java" - }, - "-8387262166329116492": { - "message": "No longer freezing: %s", - "level": "VERBOSE", - "group": "WM_DEBUG_ORIENTATION", - "at": "com\/android\/server\/wm\/ActivityRecord.java" - }, - "-6965298896142649709": { - "message": "Finish starting %s: first real window is shown, no animation", - "level": "VERBOSE", - "group": "WM_DEBUG_STARTING_WINDOW", - "at": "com\/android\/server\/wm\/ActivityRecord.java" - }, - "3235691043029201724": { - "message": "Setting mOrientationChangeComplete=true because wtoken %s numInteresting=%d numDrawn=%d", - "level": "INFO", - "group": "WM_DEBUG_ORIENTATION", - "at": "com\/android\/server\/wm\/ActivityRecord.java" - }, - "5991628884266137609": { - "message": "Creating animation bounds layer", - "level": "INFO", - "group": "WM_DEBUG_APP_TRANSITIONS_ANIM", - "at": "com\/android\/server\/wm\/ActivityRecord.java" - }, - "-1836789237982086339": { - "message": "No thumbnail header bitmap for: %s", - "level": "DEBUG", - "group": "WM_DEBUG_APP_TRANSITIONS", - "at": "com\/android\/server\/wm\/ActivityRecord.java" - }, - "-8809523216004991008": { - "message": "Animation done in %s: reportedVisible=%b okToDisplay=%b okToAnimate=%b startingDisplayed=%b", - "level": "VERBOSE", - "group": "WM_DEBUG_ANIM", - "at": "com\/android\/server\/wm\/ActivityRecord.java" - }, - "-9178011226407552682": { - "message": "Setting requested orientation %s for %s", - "level": "VERBOSE", - "group": "WM_DEBUG_ORIENTATION", - "at": "com\/android\/server\/wm\/ActivityRecord.java" - }, - "-1963190756391505590": { - "message": "Sandbox max bounds for uid %s to bounds %s. config to never sandbox = %s, config to always sandbox = %s, letterboxing from mismatch with parent bounds = %s, has mCompatDisplayInsets = %s, should create compatDisplayInsets = %s", - "level": "DEBUG", - "group": "WM_DEBUG_CONFIGURATION", - "at": "com\/android\/server\/wm\/ActivityRecord.java" - }, - "2612201759169917322": { - "message": "Pausing configuration dispatch for %s", - "level": "VERBOSE", - "group": "WM_DEBUG_WINDOW_TRANSITIONS_MIN", - "at": "com\/android\/server\/wm\/ActivityRecord.java" - }, - "5153784493059555057": { - "message": "Resuming configuration dispatch for %s", - "level": "VERBOSE", - "group": "WM_DEBUG_WINDOW_TRANSITIONS_MIN", - "at": "com\/android\/server\/wm\/ActivityRecord.java" - }, - "-8630021188868292872": { - "message": "Skipping config check (will change): %s", - "level": "VERBOSE", - "group": "WM_DEBUG_CONFIGURATION", - "at": "com\/android\/server\/wm\/ActivityRecord.java" - }, - "-3976984054291875926": { - "message": "Configuration doesn't matter in finishing %s", - "level": "VERBOSE", - "group": "WM_DEBUG_CONFIGURATION", - "at": "com\/android\/server\/wm\/ActivityRecord.java" - }, - "-1036762753077003128": { - "message": "Skipping config check in destroyed state %s", - "level": "VERBOSE", - "group": "WM_DEBUG_CONFIGURATION", - "at": "com\/android\/server\/wm\/ActivityRecord.java" - }, - "-6543078196636665108": { - "message": "Skipping config check invisible: %s", - "level": "VERBOSE", - "group": "WM_DEBUG_CONFIGURATION", - "at": "com\/android\/server\/wm\/ActivityRecord.java" - }, - "-3588725633248053181": { - "message": "Ensuring correct configuration: %s", - "level": "VERBOSE", - "group": "WM_DEBUG_CONFIGURATION", - "at": "com\/android\/server\/wm\/ActivityRecord.java" - }, - "4672360193194734037": { - "message": "Configuration & display unchanged in %s", - "level": "VERBOSE", - "group": "WM_DEBUG_CONFIGURATION", - "at": "com\/android\/server\/wm\/ActivityRecord.java" - }, - "-8624278141553396410": { - "message": "Skipping config check for initializing activity: %s", - "level": "VERBOSE", - "group": "WM_DEBUG_CONFIGURATION", - "at": "com\/android\/server\/wm\/ActivityRecord.java" - }, - "2485365009287691179": { - "message": "Configuration no differences in %s", - "level": "VERBOSE", - "group": "WM_DEBUG_CONFIGURATION", - "at": "com\/android\/server\/wm\/ActivityRecord.java" - }, - "-8909639363543223474": { - "message": "Configuration changes for %s, allChanges=%s", - "level": "VERBOSE", - "group": "WM_DEBUG_CONFIGURATION", - "at": "com\/android\/server\/wm\/ActivityRecord.java" - }, - "-8048404379899908050": { - "message": "Configuration doesn't matter not running %s", - "level": "VERBOSE", - "group": "WM_DEBUG_CONFIGURATION", - "at": "com\/android\/server\/wm\/ActivityRecord.java" - }, - "4979286847769557939": { - "message": "Checking to restart %s: changed=0x%s, handles=0x%s, mLastReportedConfiguration=%s", - "level": "VERBOSE", - "group": "WM_DEBUG_CONFIGURATION", - "at": "com\/android\/server\/wm\/ActivityRecord.java" - }, - "6779426581354721909": { - "message": "Config is relaunching %s", - "level": "VERBOSE", - "group": "WM_DEBUG_CONFIGURATION", - "at": "com\/android\/server\/wm\/ActivityRecord.java" - }, - "8969401915706456725": { - "message": "Config is relaunching invisible activity %s called by %s", - "level": "VERBOSE", - "group": "WM_DEBUG_STATES", - "at": "com\/android\/server\/wm\/ActivityRecord.java" - }, - "328802837600679598": { - "message": "Moving to %s Relaunching %s callers=%s", - "level": "INFO", - "group": "WM_DEBUG_STATES", - "at": "com\/android\/server\/wm\/ActivityRecord.java" - }, - "-3997125892953197985": { - "message": "Resumed after relaunch %s", - "level": "DEBUG", - "group": "WM_DEBUG_STATES", - "at": "com\/android\/server\/wm\/ActivityRecord.java" - }, - "7211222997110112110": { - "message": "Refreshing activity for freeform camera compatibility treatment, activityRecord=%s", - "level": "VERBOSE", - "group": "WM_DEBUG_STATES", - "at": "com\/android\/server\/wm\/ActivityRefresher.java" - }, - "1665699123574159131": { - "message": "Starting activity when config will change = %b", - "level": "VERBOSE", - "group": "WM_DEBUG_CONFIGURATION", - "at": "com\/android\/server\/wm\/ActivityStarter.java" - }, - "4748139468532105082": { - "message": "Updating to new configuration after starting activity.", - "level": "VERBOSE", - "group": "WM_DEBUG_CONFIGURATION", - "at": "com\/android\/server\/wm\/ActivityStarter.java" - }, - "-2867366986304729": { - "message": "Bring to front target: %s from %s", - "level": "DEBUG", - "group": "WM_DEBUG_TASKS", - "at": "com\/android\/server\/wm\/ActivityStarter.java" - }, - "-2190454940975874759": { - "message": "Starting new activity %s in new task %s", - "level": "VERBOSE", - "group": "WM_DEBUG_TASKS", - "at": "com\/android\/server\/wm\/ActivityStarter.java" - }, - "5445799252721678675": { - "message": "Initial config: %s", - "level": "VERBOSE", - "group": "WM_DEBUG_CONFIGURATION", - "at": "com\/android\/server\/wm\/ActivityTaskManagerService.java" - }, - "-3811526397232923712": { - "message": "Cannot launch dream activity due to invalid state. dream component: %s packageName: %s", - "level": "ERROR", - "group": "WM_DEBUG_DREAM", - "at": "com\/android\/server\/wm\/ActivityTaskManagerService.java" - }, - "-6981899770129924827": { - "message": "Dream packageName does not match active dream. Package %s does not match %s", - "level": "ERROR", - "group": "WM_DEBUG_DREAM", - "at": "com\/android\/server\/wm\/ActivityTaskManagerService.java" - }, - "6075150529915862250": { - "message": "Applying new update lock state '%s' for %s", - "level": "DEBUG", - "group": "WM_DEBUG_IMMERSIVE", - "at": "com\/android\/server\/wm\/ActivityTaskManagerService.java" - }, - "-4356952232698761083": { - "message": "setFocusedRootTask: taskId=%d", - "level": "DEBUG", - "group": "WM_DEBUG_FOCUS", - "at": "com\/android\/server\/wm\/ActivityTaskManagerService.java" - }, - "301842347780487555": { - "message": "setFocusedTask: taskId=%d touchedActivity=%s", - "level": "DEBUG", - "group": "WM_DEBUG_FOCUS", - "at": "com\/android\/server\/wm\/ActivityTaskManagerService.java" - }, - "7095858131234795548": { - "message": "moveTaskToFront: moving taskId=%d", - "level": "DEBUG", - "group": "WM_DEBUG_TASKS", - "at": "com\/android\/server\/wm\/ActivityTaskManagerService.java" - }, - "-4458288191054594222": { - "message": "Could not find task for id: %d", - "level": "DEBUG", - "group": "WM_DEBUG_TASKS", - "at": "com\/android\/server\/wm\/ActivityTaskManagerService.java" - }, - "-1136891560663761442": { - "message": "moveTaskToRootTask: moving task=%d to rootTaskId=%d toTop=%b", - "level": "DEBUG", - "group": "WM_DEBUG_TASKS", - "at": "com\/android\/server\/wm\/ActivityTaskManagerService.java" - }, - "6954122272402912822": { - "message": "startLockTaskMode: %s", - "level": "WARN", - "group": "WM_DEBUG_LOCKTASK", - "at": "com\/android\/server\/wm\/ActivityTaskManagerService.java" - }, - "-829638795650515884": { - "message": "Allowlisting %d:%s", - "level": "WARN", - "group": "WM_DEBUG_LOCKTASK", - "at": "com\/android\/server\/wm\/ActivityTaskManagerService.java" - }, - "893763316922465955": { - "message": "moveRootTaskToDisplay: moving taskId=%d to displayId=%d", - "level": "DEBUG", - "group": "WM_DEBUG_TASKS", - "at": "com\/android\/server\/wm\/ActivityTaskManagerService.java" - }, - "8392804603924461448": { - "message": "%s: caller %d is using old GET_TASKS but privileged; allowing", - "level": "WARN", - "group": "WM_DEBUG_TASKS", - "at": "com\/android\/server\/wm\/ActivityTaskManagerService.java" - }, - "4303745325174700522": { - "message": "%s: caller %d does not hold REAL_GET_TASKS; limiting output", - "level": "WARN", - "group": "WM_DEBUG_TASKS", - "at": "com\/android\/server\/wm\/ActivityTaskManagerService.java" - }, - "-559595900417262876": { - "message": "Allowing features %d:0x%s", - "level": "WARN", - "group": "WM_DEBUG_LOCKTASK", - "at": "com\/android\/server\/wm\/ActivityTaskManagerService.java" - }, - "2008996027621913637": { - "message": "Updating global configuration to: %s", - "level": "INFO", - "group": "WM_DEBUG_CONFIGURATION", - "at": "com\/android\/server\/wm\/ActivityTaskManagerService.java" - }, - "-6404059840638143757": { - "message": "Update process config of %s to new config %s", - "level": "VERBOSE", - "group": "WM_DEBUG_CONFIGURATION", - "at": "com\/android\/server\/wm\/ActivityTaskManagerService.java" - }, - "-1123414663662718691": { - "message": "setVr2dDisplayId called for: %d", - "level": "DEBUG", - "group": "WM_DEBUG_TASKS", - "at": "com\/android\/server\/wm\/ActivityTaskManagerService.java" - }, - "7803197981786977817": { - "message": "no-history finish of %s on new resume", - "level": "DEBUG", - "group": "WM_DEBUG_STATES", - "at": "com\/android\/server\/wm\/ActivityTaskSupervisor.java" - }, - "4094852138446437211": { - "message": "realStartActivityLocked: Skipping start of r=%s some activities pausing...", - "level": "VERBOSE", - "group": "WM_DEBUG_STATES", - "at": "com\/android\/server\/wm\/ActivityTaskSupervisor.java" - }, - "1045761390992110034": { - "message": "Moving to PAUSED: %s (starting in paused state)", - "level": "VERBOSE", - "group": "WM_DEBUG_STATES", - "at": "com\/android\/server\/wm\/ActivityTaskSupervisor.java" - }, - "-8529426827020190143": { - "message": "Launch on display check: displayId=%d callingPid=%d callingUid=%d", - "level": "DEBUG", - "group": "WM_DEBUG_TASKS", - "at": "com\/android\/server\/wm\/ActivityTaskSupervisor.java" - }, - "9147909968067116569": { - "message": "Launch on display check: no caller info, skip check", - "level": "DEBUG", - "group": "WM_DEBUG_TASKS", - "at": "com\/android\/server\/wm\/ActivityTaskSupervisor.java" - }, - "4781135167649953680": { - "message": "Launch on display check: allow launch any on display", - "level": "DEBUG", - "group": "WM_DEBUG_TASKS", - "at": "com\/android\/server\/wm\/ActivityTaskSupervisor.java" - }, - "7828411869729995271": { - "message": "Launch on display check: disallow launch on virtual display for not-embedded activity.", - "level": "DEBUG", - "group": "WM_DEBUG_TASKS", - "at": "com\/android\/server\/wm\/ActivityTaskSupervisor.java" - }, - "-2215878620906309682": { - "message": "Launch on display check: disallow activity embedding without permission.", - "level": "DEBUG", - "group": "WM_DEBUG_TASKS", - "at": "com\/android\/server\/wm\/ActivityTaskSupervisor.java" - }, - "986565579776405555": { - "message": "Launch on display check: %s launch for userId=%d on displayId=%d", - "level": "DEBUG", - "group": "WM_DEBUG_TASKS", - "at": "com\/android\/server\/wm\/ActivityTaskSupervisor.java" - }, - "-2201418325681949201": { - "message": "Launch on display check: allow launch for owner of the display", - "level": "DEBUG", - "group": "WM_DEBUG_TASKS", - "at": "com\/android\/server\/wm\/ActivityTaskSupervisor.java" - }, - "-4258279435559028377": { - "message": "Launch on display check: allow launch for caller present on the display", - "level": "DEBUG", - "group": "WM_DEBUG_TASKS", - "at": "com\/android\/server\/wm\/ActivityTaskSupervisor.java" - }, - "1496536241884839051": { - "message": "Stopping %s: nowVisible=%b animating=%b finishing=%s", - "level": "VERBOSE", - "group": "WM_DEBUG_STATES", - "at": "com\/android\/server\/wm\/ActivityTaskSupervisor.java" - }, - "5677125188685281770": { - "message": "Ready to stop: %s", - "level": "VERBOSE", - "group": "WM_DEBUG_STATES", - "at": "com\/android\/server\/wm\/ActivityTaskSupervisor.java" - }, - "3604633008357193496": { - "message": "Waiting for top state to be released by %s", - "level": "VERBOSE", - "group": "WM_DEBUG_STATES", - "at": "com\/android\/server\/wm\/ActivityTaskSupervisor.java" - }, - "3997062844427155487": { - "message": "Top resumed state released %s", - "level": "VERBOSE", - "group": "WM_DEBUG_STATES", - "at": "com\/android\/server\/wm\/ActivityTaskSupervisor.java" - }, - "-4049608245387511746": { - "message": "applyAnimation: override requested, but it is prohibited by policy.", - "level": "ERROR", - "group": "WM_DEBUG_APP_TRANSITIONS_ANIM", - "at": "com\/android\/server\/wm\/AppTransition.java" - }, - "-2133100418670643322": { - "message": "applyAnimation voice: anim=%s transit=%s isEntrance=%b Callers=%s", - "level": "VERBOSE", - "group": "WM_DEBUG_APP_TRANSITIONS_ANIM", - "at": "com\/android\/server\/wm\/AppTransition.java" - }, - "6121116119545820299": { - "message": "applyAnimation: anim=%s transit=%s Callers=%s", - "level": "VERBOSE", - "group": "WM_DEBUG_APP_TRANSITIONS_ANIM", - "at": "com\/android\/server\/wm\/AppTransition.java" - }, - "-8382864384468306610": { - "message": "applyAnimation: anim=%s nextAppTransition=ANIM_CUSTOM transit=%s isEntrance=%b Callers=%s", - "level": "VERBOSE", - "group": "WM_DEBUG_APP_TRANSITIONS_ANIM", - "at": "com\/android\/server\/wm\/AppTransition.java" - }, - "222576013987954454": { - "message": "applyAnimation: anim=%s nextAppTransition=ANIM_CUSTOM_IN_PLACE transit=%s Callers=%s", - "level": "VERBOSE", - "group": "WM_DEBUG_APP_TRANSITIONS_ANIM", - "at": "com\/android\/server\/wm\/AppTransition.java" - }, - "4808089291562562413": { - "message": "applyAnimation: anim=%s nextAppTransition=ANIM_CLIP_REVEAL transit=%s Callers=%s", - "level": "VERBOSE", - "group": "WM_DEBUG_APP_TRANSITIONS_ANIM", - "at": "com\/android\/server\/wm\/AppTransition.java" - }, - "-1463563572526433695": { - "message": "applyAnimation: anim=%s nextAppTransition=ANIM_SCALE_UP transit=%s isEntrance=%s Callers=%s", - "level": "VERBOSE", - "group": "WM_DEBUG_APP_TRANSITIONS_ANIM", - "at": "com\/android\/server\/wm\/AppTransition.java" - }, - "-8749850292010208926": { - "message": "applyAnimation: anim=%s nextAppTransition=%s transit=%s isEntrance=%b Callers=%s", - "level": "VERBOSE", - "group": "WM_DEBUG_APP_TRANSITIONS_ANIM", - "at": "com\/android\/server\/wm\/AppTransition.java" - }, - "5939232373291430513": { - "message": "applyAnimation NEXT_TRANSIT_TYPE_OPEN_CROSS_PROFILE_APPS: anim=%s transit=%s isEntrance=true Callers=%s", - "level": "VERBOSE", - "group": "WM_DEBUG_APP_TRANSITIONS_ANIM", - "at": "com\/android\/server\/wm\/AppTransition.java" - }, - "9082776604722675018": { - "message": "applyAnimation: anim=%s transit=%s isEntrance=%b Callers=%s", - "level": "VERBOSE", - "group": "WM_DEBUG_APP_TRANSITIONS_ANIM", - "at": "com\/android\/server\/wm\/AppTransition.java" - }, - "-1218632020771063497": { - "message": "applyAnimation: anim=%s animAttr=0x%x transit=%s isEntrance=%b canCustomizeAppTransition=%b Callers=%s", - "level": "VERBOSE", - "group": "WM_DEBUG_APP_TRANSITIONS_ANIM", - "at": "com\/android\/server\/wm\/AppTransition.java" - }, - "6217525691846442213": { - "message": "Override pending remote transitionSet=%b adapter=%s", - "level": "INFO", - "group": "WM_DEBUG_APP_TRANSITIONS", - "at": "com\/android\/server\/wm\/AppTransition.java" - }, - "5233255302148535928": { - "message": "*** APP TRANSITION TIMEOUT. displayId=%d isTransitionSet()=%b mOpeningApps.size()=%d mClosingApps.size()=%d mChangingApps.size()=%d", - "level": "VERBOSE", - "group": "WM_DEBUG_APP_TRANSITIONS", - "at": "com\/android\/server\/wm\/AppTransition.java" - }, - "-5726018006883159788": { - "message": "Delaying app transition for recents animation to finish", - "level": "VERBOSE", - "group": "WM_DEBUG_APP_TRANSITIONS", - "at": "com\/android\/server\/wm\/AppTransitionController.java" - }, - "6514556033257323299": { - "message": "**** GOOD TO GO", - "level": "VERBOSE", - "group": "WM_DEBUG_APP_TRANSITIONS", - "at": "com\/android\/server\/wm\/AppTransitionController.java" - }, - "3518082157667760495": { - "message": "handleAppTransitionReady: displayId=%d appTransition={%s} openingApps=[%s] closingApps=[%s] transit=%s", - "level": "VERBOSE", - "group": "WM_DEBUG_APP_TRANSITIONS", - "at": "com\/android\/server\/wm\/AppTransitionController.java" - }, - "-2503124388387340567": { - "message": "Wallpaper animation!", - "level": "VERBOSE", - "group": "WM_DEBUG_APP_TRANSITIONS", - "at": "com\/android\/server\/wm\/AppTransitionController.java" - }, - "855146509305002043": { - "message": "We don't support remote animation for Task with multiple TaskFragmentOrganizers.", - "level": "ERROR", - "group": "WM_DEBUG_APP_TRANSITIONS", - "at": "com\/android\/server\/wm\/AppTransitionController.java" - }, - "59396412370137517": { - "message": "Override with TaskFragment remote animation for transit=%s", - "level": "VERBOSE", - "group": "WM_DEBUG_APP_TRANSITIONS", - "at": "com\/android\/server\/wm\/AppTransitionController.java" - }, - "2280055488397326910": { - "message": "Task=%d contains embedded TaskFragment. Disabled all input during TaskFragment remote animation.", - "level": "DEBUG", - "group": "WM_DEBUG_APP_TRANSITIONS", - "at": "com\/android\/server\/wm\/AppTransitionController.java" - }, - "-3156084190956669377": { - "message": "Changing app %s visible=%b performLayout=%b", - "level": "VERBOSE", - "group": "WM_DEBUG_APP_TRANSITIONS", - "at": "com\/android\/server\/wm\/AppTransitionController.java" - }, - "-8226278785414579647": { - "message": "getAnimationTarget in=%s, out=%s", - "level": "VERBOSE", - "group": "WM_DEBUG_APP_TRANSITIONS_ANIM", - "at": "com\/android\/server\/wm\/AppTransitionController.java" - }, - "4418653408751596915": { - "message": "Now opening app %s", - "level": "VERBOSE", - "group": "WM_DEBUG_APP_TRANSITIONS", - "at": "com\/android\/server\/wm\/AppTransitionController.java" - }, - "-8367738619313176909": { - "message": "Now closing app %s", - "level": "VERBOSE", - "group": "WM_DEBUG_APP_TRANSITIONS", - "at": "com\/android\/server\/wm\/AppTransitionController.java" - }, - "1855459282905873641": { - "message": "Now changing app %s", - "level": "VERBOSE", - "group": "WM_DEBUG_APP_TRANSITIONS", - "at": "com\/android\/server\/wm\/AppTransitionController.java" - }, - "2951634988136738868": { - "message": "Checking %d opening apps (frozen=%b timeout=%b)...", - "level": "VERBOSE", - "group": "WM_DEBUG_APP_TRANSITIONS", - "at": "com\/android\/server\/wm\/AppTransitionController.java" - }, - "4963754906024950916": { - "message": "Delaying app transition for screen rotation animation to finish", - "level": "VERBOSE", - "group": "WM_DEBUG_APP_TRANSITIONS", - "at": "com\/android\/server\/wm\/AppTransitionController.java" - }, - "5073676463280304697": { - "message": "Check opening app=%s: allDrawn=%b startingDisplayed=%b startingMoved=%b isRelaunching()=%b startingWindow=%s", - "level": "VERBOSE", - "group": "WM_DEBUG_APP_TRANSITIONS", - "at": "com\/android\/server\/wm\/AppTransitionController.java" - }, - "3437142041296647115": { - "message": "isFetchingAppTransitionSpecs=true", - "level": "VERBOSE", - "group": "WM_DEBUG_APP_TRANSITIONS", - "at": "com\/android\/server\/wm\/AppTransitionController.java" - }, - "1461079689316480707": { - "message": "unknownApps is not empty: %s", - "level": "VERBOSE", - "group": "WM_DEBUG_APP_TRANSITIONS", - "at": "com\/android\/server\/wm\/AppTransitionController.java" - }, - "3579533288018884842": { - "message": "Organized TaskFragment is not ready= %s", - "level": "VERBOSE", - "group": "WM_DEBUG_APP_TRANSITIONS", - "at": "com\/android\/server\/wm\/AppTransitionController.java" - }, - "495867940519492701": { - "message": "SyncGroup %d: onSurfacePlacement checking %s", - "level": "VERBOSE", - "group": "WM_DEBUG_SYNC_ENGINE", - "at": "com\/android\/server\/wm\/BLASTSyncEngine.java" - }, - "8452501904614439940": { - "message": "SyncGroup %d: Unfinished dependencies: %s", - "level": "VERBOSE", - "group": "WM_DEBUG_SYNC_ENGINE", - "at": "com\/android\/server\/wm\/BLASTSyncEngine.java" - }, - "616739530932040800": { - "message": "SyncGroup %d: Unfinished container: %s", - "level": "VERBOSE", - "group": "WM_DEBUG_SYNC_ENGINE", - "at": "com\/android\/server\/wm\/BLASTSyncEngine.java" - }, - "6649777898123506907": { - "message": "SyncGroup %d: Finished!", - "level": "VERBOSE", - "group": "WM_DEBUG_SYNC_ENGINE", - "at": "com\/android\/server\/wm\/BLASTSyncEngine.java" - }, - "4174320302463990554": { - "message": "PendingStartTransaction found", - "level": "VERBOSE", - "group": "WM_DEBUG_SYNC_ENGINE", - "at": "com\/android\/server\/wm\/BLASTSyncEngine.java" - }, - "6310906192788668020": { - "message": "SyncGroup %d: Set ready %b", - "level": "VERBOSE", - "group": "WM_DEBUG_SYNC_ENGINE", - "at": "com\/android\/server\/wm\/BLASTSyncEngine.java" - }, - "-476337038362199951": { - "message": "SyncGroup %d: Adding to group: %s", - "level": "VERBOSE", - "group": "WM_DEBUG_SYNC_ENGINE", - "at": "com\/android\/server\/wm\/BLASTSyncEngine.java" - }, - "-2978812352001196863": { - "message": "SyncGroup %d: Started %sfor listener: %s", - "level": "VERBOSE", - "group": "WM_DEBUG_SYNC_ENGINE", - "at": "com\/android\/server\/wm\/BLASTSyncEngine.java" - }, - "-699215053676660941": { - "message": "No focused window, defaulting to top current task's window", - "level": "WARN", - "group": "WM_DEBUG_BACK_PREVIEW", - "at": "com\/android\/server\/wm\/BackNavigationController.java" - }, - "2881085074175114605": { - "message": "Focused window didn't have a valid surface drawn.", - "level": "DEBUG", - "group": "WM_DEBUG_BACK_PREVIEW", - "at": "com\/android\/server\/wm\/BackNavigationController.java" - }, - "-6183551796617134986": { - "message": "Focus window is closing.", - "level": "DEBUG", - "group": "WM_DEBUG_BACK_PREVIEW", - "at": "com\/android\/server\/wm\/BackNavigationController.java" - }, - "4039315468791789889": { - "message": "startBackNavigation currentTask=%s, topRunningActivity=%s, callbackInfo=%s, currentFocus=%s", - "level": "DEBUG", - "group": "WM_DEBUG_BACK_PREVIEW", - "at": "com\/android\/server\/wm\/BackNavigationController.java" - }, - "8456834061534378653": { - "message": "Previous Destination is Activity:%s Task:%s removedContainer:%s, backType=%s", - "level": "DEBUG", - "group": "WM_DEBUG_BACK_PREVIEW", - "at": "com\/android\/server\/wm\/BackNavigationController.java" - }, - "4900967164780429209": { - "message": "Pending back animation due to another animation is running", - "level": "WARN", - "group": "WM_DEBUG_BACK_PREVIEW", - "at": "com\/android\/server\/wm\/BackNavigationController.java" - }, - "-6431452312492819825": { - "message": "onTransactionReady, opening: %s, closing: %s, animating: %s, match: %b", - "level": "DEBUG", - "group": "WM_DEBUG_BACK_PREVIEW", - "at": "com\/android\/server\/wm\/BackNavigationController.java" - }, - "-4051770154814262074": { - "message": "Handling the deferred animation after transition finished", - "level": "DEBUG", - "group": "WM_DEBUG_BACK_PREVIEW", - "at": "com\/android\/server\/wm\/BackNavigationController.java" - }, - "2077221835543623088": { - "message": "Setting Activity.mLauncherTaskBehind to true. Activity=%s", - "level": "DEBUG", - "group": "WM_DEBUG_BACK_PREVIEW", - "at": "com\/android\/server\/wm\/BackNavigationController.java" - }, - "-4442170697458371588": { - "message": "Setting Activity.mLauncherTaskBehind to false. Activity=%s", - "level": "DEBUG", - "group": "WM_DEBUG_BACK_PREVIEW", - "at": "com\/android\/server\/wm\/BackNavigationController.java" - }, - "267946503010201613": { - "message": "onBackNavigationDone backType=%s, triggerBack=%b", - "level": "DEBUG", - "group": "WM_DEBUG_BACK_PREVIEW", - "at": "com\/android\/server\/wm\/BackNavigationController.java" - }, - "-2963535976860666511": { - "message": " BLACK %s: CREATE layer=%d", - "level": "INFO", - "group": "WM_SHOW_SURFACE_ALLOC", - "at": "com\/android\/server\/wm\/BlackFrame.java" - }, - "-5633771912572750947": { - "message": " BLACK %s: DESTROY", - "level": "INFO", - "group": "WM_SHOW_SURFACE_ALLOC", - "at": "com\/android\/server\/wm\/BlackFrame.java" - }, - "8116030277393789125": { - "message": "Display id=%d is notified that Camera %s is open for package %s", - "level": "VERBOSE", - "group": "WM_DEBUG_STATES", - "at": "com\/android\/server\/wm\/CameraStateMonitor.java" - }, - "-3774458166471278611": { - "message": "Display id=%d is notified that Camera %s is closed.", - "level": "VERBOSE", - "group": "WM_DEBUG_STATES", - "at": "com\/android\/server\/wm\/CameraStateMonitor.java" - }, - "-74949168947384056": { - "message": "Sending to proc %s new compat %s", - "level": "VERBOSE", - "group": "WM_DEBUG_CONFIGURATION", - "at": "com\/android\/server\/wm\/CompatModePackages.java" - }, - "-6620483833570774987": { - "message": "Content Recording: Unexpectedly null window container; unable to update recording for display %d", - "level": "VERBOSE", - "group": "WM_DEBUG_CONTENT_RECORDING", - "at": "com\/android\/server\/wm\/ContentRecorder.java" - }, - "7226080178642957768": { - "message": "Content Recording: Display %d was already recording, but pause capture since the task is in PIP", - "level": "VERBOSE", - "group": "WM_DEBUG_CONTENT_RECORDING", - "at": "com\/android\/server\/wm\/ContentRecorder.java" - }, - "-311001578548807570": { - "message": "Content Recording: Display %d was already recording, so apply transformations if necessary", - "level": "VERBOSE", - "group": "WM_DEBUG_CONTENT_RECORDING", - "at": "com\/android\/server\/wm\/ContentRecorder.java" - }, - "2350883351096538149": { - "message": "Content Recording: Going ahead with updating recording for display %d to new bounds %s and\/or orientation %d and\/or surface size %s", - "level": "VERBOSE", - "group": "WM_DEBUG_CONTENT_RECORDING", - "at": "com\/android\/server\/wm\/ContentRecorder.java" - }, - "8446758574558556540": { - "message": "Content Recording: Unable to update recording for display %d to new bounds %s and\/or orientation %d and\/or surface size %s, since the surface is not available.", - "level": "VERBOSE", - "group": "WM_DEBUG_CONTENT_RECORDING", - "at": "com\/android\/server\/wm\/ContentRecorder.java" - }, - "-4320004054011530388": { - "message": "Content Recording: Display %d has content (%b) so pause recording", - "level": "VERBOSE", - "group": "WM_DEBUG_CONTENT_RECORDING", - "at": "com\/android\/server\/wm\/ContentRecorder.java" - }, - "5951434375221687741": { - "message": "Content Recording: Stop MediaProjection on virtual display %d", - "level": "VERBOSE", - "group": "WM_DEBUG_CONTENT_RECORDING", - "at": "com\/android\/server\/wm\/ContentRecorder.java" - }, - "-3395581813971405090": { - "message": "Content Recording: waiting to record, so do nothing", - "level": "VERBOSE", - "group": "WM_DEBUG_CONTENT_RECORDING", - "at": "com\/android\/server\/wm\/ContentRecorder.java" - }, - "6779858226066635065": { - "message": "Content Recording: Display %d should start recording, but don't yet since the task is in PIP", - "level": "VERBOSE", - "group": "WM_DEBUG_CONTENT_RECORDING", - "at": "com\/android\/server\/wm\/ContentRecorder.java" - }, - "7051210836345306671": { - "message": "Content Recording: Unable to start recording for display %d since the surface is not available.", - "level": "VERBOSE", - "group": "WM_DEBUG_CONTENT_RECORDING", - "at": "com\/android\/server\/wm\/ContentRecorder.java" - }, - "2255758299558330282": { - "message": "Content Recording: Display %d has no content and is on, so start recording for state %d", - "level": "VERBOSE", - "group": "WM_DEBUG_CONTENT_RECORDING", - "at": "com\/android\/server\/wm\/ContentRecorder.java" - }, - "2269158922723670768": { - "message": "Unable to retrieve window container to start recording for display %d", - "level": "VERBOSE", - "group": "WM_DEBUG_CONTENT_RECORDING", - "at": "com\/android\/server\/wm\/ContentRecorder.java" - }, - "-2177493963028285555": { - "message": "Content Recording: Unable to start recording due to null token for display %d", - "level": "VERBOSE", - "group": "WM_DEBUG_CONTENT_RECORDING", - "at": "com\/android\/server\/wm\/ContentRecorder.java" - }, - "-928577038848872043": { - "message": "Content Recording: Unable to retrieve task to start recording for display %d", - "level": "VERBOSE", - "group": "WM_DEBUG_CONTENT_RECORDING", - "at": "com\/android\/server\/wm\/ContentRecorder.java" - }, - "-3564317873468917405": { - "message": "Content Recording: Unable to start recording due to invalid region for display %d", - "level": "VERBOSE", - "group": "WM_DEBUG_CONTENT_RECORDING", - "at": "com\/android\/server\/wm\/ContentRecorder.java" - }, - "1100676037289065396": { - "message": "Content Recording: Apply transformations of shift %d x %d, scale %f x %f, crop (aka recorded content size) %d x %d for display %d; display has size %d x %d; surface has size %d x %d", - "level": "VERBOSE", - "group": "WM_DEBUG_CONTENT_RECORDING", - "at": "com\/android\/server\/wm\/ContentRecorder.java" - }, - "2330946591287751995": { - "message": "Content Recording: Provided surface for recording on display %d is not present, so do not update the surface", - "level": "VERBOSE", - "group": "WM_DEBUG_CONTENT_RECORDING", - "at": "com\/android\/server\/wm\/ContentRecorder.java" - }, - "7993045936648632984": { - "message": "Content Recording: Recorded task is removed, so stop recording on display %d", - "level": "VERBOSE", - "group": "WM_DEBUG_CONTENT_RECORDING", - "at": "com\/android\/server\/wm\/ContentRecorder.java" - }, - "3197882223327917085": { - "message": "Content Recording: stopping active projection for display %d", - "level": "ERROR", - "group": "WM_DEBUG_CONTENT_RECORDING", - "at": "com\/android\/server\/wm\/ContentRecorder.java" - }, - "4391984931064789228": { - "message": "Content Recording: Unable to tell MediaProjectionManagerService to stop the active projection for display %d: %s", - "level": "ERROR", - "group": "WM_DEBUG_CONTENT_RECORDING", - "at": "com\/android\/server\/wm\/ContentRecorder.java" - }, - "6721270269112237694": { - "message": "Content Recording: Unable to tell MediaProjectionManagerService about resizing the active projection: %s", - "level": "ERROR", - "group": "WM_DEBUG_CONTENT_RECORDING", - "at": "com\/android\/server\/wm\/ContentRecorder.java" - }, - "1600318776990120244": { - "message": "Content Recording: Unable to tell MediaProjectionManagerService about visibility change on the active projection: %s", - "level": "ERROR", - "group": "WM_DEBUG_CONTENT_RECORDING", - "at": "com\/android\/server\/wm\/ContentRecorder.java" - }, - "-1451477179301743956": { - "message": "Content Recording: Unable to tell log windowing mode change: %s", - "level": "ERROR", - "group": "WM_DEBUG_CONTENT_RECORDING", - "at": "com\/android\/server\/wm\/ContentRecorder.java" - }, - "-225319884529912382": { - "message": "Content Recording: Accept session updating same display %d with granted consent, with an existing session %s", - "level": "VERBOSE", - "group": "WM_DEBUG_CONTENT_RECORDING", - "at": "com\/android\/server\/wm\/ContentRecordingController.java" - }, - "-5981322449150461244": { - "message": "Content Recording: Ignoring session on same display %d, with an existing session %s", - "level": "VERBOSE", - "group": "WM_DEBUG_CONTENT_RECORDING", - "at": "com\/android\/server\/wm\/ContentRecordingController.java" - }, - "4226710957373144819": { - "message": "Content Recording: Handle incoming session on display %d, with a pre-existing session %s", - "level": "VERBOSE", - "group": "WM_DEBUG_CONTENT_RECORDING", - "at": "com\/android\/server\/wm\/ContentRecordingController.java" - }, - "-1415855962859555663": { - "message": "Content Recording: Incoming session on display %d can't be set since it is already null; the corresponding VirtualDisplay must have already been removed.", - "level": "VERBOSE", - "group": "WM_DEBUG_CONTENT_RECORDING", - "at": "com\/android\/server\/wm\/ContentRecordingController.java" - }, - "-5750232782380780139": { - "message": "Content Recording: Pause the recording session on display %s", - "level": "VERBOSE", - "group": "WM_DEBUG_CONTENT_RECORDING", - "at": "com\/android\/server\/wm\/ContentRecordingController.java" - }, - "-8058211784911995417": { - "message": "DeferredDisplayUpdater: applying DisplayInfo immediately", - "level": "DEBUG", - "group": "WM_DEBUG_WINDOW_TRANSITIONS", - "at": "com\/android\/server\/wm\/DeferredDisplayUpdater.java" - }, - "1944392458089872195": { - "message": "DeferredDisplayUpdater: partially applying DisplayInfo immediately", - "level": "DEBUG", - "group": "WM_DEBUG_WINDOW_TRANSITIONS", - "at": "com\/android\/server\/wm\/DeferredDisplayUpdater.java" - }, - "8391643185322408089": { - "message": "DeferredDisplayUpdater: deferring DisplayInfo update", - "level": "DEBUG", - "group": "WM_DEBUG_WINDOW_TRANSITIONS", - "at": "com\/android\/server\/wm\/DeferredDisplayUpdater.java" - }, - "-915675022936690176": { - "message": "DeferredDisplayUpdater: applied DisplayInfo after deferring", - "level": "DEBUG", - "group": "WM_DEBUG_WINDOW_TRANSITIONS", - "at": "com\/android\/server\/wm\/DeferredDisplayUpdater.java" - }, - "3778139410556664218": { - "message": "%s skipping animation and directly setting alpha=%f, blur=%d", - "level": "DEBUG", - "group": "WM_DEBUG_DIMMER", - "at": "com\/android\/server\/wm\/DimmerAnimationHelper.java" - }, - "-6357087772993832060": { - "message": "Starting animation on %s", - "level": "VERBOSE", - "group": "WM_DEBUG_DIMMER", - "at": "com\/android\/server\/wm\/DimmerAnimationHelper.java" - }, - "-1187783168730646350": { - "message": "Dim animation requested: %s", - "level": "VERBOSE", - "group": "WM_DEBUG_DIMMER", - "at": "com\/android\/server\/wm\/DimmerAnimationHelper.java" - }, - "2230151187668089583": { - "message": "%s forcing orientation to %d for display id=%d", - "level": "VERBOSE", - "group": "WM_DEBUG_ORIENTATION", - "at": "com\/android\/server\/wm\/DisplayArea.java" - }, - "3968604152682328317": { - "message": "Register display organizer=%s uid=%d", - "level": "VERBOSE", - "group": "WM_DEBUG_WINDOW_ORGANIZER", - "at": "com\/android\/server\/wm\/DisplayAreaOrganizerController.java" - }, - "-3066370283926570943": { - "message": "Don't organize or trigger events for untrusted displayId=%d", - "level": "WARN", - "group": "WM_DEBUG_WINDOW_ORGANIZER", - "at": "com\/android\/server\/wm\/DisplayAreaOrganizerController.java" - }, - "-943497726140336963": { - "message": "Unregister display organizer=%s uid=%d", - "level": "VERBOSE", - "group": "WM_DEBUG_WINDOW_ORGANIZER", - "at": "com\/android\/server\/wm\/DisplayAreaOrganizerController.java" - }, - "5147103403966149923": { - "message": "Create TaskDisplayArea uid=%d", - "level": "VERBOSE", - "group": "WM_DEBUG_WINDOW_ORGANIZER", - "at": "com\/android\/server\/wm\/DisplayAreaOrganizerController.java" - }, - "-1659480097203667175": { - "message": "Delete TaskDisplayArea uid=%d", - "level": "VERBOSE", - "group": "WM_DEBUG_WINDOW_ORGANIZER", - "at": "com\/android\/server\/wm\/DisplayAreaOrganizerController.java" - }, - "-4514772405648277945": { - "message": "DisplayArea appeared name=%s", - "level": "VERBOSE", - "group": "WM_DEBUG_WINDOW_ORGANIZER", - "at": "com\/android\/server\/wm\/DisplayAreaOrganizerController.java" - }, - "995846188225477231": { - "message": "DisplayArea vanished name=%s", - "level": "VERBOSE", - "group": "WM_DEBUG_WINDOW_ORGANIZER", - "at": "com\/android\/server\/wm\/DisplayAreaOrganizerController.java" - }, - "-1007032390526684388": { - "message": "DisplayArea info changed name=%s", - "level": "VERBOSE", - "group": "WM_DEBUG_WINDOW_ORGANIZER", - "at": "com\/android\/server\/wm\/DisplayAreaOrganizerController.java" - }, - "4917824058925068521": { - "message": "The TaskDisplayArea with %s does not exist.", - "level": "WARN", - "group": "WM_DEBUG_WINDOW_ORGANIZER", - "at": "com\/android\/server\/wm\/DisplayAreaPolicyBuilder.java" - }, - "1432179297701477868": { - "message": "Looking for focus: %s, flags=%d, canReceive=%b, reason=%s", - "level": "VERBOSE", - "group": "WM_DEBUG_FOCUS", - "at": "com\/android\/server\/wm\/DisplayContent.java" - }, - "-1998969924927409574": { - "message": "findFocusedWindow: focusedApp=null using new focus @ %s", - "level": "VERBOSE", - "group": "WM_DEBUG_FOCUS_LIGHT", - "at": "com\/android\/server\/wm\/DisplayContent.java" - }, - "-1513212297283619351": { - "message": "findFocusedWindow: focusedApp windows not focusable using new focus @ %s", - "level": "VERBOSE", - "group": "WM_DEBUG_FOCUS_LIGHT", - "at": "com\/android\/server\/wm\/DisplayContent.java" - }, - "271075236829935631": { - "message": "findFocusedWindow: Reached focused app=%s", - "level": "VERBOSE", - "group": "WM_DEBUG_FOCUS_LIGHT", - "at": "com\/android\/server\/wm\/DisplayContent.java" - }, - "3066566560703920191": { - "message": "findFocusedWindow: Found new focus @ %s", - "level": "VERBOSE", - "group": "WM_DEBUG_FOCUS_LIGHT", - "at": "com\/android\/server\/wm\/DisplayContent.java" - }, - "-8667452489821572603": { - "message": "First draw done in potential wallpaper target %s", - "level": "VERBOSE", - "group": "WM_DEBUG_WALLPAPER", - "at": "com\/android\/server\/wm\/DisplayContent.java" - }, - "6283995720623600346": { - "message": "handleNotObscuredLocked: %s was holding screen wakelock but no longer has FLAG_KEEP_SCREEN_ON!!! called by%s", - "level": "DEBUG", - "group": "WM_DEBUG_KEEP_SCREEN_ON", - "at": "com\/android\/server\/wm\/DisplayContent.java" - }, - "1959209522588955826": { - "message": "Acquiring screen wakelock due to %s", - "level": "DEBUG", - "group": "WM_DEBUG_KEEP_SCREEN_ON", - "at": "com\/android\/server\/wm\/DisplayContent.java" - }, - "352937214222086717": { - "message": "Releasing screen wakelock, obscured by %s", - "level": "DEBUG", - "group": "WM_DEBUG_KEEP_SCREEN_ON", - "at": "com\/android\/server\/wm\/DisplayContent.java" - }, - "2632363530212357762": { - "message": "Set mOrientationChanging of %s", - "level": "VERBOSE", - "group": "WM_DEBUG_ORIENTATION", - "at": "com\/android\/server\/wm\/DisplayContent.java" - }, - "-9191821315942566105": { - "message": "Display id=%d is frozen while keyguard locked, return %d", - "level": "VERBOSE", - "group": "WM_DEBUG_ORIENTATION", - "at": "com\/android\/server\/wm\/DisplayContent.java" - }, - "-74384795669614579": { - "message": "Display id=%d is ignoring orientation request for %d, return %d following a per-app override for %s", - "level": "VERBOSE", - "group": "WM_DEBUG_ORIENTATION", - "at": "com\/android\/server\/wm\/DisplayContent.java" - }, - "-3395592185328682328": { - "message": "Display id=%d is ignoring orientation request for %d, return %d", - "level": "VERBOSE", - "group": "WM_DEBUG_ORIENTATION", - "at": "com\/android\/server\/wm\/DisplayContent.java" - }, - "3438870491084701232": { - "message": "No app or window is requesting an orientation, return %d for display id=%d", - "level": "VERBOSE", - "group": "WM_DEBUG_ORIENTATION", - "at": "com\/android\/server\/wm\/DisplayContent.java" - }, - "-1123818872155982592": { - "message": "findFocusedWindow: No focusable windows, display=%d", - "level": "VERBOSE", - "group": "WM_DEBUG_FOCUS_LIGHT", - "at": "com\/android\/server\/wm\/DisplayContent.java" - }, - "-2192125645150932161": { - "message": "Current transition prevents automatic focus change", - "level": "VERBOSE", - "group": "WM_DEBUG_FOCUS", - "at": "com\/android\/server\/wm\/DisplayContent.java" - }, - "3101160328044493048": { - "message": "Changing focus from %s to %s displayId=%d Callers=%s", - "level": "DEBUG", - "group": "WM_DEBUG_FOCUS_LIGHT", - "at": "com\/android\/server\/wm\/DisplayContent.java" - }, - "7634130879993688940": { - "message": "setFocusedApp %s displayId=%d Callers=%s", - "level": "INFO", - "group": "WM_DEBUG_FOCUS_LIGHT", - "at": "com\/android\/server\/wm\/DisplayContent.java" - }, - "-4130402450005935184": { - "message": "SURFACE LEAK DESTROY: %s", - "level": "INFO", - "group": "WM_SHOW_TRANSACTIONS", - "at": "com\/android\/server\/wm\/DisplayContent.java" - }, - "4464269036743635127": { - "message": "setInputMethodTarget %s", - "level": "INFO", - "group": "WM_DEBUG_IME", - "at": "com\/android\/server\/wm\/DisplayContent.java" - }, - "4835192778854186097": { - "message": "create IME snapshot for %s, buff width=%s, height=%s", - "level": "INFO", - "group": "WM_DEBUG_IME", - "at": "com\/android\/server\/wm\/DisplayContent.java" - }, - "2408509162360028352": { - "message": "Set IME snapshot position: (%d, %d)", - "level": "INFO", - "group": "WM_DEBUG_IME", - "at": "com\/android\/server\/wm\/DisplayContent.java" - }, - "2005731931732324688": { - "message": "remove IME snapshot, caller=%s", - "level": "INFO", - "group": "WM_DEBUG_IME", - "at": "com\/android\/server\/wm\/DisplayContent.java" - }, - "-6495118720675662641": { - "message": "show IME snapshot, ime target=%s, callers=%s", - "level": "INFO", - "group": "WM_DEBUG_IME", - "at": "com\/android\/server\/wm\/DisplayContent.java" - }, - "-4354595179162289537": { - "message": "setInputMethodInputTarget %s", - "level": "INFO", - "group": "WM_DEBUG_IME", - "at": "com\/android\/server\/wm\/DisplayContent.java" - }, - "2432701541536053712": { - "message": "DisplayContent: boot is waiting for window of type %d to be drawn", - "level": "DEBUG", - "group": "WM_DEBUG_BOOT", - "at": "com\/android\/server\/wm\/DisplayContent.java" - }, - "5683557566110711213": { - "message": "******** booted=%b msg=%b haveBoot=%b haveApp=%b haveWall=%b wallEnabled=%b haveKeyguard=%b", - "level": "INFO", - "group": "WM_DEBUG_SCREEN_ON", - "at": "com\/android\/server\/wm\/DisplayContent.java" - }, - "-124113386733162358": { - "message": "onWindowAnimationFinished, wc=%s, type=%s, imeSnapshot=%s, target=%s", - "level": "INFO", - "group": "WM_DEBUG_IME", - "at": "com\/android\/server\/wm\/DisplayContent.java" - }, - "-1556099709547629010": { - "message": "ImeContainer just became organized. Reparenting under parent. imeParentSurfaceControl=%s", - "level": "INFO", - "group": "WM_DEBUG_IME", - "at": "com\/android\/server\/wm\/DisplayContent.java" - }, - "1119786654111970652": { - "message": "ImeContainer just became organized but it doesn't have a parent or the parent doesn't have a surface control. mSurfaceControl=%s imeParentSurfaceControl=%s", - "level": "ERROR", - "group": "WM_DEBUG_IME", - "at": "com\/android\/server\/wm\/DisplayContent.java" - }, - "7019634211809476510": { - "message": "Execute app transition: %s, displayId: %d Callers=%s", - "level": "WARN", - "group": "WM_DEBUG_APP_TRANSITIONS", - "at": "com\/android\/server\/wm\/DisplayContent.java" - }, - "-3219913508985161450": { - "message": "Wallpaper layer changed: assigning layers + relayout", - "level": "VERBOSE", - "group": "WM_DEBUG_WALLPAPER", - "at": "com\/android\/server\/wm\/DisplayContent.java" - }, - "-8165317816061445169": { - "message": "Content Recording: Display %d state was (%d), is now (%d), so update recording?", - "level": "VERBOSE", - "group": "WM_DEBUG_CONTENT_RECORDING", - "at": "com\/android\/server\/wm\/DisplayContent.java" - }, - "4162342172327950908": { - "message": "Content Recording: Attempting to mirror self on %d", - "level": "WARN", - "group": "WM_DEBUG_CONTENT_RECORDING", - "at": "com\/android\/server\/wm\/DisplayContent.java" - }, - "5489691866309868814": { - "message": "Content Recording: Found no matching mirror display for id=%d for DEFAULT_DISPLAY. Nothing to mirror.", - "level": "WARN", - "group": "WM_DEBUG_CONTENT_RECORDING", - "at": "com\/android\/server\/wm\/DisplayContent.java" - }, - "-39794010824230928": { - "message": "Content Recording: Attempting to mirror %d from %d but no DisplayContent associated. Changing to mirror default display.", - "level": "WARN", - "group": "WM_DEBUG_CONTENT_RECORDING", - "at": "com\/android\/server\/wm\/DisplayContent.java" - }, - "6545352723229848841": { - "message": "Content Recording: Successfully created a ContentRecordingSession for displayId=%d to mirror content from displayId=%d", - "level": "VERBOSE", - "group": "WM_DEBUG_CONTENT_RECORDING", - "at": "com\/android\/server\/wm\/DisplayContent.java" - }, - "-6228339285356824882": { - "message": "finishScreenTurningOn: mAwake=%b, mScreenOnEarly=%b, mScreenOnFully=%b, mKeyguardDrawComplete=%b, mWindowManagerDrawComplete=%b", - "level": "DEBUG", - "group": "WM_DEBUG_SCREEN_ON", - "at": "com\/android\/server\/wm\/DisplayPolicy.java" - }, - "-6028033043540330282": { - "message": "Finished screen turning on...", - "level": "INFO", - "group": "WM_DEBUG_SCREEN_ON", - "at": "com\/android\/server\/wm\/DisplayPolicy.java" - }, - "-7427596081878257508": { - "message": "selectAnimation in %s: transit=%d", - "level": "INFO", - "group": "WM_DEBUG_ANIM", - "at": "com\/android\/server\/wm\/DisplayPolicy.java" - }, - "-6269658847003264525": { - "message": "**** STARTING EXIT", - "level": "INFO", - "group": "WM_DEBUG_ANIM", - "at": "com\/android\/server\/wm\/DisplayPolicy.java" - }, - "-6776561147903919733": { - "message": "Deferring rotation, rotation is paused.", - "level": "VERBOSE", - "group": "WM_DEBUG_ORIENTATION", - "at": "com\/android\/server\/wm\/DisplayRotation.java" - }, - "7439675997626642740": { - "message": "Deferring rotation, animation in progress.", - "level": "VERBOSE", - "group": "WM_DEBUG_ORIENTATION", - "at": "com\/android\/server\/wm\/DisplayRotation.java" - }, - "1104181226551849840": { - "message": "Deferring rotation, still finishing previous rotation", - "level": "VERBOSE", - "group": "WM_DEBUG_ORIENTATION", - "at": "com\/android\/server\/wm\/DisplayRotation.java" - }, - "-2222079183499215612": { - "message": "Deferring rotation, display is not enabled.", - "level": "VERBOSE", - "group": "WM_DEBUG_ORIENTATION", - "at": "com\/android\/server\/wm\/DisplayRotation.java" - }, - "662988298513100908": { - "message": "Reverting orientation. Rotating to %s from %s rather than %s.", - "level": "VERBOSE", - "group": "WM_DEBUG_ORIENTATION", - "at": "com\/android\/server\/wm\/DisplayRotation.java" - }, - "-7113483678655694375": { - "message": "Computed rotation=%s (%d) for display id=%d based on lastOrientation=%s (%d) and oldRotation=%s (%d)", - "level": "VERBOSE", - "group": "WM_DEBUG_ORIENTATION", - "at": "com\/android\/server\/wm\/DisplayRotation.java" - }, - "-8809129029906317617": { - "message": "Display id=%d selected orientation %s (%d), got rotation %s (%d)", - "level": "VERBOSE", - "group": "WM_DEBUG_ORIENTATION", - "at": "com\/android\/server\/wm\/DisplayRotation.java" - }, - "6753221849083491323": { - "message": "Display id=%d rotation changed to %d from %d, lastOrientation=%d", - "level": "VERBOSE", - "group": "WM_DEBUG_ORIENTATION", - "at": "com\/android\/server\/wm\/DisplayRotation.java" - }, - "-1216224951455892544": { - "message": "Performing post-rotate rotation after seamless rotation", - "level": "INFO", - "group": "WM_DEBUG_ORIENTATION", - "at": "com\/android\/server\/wm\/DisplayRotation.java" - }, - "-7672508047849737424": { - "message": "selectRotationAnimation topFullscreen=%s rotationAnimation=%d forceJumpcut=%b", - "level": "INFO", - "group": "WM_DEBUG_ANIM", - "at": "com\/android\/server\/wm\/DisplayRotation.java" - }, - "-2426404033822048710": { - "message": "screenOnEarly=%b, awake=%b, currentAppOrientation=%d, orientationSensorEnabled=%b, keyguardDrawComplete=%b, windowManagerDrawComplete=%b", - "level": "VERBOSE", - "group": "WM_DEBUG_ORIENTATION", - "at": "com\/android\/server\/wm\/DisplayRotation.java" - }, - "7339471241580327852": { - "message": "rotationForOrientation(orient=%s (%d), last=%s (%d)); user=%s (%d) %s", - "level": "VERBOSE", - "group": "WM_DEBUG_ORIENTATION", - "at": "com\/android\/server\/wm\/DisplayRotation.java" - }, - "5325136615007859122": { - "message": "Invalid surface rotation angle in config_deviceTabletopRotations: %d", - "level": "ERROR", - "group": "WM_DEBUG_ORIENTATION", - "at": "com\/android\/server\/wm\/DisplayRotation.java" - }, - "4616480353797749295": { - "message": "config_deviceTabletopRotations is not defined. Half-fold letterboxing will work inconsistently.", - "level": "WARN", - "group": "WM_DEBUG_ORIENTATION", - "at": "com\/android\/server\/wm\/DisplayRotation.java" - }, - "8852346340572084230": { - "message": "foldStateChanged: displayId %d, halfFoldStateChanged %s, saved rotation: %d, mUserRotation: %d, mLastSensorRotation: %d, mLastOrientation: %d, mRotation: %d", - "level": "VERBOSE", - "group": "WM_DEBUG_ORIENTATION", - "at": "com\/android\/server\/wm\/DisplayRotation.java" - }, - "-8674269704471038429": { - "message": "onProposedRotationChanged, rotation=%d", - "level": "VERBOSE", - "group": "WM_DEBUG_ORIENTATION", - "at": "com\/android\/server\/wm\/DisplayRotation.java" - }, - "418312772547457152": { - "message": "Enabling listeners", - "level": "VERBOSE", - "group": "WM_DEBUG_ORIENTATION", - "at": "com\/android\/server\/wm\/DisplayRotation.java" - }, - "4641814558273780952": { - "message": "Disabling listeners", - "level": "VERBOSE", - "group": "WM_DEBUG_ORIENTATION", - "at": "com\/android\/server\/wm\/DisplayRotation.java" - }, - "7429138692709430028": { - "message": "Display id=%d is ignoring all orientation requests, camera is active and the top activity is eligible for force rotation, return %s,portrait activity: %b, is natural orientation portrait: %b.", - "level": "VERBOSE", - "group": "WM_DEBUG_ORIENTATION", - "at": "com\/android\/server\/wm\/DisplayRotationCompatPolicy.java" - }, - "-5176775281239247368": { - "message": "Reverting orientation after camera compat force rotation", - "level": "VERBOSE", - "group": "WM_DEBUG_ORIENTATION", - "at": "com\/android\/server\/wm\/DisplayRotationCompatPolicy.java" - }, - "-2188976047008497712": { - "message": "Saving original orientation before camera compat, last orientation is %d", - "level": "VERBOSE", - "group": "WM_DEBUG_ORIENTATION", - "at": "com\/android\/server\/wm\/DisplayRotationCompatPolicy.java" - }, - "-1534784331886673955": { - "message": "DisplayRotationCompatPolicy: Multi-window toast not shown as package '%s' cannot be found.", - "level": "ERROR", - "group": "WM_DEBUG_ORIENTATION", - "at": "com\/android\/server\/wm\/DisplayRotationCompatPolicy.java" - }, - "-5121743609317543819": { - "message": "Display id=%d is notified that camera is closed but activity is still refreshing. Rescheduling an update.", - "level": "VERBOSE", - "group": "WM_DEBUG_ORIENTATION", - "at": "com\/android\/server\/wm\/DisplayRotationCompatPolicy.java" - }, - "1769752961776628557": { - "message": "Display id=%d is notified that Camera is closed, updating rotation.", - "level": "VERBOSE", - "group": "WM_DEBUG_ORIENTATION", - "at": "com\/android\/server\/wm\/DisplayRotationCompatPolicy.java" - }, - "-6949326633913532620": { - "message": "NOSENSOR override detected", - "level": "VERBOSE", - "group": "WM_DEBUG_ORIENTATION", - "at": "com\/android\/server\/wm\/DisplayRotationReversionController.java" - }, - "-2060428960792625366": { - "message": "NOSENSOR override is absent: reverting", - "level": "VERBOSE", - "group": "WM_DEBUG_ORIENTATION", - "at": "com\/android\/server\/wm\/DisplayRotationReversionController.java" - }, - "-4296736202875980050": { - "message": "Other orientation overrides are in place: not reverting", - "level": "VERBOSE", - "group": "WM_DEBUG_ORIENTATION", - "at": "com\/android\/server\/wm\/DisplayRotationReversionController.java" - }, - "7928129513685401229": { - "message": "Pausing rotation during drag", - "level": "DEBUG", - "group": "WM_DEBUG_ORIENTATION", - "at": "com\/android\/server\/wm\/DragState.java" - }, - "8231481023986546563": { - "message": "Resuming rotation after drag", - "level": "DEBUG", - "group": "WM_DEBUG_ORIENTATION", - "at": "com\/android\/server\/wm\/DragState.java" - }, - "12662399232325663": { - "message": "DRAG %s: pos=(%d,%d)", - "level": "INFO", - "group": "WM_SHOW_TRANSACTIONS", - "at": "com\/android\/server\/wm\/DragState.java" - }, - "-1797662102094201628": { - "message": "Attempt to transfer touch gesture with non-existent embedded window", - "level": "WARN", - "group": "WM_DEBUG_EMBEDDED_WINDOWS", - "at": "com\/android\/server\/wm\/EmbeddedWindowController.java" - }, - "929964979835124721": { - "message": "Attempt to transfer touch gesture using embedded window with no associated host", - "level": "WARN", - "group": "WM_DEBUG_EMBEDDED_WINDOWS", - "at": "com\/android\/server\/wm\/EmbeddedWindowController.java" - }, - "676191989331669410": { - "message": "Attempt to transfer touch gesture with host window not associated with embedded window", - "level": "WARN", - "group": "WM_DEBUG_EMBEDDED_WINDOWS", - "at": "com\/android\/server\/wm\/EmbeddedWindowController.java" - }, - "553249487221306249": { - "message": "Attempt to transfer touch gesture using embedded window that has no input channel", - "level": "WARN", - "group": "WM_DEBUG_EMBEDDED_WINDOWS", - "at": "com\/android\/server\/wm\/EmbeddedWindowController.java" - }, - "-8678904073078032058": { - "message": "Attempt to transfer touch gesture using a host window with no input channel", - "level": "WARN", - "group": "WM_DEBUG_EMBEDDED_WINDOWS", - "at": "com\/android\/server\/wm\/EmbeddedWindowController.java" - }, - "-786355099910065121": { - "message": "IME target changed within ActivityRecord", - "level": "DEBUG", - "group": "WM_DEBUG_IME", - "at": "com\/android\/server\/wm\/ImeInsetsSourceProvider.java" - }, - "2634707843050913730": { - "message": "Schedule IME show for %s", - "level": "DEBUG", - "group": "WM_DEBUG_IME", - "at": "com\/android\/server\/wm\/ImeInsetsSourceProvider.java" - }, - "8923821958256605927": { - "message": "Run showImeRunner", - "level": "DEBUG", - "group": "WM_DEBUG_IME", - "at": "com\/android\/server\/wm\/ImeInsetsSourceProvider.java" - }, - "-3529253275087521638": { - "message": "call showInsets(ime) on %s", - "level": "INFO", - "group": "WM_DEBUG_IME", - "at": "com\/android\/server\/wm\/ImeInsetsSourceProvider.java" - }, - "7927729210300708186": { - "message": "showInsets(ime) was requested by different window: %s ", - "level": "WARN", - "group": "WM_DEBUG_IME", - "at": "com\/android\/server\/wm\/ImeInsetsSourceProvider.java" - }, - "-6529782994356455131": { - "message": "abortShowImePostLayout", - "level": "DEBUG", - "group": "WM_DEBUG_IME", - "at": "com\/android\/server\/wm\/ImeInsetsSourceProvider.java" - }, - "-6629998049460863403": { - "message": "dcTarget: %s mImeRequester: %s", - "level": "DEBUG", - "group": "WM_DEBUG_IME", - "at": "com\/android\/server\/wm\/ImeInsetsSourceProvider.java" - }, - "-8553129529717081823": { - "message": "Input focus has changed to %s display=%d", - "level": "VERBOSE", - "group": "WM_DEBUG_FOCUS_LIGHT", - "at": "com\/android\/server\/wm\/InputMonitor.java" - }, - "4027486077547983902": { - "message": "App %s is focused, but the window is not ready. Start a transaction to remove focus from the window of non-focused apps.", - "level": "VERBOSE", - "group": "WM_DEBUG_FOCUS_LIGHT", - "at": "com\/android\/server\/wm\/InputMonitor.java" - }, - "-8537908614386667236": { - "message": "Focus not requested for window=%s because it has no surface or is not focusable.", - "level": "VERBOSE", - "group": "WM_DEBUG_FOCUS_LIGHT", - "at": "com\/android\/server\/wm\/InputMonitor.java" - }, - "-6346673514571615151": { - "message": "Focus requested for window=%s", - "level": "VERBOSE", - "group": "WM_DEBUG_FOCUS_LIGHT", - "at": "com\/android\/server\/wm\/InputMonitor.java" - }, - "1522894362518893789": { - "message": "InsetsSource setWin %s for type %s", - "level": "DEBUG", - "group": "WM_DEBUG_WINDOW_INSETS", - "at": "com\/android\/server\/wm\/InsetsSourceProvider.java" - }, - "6243049416211184258": { - "message": "InsetsSource Control %s for target %s", - "level": "DEBUG", - "group": "WM_DEBUG_WINDOW_INSETS", - "at": "com\/android\/server\/wm\/InsetsSourceProvider.java" - }, - "-8234068212532234206": { - "message": "InsetsSource updateVisibility for %s, serverVisible: %s clientVisible: %s", - "level": "DEBUG", - "group": "WM_DEBUG_WINDOW_INSETS", - "at": "com\/android\/server\/wm\/InsetsSourceProvider.java" - }, - "-8601070090234611338": { - "message": "ControlAdapter startAnimation mSource: %s controlTarget: %s", - "level": "INFO", - "group": "WM_DEBUG_WINDOW_INSETS", - "at": "com\/android\/server\/wm\/InsetsSourceProvider.java" - }, - "-6857870589074001153": { - "message": "ControlAdapter onAnimationCancelled mSource: %s mControlTarget: %s", - "level": "INFO", - "group": "WM_DEBUG_WINDOW_INSETS", - "at": "com\/android\/server\/wm\/InsetsSourceProvider.java" - }, - "-6684172224226118673": { - "message": "onImeControlTargetChanged %s", - "level": "DEBUG", - "group": "WM_DEBUG_IME", - "at": "com\/android\/server\/wm\/InsetsStateController.java" - }, - "8891808212671675155": { - "message": "clearLockedTasks: %s", - "level": "INFO", - "group": "WM_DEBUG_LOCKTASK", - "at": "com\/android\/server\/wm\/LockTaskController.java" - }, - "8970634498594714645": { - "message": "removeLockedTask: removed %s", - "level": "DEBUG", - "group": "WM_DEBUG_LOCKTASK", - "at": "com\/android\/server\/wm\/LockTaskController.java" - }, - "8735562128135241598": { - "message": "removeLockedTask: task=%s last task, reverting locktask mode. Callers=%s", - "level": "DEBUG", - "group": "WM_DEBUG_LOCKTASK", - "at": "com\/android\/server\/wm\/LockTaskController.java" - }, - "737192738184050156": { - "message": "startLockTaskMode: Can't lock due to auth", - "level": "WARN", - "group": "WM_DEBUG_LOCKTASK", - "at": "com\/android\/server\/wm\/LockTaskController.java" - }, - "-7119521978513736788": { - "message": "Mode default, asking user", - "level": "WARN", - "group": "WM_DEBUG_LOCKTASK", - "at": "com\/android\/server\/wm\/LockTaskController.java" - }, - "-1557441750657584614": { - "message": "%s", - "level": "WARN", - "group": "WM_DEBUG_LOCKTASK", - "at": "com\/android\/server\/wm\/LockTaskController.java" - }, - "-4314079913933391851": { - "message": "setLockTaskMode: Can't lock due to auth", - "level": "WARN", - "group": "WM_DEBUG_LOCKTASK", - "at": "com\/android\/server\/wm\/LockTaskController.java" - }, - "3321878763832425380": { - "message": "setLockTaskMode: Locking to %s Callers=%s", - "level": "WARN", - "group": "WM_DEBUG_LOCKTASK", - "at": "com\/android\/server\/wm\/LockTaskController.java" - }, - "-4819015209006579825": { - "message": "onLockTaskPackagesUpdated: removing %s mLockTaskAuth()=%s", - "level": "DEBUG", - "group": "WM_DEBUG_LOCKTASK", - "at": "com\/android\/server\/wm\/LockTaskController.java" - }, - "2119751067469297845": { - "message": "onLockTaskPackagesUpdated: starting new locktask task=%s", - "level": "DEBUG", - "group": "WM_DEBUG_LOCKTASK", - "at": "com\/android\/server\/wm\/LockTaskController.java" - }, - "3788905348567806832": { - "message": "startAnimation", - "level": "DEBUG", - "group": "WM_DEBUG_REMOTE_ANIMATIONS", - "at": "com\/android\/server\/wm\/NonAppWindowAnimationAdapter.java" - }, - "705955074330737483": { - "message": "onAnimationCancelled", - "level": "DEBUG", - "group": "WM_DEBUG_REMOTE_ANIMATIONS", - "at": "com\/android\/server\/wm\/NonAppWindowAnimationAdapter.java" - }, - "5106303602270682056": { - "message": "Adding display switch to existing collecting transition", - "level": "DEBUG", - "group": "WM_DEBUG_WINDOW_TRANSITIONS", - "at": "com\/android\/server\/wm\/PhysicalDisplaySwitchTransitionLauncher.java" - }, - "-1640401313436844534": { - "message": "Resetting frozen recents task list reason=app touch win=%s x=%d y=%d insetFrame=%s", - "level": "INFO", - "group": "WM_DEBUG_TASKS", - "at": "com\/android\/server\/wm\/RecentTasks.java" - }, - "-8803811426486764449": { - "message": "Setting frozen recents task list", - "level": "INFO", - "group": "WM_DEBUG_TASKS", - "at": "com\/android\/server\/wm\/RecentTasks.java" - }, - "4040735335719974079": { - "message": "Resetting frozen recents task list reason=timeout", - "level": "INFO", - "group": "WM_DEBUG_TASKS", - "at": "com\/android\/server\/wm\/RecentTasks.java" - }, - "3308140128142966415": { - "message": "remove RecentTask %s when finishing user %d", - "level": "INFO", - "group": "WM_DEBUG_TASKS", - "at": "com\/android\/server\/wm\/RecentTasks.java" - }, - "-3758280623533049031": { - "message": "Preload recents with %s", - "level": "DEBUG", - "group": "WM_DEBUG_RECENTS_ANIMATIONS", - "at": "com\/android\/server\/wm\/RecentsAnimation.java" - }, - "-3365656764099317101": { - "message": "Updated config=%s", - "level": "DEBUG", - "group": "WM_DEBUG_RECENTS_ANIMATIONS", - "at": "com\/android\/server\/wm\/RecentsAnimation.java" - }, - "-7165162073742035900": { - "message": "Real start recents", - "level": "DEBUG", - "group": "WM_DEBUG_RECENTS_ANIMATIONS", - "at": "com\/android\/server\/wm\/RecentsAnimation.java" - }, - "-3403665718306852375": { - "message": "startRecentsActivity(): intent=%s", - "level": "DEBUG", - "group": "WM_DEBUG_RECENTS_ANIMATIONS", - "at": "com\/android\/server\/wm\/RecentsAnimation.java" - }, - "-8325607672707336373": { - "message": "No root task above target root task=%s", - "level": "DEBUG", - "group": "WM_DEBUG_RECENTS_ANIMATIONS", - "at": "com\/android\/server\/wm\/RecentsAnimation.java" - }, - "-7278356485797757819": { - "message": "Moved rootTask=%s behind rootTask=%s", - "level": "DEBUG", - "group": "WM_DEBUG_RECENTS_ANIMATIONS", - "at": "com\/android\/server\/wm\/RecentsAnimation.java" - }, - "1012359606301505741": { - "message": "Started intent=%s", - "level": "DEBUG", - "group": "WM_DEBUG_RECENTS_ANIMATIONS", - "at": "com\/android\/server\/wm\/RecentsAnimation.java" - }, - "5474198007669537235": { - "message": "onAnimationFinished(): controller=%s reorderMode=%d", - "level": "DEBUG", - "group": "WM_DEBUG_RECENTS_ANIMATIONS", - "at": "com\/android\/server\/wm\/RecentsAnimation.java" - }, - "3525834288436624965": { - "message": "onAnimationFinished(): targetRootTask=%s targetActivity=%s mRestoreTargetBehindRootTask=%s", - "level": "DEBUG", - "group": "WM_DEBUG_RECENTS_ANIMATIONS", - "at": "com\/android\/server\/wm\/RecentsAnimation.java" - }, - "-5961176083217302671": { - "message": "Expected target rootTask=%s to be top most but found rootTask=%s", - "level": "WARN", - "group": "WM_DEBUG_RECENTS_ANIMATIONS", - "at": "com\/android\/server\/wm\/RecentsAnimation.java" - }, - "-5893976429537642045": { - "message": "Expected target rootTask=%s to restored behind rootTask=%s but it is behind rootTask=%s", - "level": "WARN", - "group": "WM_DEBUG_RECENTS_ANIMATIONS", - "at": "com\/android\/server\/wm\/RecentsAnimation.java" - }, - "4515487264815398694": { - "message": "onRootTaskOrderChanged(): rootTask=%s", - "level": "DEBUG", - "group": "WM_DEBUG_RECENTS_ANIMATIONS", - "at": "com\/android\/server\/wm\/RecentsAnimation.java" - }, - "6530904107141905844": { - "message": "screenshotTask(%d): mCanceled=%b", - "level": "DEBUG", - "group": "WM_DEBUG_RECENTS_ANIMATIONS", - "at": "com\/android\/server\/wm\/RecentsAnimationController.java" - }, - "-3286551982713129633": { - "message": "setFinishTaskTransaction(%d): transaction=%s", - "level": "DEBUG", - "group": "WM_DEBUG_RECENTS_ANIMATIONS", - "at": "com\/android\/server\/wm\/RecentsAnimationController.java" - }, - "5187133389446459984": { - "message": "finish(%b): mCanceled=%b", - "level": "DEBUG", - "group": "WM_DEBUG_RECENTS_ANIMATIONS", - "at": "com\/android\/server\/wm\/RecentsAnimationController.java" - }, - "6879496555046975661": { - "message": "setInputConsumerEnabled(%s): mCanceled=%b", - "level": "DEBUG", - "group": "WM_DEBUG_RECENTS_ANIMATIONS", - "at": "com\/android\/server\/wm\/RecentsAnimationController.java" - }, - "-5305978958548091997": { - "message": "setHomeApp(%s)", - "level": "DEBUG", - "group": "WM_DEBUG_RECENTS_ANIMATIONS", - "at": "com\/android\/server\/wm\/RecentsAnimationController.java" - }, - "-3801497203749932106": { - "message": "addAnimation(%s)", - "level": "DEBUG", - "group": "WM_DEBUG_RECENTS_ANIMATIONS", - "at": "com\/android\/server\/wm\/RecentsAnimationController.java" - }, - "3721473589747203697": { - "message": "removeAnimation(%d)", - "level": "DEBUG", - "group": "WM_DEBUG_RECENTS_ANIMATIONS", - "at": "com\/android\/server\/wm\/RecentsAnimationController.java" - }, - "5156407755139006078": { - "message": "removeWallpaperAnimation()", - "level": "DEBUG", - "group": "WM_DEBUG_RECENTS_ANIMATIONS", - "at": "com\/android\/server\/wm\/RecentsAnimationController.java" - }, - "-1997836523186474317": { - "message": "startAnimation(): mPendingStart=%b mCanceled=%b", - "level": "DEBUG", - "group": "WM_DEBUG_RECENTS_ANIMATIONS", - "at": "com\/android\/server\/wm\/RecentsAnimationController.java" - }, - "-7532294363367395195": { - "message": "startAnimation(): Notify animation start: %s", - "level": "DEBUG", - "group": "WM_DEBUG_RECENTS_ANIMATIONS", - "at": "com\/android\/server\/wm\/RecentsAnimationController.java" - }, - "-1336603089105439710": { - "message": "collectTaskRemoteAnimations, target: %s", - "level": "DEBUG", - "group": "WM_DEBUG_RECENTS_ANIMATIONS", - "at": "com\/android\/server\/wm\/RecentsAnimationController.java" - }, - "2547528895718568379": { - "message": "createWallpaperAnimations()", - "level": "DEBUG", - "group": "WM_DEBUG_RECENTS_ANIMATIONS", - "at": "com\/android\/server\/wm\/RecentsAnimationController.java" - }, - "5444932814080651576": { - "message": "cancelAnimation(): reason=%s", - "level": "DEBUG", - "group": "WM_DEBUG_RECENTS_ANIMATIONS", - "at": "com\/android\/server\/wm\/RecentsAnimationController.java" - }, - "622027757443954945": { - "message": "cleanupAnimation(): Notify animation finished mPendingAnimations=%d reorderMode=%d", - "level": "DEBUG", - "group": "WM_DEBUG_RECENTS_ANIMATIONS", - "at": "com\/android\/server\/wm\/RecentsAnimationController.java" - }, - "-5444412205083968021": { - "message": "createAnimationAdapter(): container=%s", - "level": "DEBUG", - "group": "WM_DEBUG_REMOTE_ANIMATIONS", - "at": "com\/android\/server\/wm\/RemoteAnimationController.java" - }, - "6986037643494242400": { - "message": "goodToGo()", - "level": "DEBUG", - "group": "WM_DEBUG_REMOTE_ANIMATIONS", - "at": "com\/android\/server\/wm\/RemoteAnimationController.java" - }, - "-1902984034737899928": { - "message": "goodToGo(): Animation canceled already", - "level": "DEBUG", - "group": "WM_DEBUG_REMOTE_ANIMATIONS", - "at": "com\/android\/server\/wm\/RemoteAnimationController.java" - }, - "6727618365838540075": { - "message": "goodToGo(): No apps to animate, mPendingAnimations=%d", - "level": "DEBUG", - "group": "WM_DEBUG_REMOTE_ANIMATIONS", - "at": "com\/android\/server\/wm\/RemoteAnimationController.java" - }, - "-2525509826755873433": { - "message": "goodToGo(): onAnimationStart, transit=%s, apps=%d, wallpapers=%d, nonApps=%d", - "level": "DEBUG", - "group": "WM_DEBUG_REMOTE_ANIMATIONS", - "at": "com\/android\/server\/wm\/RemoteAnimationController.java" - }, - "-1148281153370899511": { - "message": "startAnimation(): Notify animation start:", - "level": "DEBUG", - "group": "WM_DEBUG_REMOTE_ANIMATIONS", - "at": "com\/android\/server\/wm\/RemoteAnimationController.java" - }, - "7501495587927045391": { - "message": "cancelAnimation(): reason=%s", - "level": "DEBUG", - "group": "WM_DEBUG_REMOTE_ANIMATIONS", - "at": "com\/android\/server\/wm\/RemoteAnimationController.java" - }, - "-1424368765415574722": { - "message": "Starting remote animation", - "level": "INFO", - "group": "WM_DEBUG_REMOTE_ANIMATIONS", - "at": "com\/android\/server\/wm\/RemoteAnimationController.java" - }, - "-2676700429940607853": { - "message": "%s", - "level": "INFO", - "group": "WM_DEBUG_REMOTE_ANIMATIONS", - "at": "com\/android\/server\/wm\/RemoteAnimationController.java" - }, - "7094394833775573933": { - "message": "createAppAnimations()", - "level": "DEBUG", - "group": "WM_DEBUG_REMOTE_ANIMATIONS", - "at": "com\/android\/server\/wm\/RemoteAnimationController.java" - }, - "-4411070227420990074": { - "message": "\tAdd container=%s", - "level": "DEBUG", - "group": "WM_DEBUG_REMOTE_ANIMATIONS", - "at": "com\/android\/server\/wm\/RemoteAnimationController.java" - }, - "-4411631520586057580": { - "message": "\tRemove container=%s", - "level": "DEBUG", - "group": "WM_DEBUG_REMOTE_ANIMATIONS", - "at": "com\/android\/server\/wm\/RemoteAnimationController.java" - }, - "-7002230949892506736": { - "message": "createWallpaperAnimations()", - "level": "DEBUG", - "group": "WM_DEBUG_REMOTE_ANIMATIONS", - "at": "com\/android\/server\/wm\/RemoteAnimationController.java" - }, - "8743612568733301175": { - "message": "createNonAppWindowAnimations()", - "level": "DEBUG", - "group": "WM_DEBUG_REMOTE_ANIMATIONS", - "at": "com\/android\/server\/wm\/RemoteAnimationController.java" - }, - "-2716313493239418198": { - "message": "onAnimationFinished(): mPendingAnimations=%d", - "level": "DEBUG", - "group": "WM_DEBUG_REMOTE_ANIMATIONS", - "at": "com\/android\/server\/wm\/RemoteAnimationController.java" - }, - "7221400292415257709": { - "message": "onAnimationFinished(): Notify animation finished:", - "level": "DEBUG", - "group": "WM_DEBUG_REMOTE_ANIMATIONS", - "at": "com\/android\/server\/wm\/RemoteAnimationController.java" - }, - "7483194715776694698": { - "message": "\tcontainer=%s", - "level": "DEBUG", - "group": "WM_DEBUG_REMOTE_ANIMATIONS", - "at": "com\/android\/server\/wm\/RemoteAnimationController.java" - }, - "6697982664439247822": { - "message": "\twallpaper=%s", - "level": "DEBUG", - "group": "WM_DEBUG_REMOTE_ANIMATIONS", - "at": "com\/android\/server\/wm\/RemoteAnimationController.java" - }, - "6938838346517131964": { - "message": "\tnonApp=%s", - "level": "DEBUG", - "group": "WM_DEBUG_REMOTE_ANIMATIONS", - "at": "com\/android\/server\/wm\/RemoteAnimationController.java" - }, - "-3880290251819699866": { - "message": "Finishing remote animation", - "level": "INFO", - "group": "WM_DEBUG_REMOTE_ANIMATIONS", - "at": "com\/android\/server\/wm\/RemoteAnimationController.java" - }, - "-7169244688499657832": { - "message": "app-onAnimationFinished(): mOuter=%s", - "level": "DEBUG", - "group": "WM_DEBUG_REMOTE_ANIMATIONS", - "at": "com\/android\/server\/wm\/RemoteAnimationController.java" - }, - "3923111589554171989": { - "message": "app-release(): mOuter=%s", - "level": "DEBUG", - "group": "WM_DEBUG_REMOTE_ANIMATIONS", - "at": "com\/android\/server\/wm\/RemoteAnimationController.java" - }, - "8918152561092803537": { - "message": "startAnimation", - "level": "DEBUG", - "group": "WM_DEBUG_REMOTE_ANIMATIONS", - "at": "com\/android\/server\/wm\/RemoteAnimationController.java" - }, - "1736084564226683342": { - "message": "Starting remote display change: from [rot = %d], to [%dx%d, rot = %d]", - "level": "VERBOSE", - "group": "WM_DEBUG_CONFIGURATION", - "at": "com\/android\/server\/wm\/RemoteDisplayChangeController.java" - }, - "-4617490621756721600": { - "message": "resetTaskIntendedTask: calling finishActivity on %s", - "level": "WARN", - "group": "WM_DEBUG_TASKS", - "at": "com\/android\/server\/wm\/ResetTargetTaskHelper.java" - }, - "3361857745281957526": { - "message": "Removing activity %s from task=%s adding to task=%s Callers=%s", - "level": "INFO", - "group": "WM_DEBUG_ADD_REMOVE", - "at": "com\/android\/server\/wm\/ResetTargetTaskHelper.java" - }, - "3958829063955690349": { - "message": "Pushing next activity %s out to target's task %s", - "level": "VERBOSE", - "group": "WM_DEBUG_TASKS", - "at": "com\/android\/server\/wm\/ResetTargetTaskHelper.java" - }, - "1730793580703791926": { - "message": "Start pushing activity %s out to bottom task %s", - "level": "VERBOSE", - "group": "WM_DEBUG_TASKS", - "at": "com\/android\/server\/wm\/ResetTargetTaskHelper.java" - }, - "-8961882615747561040": { - "message": "Looking for task of %s in %s", - "level": "DEBUG", - "group": "WM_DEBUG_TASKS", - "at": "com\/android\/server\/wm\/RootWindowContainer.java" - }, - "8899721161806265460": { - "message": "Skipping task: (mismatch activity\/task) %s", - "level": "DEBUG", - "group": "WM_DEBUG_TASKS", - "at": "com\/android\/server\/wm\/RootWindowContainer.java" - }, - "6841550641928224256": { - "message": "Skipping %s: voice session", - "level": "DEBUG", - "group": "WM_DEBUG_TASKS", - "at": "com\/android\/server\/wm\/RootWindowContainer.java" - }, - "4468520936943270392": { - "message": "Skipping %s: different user", - "level": "DEBUG", - "group": "WM_DEBUG_TASKS", - "at": "com\/android\/server\/wm\/RootWindowContainer.java" - }, - "-4764624740388751268": { - "message": "Skipping %s: mismatch root %s", - "level": "DEBUG", - "group": "WM_DEBUG_TASKS", - "at": "com\/android\/server\/wm\/RootWindowContainer.java" - }, - "9031436623838917667": { - "message": "Skipping %s: mismatch activity type", - "level": "DEBUG", - "group": "WM_DEBUG_TASKS", - "at": "com\/android\/server\/wm\/RootWindowContainer.java" - }, - "6022828946761399284": { - "message": "Comparing existing cls=%s \/aff=%s to new cls=%s \/aff=%s", - "level": "DEBUG", - "group": "WM_DEBUG_TASKS", - "at": "com\/android\/server\/wm\/RootWindowContainer.java" - }, - "-3413620974545388702": { - "message": "Found matching class!", - "level": "DEBUG", - "group": "WM_DEBUG_TASKS", - "at": "com\/android\/server\/wm\/RootWindowContainer.java" - }, - "-2649361982747625232": { - "message": "For Intent %s bringing to top: %s", - "level": "DEBUG", - "group": "WM_DEBUG_TASKS", - "at": "com\/android\/server\/wm\/RootWindowContainer.java" - }, - "7046266138098744790": { - "message": "Found matching affinity candidate!", - "level": "DEBUG", - "group": "WM_DEBUG_TASKS", - "at": "com\/android\/server\/wm\/RootWindowContainer.java" - }, - "6481733556290926693": { - "message": "Not a match: %s", - "level": "DEBUG", - "group": "WM_DEBUG_TASKS", - "at": "com\/android\/server\/wm\/RootWindowContainer.java" - }, - "3331249072840061049": { - "message": "New topFocusedDisplayId=%d", - "level": "DEBUG", - "group": "WM_DEBUG_FOCUS_LIGHT", - "at": "com\/android\/server\/wm\/RootWindowContainer.java" - }, - "865845626039449679": { - "message": "SURFACE RECOVER DESTROY: %s", - "level": "INFO", - "group": "WM_SHOW_SURFACE_ALLOC", - "at": "com\/android\/server\/wm\/RootWindowContainer.java" - }, - "-4150611780753674023": { - "message": "Wallpaper may change! Adjusting", - "level": "VERBOSE", - "group": "WM_DEBUG_WALLPAPER", - "at": "com\/android\/server\/wm\/RootWindowContainer.java" - }, - "4177291132772627699": { - "message": "With display frozen, orientationChangeComplete=%b", - "level": "VERBOSE", - "group": "WM_DEBUG_ORIENTATION", - "at": "com\/android\/server\/wm\/RootWindowContainer.java" - }, - "-5513616928833586179": { - "message": "Performing post-rotate rotation", - "level": "DEBUG", - "group": "WM_DEBUG_ORIENTATION", - "at": "com\/android\/server\/wm\/RootWindowContainer.java" - }, - "-7698723716637247994": { - "message": "handleNotObscuredLocked w: %s, w.mHasSurface: %b, w.isOnScreen(): %b, w.isDisplayedLw(): %b, w.mAttrs.userActivityTimeout: %d", - "level": "DEBUG", - "group": "WM_DEBUG_KEEP_SCREEN_ON", - "at": "com\/android\/server\/wm\/RootWindowContainer.java" - }, - "8621291657500572364": { - "message": "mUserActivityTimeout set to %d", - "level": "DEBUG", - "group": "WM_DEBUG_KEEP_SCREEN_ON", - "at": "com\/android\/server\/wm\/RootWindowContainer.java" - }, - "-1418592110950138870": { - "message": "Looking for task of type=%s, taskAffinity=%s, intent=%s, info=%s, preferredTDA=%s", - "level": "DEBUG", - "group": "WM_DEBUG_TASKS", - "at": "com\/android\/server\/wm\/RootWindowContainer.java" - }, - "2828976699481734755": { - "message": "No task found", - "level": "DEBUG", - "group": "WM_DEBUG_TASKS", - "at": "com\/android\/server\/wm\/RootWindowContainer.java" - }, - "-4405347314716558580": { - "message": "Create sleep token: tag=%s, displayId=%d", - "level": "DEBUG", - "group": "WM_DEBUG_STATES", - "at": "com\/android\/server\/wm\/RootWindowContainer.java" - }, - "1329131651776855609": { - "message": "Remove sleep token: tag=%s, displayId=%d", - "level": "DEBUG", - "group": "WM_DEBUG_STATES", - "at": "com\/android\/server\/wm\/RootWindowContainer.java" - }, - "1653728842643223887": { - "message": "allResumedActivitiesIdle: rootTask=%d %s not idle", - "level": "DEBUG", - "group": "WM_DEBUG_STATES", - "at": "com\/android\/server\/wm\/RootWindowContainer.java" - }, - "3785779399471740019": { - "message": "allPausedActivitiesComplete: r=%s state=%s", - "level": "DEBUG", - "group": "WM_DEBUG_STATES", - "at": "com\/android\/server\/wm\/RootWindowContainer.java" - }, - "4666728330189027178": { - "message": "Failed to register MediaProjectionWatcherCallback", - "level": "ERROR", - "group": "WM_ERROR", - "at": "com\/android\/server\/wm\/ScreenRecordingCallbackController.java" - }, - "8010999385228654193": { - "message": " FREEZE %s: CREATE", - "level": "INFO", - "group": "WM_SHOW_SURFACE_ALLOC", - "at": "com\/android\/server\/wm\/ScreenRotationAnimation.java" - }, - "-6586462455018013482": { - "message": "Start rotation animation. customAnim=%s, mCurRotation=%s, mOriginalRotation=%s", - "level": "DEBUG", - "group": "WM_DEBUG_ORIENTATION", - "at": "com\/android\/server\/wm\/ScreenRotationAnimation.java" - }, - "-5825336546511998057": { - "message": " FREEZE %s: DESTROY", - "level": "INFO", - "group": "WM_SHOW_SURFACE_ALLOC", - "at": "com\/android\/server\/wm\/ScreenRotationAnimation.java" - }, - "6883897856740637908": { - "message": "ScreenRotation still animating: type: %d\nmDisplayAnimator: %s\nmEnterBlackFrameAnimator: %s\nmRotateScreenAnimator: %s\nmScreenshotRotationAnimator: %s", - "level": "VERBOSE", - "group": "WM_DEBUG_ORIENTATION", - "at": "com\/android\/server\/wm\/ScreenRotationAnimation.java" - }, - "-3943622313307983155": { - "message": "ScreenRotationAnimation onAnimationEnd", - "level": "DEBUG", - "group": "WM_DEBUG_ORIENTATION", - "at": "com\/android\/server\/wm\/ScreenRotationAnimation.java" - }, - "-1594708154257031561": { - "message": " NEW SURFACE SESSION %s", - "level": "INFO", - "group": "WM_SHOW_TRANSACTIONS", - "at": "com\/android\/server\/wm\/Session.java" - }, - "2638961674625826260": { - "message": " KILL SURFACE SESSION %s", - "level": "INFO", - "group": "WM_SHOW_TRANSACTIONS", - "at": "com\/android\/server\/wm\/Session.java" - }, - "5380455212389185829": { - "message": "Removing dim surface %s on transaction %s", - "level": "DEBUG", - "group": "WM_DEBUG_DIMMER", - "at": "com\/android\/server\/wm\/SmoothDimmer.java" - }, - "-820649637734629482": { - "message": "Animation start delayed for %s", - "level": "INFO", - "group": "WM_DEBUG_ANIM", - "at": "com\/android\/server\/wm\/SurfaceAnimator.java" - }, - "1371702561758591499": { - "message": "Animation start for %s, anim=%s", - "level": "DEBUG", - "group": "WM_DEBUG_ANIM", - "at": "com\/android\/server\/wm\/SurfaceAnimator.java" - }, - "-5370506662233296228": { - "message": "Cancelling animation restarting=%b for %s", - "level": "INFO", - "group": "WM_DEBUG_ANIM", - "at": "com\/android\/server\/wm\/SurfaceAnimator.java" - }, - "-3045933321063743917": { - "message": "Reparenting to original parent: %s for %s", - "level": "INFO", - "group": "WM_DEBUG_ANIM", - "at": "com\/android\/server\/wm\/SurfaceAnimator.java" - }, - "-855083149623806053": { - "message": "Reparenting to leash for %s", - "level": "INFO", - "group": "WM_DEBUG_ANIM", - "at": "com\/android\/server\/wm\/SurfaceAnimator.java" - }, - "-2595923278763115975": { - "message": " THUMBNAIL %s: CREATE", - "level": "INFO", - "group": "WM_SHOW_TRANSACTIONS", - "at": "com\/android\/server\/wm\/SurfaceFreezer.java" - }, - "-8609432747982701423": { - "message": "Setting Intent of %s to %s", - "level": "VERBOSE", - "group": "WM_DEBUG_TASKS", - "at": "com\/android\/server\/wm\/Task.java" - }, - "-9155008290180285590": { - "message": "Setting Intent of %s to target %s", - "level": "VERBOSE", - "group": "WM_DEBUG_TASKS", - "at": "com\/android\/server\/wm\/Task.java" - }, - "6424220442758232673": { - "message": "Removing and adding activity %s to root task at top callers=%s", - "level": "INFO", - "group": "WM_DEBUG_ADD_REMOVE", - "at": "com\/android\/server\/wm\/Task.java" - }, - "-1028890010429408946": { - "message": "addChild: %s at top.", - "level": "VERBOSE", - "group": "WM_DEBUG_ADD_REMOVE", - "at": "com\/android\/server\/wm\/Task.java" - }, - "38991867929900764": { - "message": "setLockTaskAuth: task=%s mLockTaskAuth=%s", - "level": "DEBUG", - "group": "WM_DEBUG_LOCKTASK", - "at": "com\/android\/server\/wm\/Task.java" - }, - "-3401780415681318335": { - "message": "applyAnimationUnchecked, control: %s, task: %s, transit: %s", - "level": "DEBUG", - "group": "WM_DEBUG_RECENTS_ANIMATIONS", - "at": "com\/android\/server\/wm\/Task.java" - }, - "4037728373502324767": { - "message": "resumeNextFocusableActivityWhenRootTaskIsEmpty: %s, go home", - "level": "DEBUG", - "group": "WM_DEBUG_STATES", - "at": "com\/android\/server\/wm\/Task.java" - }, - "-2261257617975724313": { - "message": "Adding activity %s to task %s callers: %s", - "level": "INFO", - "group": "WM_DEBUG_ADD_REMOVE", - "at": "com\/android\/server\/wm\/Task.java" - }, - "7378236902389922467": { - "message": "App is requesting an orientation, return %d for display id=%d", - "level": "VERBOSE", - "group": "WM_DEBUG_ORIENTATION", - "at": "com\/android\/server\/wm\/TaskDisplayArea.java" - }, - "2005499548343677845": { - "message": "No app is requesting an orientation, return %d for display id=%d", - "level": "VERBOSE", - "group": "WM_DEBUG_ORIENTATION", - "at": "com\/android\/server\/wm\/TaskDisplayArea.java" - }, - "646076184396185067": { - "message": "App died while pausing: %s", - "level": "VERBOSE", - "group": "WM_DEBUG_STATES", - "at": "com\/android\/server\/wm\/TaskFragment.java" - }, - "-7596917112222697106": { - "message": "Waiting for screen on due to %s", - "level": "VERBOSE", - "group": "WM_DEBUG_STATES", - "at": "com\/android\/server\/wm\/TaskFragment.java" - }, - "-8472961767591168851": { - "message": "Sleep needs to pause %s", - "level": "VERBOSE", - "group": "WM_DEBUG_STATES", - "at": "com\/android\/server\/wm\/TaskFragment.java" - }, - "-1472885369931482317": { - "message": "Sleep still waiting to pause %s", - "level": "VERBOSE", - "group": "WM_DEBUG_STATES", - "at": "com\/android\/server\/wm\/TaskFragment.java" - }, - "-2693016397674039814": { - "message": "Sleep still need to stop %d activities", - "level": "VERBOSE", - "group": "WM_DEBUG_STATES", - "at": "com\/android\/server\/wm\/TaskFragment.java" - }, - "8892147402270850613": { - "message": "resumeTopActivity: Skip resume: some activity pausing.", - "level": "VERBOSE", - "group": "WM_DEBUG_STATES", - "at": "com\/android\/server\/wm\/TaskFragment.java" - }, - "958293038551087087": { - "message": "resumeTopActivity: Top activity resumed %s", - "level": "DEBUG", - "group": "WM_DEBUG_STATES", - "at": "com\/android\/server\/wm\/TaskFragment.java" - }, - "4340810061306869942": { - "message": "resumeTopActivity: Going to sleep and all paused", - "level": "DEBUG", - "group": "WM_DEBUG_STATES", - "at": "com\/android\/server\/wm\/TaskFragment.java" - }, - "-7681635901109618685": { - "message": "resumeTopActivity: Pausing %s", - "level": "DEBUG", - "group": "WM_DEBUG_STATES", - "at": "com\/android\/server\/wm\/TaskFragment.java" - }, - "-3463034909521330970": { - "message": "resumeTopActivity: Skip resume: need to start pausing", - "level": "VERBOSE", - "group": "WM_DEBUG_STATES", - "at": "com\/android\/server\/wm\/TaskFragment.java" - }, - "-2264725269594226780": { - "message": "resumeTopActivity: Top activity resumed (dontWaitForPause) %s", - "level": "DEBUG", - "group": "WM_DEBUG_STATES", - "at": "com\/android\/server\/wm\/TaskFragment.java" - }, - "-8359248677489986541": { - "message": "Moving to RESUMED: %s (in existing)", - "level": "VERBOSE", - "group": "WM_DEBUG_STATES", - "at": "com\/android\/server\/wm\/TaskFragment.java" - }, - "-8483536760290526299": { - "message": "resumeTopActivity: Resumed %s", - "level": "DEBUG", - "group": "WM_DEBUG_STATES", - "at": "com\/android\/server\/wm\/TaskFragment.java" - }, - "-4911500660485375799": { - "message": "Resume failed; resetting state to %s: %s", - "level": "VERBOSE", - "group": "WM_DEBUG_STATES", - "at": "com\/android\/server\/wm\/TaskFragment.java" - }, - "3723891427717889172": { - "message": "resumeTopActivity: Restarting %s", - "level": "DEBUG", - "group": "WM_DEBUG_STATES", - "at": "com\/android\/server\/wm\/TaskFragment.java" - }, - "1529152423206006904": { - "message": "startPausing: taskFrag =%s mResumedActivity=%s", - "level": "DEBUG", - "group": "WM_DEBUG_STATES", - "at": "com\/android\/server\/wm\/TaskFragment.java" - }, - "136971836458873178": { - "message": "Moving to PAUSING: %s", - "level": "VERBOSE", - "group": "WM_DEBUG_STATES", - "at": "com\/android\/server\/wm\/TaskFragment.java" - }, - "-208996201631695262": { - "message": "Auto-PIP allowed, requesting PIP mode via requestStartTransition(): %s, willAutoPip: %b", - "level": "DEBUG", - "group": "WM_DEBUG_STATES", - "at": "com\/android\/server\/wm\/TaskFragment.java" - }, - "-4123447037565780632": { - "message": "Auto-PIP allowed, entering PIP mode directly: %s, didAutoPip: %b", - "level": "DEBUG", - "group": "WM_DEBUG_STATES", - "at": "com\/android\/server\/wm\/TaskFragment.java" - }, - "-3710776151994843320": { - "message": "Key dispatch not paused for screen off", - "level": "VERBOSE", - "group": "WM_DEBUG_STATES", - "at": "com\/android\/server\/wm\/TaskFragment.java" - }, - "8543865526552245064": { - "message": "Activity not running or entered PiP, resuming next.", - "level": "VERBOSE", - "group": "WM_DEBUG_STATES", - "at": "com\/android\/server\/wm\/TaskFragment.java" - }, - "1917394294249960915": { - "message": "Enqueueing pending pause: %s", - "level": "VERBOSE", - "group": "WM_DEBUG_STATES", - "at": "com\/android\/server\/wm\/TaskFragment.java" - }, - "-8936154984341817384": { - "message": "Complete pause: %s", - "level": "VERBOSE", - "group": "WM_DEBUG_STATES", - "at": "com\/android\/server\/wm\/TaskFragment.java" - }, - "4971958459026584561": { - "message": "Executing finish of activity: %s", - "level": "VERBOSE", - "group": "WM_DEBUG_STATES", - "at": "com\/android\/server\/wm\/TaskFragment.java" - }, - "-7113165071559345173": { - "message": "Enqueue pending stop if needed: %s wasStopping=%b visibleRequested=%b", - "level": "VERBOSE", - "group": "WM_DEBUG_STATES", - "at": "com\/android\/server\/wm\/TaskFragment.java" - }, - "-3777748052684097788": { - "message": "App died during pause, not stopping: %s", - "level": "VERBOSE", - "group": "WM_DEBUG_STATES", - "at": "com\/android\/server\/wm\/TaskFragment.java" - }, - "-2808577027789344626": { - "message": "TaskFragment appeared name=%s", - "level": "VERBOSE", - "group": "WM_DEBUG_WINDOW_ORGANIZER", - "at": "com\/android\/server\/wm\/TaskFragmentOrganizerController.java" - }, - "-3582112419663037270": { - "message": "TaskFragment vanished name=%s", - "level": "VERBOSE", - "group": "WM_DEBUG_WINDOW_ORGANIZER", - "at": "com\/android\/server\/wm\/TaskFragmentOrganizerController.java" - }, - "3294593748816836746": { - "message": "TaskFragment info changed name=%s", - "level": "VERBOSE", - "group": "WM_DEBUG_WINDOW_ORGANIZER", - "at": "com\/android\/server\/wm\/TaskFragmentOrganizerController.java" - }, - "5007230330523630579": { - "message": "TaskFragment parent info changed name=%s parentTaskId=%d", - "level": "VERBOSE", - "group": "WM_DEBUG_WINDOW_ORGANIZER", - "at": "com\/android\/server\/wm\/TaskFragmentOrganizerController.java" - }, - "6475066005515810081": { - "message": "Sending TaskFragment error exception=%s", - "level": "VERBOSE", - "group": "WM_DEBUG_WINDOW_ORGANIZER", - "at": "com\/android\/server\/wm\/TaskFragmentOrganizerController.java" - }, - "-7893265697482064583": { - "message": "Activity=%s reparent to taskId=%d", - "level": "VERBOSE", - "group": "WM_DEBUG_WINDOW_ORGANIZER", - "at": "com\/android\/server\/wm\/TaskFragmentOrganizerController.java" - }, - "7048981249808281819": { - "message": "Defer transition id=%d for TaskFragmentTransaction=%s", - "level": "VERBOSE", - "group": "WM_DEBUG_WINDOW_TRANSITIONS", - "at": "com\/android\/server\/wm\/TaskFragmentOrganizerController.java" - }, - "-1315509853595025526": { - "message": "Deferred transition id=%d has been continued before the TaskFragmentTransaction=%s is finished", - "level": "WARN", - "group": "WM_DEBUG_WINDOW_TRANSITIONS", - "at": "com\/android\/server\/wm\/TaskFragmentOrganizerController.java" - }, - "7421521217481553621": { - "message": "Continue transition id=%d for TaskFragmentTransaction=%s", - "level": "VERBOSE", - "group": "WM_DEBUG_WINDOW_TRANSITIONS", - "at": "com\/android\/server\/wm\/TaskFragmentOrganizerController.java" - }, - "3509684748201636981": { - "message": "Register task fragment organizer=%s uid=%d pid=%d", - "level": "VERBOSE", - "group": "WM_DEBUG_WINDOW_ORGANIZER", - "at": "com\/android\/server\/wm\/TaskFragmentOrganizerController.java" - }, - "-6777461169027010201": { - "message": "Unregister task fragment organizer=%s uid=%d pid=%d", - "level": "VERBOSE", - "group": "WM_DEBUG_WINDOW_ORGANIZER", - "at": "com\/android\/server\/wm\/TaskFragmentOrganizerController.java" - }, - "1327792561585467865": { - "message": "Register remote animations for organizer=%s uid=%d pid=%d", - "level": "VERBOSE", - "group": "WM_DEBUG_WINDOW_ORGANIZER", - "at": "com\/android\/server\/wm\/TaskFragmentOrganizerController.java" - }, - "-2524361347368208519": { - "message": "Unregister remote animations for organizer=%s uid=%d pid=%d", - "level": "VERBOSE", - "group": "WM_DEBUG_WINDOW_ORGANIZER", - "at": "com\/android\/server\/wm\/TaskFragmentOrganizerController.java" - }, - "-6181189296332065162": { - "message": "Task appeared taskId=%d", - "level": "VERBOSE", - "group": "WM_DEBUG_WINDOW_ORGANIZER", - "at": "com\/android\/server\/wm\/TaskOrganizerController.java" - }, - "6535296991997214354": { - "message": "Task vanished taskId=%d", - "level": "VERBOSE", - "group": "WM_DEBUG_WINDOW_ORGANIZER", - "at": "com\/android\/server\/wm\/TaskOrganizerController.java" - }, - "-6638141753476761854": { - "message": "Task info changed taskId=%d", - "level": "VERBOSE", - "group": "WM_DEBUG_WINDOW_ORGANIZER", - "at": "com\/android\/server\/wm\/TaskOrganizerController.java" - }, - "-8100069665346602959": { - "message": "Task back pressed on root taskId=%d", - "level": "VERBOSE", - "group": "WM_DEBUG_WINDOW_ORGANIZER", - "at": "com\/android\/server\/wm\/TaskOrganizerController.java" - }, - "-610138383571469481": { - "message": "Register task organizer=%s uid=%d", - "level": "VERBOSE", - "group": "WM_DEBUG_WINDOW_ORGANIZER", - "at": "com\/android\/server\/wm\/TaskOrganizerController.java" - }, - "1705860547080436016": { - "message": "Unregister task organizer=%s uid=%d", - "level": "VERBOSE", - "group": "WM_DEBUG_WINDOW_ORGANIZER", - "at": "com\/android\/server\/wm\/TaskOrganizerController.java" - }, - "-2286607251115721394": { - "message": "createRootTask unknown displayId=%d", - "level": "ERROR", - "group": "WM_DEBUG_WINDOW_ORGANIZER", - "at": "com\/android\/server\/wm\/TaskOrganizerController.java" - }, - "8466395828406204368": { - "message": "Create root task displayId=%d winMode=%d", - "level": "VERBOSE", - "group": "WM_DEBUG_WINDOW_ORGANIZER", - "at": "com\/android\/server\/wm\/TaskOrganizerController.java" - }, - "6867170298997192615": { - "message": "Delete root task display=%d winMode=%d", - "level": "VERBOSE", - "group": "WM_DEBUG_WINDOW_ORGANIZER", - "at": "com\/android\/server\/wm\/TaskOrganizerController.java" - }, - "-4296644831871159510": { - "message": "Set intercept back pressed on root=%b", - "level": "VERBOSE", - "group": "WM_DEBUG_WINDOW_ORGANIZER", - "at": "com\/android\/server\/wm\/TaskOrganizerController.java" - }, - "-558727273888268534": { - "message": "Restart top activity process of Task taskId=%d", - "level": "VERBOSE", - "group": "WM_DEBUG_WINDOW_ORGANIZER", - "at": "com\/android\/server\/wm\/TaskOrganizerController.java" - }, - "-7064081458956324316": { - "message": "Update camera compat control state to %s for taskId=%d", - "level": "VERBOSE", - "group": "WM_DEBUG_WINDOW_ORGANIZER", - "at": "com\/android\/server\/wm\/TaskOrganizerController.java" - }, - "3007492640459931179": { - "message": "Pausing rotation during re-position", - "level": "DEBUG", - "group": "WM_DEBUG_ORIENTATION", - "at": "com\/android\/server\/wm\/TaskPositioner.java" - }, - "5478864901888225320": { - "message": "Resuming rotation after re-position", - "level": "DEBUG", - "group": "WM_DEBUG_ORIENTATION", - "at": "com\/android\/server\/wm\/TaskPositioner.java" - }, - "-2700498872917476567": { - "message": "Starting a Recents transition which can be parallel.", - "level": "VERBOSE", - "group": "WM_DEBUG_WINDOW_TRANSITIONS", - "at": "com\/android\/server\/wm\/Transition.java" - }, - "-8676279589273455859": { - "message": "Transition %d: Set %s as transient-launch", - "level": "VERBOSE", - "group": "WM_DEBUG_WINDOW_TRANSITIONS", - "at": "com\/android\/server\/wm\/Transition.java" - }, - "2734227875286695843": { - "message": "Override sync-method for %s because seamless rotating", - "level": "VERBOSE", - "group": "WM_DEBUG_WINDOW_TRANSITIONS", - "at": "com\/android\/server\/wm\/Transition.java" - }, - "2808217645990556209": { - "message": "Starting Transition %d", - "level": "VERBOSE", - "group": "WM_DEBUG_WINDOW_TRANSITIONS", - "at": "com\/android\/server\/wm\/Transition.java" - }, - "-4672522645315112127": { - "message": "Collecting in transition %d: %s", - "level": "VERBOSE", - "group": "WM_DEBUG_WINDOW_TRANSITIONS", - "at": "com\/android\/server\/wm\/Transition.java" - }, - "65881049096729394": { - "message": " Creating Ready-group for Transition %d with root=%s", - "level": "VERBOSE", - "group": "WM_DEBUG_WINDOW_TRANSITIONS", - "at": "com\/android\/server\/wm\/Transition.java" - }, - "1101215730201607371": { - "message": "Existence Changed in transition %d: %s", - "level": "VERBOSE", - "group": "WM_DEBUG_WINDOW_TRANSITIONS", - "at": "com\/android\/server\/wm\/Transition.java" - }, - "-3942072270654590479": { - "message": "Set transition ready=%b %d", - "level": "VERBOSE", - "group": "WM_DEBUG_WINDOW_TRANSITIONS", - "at": "com\/android\/server\/wm\/Transition.java" - }, - "-4688704756793656554": { - "message": " Commit activity becoming invisible: %s", - "level": "VERBOSE", - "group": "WM_DEBUG_WINDOW_TRANSITIONS", - "at": "com\/android\/server\/wm\/Transition.java" - }, - "1817207111271920503": { - "message": " Skipping post-transition snapshot for task %d", - "level": "VERBOSE", - "group": "WM_DEBUG_WINDOW_TRANSITIONS", - "at": "com\/android\/server\/wm\/Transition.java" - }, - "-2960171012238790176": { - "message": " Commit wallpaper becoming invisible: %s", - "level": "VERBOSE", - "group": "WM_DEBUG_WINDOW_TRANSITIONS", - "at": "com\/android\/server\/wm\/Transition.java" - }, - "1230784960534033968": { - "message": "Aborting Transition: %d", - "level": "VERBOSE", - "group": "WM_DEBUG_WINDOW_TRANSITIONS", - "at": "com\/android\/server\/wm\/Transition.java" - }, - "-892865733969888022": { - "message": "Force Playing Transition: %d", - "level": "VERBOSE", - "group": "WM_DEBUG_WINDOW_TRANSITIONS", - "at": "com\/android\/server\/wm\/Transition.java" - }, - "-1354622424895965634": { - "message": "#%d: Met condition: %s", - "level": "VERBOSE", - "group": "WM_DEBUG_WINDOW_TRANSITIONS", - "at": "com\/android\/server\/wm\/Transition.java" - }, - "-5350671621840749173": { - "message": "Calling onTransitionReady: %s", - "level": "VERBOSE", - "group": "WM_DEBUG_WINDOW_TRANSITIONS", - "at": "com\/android\/server\/wm\/Transition.java" - }, - "1830385055586991567": { - "message": "Apply and finish immediately because player is disabled for transition #%d .", - "level": "VERBOSE", - "group": "WM_DEBUG_WINDOW_TRANSITIONS", - "at": "com\/android\/server\/wm\/Transition.java" - }, - "-758501334967569539": { - "message": " SKIP: %s", - "level": "VERBOSE", - "group": "WM_DEBUG_WINDOW_TRANSITIONS", - "at": "com\/android\/server\/wm\/Transition.java" - }, - "-2714847784842612086": { - "message": " SKIP: is wallpaper", - "level": "VERBOSE", - "group": "WM_DEBUG_WINDOW_TRANSITIONS", - "at": "com\/android\/server\/wm\/Transition.java" - }, - "1855461834864671586": { - "message": " check sibling %s", - "level": "VERBOSE", - "group": "WM_DEBUG_WINDOW_TRANSITIONS", - "at": "com\/android\/server\/wm\/Transition.java" - }, - "-6292043690918793069": { - "message": " SKIP: sibling is visible but not part of transition", - "level": "VERBOSE", - "group": "WM_DEBUG_WINDOW_TRANSITIONS", - "at": "com\/android\/server\/wm\/Transition.java" - }, - "7897657428993391672": { - "message": " unrelated invisible sibling %s", - "level": "VERBOSE", - "group": "WM_DEBUG_WINDOW_TRANSITIONS", - "at": "com\/android\/server\/wm\/Transition.java" - }, - "3873493605120555608": { - "message": " sibling is a participant with mode %s", - "level": "VERBOSE", - "group": "WM_DEBUG_WINDOW_TRANSITIONS", - "at": "com\/android\/server\/wm\/Transition.java" - }, - "7665553560859456426": { - "message": " SKIP: common mode mismatch. was %s", - "level": "VERBOSE", - "group": "WM_DEBUG_WINDOW_TRANSITIONS", - "at": "com\/android\/server\/wm\/Transition.java" - }, - "-8916099332247176657": { - "message": " checking %s", - "level": "VERBOSE", - "group": "WM_DEBUG_WINDOW_TRANSITIONS", - "at": "com\/android\/server\/wm\/Transition.java" - }, - "-6818387694968032301": { - "message": " SKIP: its sibling was rejected", - "level": "VERBOSE", - "group": "WM_DEBUG_WINDOW_TRANSITIONS", - "at": "com\/android\/server\/wm\/Transition.java" - }, - "-7326702978448933012": { - "message": " keep as target %s", - "level": "VERBOSE", - "group": "WM_DEBUG_WINDOW_TRANSITIONS", - "at": "com\/android\/server\/wm\/Transition.java" - }, - "943961036184959431": { - "message": " remove from targets %s", - "level": "VERBOSE", - "group": "WM_DEBUG_WINDOW_TRANSITIONS", - "at": "com\/android\/server\/wm\/Transition.java" - }, - "841543868388687804": { - "message": " CAN PROMOTE: promoting to parent %s", - "level": "VERBOSE", - "group": "WM_DEBUG_WINDOW_TRANSITIONS", - "at": "com\/android\/server\/wm\/Transition.java" - }, - "743586316159041023": { - "message": "Start calculating TransitionInfo based on participants: %s", - "level": "VERBOSE", - "group": "WM_DEBUG_WINDOW_TRANSITIONS", - "at": "com\/android\/server\/wm\/Transition.java" - }, - "-7247430213293162757": { - "message": " Rejecting as detached: %s", - "level": "VERBOSE", - "group": "WM_DEBUG_WINDOW_TRANSITIONS", - "at": "com\/android\/server\/wm\/Transition.java" - }, - "-5811837191094192313": { - "message": " Rejecting as no-op: %s", - "level": "VERBOSE", - "group": "WM_DEBUG_WINDOW_TRANSITIONS", - "at": "com\/android\/server\/wm\/Transition.java" - }, - "-1153926883525904120": { - "message": " Initial targets: %s", - "level": "VERBOSE", - "group": "WM_DEBUG_WINDOW_TRANSITIONS", - "at": "com\/android\/server\/wm\/Transition.java" - }, - "-9191328656870721224": { - "message": " Final targets: %s", - "level": "VERBOSE", - "group": "WM_DEBUG_WINDOW_TRANSITIONS", - "at": "com\/android\/server\/wm\/Transition.java" - }, - "-2971560715211489406": { - "message": " Add condition %s for #%d", - "level": "VERBOSE", - "group": "WM_DEBUG_WINDOW_TRANSITIONS", - "at": "com\/android\/server\/wm\/Transition.java" - }, - "7631061720069910622": { - "message": " Met condition %s for #%d (%d left)", - "level": "VERBOSE", - "group": "WM_DEBUG_WINDOW_TRANSITIONS", - "at": "com\/android\/server\/wm\/Transition.java" - }, - "-4770394322045550928": { - "message": " Setting Ready-group to %b. group=%s from %s", - "level": "VERBOSE", - "group": "WM_DEBUG_WINDOW_TRANSITIONS", - "at": "com\/android\/server\/wm\/Transition.java" - }, - "6039132370452820927": { - "message": " Setting allReady override", - "level": "VERBOSE", - "group": "WM_DEBUG_WINDOW_TRANSITIONS", - "at": "com\/android\/server\/wm\/Transition.java" - }, - "-3263748870548668913": { - "message": " allReady query: used=%b override=%b defer=%d states=[%s]", - "level": "VERBOSE", - "group": "WM_DEBUG_WINDOW_TRANSITIONS", - "at": "com\/android\/server\/wm\/Transition.java" - }, - "2699903406935781477": { - "message": "Screenshotting %s [%s]", - "level": "VERBOSE", - "group": "WM_DEBUG_WINDOW_TRANSITIONS", - "at": "com\/android\/server\/wm\/Transition.java" - }, - "-233096875591058130": { - "message": "Creating Transition: %s", - "level": "VERBOSE", - "group": "WM_DEBUG_WINDOW_TRANSITIONS", - "at": "com\/android\/server\/wm\/TransitionController.java" - }, - "2154694726162725342": { - "message": "Start collecting in Transition: %s", - "level": "VERBOSE", - "group": "WM_DEBUG_WINDOW_TRANSITIONS", - "at": "com\/android\/server\/wm\/TransitionController.java" - }, - "-4546322749928357965": { - "message": "Registering transition player %s over %d other players", - "level": "VERBOSE", - "group": "WM_DEBUG_WINDOW_TRANSITIONS_MIN", - "at": "com\/android\/server\/wm\/TransitionController.java" - }, - "-4250307779892136611": { - "message": "Registering transition player %s ", - "level": "VERBOSE", - "group": "WM_DEBUG_WINDOW_TRANSITIONS_MIN", - "at": "com\/android\/server\/wm\/TransitionController.java" - }, - "3242771541905259983": { - "message": "Attempt to unregister transition player %s but it isn't registered", - "level": "WARN", - "group": "WM_DEBUG_WINDOW_TRANSITIONS_MIN", - "at": "com\/android\/server\/wm\/TransitionController.java" - }, - "3691912781236221027": { - "message": "Unregistering active transition player %s at index=%d leaving %d in stack", - "level": "VERBOSE", - "group": "WM_DEBUG_WINDOW_TRANSITIONS_MIN", - "at": "com\/android\/server\/wm\/TransitionController.java" - }, - "-2879980134100946679": { - "message": "Unregistering transition player %s at index=%d leaving %d in stack", - "level": "VERBOSE", - "group": "WM_DEBUG_WINDOW_TRANSITIONS_MIN", - "at": "com\/android\/server\/wm\/TransitionController.java" - }, - "-4235778637051052061": { - "message": "Disabling player for transition #%d because display isn't enabled yet", - "level": "VERBOSE", - "group": "WM_DEBUG_WINDOW_TRANSITIONS", - "at": "com\/android\/server\/wm\/TransitionController.java" - }, - "4005704720444963797": { - "message": "Requesting StartTransition: %s", - "level": "VERBOSE", - "group": "WM_DEBUG_WINDOW_TRANSITIONS", - "at": "com\/android\/server\/wm\/TransitionController.java" - }, - "-6030030735787868329": { - "message": "Finish Transition: %s", - "level": "VERBOSE", - "group": "WM_DEBUG_WINDOW_TRANSITIONS", - "at": "com\/android\/server\/wm\/TransitionController.java" - }, - "-1611886029896664304": { - "message": "Moving #%d from collecting to waiting.", - "level": "VERBOSE", - "group": "WM_DEBUG_WINDOW_TRANSITIONS_MIN", - "at": "com\/android\/server\/wm\/TransitionController.java" - }, - "-7097461682459496366": { - "message": "Playing #%d in parallel on track #%d", - "level": "VERBOSE", - "group": "WM_DEBUG_WINDOW_TRANSITIONS", - "at": "com\/android\/server\/wm\/TransitionController.java" - }, - "-7364464699035275052": { - "message": "Marking #%d animation as SYNC.", - "level": "VERBOSE", - "group": "WM_DEBUG_WINDOW_TRANSITIONS", - "at": "com\/android\/server\/wm\/TransitionController.java" - }, - "-5509640937151643757": { - "message": "Queueing transition: %s", - "level": "VERBOSE", - "group": "WM_DEBUG_WINDOW_TRANSITIONS_MIN", - "at": "com\/android\/server\/wm\/TransitionController.java" - }, - "-2741593375634604522": { - "message": "Queueing legacy sync-set: %s", - "level": "VERBOSE", - "group": "WM_DEBUG_WINDOW_TRANSITIONS_MIN", - "at": "com\/android\/server\/wm\/TransitionController.java" - }, - "-5051723169912572741": { - "message": "%s", - "level": "VERBOSE", - "group": "WM_DEBUG_WINDOW_TRANSITIONS_MIN", - "at": "com\/android\/server\/wm\/TransitionController.java" - }, - "4281568181321808508": { - "message": " startWCT=%s", - "level": "VERBOSE", - "group": "WM_DEBUG_WINDOW_TRANSITIONS_MIN", - "at": "com\/android\/server\/wm\/TransitionController.java" - }, - "5141999957143860655": { - "message": " info=%s", - "level": "VERBOSE", - "group": "WM_DEBUG_WINDOW_TRANSITIONS_MIN", - "at": "com\/android\/server\/wm\/TransitionController.java" - }, - "3445530300764535903": { - "message": "unregister failed, couldn't find deathRecipient for %s with id=%d", - "level": "ERROR", - "group": "WM_DEBUG_TPL", - "at": "com\/android\/server\/wm\/TrustedPresentationListenerController.java" - }, - "-6140852484700685564": { - "message": "Registering listener=%s with id=%d for window=%s with %s", - "level": "DEBUG", - "group": "WM_DEBUG_TPL", - "at": "com\/android\/server\/wm\/TrustedPresentationListenerController.java" - }, - "3691097873058247482": { - "message": "Unregistering listener=%s with id=%d", - "level": "DEBUG", - "group": "WM_DEBUG_TPL", - "at": "com\/android\/server\/wm\/TrustedPresentationListenerController.java" - }, - "6408851516381868623": { - "message": "Checking %d windows", - "level": "VERBOSE", - "group": "WM_DEBUG_TPL", - "at": "com\/android\/server\/wm\/TrustedPresentationListenerController.java" - }, - "7718187745767272532": { - "message": "Skipping %s", - "level": "VERBOSE", - "group": "WM_DEBUG_TPL", - "at": "com\/android\/server\/wm\/TrustedPresentationListenerController.java" - }, - "-1135667737459933313": { - "message": "coveredRegionsAbove updated with %s frame:%s region:%s", - "level": "VERBOSE", - "group": "WM_DEBUG_TPL", - "at": "com\/android\/server\/wm\/TrustedPresentationListenerController.java" - }, - "854487339271667012": { - "message": "checkIfInThreshold fractionRendered=%f alpha=%f currTimeMs=%d", - "level": "VERBOSE", - "group": "WM_DEBUG_TPL", - "at": "com\/android\/server\/wm\/TrustedPresentationListenerController.java" - }, - "-2248576188205088843": { - "message": "lastState=%s newState=%s alpha=%f minAlpha=%f fractionRendered=%f minFractionRendered=%f", - "level": "VERBOSE", - "group": "WM_DEBUG_TPL", - "at": "com\/android\/server\/wm\/TrustedPresentationListenerController.java" - }, - "6236170793308011579": { - "message": "Adding untrusted state listener=%s with id=%d", - "level": "DEBUG", - "group": "WM_DEBUG_TPL", - "at": "com\/android\/server\/wm\/TrustedPresentationListenerController.java" - }, - "5405816744363636527": { - "message": "Adding trusted state listener=%s with id=%d", - "level": "DEBUG", - "group": "WM_DEBUG_TPL", - "at": "com\/android\/server\/wm\/TrustedPresentationListenerController.java" - }, - "-5162728346383863020": { - "message": "computeFractionRendered: visibleRegion=%s screenBounds=%s contentSize=%s scale=%f,%f", - "level": "VERBOSE", - "group": "WM_DEBUG_TPL", - "at": "com\/android\/server\/wm\/TrustedPresentationListenerController.java" - }, - "898769258643799441": { - "message": "fractionRendered scale=%f", - "level": "VERBOSE", - "group": "WM_DEBUG_TPL", - "at": "com\/android\/server\/wm\/TrustedPresentationListenerController.java" - }, - "-455501334697331596": { - "message": "fractionRendered boundsOverSource=%f", - "level": "VERBOSE", - "group": "WM_DEBUG_TPL", - "at": "com\/android\/server\/wm\/TrustedPresentationListenerController.java" - }, - "1964980935866463086": { - "message": "\tWallpaper of display=%s is not visible", - "level": "DEBUG", - "group": "WM_DEBUG_REMOTE_ANIMATIONS", - "at": "com\/android\/server\/wm\/WallpaperAnimationAdapter.java" - }, - "8131665298937888044": { - "message": "startAnimation", - "level": "DEBUG", - "group": "WM_DEBUG_REMOTE_ANIMATIONS", - "at": "com\/android\/server\/wm\/WallpaperAnimationAdapter.java" - }, - "8030745595351281943": { - "message": "onAnimationCancelled", - "level": "DEBUG", - "group": "WM_DEBUG_REMOTE_ANIMATIONS", - "at": "com\/android\/server\/wm\/WallpaperAnimationAdapter.java" - }, - "-5254364639040552989": { - "message": "Hiding wallpaper %s from %s target=%s prev=%s callers=%s", - "level": "DEBUG", - "group": "WM_DEBUG_WALLPAPER", - "at": "com\/android\/server\/wm\/WallpaperController.java" - }, - "-6856158722649737204": { - "message": "Waiting for offset complete...", - "level": "VERBOSE", - "group": "WM_DEBUG_WALLPAPER", - "at": "com\/android\/server\/wm\/WallpaperController.java" - }, - "-5966696477376431672": { - "message": "Offset complete!", - "level": "VERBOSE", - "group": "WM_DEBUG_WALLPAPER", - "at": "com\/android\/server\/wm\/WallpaperController.java" - }, - "4198834090919802045": { - "message": "Timeout waiting for wallpaper to offset: %s", - "level": "VERBOSE", - "group": "WM_DEBUG_WALLPAPER", - "at": "com\/android\/server\/wm\/WallpaperController.java" - }, - "-3477087868568520027": { - "message": "No longer animating wallpaper targets!", - "level": "VERBOSE", - "group": "WM_DEBUG_WALLPAPER", - "at": "com\/android\/server\/wm\/WallpaperController.java" - }, - "-3751289048117070874": { - "message": "New wallpaper target: %s prevTarget: %s caller=%s", - "level": "VERBOSE", - "group": "WM_DEBUG_WALLPAPER", - "at": "com\/android\/server\/wm\/WallpaperController.java" - }, - "5625223922466895079": { - "message": "New animation: %s old animation: %s", - "level": "VERBOSE", - "group": "WM_DEBUG_WALLPAPER", - "at": "com\/android\/server\/wm\/WallpaperController.java" - }, - "7634524672408826188": { - "message": "Animating wallpapers: old: %s hidden=%b new: %s hidden=%b", - "level": "VERBOSE", - "group": "WM_DEBUG_WALLPAPER", - "at": "com\/android\/server\/wm\/WallpaperController.java" - }, - "-4345077332231178044": { - "message": "Old wallpaper still the target.", - "level": "VERBOSE", - "group": "WM_DEBUG_WALLPAPER", - "at": "com\/android\/server\/wm\/WallpaperController.java" - }, - "257349083882992098": { - "message": "updateWallpaperTokens requestedVisibility=%b on keyguardLocked=%b", - "level": "VERBOSE", - "group": "WM_DEBUG_WALLPAPER", - "at": "com\/android\/server\/wm\/WallpaperController.java" - }, - "7408402065665963407": { - "message": "Wallpaper at display %d - visibility: %b, keyguardLocked: %b", - "level": "VERBOSE", - "group": "WM_DEBUG_WALLPAPER", - "at": "com\/android\/server\/wm\/WallpaperController.java" - }, - "-8598497865499265448": { - "message": "Wallpaper target=%s prev=%s", - "level": "DEBUG", - "group": "WM_DEBUG_WALLPAPER", - "at": "com\/android\/server\/wm\/WallpaperController.java" - }, - "-5402010429724738603": { - "message": "Wallpaper should be visible but has not been drawn yet. mWallpaperDrawState=%d", - "level": "VERBOSE", - "group": "WM_DEBUG_WALLPAPER", - "at": "com\/android\/server\/wm\/WallpaperController.java" - }, - "4151327328872447804": { - "message": "New home screen wallpaper: %s, prev: %s", - "level": "DEBUG", - "group": "WM_DEBUG_WALLPAPER", - "at": "com\/android\/server\/wm\/WallpaperController.java" - }, - "6943105284590482059": { - "message": "New lock\/shared screen wallpaper: %s, prev: %s", - "level": "DEBUG", - "group": "WM_DEBUG_WALLPAPER", - "at": "com\/android\/server\/wm\/WallpaperController.java" - }, - "-7936547457136708587": { - "message": "Wallpaper token %s visible=%b", - "level": "DEBUG", - "group": "WM_DEBUG_WALLPAPER", - "at": "com\/android\/server\/wm\/WallpaperWindowToken.java" - }, - "7214407534407465113": { - "message": "commitVisibility: %s: visible=%b mVisibleRequested=%b", - "level": "VERBOSE", - "group": "WM_DEBUG_APP_TRANSITIONS", - "at": "com\/android\/server\/wm\/WallpaperWindowToken.java" - }, - "-5360147928134631656": { - "message": ">>> OPEN TRANSACTION animate", - "level": "INFO", - "group": "WM_SHOW_TRANSACTIONS", - "at": "com\/android\/server\/wm\/WindowAnimator.java" - }, - "-3993586364046165922": { - "message": "<<< CLOSE TRANSACTION animate", - "level": "INFO", - "group": "WM_SHOW_TRANSACTIONS", - "at": "com\/android\/server\/wm\/WindowAnimator.java" - }, - "-5231580410559054259": { - "message": "%s is requesting orientation %d (%s)", - "level": "VERBOSE", - "group": "WM_DEBUG_ORIENTATION", - "at": "com\/android\/server\/wm\/WindowContainer.java" - }, - "6949303417875346627": { - "message": "Starting animation on %s: type=%d, anim=%s", - "level": "VERBOSE", - "group": "WM_DEBUG_ANIM", - "at": "com\/android\/server\/wm\/WindowContainer.java" - }, - "-8730310387200541562": { - "message": "applyAnimation: transition animation is disabled or skipped. container=%s", - "level": "VERBOSE", - "group": "WM_DEBUG_APP_TRANSITIONS_ANIM", - "at": "com\/android\/server\/wm\/WindowContainer.java" - }, - "2363818604357955690": { - "message": "applyAnimation: transit=%s, enter=%b, wc=%s", - "level": "VERBOSE", - "group": "WM_DEBUG_APP_TRANSITIONS_ANIM", - "at": "com\/android\/server\/wm\/WindowContainer.java" - }, - "2262119454684034794": { - "message": "applyAnimation: container=%s", - "level": "VERBOSE", - "group": "WM_DEBUG_APP_TRANSITIONS_ANIM", - "at": "com\/android\/server\/wm\/WindowContainer.java" - }, - "5857165752965610762": { - "message": "Loading animation for app transition. transit=%s enter=%b frame=%s insets=%s surfaceInsets=%s", - "level": "DEBUG", - "group": "WM_DEBUG_APP_TRANSITIONS", - "at": "com\/android\/server\/wm\/WindowContainer.java" - }, - "9017113545720281233": { - "message": "Loaded animation %s for %s, duration: %d, stack=%s", - "level": "INFO", - "group": "WM_DEBUG_ANIM", - "at": "com\/android\/server\/wm\/WindowContainer.java" - }, - "5272307326252759722": { - "message": "onSyncFinishedDrawing %s", - "level": "VERBOSE", - "group": "WM_DEBUG_SYNC_ENGINE", - "at": "com\/android\/server\/wm\/WindowContainer.java" - }, - "-8311909671193661340": { - "message": "setSyncGroup #%d on %s", - "level": "VERBOSE", - "group": "WM_DEBUG_SYNC_ENGINE", - "at": "com\/android\/server\/wm\/WindowContainer.java" - }, - "-3871009616397322067": { - "message": "finishSync cancel=%b for %s", - "level": "VERBOSE", - "group": "WM_DEBUG_SYNC_ENGINE", - "at": "com\/android\/server\/wm\/WindowContainer.java" - }, - "-4267530270533009730": { - "message": "Error sending initial configuration change to WindowContainer overlay", - "level": "ERROR", - "group": "WM_DEBUG_ANIM", - "at": "com\/android\/server\/wm\/WindowContainer.java" - }, - "5179630990780610966": { - "message": "Error sending initial insets change to WindowContainer overlay", - "level": "ERROR", - "group": "WM_DEBUG_ANIM", - "at": "com\/android\/server\/wm\/WindowContainer.java" - }, - "-131600102855790053": { - "message": " THUMBNAIL %s: CREATE", - "level": "INFO", - "group": "WM_SHOW_TRANSACTIONS", - "at": "com\/android\/server\/wm\/WindowContainerThumbnail.java" - }, - "2163930285157267092": { - "message": "The listener does not exist.", - "level": "INFO", - "group": "WM_DEBUG_ADD_REMOVE", - "at": "com\/android\/server\/wm\/WindowContextListenerController.java" - }, - "6139364662459841509": { - "message": "Could not register window container listener token=%s, container=%s", - "level": "ERROR", - "group": "WM_ERROR", - "at": "com\/android\/server\/wm\/WindowContextListenerController.java" - }, - "3655576047584951173": { - "message": "Window Manager Crash %s", - "level": "WTF", - "group": "WM_ERROR", - "at": "com\/android\/server\/wm\/WindowManagerService.java" - }, - "-3029436704707366221": { - "message": "Attempted to add window with a client %s that is dead. Aborting.", - "level": "WARN", - "group": "WM_ERROR", - "at": "com\/android\/server\/wm\/WindowManagerService.java" - }, - "-1303710477998542095": { - "message": "Attempted to add window to a display that does not exist: %d. Aborting.", - "level": "WARN", - "group": "WM_ERROR", - "at": "com\/android\/server\/wm\/WindowManagerService.java" - }, - "8039410207325630747": { - "message": "Attempted to add window to a display for which the application does not have access: %d. Aborting.", - "level": "WARN", - "group": "WM_ERROR", - "at": "com\/android\/server\/wm\/WindowManagerService.java" - }, - "-3451016577701561221": { - "message": "Window %s is already added", - "level": "WARN", - "group": "WM_ERROR", - "at": "com\/android\/server\/wm\/WindowManagerService.java" - }, - "7245919222637411747": { - "message": "Attempted to add window with token that is not a window: %s. Aborting.", - "level": "WARN", - "group": "WM_ERROR", - "at": "com\/android\/server\/wm\/WindowManagerService.java" - }, - "-8579305050440451727": { - "message": "Attempted to add window with token that is a sub-window: %s. Aborting.", - "level": "WARN", - "group": "WM_ERROR", - "at": "com\/android\/server\/wm\/WindowManagerService.java" - }, - "-1075040941127814341": { - "message": "Attempted to add private presentation window to a non-private display. Aborting.", - "level": "WARN", - "group": "WM_ERROR", - "at": "com\/android\/server\/wm\/WindowManagerService.java" - }, - "7599690046549866326": { - "message": "Attempted to add presentation window to a non-suitable display. Aborting.", - "level": "WARN", - "group": "WM_ERROR", - "at": "com\/android\/server\/wm\/WindowManagerService.java" - }, - "-2546047231197102533": { - "message": "Trying to add window with invalid user=%d", - "level": "WARN", - "group": "WM_ERROR", - "at": "com\/android\/server\/wm\/WindowManagerService.java" - }, - "3713874359318494804": { - "message": "Attempted to add window with non-application token .%s Aborting.", - "level": "WARN", - "group": "WM_ERROR", - "at": "com\/android\/server\/wm\/WindowManagerService.java" - }, - "-6507147599943157469": { - "message": "Attempted to add window with exiting application token .%s Aborting.", - "level": "WARN", - "group": "WM_ERROR", - "at": "com\/android\/server\/wm\/WindowManagerService.java" - }, - "-1409483453189443362": { - "message": "Attempted to add starting window to token with already existing starting window", - "level": "WARN", - "group": "WM_ERROR", - "at": "com\/android\/server\/wm\/WindowManagerService.java" - }, - "-1806907994917883598": { - "message": "Attempted to add starting window to token but already cleaned", - "level": "WARN", - "group": "WM_ERROR", - "at": "com\/android\/server\/wm\/WindowManagerService.java" - }, - "-5450131464624918523": { - "message": "Attempted to add input method window with bad token %s. Aborting.", - "level": "WARN", - "group": "WM_ERROR", - "at": "com\/android\/server\/wm\/WindowManagerService.java" - }, - "-6484128707849211138": { - "message": "Attempted to add voice interaction window with bad token %s. Aborting.", - "level": "WARN", - "group": "WM_ERROR", - "at": "com\/android\/server\/wm\/WindowManagerService.java" - }, - "7768591536609704658": { - "message": "Attempted to add wallpaper window with bad token %s. Aborting.", - "level": "WARN", - "group": "WM_ERROR", - "at": "com\/android\/server\/wm\/WindowManagerService.java" - }, - "7497077135474110999": { - "message": "Attempted to add Accessibility overlay window with bad token %s. Aborting.", - "level": "WARN", - "group": "WM_ERROR", - "at": "com\/android\/server\/wm\/WindowManagerService.java" - }, - "8957851092580119204": { - "message": "Attempted to add a toast window with bad token %s. Aborting.", - "level": "WARN", - "group": "WM_ERROR", - "at": "com\/android\/server\/wm\/WindowManagerService.java" - }, - "-1945746969404688952": { - "message": "Attempted to add QS dialog window with bad token %s. Aborting.", - "level": "WARN", - "group": "WM_ERROR", - "at": "com\/android\/server\/wm\/WindowManagerService.java" - }, - "3419934373251134563": { - "message": "Non-null activity for system window of rootType=%d", - "level": "WARN", - "group": "WM_ERROR", - "at": "com\/android\/server\/wm\/WindowManagerService.java" - }, - "-1161056447389155729": { - "message": "Adding more than one toast window for UID at a time.", - "level": "WARN", - "group": "WM_ERROR", - "at": "com\/android\/server\/wm\/WindowManagerService.java" - }, - "-7518552252637236411": { - "message": "Window types in WindowContext and LayoutParams.type should match! Type from LayoutParams is %d, but type from WindowContext is %d", - "level": "WARN", - "group": "WM_ERROR", - "at": "com\/android\/server\/wm\/WindowManagerService.java" - }, - "-6055615852717459196": { - "message": "addWindow: %s startingWindow=%s", - "level": "VERBOSE", - "group": "WM_DEBUG_STARTING_WINDOW", - "at": "com\/android\/server\/wm\/WindowManagerService.java" - }, - "-2829980616540274784": { - "message": "addWindow: New client %s: window=%s Callers=%s", - "level": "VERBOSE", - "group": "WM_DEBUG_ADD_REMOVE", - "at": "com\/android\/server\/wm\/WindowManagerService.java" - }, - "-7315179333005789167": { - "message": "Attempted to add application window with unknown token %s. Aborting.", - "level": "WARN", - "group": "WM_ERROR", - "at": "com\/android\/server\/wm\/WindowManagerService.java" - }, - "-7547709658889961930": { - "message": "Attempted to add input method window with unknown token %s. Aborting.", - "level": "WARN", - "group": "WM_ERROR", - "at": "com\/android\/server\/wm\/WindowManagerService.java" - }, - "3009864422591182484": { - "message": "Attempted to add voice interaction window with unknown token %s. Aborting.", - "level": "WARN", - "group": "WM_ERROR", - "at": "com\/android\/server\/wm\/WindowManagerService.java" - }, - "-2639914438438144071": { - "message": "Attempted to add wallpaper window with unknown token %s. Aborting.", - "level": "WARN", - "group": "WM_ERROR", - "at": "com\/android\/server\/wm\/WindowManagerService.java" - }, - "-7529563697886120786": { - "message": "Attempted to add QS dialog window with unknown token %s. Aborting.", - "level": "WARN", - "group": "WM_ERROR", - "at": "com\/android\/server\/wm\/WindowManagerService.java" - }, - "4253401518117961686": { - "message": "Attempted to add Accessibility overlay window with unknown token %s. Aborting.", - "level": "WARN", - "group": "WM_ERROR", - "at": "com\/android\/server\/wm\/WindowManagerService.java" - }, - "5834230650841873680": { - "message": "Attempted to add a toast window with unknown token %s. Aborting.", - "level": "WARN", - "group": "WM_ERROR", - "at": "com\/android\/server\/wm\/WindowManagerService.java" - }, - "5265273548711408921": { - "message": "postWindowRemoveCleanupLocked: %s", - "level": "VERBOSE", - "group": "WM_DEBUG_ADD_REMOVE", - "at": "com\/android\/server\/wm\/WindowManagerService.java" - }, - "-3847568084407666790": { - "message": "Final remove of window: %s", - "level": "VERBOSE", - "group": "WM_DEBUG_WINDOW_MOVEMENT", - "at": "com\/android\/server\/wm\/WindowManagerService.java" - }, - "1419572818243106725": { - "message": "Removing %s from %s", - "level": "VERBOSE", - "group": "WM_DEBUG_ADD_REMOVE", - "at": "com\/android\/server\/wm\/WindowManagerService.java" - }, - "8312693933819247897": { - "message": "Relayout %s: oldVis=%d newVis=%d. %s", - "level": "INFO", - "group": "WM_DEBUG_SCREEN_ON", - "at": "com\/android\/server\/wm\/WindowManagerService.java" - }, - "8319702790708803735": { - "message": "Exception thrown when creating surface for client %s (%s). %s", - "level": "WARN", - "group": "WM_ERROR", - "at": "com\/android\/server\/wm\/WindowManagerService.java" - }, - "212929172223901460": { - "message": "Relayout of %s: focusMayChange=%b", - "level": "VERBOSE", - "group": "WM_DEBUG_FOCUS", - "at": "com\/android\/server\/wm\/WindowManagerService.java" - }, - "-255991894956556845": { - "message": "Set animatingExit: reason=startExitingAnimation\/%s win=%s", - "level": "DEBUG", - "group": "WM_DEBUG_ANIM", - "at": "com\/android\/server\/wm\/WindowManagerService.java" - }, - "6555160513135851764": { - "message": "OUT SURFACE %s: copied", - "level": "INFO", - "group": "WM_SHOW_TRANSACTIONS", - "at": "com\/android\/server\/wm\/WindowManagerService.java" - }, - "-196459205494031145": { - "message": "Failed to create surface control for %s", - "level": "WARN", - "group": "WM_ERROR", - "at": "com\/android\/server\/wm\/WindowManagerService.java" - }, - "-5512006943172316333": { - "message": "finishDrawingWindow: %s mDrawState=%s", - "level": "DEBUG", - "group": "WM_DEBUG_ADD_REMOVE", - "at": "com\/android\/server\/wm\/WindowManagerService.java" - }, - "-2577785761087081584": { - "message": "Permission Denial: %s from pid=%d, uid=%d requires %s", - "level": "WARN", - "group": "WM_ERROR", - "at": "com\/android\/server\/wm\/WindowManagerService.java" - }, - "4547566763172245740": { - "message": "addWindowToken: Attempted to add token: %s for non-exiting displayId=%d", - "level": "WARN", - "group": "WM_ERROR", - "at": "com\/android\/server\/wm\/WindowManagerService.java" - }, - "-972832559831959983": { - "message": "addWindowToken: Attempted to add binder token: %s for already created window token: %s displayId=%d", - "level": "WARN", - "group": "WM_ERROR", - "at": "com\/android\/server\/wm\/WindowManagerService.java" - }, - "8372202339190060748": { - "message": "attachWindowContextToDisplayArea: calling from non-existing process pid=%d uid=%d", - "level": "WARN", - "group": "WM_ERROR", - "at": "com\/android\/server\/wm\/WindowManagerService.java" - }, - "1904306629015452865": { - "message": "attachWindowContextToDisplayArea: trying to attach to a non-existing display:%d", - "level": "WARN", - "group": "WM_ERROR", - "at": "com\/android\/server\/wm\/WindowManagerService.java" - }, - "-6845859096032432107": { - "message": "attachWindowContextToDisplayContent: calling from non-existing process pid=%d uid=%d", - "level": "WARN", - "group": "WM_ERROR", - "at": "com\/android\/server\/wm\/WindowManagerService.java" - }, - "1473791807245791604": { - "message": "attachWindowContextToWindowToken: calling from non-existing process pid=%d uid=%d", - "level": "WARN", - "group": "WM_ERROR", - "at": "com\/android\/server\/wm\/WindowManagerService.java" - }, - "-2056866750160555704": { - "message": "Then token:%s is invalid. It might be removed", - "level": "WARN", - "group": "WM_ERROR", - "at": "com\/android\/server\/wm\/WindowManagerService.java" - }, - "-1045756671264607145": { - "message": "removeWindowToken: Attempted to remove token: %s for non-exiting displayId=%d", - "level": "WARN", - "group": "WM_ERROR", - "at": "com\/android\/server\/wm\/WindowManagerService.java" - }, - "874825105313641295": { - "message": "removeWindowToken: Attempted to remove non-existing token: %s", - "level": "WARN", - "group": "WM_ERROR", - "at": "com\/android\/server\/wm\/WindowManagerService.java" - }, - "5128669121055635771": { - "message": "moveWindowTokenToDisplay: Attempted to move token: %s to non-exiting displayId=%d", - "level": "WARN", - "group": "WM_ERROR", - "at": "com\/android\/server\/wm\/WindowManagerService.java" - }, - "6497954191906583839": { - "message": "moveWindowTokenToDisplay: Attempted to move non-existing token: %s", - "level": "WARN", - "group": "WM_ERROR", - "at": "com\/android\/server\/wm\/WindowManagerService.java" - }, - "2865882097969084039": { - "message": "moveWindowTokenToDisplay: Cannot move to the original display for token: %s", - "level": "WARN", - "group": "WM_ERROR", - "at": "com\/android\/server\/wm\/WindowManagerService.java" - }, - "-886583195545553099": { - "message": "Not moving display (displayId=%d) to top. Top focused displayId=%d. Reason: FLAG_STEAL_TOP_FOCUS_DISABLED", - "level": "INFO", - "group": "WM_DEBUG_FOCUS_LIGHT", - "at": "com\/android\/server\/wm\/WindowManagerService.java" - }, - "-1557387535886241553": { - "message": "enableScreenAfterBoot: mDisplayEnabled=%b mForceDisplayEnabled=%b mShowingBootMessages=%b mSystemBooted=%b. %s", - "level": "INFO", - "group": "WM_DEBUG_BOOT", - "at": "com\/android\/server\/wm\/WindowManagerService.java" - }, - "-6467850045030187736": { - "message": "enableScreenIfNeededLocked: mDisplayEnabled=%b mForceDisplayEnabled=%b mShowingBootMessages=%b mSystemBooted=%b. %s", - "level": "INFO", - "group": "WM_DEBUG_BOOT", - "at": "com\/android\/server\/wm\/WindowManagerService.java" - }, - "179762478329442868": { - "message": "***** BOOT TIMEOUT: forcing display enabled", - "level": "WARN", - "group": "WM_ERROR", - "at": "com\/android\/server\/wm\/WindowManagerService.java" - }, - "-3417569256875279779": { - "message": "performEnableScreen: mDisplayEnabled=%b mForceDisplayEnabled=%b mShowingBootMessages=%b mSystemBooted=%b. %s", - "level": "INFO", - "group": "WM_DEBUG_BOOT", - "at": "com\/android\/server\/wm\/WindowManagerService.java" - }, - "-7516915153725082358": { - "message": "performEnableScreen: Waited %dms for all windows to be drawn", - "level": "INFO", - "group": "WM_DEBUG_BOOT", - "at": "com\/android\/server\/wm\/WindowManagerService.java" - }, - "-1541244520024033685": { - "message": "performEnableScreen: Waiting for anim complete", - "level": "INFO", - "group": "WM_DEBUG_BOOT", - "at": "com\/android\/server\/wm\/WindowManagerService.java" - }, - "2670150656385758826": { - "message": "performEnableScreen: bootFinished() failed.", - "level": "WARN", - "group": "WM_ERROR", - "at": "com\/android\/server\/wm\/WindowManagerService.java" - }, - "530628508916855904": { - "message": "******************** ENABLING SCREEN!", - "level": "INFO", - "group": "WM_DEBUG_SCREEN_ON", - "at": "com\/android\/server\/wm\/WindowManagerService.java" - }, - "5477889324043875194": { - "message": "Notified TransitionController that the display is ready.", - "level": "VERBOSE", - "group": "WM_DEBUG_WINDOW_TRANSITIONS", - "at": "com\/android\/server\/wm\/WindowManagerService.java" - }, - "-2061779801633179448": { - "message": "checkBootAnimationComplete: Waiting for anim complete", - "level": "INFO", - "group": "WM_DEBUG_BOOT", - "at": "com\/android\/server\/wm\/WindowManagerService.java" - }, - "-8177456840019985809": { - "message": "checkBootAnimationComplete: Animation complete!", - "level": "INFO", - "group": "WM_DEBUG_BOOT", - "at": "com\/android\/server\/wm\/WindowManagerService.java" - }, - "-333924817004774456": { - "message": "showBootMessage: msg=%s always=%b mAllowBootMessages=%b mShowingBootMessages=%b mSystemBooted=%b. %s", - "level": "INFO", - "group": "WM_DEBUG_BOOT", - "at": "com\/android\/server\/wm\/WindowManagerService.java" - }, - "2994810644159608200": { - "message": "hideBootMessagesLocked: mDisplayEnabled=%b mForceDisplayEnabled=%b mShowingBootMessages=%b mSystemBooted=%b. %s", - "level": "INFO", - "group": "WM_DEBUG_BOOT", - "at": "com\/android\/server\/wm\/WindowManagerService.java" - }, - "-6625203651195752178": { - "message": "freezeDisplayRotation: current rotation=%d, new rotation=%d, caller=%s", - "level": "VERBOSE", - "group": "WM_DEBUG_ORIENTATION", - "at": "com\/android\/server\/wm\/WindowManagerService.java" - }, - "8988910478484254861": { - "message": "thawRotation: mRotation=%d, caller=%s", - "level": "VERBOSE", - "group": "WM_DEBUG_ORIENTATION", - "at": "com\/android\/server\/wm\/WindowManagerService.java" - }, - "7261084872394224738": { - "message": "updateRotationUnchecked: alwaysSendConfiguration=%b forceRelayout=%b", - "level": "VERBOSE", - "group": "WM_DEBUG_ORIENTATION", - "at": "com\/android\/server\/wm\/WindowManagerService.java" - }, - "8664813170125714536": { - "message": "View server did not start", - "level": "WARN", - "group": "WM_ERROR", - "at": "com\/android\/server\/wm\/WindowManagerService.java" - }, - "-8019372496359375449": { - "message": "Could not send command %s with parameters %s. %s", - "level": "WARN", - "group": "WM_ERROR", - "at": "com\/android\/server\/wm\/WindowManagerService.java" - }, - "1893303527772009363": { - "message": "Devices still not ready after waiting %d milliseconds before attempting to detect safe mode.", - "level": "WARN", - "group": "WM_ERROR", - "at": "com\/android\/server\/wm\/WindowManagerService.java" - }, - "-3652974372240081071": { - "message": "SAFE MODE ENABLED (menu=%d s=%d dpad=%d trackball=%d)", - "level": "INFO", - "group": "WM_ERROR", - "at": "com\/android\/server\/wm\/WindowManagerService.java" - }, - "4945624619344146947": { - "message": "SAFE MODE not enabled", - "level": "INFO", - "group": "WM_ERROR", - "at": "com\/android\/server\/wm\/WindowManagerService.java" - }, - "-3428027271337724889": { - "message": "Focus changing: %s -> %s", - "level": "INFO", - "group": "WM_DEBUG_FOCUS_LIGHT", - "at": "com\/android\/server\/wm\/WindowManagerService.java" - }, - "1624328195833150047": { - "message": "App freeze timeout expired.", - "level": "WARN", - "group": "WM_ERROR", - "at": "com\/android\/server\/wm\/WindowManagerService.java" - }, - "5830724144971462783": { - "message": "Timeout waiting for drawn: undrawn=%s", - "level": "WARN", - "group": "WM_ERROR", - "at": "com\/android\/server\/wm\/WindowManagerService.java" - }, - "-2240705227895260140": { - "message": "CHECK_IF_BOOT_ANIMATION_FINISHED:", - "level": "INFO", - "group": "WM_DEBUG_BOOT", - "at": "com\/android\/server\/wm\/WindowManagerService.java" - }, - "8641557333789260779": { - "message": "FORCED DISPLAY SIZE: %dx%d", - "level": "INFO", - "group": "WM_ERROR", - "at": "com\/android\/server\/wm\/WindowManagerService.java" - }, - "3781141652793604337": { - "message": "FORCED DISPLAY SCALING DISABLED", - "level": "INFO", - "group": "WM_ERROR", - "at": "com\/android\/server\/wm\/WindowManagerService.java" - }, - "4117606810523219596": { - "message": "Failed looking up window session=%s callers=%s", - "level": "WARN", - "group": "WM_ERROR", - "at": "com\/android\/server\/wm\/WindowManagerService.java" - }, - "1233670725456443473": { - "message": "Changing surface while display frozen: %s", - "level": "VERBOSE", - "group": "WM_DEBUG_ORIENTATION", - "at": "com\/android\/server\/wm\/WindowManagerService.java" - }, - "-1716033239040181528": { - "message": "Waiting for drawn %s: removed=%b visible=%b mHasSurface=%b drawState=%d", - "level": "INFO", - "group": "WM_DEBUG_SCREEN_ON", - "at": "com\/android\/server\/wm\/WindowManagerService.java" - }, - "-4609828204247499633": { - "message": "Aborted waiting for drawn: %s", - "level": "WARN", - "group": "WM_DEBUG_SCREEN_ON", - "at": "com\/android\/server\/wm\/WindowManagerService.java" - }, - "-7561054602203220590": { - "message": "Window drawn win=%s", - "level": "DEBUG", - "group": "WM_DEBUG_SCREEN_ON", - "at": "com\/android\/server\/wm\/WindowManagerService.java" - }, - "2809030008663191766": { - "message": "All windows drawn!", - "level": "DEBUG", - "group": "WM_DEBUG_SCREEN_ON", - "at": "com\/android\/server\/wm\/WindowManagerService.java" - }, - "-1615905649072328410": { - "message": "startFreezingDisplayLocked: exitAnim=%d enterAnim=%d called by %s", - "level": "DEBUG", - "group": "WM_DEBUG_ORIENTATION", - "at": "com\/android\/server\/wm\/WindowManagerService.java" - }, - "4565793239453546297": { - "message": "stopFreezingDisplayLocked: Returning waitingForConfig=%b, waitingForRemoteDisplayChange=%b, mAppsFreezingScreen=%d, mWindowsFreezingScreen=%d, mClientFreezingScreen=%b, mOpeningApps.size()=%d", - "level": "DEBUG", - "group": "WM_DEBUG_ORIENTATION", - "at": "com\/android\/server\/wm\/WindowManagerService.java" - }, - "-6877112251967196129": { - "message": "stopFreezingDisplayLocked: Unfreezing now", - "level": "DEBUG", - "group": "WM_DEBUG_ORIENTATION", - "at": "com\/android\/server\/wm\/WindowManagerService.java" - }, - "721393258715103117": { - "message": "%s", - "level": "INFO", - "group": "WM_ERROR", - "at": "com\/android\/server\/wm\/WindowManagerService.java" - }, - "-5706083447992207254": { - "message": "**** Dismissing screen rotation animation", - "level": "INFO", - "group": "WM_DEBUG_ORIENTATION", - "at": "com\/android\/server\/wm\/WindowManagerService.java" - }, - "2233371241933584073": { - "message": "Performing post-rotate rotation", - "level": "DEBUG", - "group": "WM_DEBUG_ORIENTATION", - "at": "com\/android\/server\/wm\/WindowManagerService.java" - }, - "1010635158502326025": { - "message": "unable to call receiver for empty keyboard shortcuts", - "level": "ERROR", - "group": "WM_ERROR", - "at": "com\/android\/server\/wm\/WindowManagerService.java" - }, - "1278715281433572858": { - "message": "Bad requesting window %s", - "level": "WARN", - "group": "WM_ERROR", - "at": "com\/android\/server\/wm\/WindowManagerService.java" - }, - "-6186782212018913664": { - "message": "Invalid displayId for requestScrollCapture: %d", - "level": "ERROR", - "group": "WM_ERROR", - "at": "com\/android\/server\/wm\/WindowManagerService.java" - }, - "51378282333944649": { - "message": "requestScrollCapture: caught exception dispatching to window.token=%s", - "level": "WARN", - "group": "WM_ERROR", - "at": "com\/android\/server\/wm\/WindowManagerService.java" - }, - "-8972916676375201577": { - "message": "requestScrollCapture: caught exception dispatching callback: %s", - "level": "WARN", - "group": "WM_ERROR", - "at": "com\/android\/server\/wm\/WindowManagerService.java" - }, - "-1875125162673622728": { - "message": "Attempted to get windowing mode of a display that does not exist: %d", - "level": "WARN", - "group": "WM_ERROR", - "at": "com\/android\/server\/wm\/WindowManagerService.java" - }, - "3938331948687900219": { - "message": "Attempted to set windowing mode to a display that does not exist: %d", - "level": "WARN", - "group": "WM_ERROR", - "at": "com\/android\/server\/wm\/WindowManagerService.java" - }, - "4200292050699107329": { - "message": "Attempted to get remove mode of a display that does not exist: %d", - "level": "WARN", - "group": "WM_ERROR", - "at": "com\/android\/server\/wm\/WindowManagerService.java" - }, - "-5574580669790275797": { - "message": "Attempted to set remove mode to a display that does not exist: %d", - "level": "WARN", - "group": "WM_ERROR", - "at": "com\/android\/server\/wm\/WindowManagerService.java" - }, - "525945815055875796": { - "message": "Attempted to get flag of a display that does not exist: %d", - "level": "WARN", - "group": "WM_ERROR", - "at": "com\/android\/server\/wm\/WindowManagerService.java" - }, - "8186524992939307511": { - "message": "Attempted to set flag to a display that does not exist: %d", - "level": "WARN", - "group": "WM_ERROR", - "at": "com\/android\/server\/wm\/WindowManagerService.java" - }, - "-600035824255550632": { - "message": "Attempted to get system decors flag of a display that does not exist: %d", - "level": "WARN", - "group": "WM_ERROR", - "at": "com\/android\/server\/wm\/WindowManagerService.java" - }, - "3056518663346732662": { - "message": "Attempted to set system decors flag to a display that does not exist: %d", - "level": "WARN", - "group": "WM_ERROR", - "at": "com\/android\/server\/wm\/WindowManagerService.java" - }, - "5177195624625618567": { - "message": "Attempted to get IME policy of a display that does not exist: %d", - "level": "WARN", - "group": "WM_ERROR", - "at": "com\/android\/server\/wm\/WindowManagerService.java" - }, - "3932627933834459400": { - "message": "Attempted to set IME policy to a display that does not exist: %d", - "level": "WARN", - "group": "WM_ERROR", - "at": "com\/android\/server\/wm\/WindowManagerService.java" - }, - "5770211341769258866": { - "message": "setWallpaperShowWhenLocked: non-existent wallpaper token: %s", - "level": "WARN", - "group": "WM_ERROR", - "at": "com\/android\/server\/wm\/WindowManagerService.java" - }, - "698926505694016512": { - "message": "setWallpaperCropHints: non-existent wallpaper token: %s", - "level": "WARN", - "group": "WM_ERROR", - "at": "com\/android\/server\/wm\/WindowManagerService.java" - }, - "-7428028317216329062": { - "message": "hideIme target: %s ", - "level": "DEBUG", - "group": "WM_DEBUG_IME", - "at": "com\/android\/server\/wm\/WindowManagerService.java" - }, - "1006302987953651112": { - "message": "hideIme Control target: %s ", - "level": "DEBUG", - "group": "WM_DEBUG_IME", - "at": "com\/android\/server\/wm\/WindowManagerService.java" - }, - "5213970642134448962": { - "message": "Attempted to get home support flag of a display that does not exist: %d", - "level": "WARN", - "group": "WM_ERROR", - "at": "com\/android\/server\/wm\/WindowManagerService.java" - }, - "-2065144681579661392": { - "message": "onPointerDownOutsideFocusLocked called on %s", - "level": "INFO", - "group": "WM_DEBUG_FOCUS_LIGHT", - "at": "com\/android\/server\/wm\/WindowManagerService.java" - }, - "-7394143854567081754": { - "message": "grantEmbeddedWindowFocus win=%s dropped focus so setting focus to null since no candidate was found", - "level": "VERBOSE", - "group": "WM_DEBUG_FOCUS", - "at": "com\/android\/server\/wm\/WindowManagerService.java" - }, - "-6056928081282320632": { - "message": "grantEmbeddedWindowFocus win=%s grantFocus=%s", - "level": "VERBOSE", - "group": "WM_DEBUG_FOCUS", - "at": "com\/android\/server\/wm\/WindowManagerService.java" - }, - "6110791601270766802": { - "message": "TaskFragmentTransaction changes are not collected in transition because there is an ongoing sync for applySyncTransaction().", - "level": "WARN", - "group": "WM_DEBUG_WINDOW_TRANSITIONS", - "at": "com\/android\/server\/wm\/WindowOrganizerController.java" - }, - "9200403125156001641": { - "message": "Apply window transaction, syncId=%d", - "level": "VERBOSE", - "group": "WM_DEBUG_WINDOW_ORGANIZER", - "at": "com\/android\/server\/wm\/WindowOrganizerController.java" - }, - "433446585990132440": { - "message": "Set sync ready, syncId=%d", - "level": "VERBOSE", - "group": "WM_DEBUG_WINDOW_ORGANIZER", - "at": "com\/android\/server\/wm\/WindowOrganizerController.java" - }, - "6552038620140878489": { - "message": "Transaction ready, syncId=%d", - "level": "VERBOSE", - "group": "WM_DEBUG_WINDOW_ORGANIZER", - "at": "com\/android\/server\/wm\/WindowOrganizerController.java" - }, - "-4629255026637000251": { - "message": "Sending to proc %s new config %s", - "level": "VERBOSE", - "group": "WM_DEBUG_CONFIGURATION", - "at": "com\/android\/server\/wm\/WindowProcessController.java" - }, - "-7237767461056267619": { - "message": "%s: Setting back callback %s", - "level": "DEBUG", - "group": "WM_DEBUG_BACK_PREVIEW", - "at": "com\/android\/server\/wm\/WindowState.java" - }, - "8135615413833185273": { - "message": "Adding %s to %s", - "level": "VERBOSE", - "group": "WM_DEBUG_ADD_REMOVE", - "at": "com\/android\/server\/wm\/WindowState.java" - }, - "8842744325264128950": { - "message": "Resize reasons for w=%s: %s configChanged=%b didFrameInsetsChange=%b", - "level": "VERBOSE", - "group": "WM_DEBUG_RESIZE", - "at": "com\/android\/server\/wm\/WindowState.java" - }, - "-8636590597069784069": { - "message": "Resizing window %s", - "level": "VERBOSE", - "group": "WM_DEBUG_RESIZE", - "at": "com\/android\/server\/wm\/WindowState.java" - }, - "-2710188685736986208": { - "message": "Orientation not waiting for draw in %s, surfaceController %s", - "level": "VERBOSE", - "group": "WM_DEBUG_ORIENTATION", - "at": "com\/android\/server\/wm\/WindowState.java" - }, - "5236278969232209904": { - "message": "onMovedByResize: Moving %s", - "level": "DEBUG", - "group": "WM_DEBUG_RESIZE", - "at": "com\/android\/server\/wm\/WindowState.java" - }, - "7646042751617940718": { - "message": "Set animatingExit: reason=onAppVisibilityChanged win=%s", - "level": "DEBUG", - "group": "WM_DEBUG_ANIM", - "at": "com\/android\/server\/wm\/WindowState.java" - }, - "1783521309242112490": { - "message": "onResize: Resizing %s", - "level": "DEBUG", - "group": "WM_DEBUG_RESIZE", - "at": "com\/android\/server\/wm\/WindowState.java" - }, - "1351053513466395411": { - "message": "WS.removeImmediately: %s Already removed...", - "level": "VERBOSE", - "group": "WM_DEBUG_ADD_REMOVE", - "at": "com\/android\/server\/wm\/WindowState.java" - }, - "3927343382258792268": { - "message": "removeIfPossible: %s callers=%s", - "level": "VERBOSE", - "group": "WM_DEBUG_ADD_REMOVE", - "at": "com\/android\/server\/wm\/WindowState.java" - }, - "-4831815184899821371": { - "message": "Starting window removed %s", - "level": "DEBUG", - "group": "WM_DEBUG_STARTING_WINDOW", - "at": "com\/android\/server\/wm\/WindowState.java" - }, - "-5803097884846965819": { - "message": "Remove client=%x, surfaceController=%s Callers=%s", - "level": "VERBOSE", - "group": "WM_DEBUG_FOCUS", - "at": "com\/android\/server\/wm\/WindowState.java" - }, - "-2547748024041128829": { - "message": "Remove %s: mSurfaceController=%s mAnimatingExit=%b mRemoveOnExit=%b mHasSurface=%b surfaceShowing=%b animating=%b app-animation=%b mDisplayFrozen=%b callers=%s", - "level": "VERBOSE", - "group": "WM_DEBUG_APP_TRANSITIONS", - "at": "com\/android\/server\/wm\/WindowState.java" - }, - "7789778354950913237": { - "message": "Set animatingExit: reason=remove\/applyAnimation win=%s", - "level": "VERBOSE", - "group": "WM_DEBUG_ANIM", - "at": "com\/android\/server\/wm\/WindowState.java" - }, - "-4143841388126586338": { - "message": "Not removing %s due to exit animation", - "level": "VERBOSE", - "group": "WM_DEBUG_ADD_REMOVE", - "at": "com\/android\/server\/wm\/WindowState.java" - }, - "4419190702135590390": { - "message": "Set animatingExit: reason=remove\/isAnimating win=%s", - "level": "VERBOSE", - "group": "WM_DEBUG_ANIM", - "at": "com\/android\/server\/wm\/WindowState.java" - }, - "-6167820560758523840": { - "message": "setAnimationLocked: setting mFocusMayChange true", - "level": "INFO", - "group": "WM_DEBUG_FOCUS_LIGHT", - "at": "com\/android\/server\/wm\/WindowState.java" - }, - "-208079497999140637": { - "message": "WindowState.hideLw: setting mFocusMayChange true", - "level": "INFO", - "group": "WM_DEBUG_FOCUS_LIGHT", - "at": "com\/android\/server\/wm\/WindowState.java" - }, - "8812513438749898553": { - "message": "set mOrientationChanging of %s", - "level": "VERBOSE", - "group": "WM_DEBUG_ORIENTATION", - "at": "com\/android\/server\/wm\/WindowState.java" - }, - "-2964267636425934067": { - "message": "win=%s destroySurfaces: appStopped=%b win.mWindowRemovalAllowed=%b win.mRemoveOnExit=%b", - "level": "ERROR", - "group": "WM_DEBUG_ADD_REMOVE", - "at": "com\/android\/server\/wm\/WindowState.java" - }, - "7336961102428192483": { - "message": "Clear animatingExit: reason=destroySurface win=%s", - "level": "DEBUG", - "group": "WM_DEBUG_ANIM", - "at": "com\/android\/server\/wm\/WindowState.java" - }, - "-6920306331987525705": { - "message": "Reporting new frame to %s: %s", - "level": "VERBOSE", - "group": "WM_DEBUG_RESIZE", - "at": "com\/android\/server\/wm\/WindowState.java" - }, - "2714651498627020992": { - "message": "Resizing %s WITH DRAW PENDING", - "level": "INFO", - "group": "WM_DEBUG_ORIENTATION", - "at": "com\/android\/server\/wm\/WindowState.java" - }, - "-5755338358883139945": { - "message": "Requested redraw for orientation change: %s", - "level": "VERBOSE", - "group": "WM_DEBUG_ORIENTATION", - "at": "com\/android\/server\/wm\/WindowState.java" - }, - "-5211036212243647844": { - "message": "notifyInsetsChanged for %s ", - "level": "DEBUG", - "group": "WM_DEBUG_WINDOW_INSETS", - "at": "com\/android\/server\/wm\/WindowState.java" - }, - "-3186229270467822891": { - "message": "notifyInsetsControlChanged for %s ", - "level": "DEBUG", - "group": "WM_DEBUG_WINDOW_INSETS", - "at": "com\/android\/server\/wm\/WindowState.java" - }, - "-7413136364930452718": { - "message": "performShowLocked: mDrawState=HAS_DRAWN in %s", - "level": "VERBOSE", - "group": "WM_DEBUG_ANIM", - "at": "com\/android\/server\/wm\/WindowState.java" - }, - "7624470121297688739": { - "message": "shouldWaitAnimatingExit: isTransition: %s", - "level": "DEBUG", - "group": "WM_DEBUG_APP_TRANSITIONS", - "at": "com\/android\/server\/wm\/WindowState.java" - }, - "810267895099109466": { - "message": "shouldWaitAnimatingExit: isAnimating: %s", - "level": "DEBUG", - "group": "WM_DEBUG_APP_TRANSITIONS", - "at": "com\/android\/server\/wm\/WindowState.java" - }, - "-1760879391350377377": { - "message": "shouldWaitAnimatingExit: isWallpaperTarget: %s", - "level": "DEBUG", - "group": "WM_DEBUG_APP_TRANSITIONS", - "at": "com\/android\/server\/wm\/WindowState.java" - }, - "272960397873328729": { - "message": "Clear window stuck on animatingExit status: %s", - "level": "WARN", - "group": "WM_DEBUG_APP_TRANSITIONS", - "at": "com\/android\/server\/wm\/WindowState.java" - }, - "-1007526574020149845": { - "message": "onExitAnimationDone in %s: exiting=%b remove=%b selfAnimating=%b anim=%s", - "level": "VERBOSE", - "group": "WM_DEBUG_ANIM", - "at": "com\/android\/server\/wm\/WindowState.java" - }, - "1738645946553610841": { - "message": "Exit animation finished in %s: remove=%b", - "level": "VERBOSE", - "group": "WM_DEBUG_ADD_REMOVE", - "at": "com\/android\/server\/wm\/WindowState.java" - }, - "-7737516306844862315": { - "message": "Clear animatingExit: reason=exitAnimationDone win=%s", - "level": "DEBUG", - "group": "WM_DEBUG_ANIM", - "at": "com\/android\/server\/wm\/WindowState.java" - }, - "-3153130647145726082": { - "message": "Clear animatingExit: reason=clearAnimatingFlags win=%s", - "level": "DEBUG", - "group": "WM_DEBUG_ANIM", - "at": "com\/android\/server\/wm\/WindowState.java" - }, - "-5202247309108694583": { - "message": "Clear animatingExit: reason=relayoutVisibleWindow win=%s", - "level": "DEBUG", - "group": "WM_DEBUG_ANIM", - "at": "com\/android\/server\/wm\/WindowState.java" - }, - "6291563604478341956": { - "message": "Setting move animation on %s", - "level": "VERBOSE", - "group": "WM_DEBUG_ANIM", - "at": "com\/android\/server\/wm\/WindowState.java" - }, - "-5774445199273871848": { - "message": "Preparing to sync a window that was already in the sync, so try dropping buffer. win=%s", - "level": "DEBUG", - "group": "WM_DEBUG_SYNC_ENGINE", - "at": "com\/android\/server\/wm\/WindowState.java" - }, - "8097934579596343476": { - "message": "Got a buffer for request id=%d but latest request is id=%d. Since the buffer is out-of-date, drop it. win=%s", - "level": "DEBUG", - "group": "WM_DEBUG_SYNC_ENGINE", - "at": "com\/android\/server\/wm\/WindowState.java" - }, - "8269653477215188641": { - "message": "SURFACE isSecure=%b: %s", - "level": "INFO", - "group": "WM_SHOW_TRANSACTIONS", - "at": "com\/android\/server\/wm\/WindowState.java" - }, - "-1495677286613044867": { - "message": "Animation done in %s: exiting=%b, reportedVisible=%b", - "level": "VERBOSE", - "group": "WM_DEBUG_ANIM", - "at": "com\/android\/server\/wm\/WindowStateAnimator.java" - }, - "3436877176443058520": { - "message": "Finishing drawing window %s: mDrawState=%s", - "level": "VERBOSE", - "group": "WM_DEBUG_STARTING_WINDOW", - "at": "com\/android\/server\/wm\/WindowStateAnimator.java" - }, - "345647873457403698": { - "message": "finishDrawingLocked: mDrawState=COMMIT_DRAW_PENDING %s in %s", - "level": "VERBOSE", - "group": "WM_DEBUG_DRAW", - "at": "com\/android\/server\/wm\/WindowStateAnimator.java" - }, - "-2385558637577093121": { - "message": "Draw state now committed in %s", - "level": "VERBOSE", - "group": "WM_DEBUG_STARTING_WINDOW", - "at": "com\/android\/server\/wm\/WindowStateAnimator.java" - }, - "-3490933626936411542": { - "message": "commitFinishDrawingLocked: mDrawState=READY_TO_SHOW %s", - "level": "INFO", - "group": "WM_DEBUG_ANIM", - "at": "com\/android\/server\/wm\/WindowStateAnimator.java" - }, - "-6088246515441976339": { - "message": "createSurface %s: mDrawState=DRAW_PENDING", - "level": "INFO", - "group": "WM_DEBUG_ANIM", - "at": "com\/android\/server\/wm\/WindowStateAnimator.java" - }, - "2353125758087345363": { - "message": " CREATE SURFACE %s IN SESSION %s: pid=%d format=%d flags=0x%x \/ %s", - "level": "INFO", - "group": "WM_SHOW_SURFACE_ALLOC", - "at": "com\/android\/server\/wm\/WindowStateAnimator.java" - }, - "-4491856282178275074": { - "message": "SURFACE DESTROY: %s. %s", - "level": "INFO", - "group": "WM_SHOW_SURFACE_ALLOC", - "at": "com\/android\/server\/wm\/WindowStateAnimator.java" - }, - "8602950884833508970": { - "message": "Orientation change skips hidden %s", - "level": "VERBOSE", - "group": "WM_DEBUG_ORIENTATION", - "at": "com\/android\/server\/wm\/WindowStateAnimator.java" - }, - "-5079712802591263622": { - "message": "SURFACE controller=%s alpha=%f HScale=%f, VScale=%f: %s", - "level": "INFO", - "group": "WM_SHOW_TRANSACTIONS", - "at": "com\/android\/server\/wm\/WindowStateAnimator.java" - }, - "-2824875917893878016": { - "message": "Orientation continue waiting for draw in %s", - "level": "VERBOSE", - "group": "WM_DEBUG_ORIENTATION", - "at": "com\/android\/server\/wm\/WindowStateAnimator.java" - }, - "7457181879495900576": { - "message": "Orientation change complete in %s", - "level": "VERBOSE", - "group": "WM_DEBUG_ORIENTATION", - "at": "com\/android\/server\/wm\/WindowStateAnimator.java" - }, - "-5668794009329913533": { - "message": "applyAnimation: win=%s anim=%d attr=0x%x a=%s transit=%d type=%d isEntrance=%b Callers %s", - "level": "VERBOSE", - "group": "WM_DEBUG_ANIM", - "at": "com\/android\/server\/wm\/WindowStateAnimator.java" - }, - "-2055407587764455051": { - "message": "SURFACE HIDE ( %s ): %s", - "level": "INFO", - "group": "WM_SHOW_TRANSACTIONS", - "at": "com\/android\/server\/wm\/WindowSurfaceController.java" - }, - "-5854683348829455340": { - "message": "Destroying surface %s called by %s", - "level": "INFO", - "group": "WM_SHOW_SURFACE_ALLOC", - "at": "com\/android\/server\/wm\/WindowSurfaceController.java" - }, - "7813672046338784579": { - "message": "SURFACE isOpaque=%b: %s", - "level": "INFO", - "group": "WM_SHOW_TRANSACTIONS", - "at": "com\/android\/server\/wm\/WindowSurfaceController.java" - }, - "-8864150640874799238": { - "message": "SURFACE isColorSpaceAgnostic=%b: %s", - "level": "INFO", - "group": "WM_SHOW_TRANSACTIONS", - "at": "com\/android\/server\/wm\/WindowSurfaceController.java" - }, - "-8398940245851553814": { - "message": "SURFACE SHOW (performLayout): %s", - "level": "INFO", - "group": "WM_SHOW_TRANSACTIONS", - "at": "com\/android\/server\/wm\/WindowSurfaceController.java" - }, - "8174298531248485625": { - "message": "removeAllWindowsIfPossible: removing win=%s", - "level": "WARN", - "group": "WM_DEBUG_WINDOW_MOVEMENT", - "at": "com\/android\/server\/wm\/WindowToken.java" - }, - "2740931087734487464": { - "message": "addWindow: win=%s Callers=%s", - "level": "DEBUG", - "group": "WM_DEBUG_FOCUS", - "at": "com\/android\/server\/wm\/WindowToken.java" - }, - "2382798629637143561": { - "message": "Adding %s to %s", - "level": "VERBOSE", - "group": "WM_DEBUG_ADD_REMOVE", - "at": "com\/android\/server\/wm\/WindowToken.java" - }, - "-7314975896738778749": { - "message": "setClientVisible: %s clientVisible=%b Callers=%s", - "level": "VERBOSE", - "group": "WM_DEBUG_APP_TRANSITIONS", - "at": "com\/android\/server\/wm\/WindowToken.java" - } - }, - "groups": { - "WM_DEBUG_ADD_REMOVE": { - "tag": "WindowManager" - }, - "WM_DEBUG_ANIM": { - "tag": "WindowManager" - }, - "WM_DEBUG_APP_TRANSITIONS": { - "tag": "WindowManager" - }, - "WM_DEBUG_APP_TRANSITIONS_ANIM": { - "tag": "WindowManager" - }, - "WM_DEBUG_BACK_PREVIEW": { - "tag": "CoreBackPreview" - }, - "WM_DEBUG_BOOT": { - "tag": "WindowManager" - }, - "WM_DEBUG_CONFIGURATION": { - "tag": "WindowManager" - }, - "WM_DEBUG_CONTAINERS": { - "tag": "WindowManager" - }, - "WM_DEBUG_CONTENT_RECORDING": { - "tag": "WindowManager" - }, - "WM_DEBUG_DIMMER": { - "tag": "WindowManager" - }, - "WM_DEBUG_DRAW": { - "tag": "WindowManager" - }, - "WM_DEBUG_DREAM": { - "tag": "WindowManager" - }, - "WM_DEBUG_EMBEDDED_WINDOWS": { - "tag": "WindowManager" - }, - "WM_DEBUG_FOCUS": { - "tag": "WindowManager" - }, - "WM_DEBUG_FOCUS_LIGHT": { - "tag": "WindowManager" - }, - "WM_DEBUG_IME": { - "tag": "WindowManager" - }, - "WM_DEBUG_IMMERSIVE": { - "tag": "WindowManager" - }, - "WM_DEBUG_KEEP_SCREEN_ON": { - "tag": "WindowManager" - }, - "WM_DEBUG_LOCKTASK": { - "tag": "WindowManager" - }, - "WM_DEBUG_ORIENTATION": { - "tag": "WindowManager" - }, - "WM_DEBUG_RECENTS_ANIMATIONS": { - "tag": "WindowManager" - }, - "WM_DEBUG_REMOTE_ANIMATIONS": { - "tag": "WindowManager" - }, - "WM_DEBUG_RESIZE": { - "tag": "WindowManager" - }, - "WM_DEBUG_SCREEN_ON": { - "tag": "WindowManager" - }, - "WM_DEBUG_STARTING_WINDOW": { - "tag": "WindowManager" - }, - "WM_DEBUG_STATES": { - "tag": "WindowManager" - }, - "WM_DEBUG_SWITCH": { - "tag": "WindowManager" - }, - "WM_DEBUG_SYNC_ENGINE": { - "tag": "WindowManager" - }, - "WM_DEBUG_TASKS": { - "tag": "WindowManager" - }, - "WM_DEBUG_TPL": { - "tag": "WindowManager" - }, - "WM_DEBUG_WALLPAPER": { - "tag": "WindowManager" - }, - "WM_DEBUG_WINDOW_INSETS": { - "tag": "WindowManager" - }, - "WM_DEBUG_WINDOW_MOVEMENT": { - "tag": "WindowManager" - }, - "WM_DEBUG_WINDOW_ORGANIZER": { - "tag": "WindowManager" - }, - "WM_DEBUG_WINDOW_TRANSITIONS": { - "tag": "WindowManager" - }, - "WM_DEBUG_WINDOW_TRANSITIONS_MIN": { - "tag": "WindowManager" - }, - "WM_ERROR": { - "tag": "WindowManager" - }, - "WM_SHOW_SURFACE_ALLOC": { - "tag": "WindowManager" - }, - "WM_SHOW_TRANSACTIONS": { - "tag": "WindowManager" - } - } -} diff --git a/data/sounds/alarms/material/ogg/Awaken_OG7_1ch_48k.ogg b/data/sounds/alarms/material/ogg/Awaken_OG7_1ch_48k.ogg Binary files differindex 47b1d346a682..47b1d346a682 100755..100644 --- a/data/sounds/alarms/material/ogg/Awaken_OG7_1ch_48k.ogg +++ b/data/sounds/alarms/material/ogg/Awaken_OG7_1ch_48k.ogg diff --git a/data/sounds/alarms/material/ogg/Bounce_OG7_1ch_48k.ogg b/data/sounds/alarms/material/ogg/Bounce_OG7_1ch_48k.ogg Binary files differindex 3d1f8ea2a8db..3d1f8ea2a8db 100755..100644 --- a/data/sounds/alarms/material/ogg/Bounce_OG7_1ch_48k.ogg +++ b/data/sounds/alarms/material/ogg/Bounce_OG7_1ch_48k.ogg diff --git a/data/sounds/alarms/material/ogg/Drip_OG7_1ch_48k.ogg b/data/sounds/alarms/material/ogg/Drip_OG7_1ch_48k.ogg Binary files differindex de3eec256c7b..de3eec256c7b 100755..100644 --- a/data/sounds/alarms/material/ogg/Drip_OG7_1ch_48k.ogg +++ b/data/sounds/alarms/material/ogg/Drip_OG7_1ch_48k.ogg diff --git a/data/sounds/alarms/material/ogg/Gallop_OG7_1ch_48k.ogg b/data/sounds/alarms/material/ogg/Gallop_OG7_1ch_48k.ogg Binary files differindex 98549b37d38b..98549b37d38b 100755..100644 --- a/data/sounds/alarms/material/ogg/Gallop_OG7_1ch_48k.ogg +++ b/data/sounds/alarms/material/ogg/Gallop_OG7_1ch_48k.ogg diff --git a/data/sounds/alarms/material/ogg/Nudge_OG7_1ch_48k.ogg b/data/sounds/alarms/material/ogg/Nudge_OG7_1ch_48k.ogg Binary files differindex 7a3d7ed474c6..7a3d7ed474c6 100755..100644 --- a/data/sounds/alarms/material/ogg/Nudge_OG7_1ch_48k.ogg +++ b/data/sounds/alarms/material/ogg/Nudge_OG7_1ch_48k.ogg diff --git a/data/sounds/alarms/material/ogg/Orbit_OG7_1ch_48k.ogg b/data/sounds/alarms/material/ogg/Orbit_OG7_1ch_48k.ogg Binary files differindex 52e3918326ed..52e3918326ed 100755..100644 --- a/data/sounds/alarms/material/ogg/Orbit_OG7_1ch_48k.ogg +++ b/data/sounds/alarms/material/ogg/Orbit_OG7_1ch_48k.ogg diff --git a/data/sounds/alarms/material/ogg/Rise_OG7_1ch_48k.ogg b/data/sounds/alarms/material/ogg/Rise_OG7_1ch_48k.ogg Binary files differindex a2d00cc8ddbc..a2d00cc8ddbc 100755..100644 --- a/data/sounds/alarms/material/ogg/Rise_OG7_1ch_48k.ogg +++ b/data/sounds/alarms/material/ogg/Rise_OG7_1ch_48k.ogg diff --git a/data/sounds/alarms/material/ogg/Sway_OG7_1ch_48k.ogg b/data/sounds/alarms/material/ogg/Sway_OG7_1ch_48k.ogg Binary files differindex 273728a6e417..273728a6e417 100755..100644 --- a/data/sounds/alarms/material/ogg/Sway_OG7_1ch_48k.ogg +++ b/data/sounds/alarms/material/ogg/Sway_OG7_1ch_48k.ogg diff --git a/data/sounds/alarms/material/ogg/Wag_OG7_1ch_48k.ogg b/data/sounds/alarms/material/ogg/Wag_OG7_1ch_48k.ogg Binary files differindex f61d5b9b75f3..f61d5b9b75f3 100755..100644 --- a/data/sounds/alarms/material/ogg/Wag_OG7_1ch_48k.ogg +++ b/data/sounds/alarms/material/ogg/Wag_OG7_1ch_48k.ogg diff --git a/docs/downloads/training/LocationUpdates.zip b/docs/downloads/training/LocationUpdates.zip Binary files differindex d3ebc6f53e6d..d3ebc6f53e6d 100755..100644 --- a/docs/downloads/training/LocationUpdates.zip +++ b/docs/downloads/training/LocationUpdates.zip diff --git a/graphics/java/android/framework_graphics.aconfig b/graphics/java/android/framework_graphics.aconfig index 0b9e72d6ffdd..5ad97e6d8992 100644 --- a/graphics/java/android/framework_graphics.aconfig +++ b/graphics/java/android/framework_graphics.aconfig @@ -24,3 +24,12 @@ flag { description: "Return null when decode from URI fails in Icon.loadDrawable()" bug: "335878768" } + +flag { + name: "ok_lab_colorspace" + is_exported: true + is_fixed_read_only: true + namespace: "core_graphics" + description: "Add OkLab ColorSpace support" + bug: "344038816" +} diff --git a/graphics/java/android/graphics/ColorSpace.java b/graphics/java/android/graphics/ColorSpace.java index 4bc3ecebb067..3752257da2bc 100644 --- a/graphics/java/android/graphics/ColorSpace.java +++ b/graphics/java/android/graphics/ColorSpace.java @@ -17,6 +17,7 @@ package android.graphics; import android.annotation.AnyThread; +import android.annotation.FlaggedApi; import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; @@ -27,9 +28,13 @@ import android.hardware.DataSpace; import android.hardware.DataSpace.ColorDataSpace; import android.util.SparseIntArray; +import com.android.graphics.flags.Flags; + import libcore.util.NativeAllocationRegistry; import java.util.Arrays; +import java.util.Collection; +import java.util.HashMap; import java.util.function.DoubleUnaryOperator; /** @@ -230,7 +235,9 @@ public abstract class ColorSpace { -2392 / 128.0, 8192 / 1305.0, Rgb.TransferParameters.TYPE_PQish); // See static initialization block next to #get(Named) - private static final ColorSpace[] sNamedColorSpaces = new ColorSpace[Named.values().length]; + private static final HashMap<Integer, ColorSpace> sNamedColorSpaceMap = + new HashMap<>(); + private static final SparseIntArray sDataToColorSpaces = new SparseIntArray(); @NonNull private final String mName; @@ -745,7 +752,23 @@ public abstract class ColorSpace { * <tr><td>Range</td><td colspan="4">\([0..1]\)</td></tr> * </table> */ - BT2020_PQ + BT2020_PQ, + + /** + * <p>{@link ColorSpace.Lab Lab} color space OkLab standardized as + * OkLab</p> + * <table summary="Color space definition"> + * <tr><th>Property</th><th colspan="4">Value</th></tr> + * <tr><td>Name</td><td colspan="4">Oklab</td></tr> + * <tr><td>CIE standard illuminant</td><td colspan="4">D65</td></tr> + * <tr> + * <td>Range</td> + * <td colspan="4">\(L: `[0.0, 1.0]`, a: `[-2, 2]`, b: `[-2, 2]`\)</td> + * </tr> + * </table> + */ + @FlaggedApi(Flags.FLAG_OK_LAB_COLORSPACE) + OK_LAB // Update the initialization block next to #get(Named) when adding new values } @@ -1428,11 +1451,11 @@ public abstract class ColorSpace { */ @NonNull static ColorSpace get(@IntRange(from = MIN_ID, to = MAX_ID) int index) { - if (index < 0 || index >= sNamedColorSpaces.length) { - throw new IllegalArgumentException("Invalid ID, must be in the range [0.." + - sNamedColorSpaces.length + ")"); + ColorSpace colorspace = sNamedColorSpaceMap.get(index); + if (colorspace == null) { + throw new IllegalArgumentException("Invalid ID: " + index); } - return sNamedColorSpaces[index]; + return colorspace; } /** @@ -1485,12 +1508,20 @@ public abstract class ColorSpace { * * <p>This method is thread-safe.</p> * + * Note that in the Android W release and later, this can return the SRGB ColorSpace if + * the {@link ColorSpace.Named} parameter is not enabled in the corresponding release. + * * @param name The name of the color space to get an instance of - * @return A non-null {@link ColorSpace} instance + * @return A non-null {@link ColorSpace} instance. If the ColorSpace is not supported + * then the SRGB ColorSpace is returned. */ @NonNull public static ColorSpace get(@NonNull Named name) { - return sNamedColorSpaces[name.ordinal()]; + ColorSpace colorSpace = sNamedColorSpaceMap.get(name.ordinal()); + if (colorSpace == null) { + return sNamedColorSpaceMap.get(Named.SRGB.ordinal()); + } + return colorSpace; } /** @@ -1511,7 +1542,8 @@ public abstract class ColorSpace { @NonNull @Size(9) float[] toXYZD50, @NonNull Rgb.TransferParameters function) { - for (ColorSpace colorSpace : sNamedColorSpaces) { + Collection<ColorSpace> colorspaces = sNamedColorSpaceMap.values(); + for (ColorSpace colorSpace : colorspaces) { if (colorSpace.getModel() == Model.RGB) { ColorSpace.Rgb rgb = (ColorSpace.Rgb) adapt(colorSpace, ILLUMINANT_D50_XYZ); if (compare(toXYZD50, rgb.mTransform) && @@ -1525,25 +1557,25 @@ public abstract class ColorSpace { } static { - sNamedColorSpaces[Named.SRGB.ordinal()] = new ColorSpace.Rgb( + sNamedColorSpaceMap.put(Named.SRGB.ordinal(), new ColorSpace.Rgb( "sRGB IEC61966-2.1", SRGB_PRIMARIES, ILLUMINANT_D65, null, SRGB_TRANSFER_PARAMETERS, Named.SRGB.ordinal() - ); + )); sDataToColorSpaces.put(DataSpace.DATASPACE_SRGB, Named.SRGB.ordinal()); - sNamedColorSpaces[Named.LINEAR_SRGB.ordinal()] = new ColorSpace.Rgb( + sNamedColorSpaceMap.put(Named.LINEAR_SRGB.ordinal(), new ColorSpace.Rgb( "sRGB IEC61966-2.1 (Linear)", SRGB_PRIMARIES, ILLUMINANT_D65, 1.0, 0.0f, 1.0f, Named.LINEAR_SRGB.ordinal() - ); + )); sDataToColorSpaces.put(DataSpace.DATASPACE_SRGB_LINEAR, Named.LINEAR_SRGB.ordinal()); - sNamedColorSpaces[Named.EXTENDED_SRGB.ordinal()] = new ColorSpace.Rgb( + sNamedColorSpaceMap.put(Named.EXTENDED_SRGB.ordinal(), new ColorSpace.Rgb( "scRGB-nl IEC 61966-2-2:2003", SRGB_PRIMARIES, ILLUMINANT_D65, @@ -1553,112 +1585,113 @@ public abstract class ColorSpace { -0.799f, 2.399f, SRGB_TRANSFER_PARAMETERS, Named.EXTENDED_SRGB.ordinal() - ); + )); sDataToColorSpaces.put(DataSpace.DATASPACE_SCRGB, Named.EXTENDED_SRGB.ordinal()); - sNamedColorSpaces[Named.LINEAR_EXTENDED_SRGB.ordinal()] = new ColorSpace.Rgb( + sNamedColorSpaceMap.put(Named.LINEAR_EXTENDED_SRGB.ordinal(), new ColorSpace.Rgb( "scRGB IEC 61966-2-2:2003", SRGB_PRIMARIES, ILLUMINANT_D65, 1.0, -0.5f, 7.499f, Named.LINEAR_EXTENDED_SRGB.ordinal() - ); + )); sDataToColorSpaces.put( DataSpace.DATASPACE_SCRGB_LINEAR, Named.LINEAR_EXTENDED_SRGB.ordinal()); - sNamedColorSpaces[Named.BT709.ordinal()] = new ColorSpace.Rgb( + sNamedColorSpaceMap.put(Named.BT709.ordinal(), new ColorSpace.Rgb( "Rec. ITU-R BT.709-5", SRGB_PRIMARIES, ILLUMINANT_D65, null, SMPTE_170M_TRANSFER_PARAMETERS, Named.BT709.ordinal() - ); + )); sDataToColorSpaces.put(DataSpace.DATASPACE_BT709, Named.BT709.ordinal()); - sNamedColorSpaces[Named.BT2020.ordinal()] = new ColorSpace.Rgb( + sNamedColorSpaceMap.put(Named.BT2020.ordinal(), new ColorSpace.Rgb( "Rec. ITU-R BT.2020-1", BT2020_PRIMARIES, ILLUMINANT_D65, null, new Rgb.TransferParameters(1 / 1.0993, 0.0993 / 1.0993, 1 / 4.5, 0.08145, 1 / 0.45), Named.BT2020.ordinal() - ); + )); + sDataToColorSpaces.put(DataSpace.DATASPACE_BT2020, Named.BT2020.ordinal()); - sNamedColorSpaces[Named.DCI_P3.ordinal()] = new ColorSpace.Rgb( + sNamedColorSpaceMap.put(Named.DCI_P3.ordinal(), new ColorSpace.Rgb( "SMPTE RP 431-2-2007 DCI (P3)", DCI_P3_PRIMARIES, new float[] { 0.314f, 0.351f }, 2.6, 0.0f, 1.0f, Named.DCI_P3.ordinal() - ); + )); sDataToColorSpaces.put(DataSpace.DATASPACE_DCI_P3, Named.DCI_P3.ordinal()); - sNamedColorSpaces[Named.DISPLAY_P3.ordinal()] = new ColorSpace.Rgb( + sNamedColorSpaceMap.put(Named.DISPLAY_P3.ordinal(), new ColorSpace.Rgb( "Display P3", DCI_P3_PRIMARIES, ILLUMINANT_D65, null, SRGB_TRANSFER_PARAMETERS, Named.DISPLAY_P3.ordinal() - ); + )); sDataToColorSpaces.put(DataSpace.DATASPACE_DISPLAY_P3, Named.DISPLAY_P3.ordinal()); - sNamedColorSpaces[Named.NTSC_1953.ordinal()] = new ColorSpace.Rgb( + sNamedColorSpaceMap.put(Named.NTSC_1953.ordinal(), new ColorSpace.Rgb( "NTSC (1953)", NTSC_1953_PRIMARIES, ILLUMINANT_C, null, SMPTE_170M_TRANSFER_PARAMETERS, Named.NTSC_1953.ordinal() - ); - sNamedColorSpaces[Named.SMPTE_C.ordinal()] = new ColorSpace.Rgb( + )); + sNamedColorSpaceMap.put(Named.SMPTE_C.ordinal(), new ColorSpace.Rgb( "SMPTE-C RGB", new float[] { 0.630f, 0.340f, 0.310f, 0.595f, 0.155f, 0.070f }, ILLUMINANT_D65, null, SMPTE_170M_TRANSFER_PARAMETERS, Named.SMPTE_C.ordinal() - ); - sNamedColorSpaces[Named.ADOBE_RGB.ordinal()] = new ColorSpace.Rgb( + )); + sNamedColorSpaceMap.put(Named.ADOBE_RGB.ordinal(), new ColorSpace.Rgb( "Adobe RGB (1998)", new float[] { 0.64f, 0.33f, 0.21f, 0.71f, 0.15f, 0.06f }, ILLUMINANT_D65, 2.2, 0.0f, 1.0f, Named.ADOBE_RGB.ordinal() - ); + )); sDataToColorSpaces.put(DataSpace.DATASPACE_ADOBE_RGB, Named.ADOBE_RGB.ordinal()); - sNamedColorSpaces[Named.PRO_PHOTO_RGB.ordinal()] = new ColorSpace.Rgb( + sNamedColorSpaceMap.put(Named.PRO_PHOTO_RGB.ordinal(), new ColorSpace.Rgb( "ROMM RGB ISO 22028-2:2013", new float[] { 0.7347f, 0.2653f, 0.1596f, 0.8404f, 0.0366f, 0.0001f }, ILLUMINANT_D50, null, new Rgb.TransferParameters(1.0, 0.0, 1 / 16.0, 0.031248, 1.8), Named.PRO_PHOTO_RGB.ordinal() - ); - sNamedColorSpaces[Named.ACES.ordinal()] = new ColorSpace.Rgb( + )); + sNamedColorSpaceMap.put(Named.ACES.ordinal(), new ColorSpace.Rgb( "SMPTE ST 2065-1:2012 ACES", new float[] { 0.73470f, 0.26530f, 0.0f, 1.0f, 0.00010f, -0.0770f }, ILLUMINANT_D60, 1.0, -65504.0f, 65504.0f, Named.ACES.ordinal() - ); - sNamedColorSpaces[Named.ACESCG.ordinal()] = new ColorSpace.Rgb( + )); + sNamedColorSpaceMap.put(Named.ACESCG.ordinal(), new ColorSpace.Rgb( "Academy S-2014-004 ACEScg", new float[] { 0.713f, 0.293f, 0.165f, 0.830f, 0.128f, 0.044f }, ILLUMINANT_D60, 1.0, -65504.0f, 65504.0f, Named.ACESCG.ordinal() - ); - sNamedColorSpaces[Named.CIE_XYZ.ordinal()] = new Xyz( + )); + sNamedColorSpaceMap.put(Named.CIE_XYZ.ordinal(), new Xyz( "Generic XYZ", Named.CIE_XYZ.ordinal() - ); - sNamedColorSpaces[Named.CIE_LAB.ordinal()] = new ColorSpace.Lab( + )); + sNamedColorSpaceMap.put(Named.CIE_LAB.ordinal(), new ColorSpace.Lab( "Generic L*a*b*", Named.CIE_LAB.ordinal() - ); - sNamedColorSpaces[Named.BT2020_HLG.ordinal()] = new ColorSpace.Rgb( + )); + sNamedColorSpaceMap.put(Named.BT2020_HLG.ordinal(), new ColorSpace.Rgb( "Hybrid Log Gamma encoding", BT2020_PRIMARIES, ILLUMINANT_D65, @@ -1668,9 +1701,9 @@ public abstract class ColorSpace { 0.0f, 1.0f, BT2020_HLG_TRANSFER_PARAMETERS, Named.BT2020_HLG.ordinal() - ); + )); sDataToColorSpaces.put(DataSpace.DATASPACE_BT2020_HLG, Named.BT2020_HLG.ordinal()); - sNamedColorSpaces[Named.BT2020_PQ.ordinal()] = new ColorSpace.Rgb( + sNamedColorSpaceMap.put(Named.BT2020_PQ.ordinal(), new ColorSpace.Rgb( "Perceptual Quantizer encoding", BT2020_PRIMARIES, ILLUMINANT_D65, @@ -1680,8 +1713,14 @@ public abstract class ColorSpace { 0.0f, 1.0f, BT2020_PQ_TRANSFER_PARAMETERS, Named.BT2020_PQ.ordinal() - ); + )); sDataToColorSpaces.put(DataSpace.DATASPACE_BT2020_PQ, Named.BT2020_PQ.ordinal()); + if (Flags.okLabColorspace()) { + sNamedColorSpaceMap.put(Named.OK_LAB.ordinal(), new ColorSpace.OkLab( + "Oklab", + Named.OK_LAB.ordinal() + )); + } } private static double transferHLGOETF(Rgb.TransferParameters params, double x) { @@ -2142,10 +2181,103 @@ public abstract class ColorSpace { return v; } + } + + private static float clamp(float x, float min, float max) { + return x < min ? min : x > max ? max : x; + } - private static float clamp(float x, float min, float max) { - return x < min ? min : x > max ? max : x; + /** + * Implementation of the Oklab color space. Oklab uses a D65 white point. + */ + @AnyThread + private static final class OkLab extends ColorSpace { + + private OkLab(@NonNull String name, @IntRange(from = MIN_ID, to = MAX_ID) int id) { + super(name, Model.LAB, id); } + + @Override + public boolean isWideGamut() { + return true; + } + + @Override + public float getMinValue(@IntRange(from = 0, to = 3) int component) { + return component == 0 ? 0.0f : -0.5f; + } + + @Override + public float getMaxValue(@IntRange(from = 0, to = 3) int component) { + return component == 0 ? 1.0f : 0.5f; + } + + @Override + public float[] toXyz(@NonNull @Size(min = 3) float[] v) { + v[0] = clamp(v[0], 0.0f, 1.0f); + v[1] = clamp(v[1], -0.5f, 0.5f); + v[2] = clamp(v[2], -0.5f, 0.5f); + + mul3x3Float3(INVERSE_M2, v); + v[0] = v[0] * v[0] * v[0]; + v[1] = v[1] * v[1] * v[1]; + v[2] = v[2] * v[2] * v[2]; + + mul3x3Float3(INVERSE_M1, v); + + return v; + } + + @Override + public float[] fromXyz(@NonNull @Size(min = 3) float[] v) { + mul3x3Float3(M1, v); + + v[0] = (float) Math.cbrt(v[0]); + v[1] = (float) Math.cbrt(v[1]); + v[2] = (float) Math.cbrt(v[2]); + + mul3x3Float3(M2, v); + return v; + } + + /** + * Temp array used as input to compute M1 below + */ + private static final float[] M1TMP = { + 0.8189330101f, 0.0329845436f, 0.0482003018f, + 0.3618667424f, 0.9293118715f, 0.2643662691f, + -0.1288597137f, 0.0361456387f, 0.6338517070f + }; + + /** + * This is the matrix applied before the nonlinear transform for (D50) XYZ-to-Oklab. + * This combines the D50-to-D65 white point transform with the normal transform matrix + * because this is always done together in [fromXyz]. + */ + private static final float[] M1 = mul3x3( + M1TMP, + chromaticAdaptation(Adaptation.BRADFORD, ILLUMINANT_D50, ILLUMINANT_D65) + ); + + /** + * Matrix applied after the nonlinear transform. + */ + private static final float[] M2 = { + 0.2104542553f, 1.9779984951f, 0.0259040371f, + 0.7936177850f, -2.4285922050f, 0.7827717662f, + -0.0040720468f, 0.4505937099f, -0.8086757660f + }; + + /** + * The inverse of the [M1] matrix, transforming back to XYZ (D50) + */ + private static final float[] INVERSE_M1 = inverse3x3(M1); + + /** + * The inverse of the [M2] matrix, doing the first linear transform in the + * Oklab-to-XYZ before doing the nonlinear transform. + */ + private static final float[] INVERSE_M2 = inverse3x3(M2); } /** diff --git a/keystore/OWNERS b/keystore/OWNERS index 913f65586cd6..689177715711 100644 --- a/keystore/OWNERS +++ b/keystore/OWNERS @@ -1,4 +1,5 @@ # Bug component: 189335 +drysdale@google.com eranm@google.com jbires@google.com swillden@google.com diff --git a/keystore/java/android/security/KeyStoreException.java b/keystore/java/android/security/KeyStoreException.java index 5825facee021..eea5690dddf1 100644 --- a/keystore/java/android/security/KeyStoreException.java +++ b/keystore/java/android/security/KeyStoreException.java @@ -679,6 +679,9 @@ public class KeyStoreException extends Exception { sErrorCodeToFailureInfo.put(ResponseCode.OUT_OF_KEYS_REQUIRES_SYSTEM_UPGRADE, new PublicErrorInformation(IS_SYSTEM_ERROR | IS_TRANSIENT_ERROR, ERROR_DEVICE_REQUIRES_UPGRADE_FOR_ATTESTATION)); + sErrorCodeToFailureInfo.put(ResponseCode.GET_ATTESTATION_APPLICATION_ID_FAILED, + new PublicErrorInformation(IS_SYSTEM_ERROR | IS_TRANSIENT_ERROR, + ERROR_INTERNAL_SYSTEM_ERROR)); sErrorCodeToFailureInfo.put(ResponseCode.OUT_OF_KEYS_PENDING_INTERNET_CONNECTIVITY, new PublicErrorInformation(IS_SYSTEM_ERROR | IS_TRANSIENT_ERROR, ERROR_ATTESTATION_KEYS_UNAVAILABLE)); diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/WindowExtensionsImpl.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/WindowExtensionsImpl.java index 16c77d0c3c81..ecf47209a802 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/WindowExtensionsImpl.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/WindowExtensionsImpl.java @@ -24,6 +24,7 @@ import android.app.Application; import android.app.compat.CompatChanges; import android.content.Context; import android.hardware.devicestate.DeviceStateManager; +import android.os.SystemProperties; import android.util.Log; import androidx.annotation.NonNull; @@ -50,6 +51,11 @@ class WindowExtensionsImpl implements WindowExtensions { private static final String TAG = "WindowExtensionsImpl"; /** + * The value of the system property that indicates no override is set. + */ + private static final int NO_LEVEL_OVERRIDE = -1; + + /** * The min version of the WM Extensions that must be supported in the current platform version. */ @VisibleForTesting @@ -66,14 +72,30 @@ class WindowExtensionsImpl implements WindowExtensions { WindowExtensionsImpl() { mIsActivityEmbeddingEnabled = isActivityEmbeddingEnabled(); - Log.i(TAG, "Initializing Window Extensions, vendor API level=" + mVersion - + ", activity embedding enabled=" + mIsActivityEmbeddingEnabled); + + Log.i(TAG, generateLogMessage()); + } + + private String generateLogMessage() { + final StringBuilder logBuilder = new StringBuilder("Initializing Window Extensions, " + + "vendor API level=" + mVersion); + final int levelOverride = getLevelOverride(); + if (levelOverride != NO_LEVEL_OVERRIDE) { + logBuilder.append(", override to ").append(levelOverride); + } + logBuilder.append(", activity embedding enabled=").append(mIsActivityEmbeddingEnabled); + return logBuilder.toString(); } // TODO(b/241126279) Introduce constants to better version functionality @Override public int getVendorApiLevel() { - return mVersion; + final int levelOverride = getLevelOverride(); + return (levelOverride != NO_LEVEL_OVERRIDE) ? levelOverride : mVersion; + } + + private int getLevelOverride() { + return SystemProperties.getInt("persist.wm.debug.ext_version_override", NO_LEVEL_OVERRIDE); } @NonNull diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/DividerPresenter.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/DividerPresenter.java index e8f01c273550..290fefa5abfa 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/DividerPresenter.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/DividerPresenter.java @@ -16,6 +16,9 @@ package androidx.window.extensions.embedding; +import static android.content.pm.ActivityInfo.CONFIG_DENSITY; +import static android.content.pm.ActivityInfo.CONFIG_LAYOUT_DIRECTION; +import static android.content.pm.ActivityInfo.CONFIG_WINDOW_CONFIGURATION; import static android.util.TypedValue.COMPLEX_UNIT_DIP; import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL; @@ -40,7 +43,6 @@ import android.annotation.Nullable; import android.app.Activity; import android.app.ActivityThread; import android.content.Context; -import android.content.pm.ActivityInfo; import android.content.res.Configuration; import android.graphics.Color; import android.graphics.PixelFormat; @@ -683,46 +685,53 @@ class DividerPresenter implements View.OnTouchListener { ? taskBounds.width() - mProperties.mDividerWidthPx : taskBounds.height() - mProperties.mDividerWidthPx; - if (isDraggingToFullscreenAllowed(mProperties.mDividerAttributes)) { - final float displayDensity = getDisplayDensity(); - return dividerPositionWithDraggingToFullscreenAllowed( - dividerPosition, - minPosition, - maxPosition, - fullyExpandedPosition, - velocity, - displayDensity); - } - return Math.clamp(dividerPosition, minPosition, maxPosition); + final float displayDensity = getDisplayDensity(); + final boolean isDraggingToFullscreenAllowed = + isDraggingToFullscreenAllowed(mProperties.mDividerAttributes); + return dividerPositionWithPositionOptions( + dividerPosition, + minPosition, + maxPosition, + fullyExpandedPosition, + velocity, + displayDensity, + isDraggingToFullscreenAllowed); } /** - * Returns the divider position given a set of position options. A snap algorithm is used to - * adjust the ending position to either fully expand one container or move the divider back to - * the specified min/max ratio depending on the dragging velocity. + * Returns the divider position given a set of position options. A snap algorithm can adjust + * the ending position to either fully expand one container or move the divider back to + * the specified min/max ratio depending on the dragging velocity and if dragging to fullscreen + * is allowed. */ @VisibleForTesting - static int dividerPositionWithDraggingToFullscreenAllowed(int dividerPosition, int minPosition, - int maxPosition, int fullyExpandedPosition, float velocity, float displayDensity) { - final float minDismissVelocityPxPerSecond = - MIN_DISMISS_VELOCITY_DP_PER_SECOND * displayDensity; + static int dividerPositionWithPositionOptions(int dividerPosition, int minPosition, + int maxPosition, int fullyExpandedPosition, float velocity, float displayDensity, + boolean isDraggingToFullscreenAllowed) { + if (isDraggingToFullscreenAllowed) { + final float minDismissVelocityPxPerSecond = + MIN_DISMISS_VELOCITY_DP_PER_SECOND * displayDensity; + if (dividerPosition < minPosition && velocity < -minDismissVelocityPxPerSecond) { + return 0; + } + if (dividerPosition > maxPosition && velocity > minDismissVelocityPxPerSecond) { + return fullyExpandedPosition; + } + } final float minFlingVelocityPxPerSecond = MIN_FLING_VELOCITY_DP_PER_SECOND * displayDensity; - if (dividerPosition < minPosition && velocity < -minDismissVelocityPxPerSecond) { - return 0; + if (Math.abs(velocity) >= minFlingVelocityPxPerSecond) { + return dividerPositionForFling( + dividerPosition, minPosition, maxPosition, velocity); } - if (dividerPosition > maxPosition && velocity > minDismissVelocityPxPerSecond) { - return fullyExpandedPosition; + if (dividerPosition >= minPosition && dividerPosition <= maxPosition) { + return dividerPosition; } - if (Math.abs(velocity) < minFlingVelocityPxPerSecond) { - if (dividerPosition >= minPosition && dividerPosition <= maxPosition) { - return dividerPosition; - } - final int[] snapPositions = {0, minPosition, maxPosition, fullyExpandedPosition}; - return snap(dividerPosition, snapPositions); - } - return dividerPositionForFling( - dividerPosition, minPosition, maxPosition, velocity); + return snap( + dividerPosition, + isDraggingToFullscreenAllowed + ? new int[] {0, minPosition, maxPosition, fullyExpandedPosition} + : new int[] {minPosition, maxPosition}); } /** @@ -959,7 +968,7 @@ class DividerPresenter implements View.OnTouchListener { @VisibleForTesting static class Properties { private static final int CONFIGURATION_MASK_FOR_DIVIDER = - ActivityInfo.CONFIG_DENSITY | ActivityInfo.CONFIG_WINDOW_CONFIGURATION; + CONFIG_DENSITY | CONFIG_WINDOW_CONFIGURATION | CONFIG_LAYOUT_DIRECTION; @NonNull private final Configuration mConfiguration; @NonNull @@ -1228,6 +1237,12 @@ class DividerPresenter implements View.OnTouchListener { FLAG_NOT_FOCUSABLE | FLAG_NOT_TOUCH_MODAL | FLAG_SLIPPERY, PixelFormat.TRANSLUCENT); lp.setTitle(WINDOW_NAME); + + // Ensure that the divider layout is always LTR regardless of the locale, because we + // already considered the locale when determining the split layout direction and the + // computed divider line position always starts from the left. This only affects the + // horizontal layout and does not have any effect on the top-to-bottom layout. + mDividerLayout.setLayoutDirection(View.LAYOUT_DIRECTION_LTR); mViewHost.setView(mDividerLayout, lp); mViewHost.relayout(lp); } @@ -1379,10 +1394,16 @@ class DividerPresenter implements View.OnTouchListener { primaryBounds = mProperties.mIsReversedLayout ? boundsBottom : boundsTop; secondaryBounds = mProperties.mIsReversedLayout ? boundsTop : boundsBottom; } - t.setWindowCrop(mPrimaryVeil, primaryBounds.width(), primaryBounds.height()); - t.setWindowCrop(mSecondaryVeil, secondaryBounds.width(), secondaryBounds.height()); - t.setPosition(mPrimaryVeil, primaryBounds.left, primaryBounds.top); - t.setPosition(mSecondaryVeil, secondaryBounds.left, secondaryBounds.top); + if (mPrimaryVeil != null) { + t.setWindowCrop(mPrimaryVeil, primaryBounds.width(), primaryBounds.height()); + t.setPosition(mPrimaryVeil, primaryBounds.left, primaryBounds.top); + t.setVisibility(mPrimaryVeil, !primaryBounds.isEmpty()); + } + if (mSecondaryVeil != null) { + t.setWindowCrop(mSecondaryVeil, secondaryBounds.width(), secondaryBounds.height()); + t.setPosition(mSecondaryVeil, secondaryBounds.left, secondaryBounds.top); + t.setVisibility(mSecondaryVeil, !secondaryBounds.isEmpty()); + } } private static float[] colorToFloatArray(@NonNull Color color) { diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java index eade86e50659..d888fa9d6feb 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java @@ -658,27 +658,28 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer { } /** - * Returns the expanded bounds if the {@code bounds} violate minimum dimension or are not fully - * covered by the task bounds. Otherwise, returns {@code bounds}. + * Returns the expanded bounds if the {@code relBounds} violate minimum dimension or are not + * fully covered by the task bounds. Otherwise, returns {@code relBounds}. */ @NonNull - static Rect sanitizeBounds(@NonNull Rect bounds, @Nullable Size minDimension, + static Rect sanitizeBounds(@NonNull Rect relBounds, @Nullable Size minDimension, @NonNull TaskFragmentContainer container) { - if (bounds.isEmpty()) { + if (relBounds.isEmpty()) { // Don't need to check if the bounds follows the task bounds. - return bounds; + return relBounds; } - if (boundsSmallerThanMinDimensions(bounds, minDimension)) { + if (boundsSmallerThanMinDimensions(relBounds, minDimension)) { // Expand the bounds if the bounds are smaller than minimum dimensions. return new Rect(); } final TaskContainer taskContainer = container.getTaskContainer(); - final Rect taskBounds = taskContainer.getBounds(); - if (!taskBounds.contains(bounds)) { + final Rect relTaskBounds = new Rect(taskContainer.getBounds()); + relTaskBounds.offsetTo(0, 0); + if (!relTaskBounds.contains(relBounds)) { // Expand the bounds if the bounds exceed the task bounds. return new Rect(); } - return bounds; + return relBounds; } @Override diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/DividerPresenterTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/DividerPresenterTest.java index af3c4dac5d67..4f51815ed05d 100644 --- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/DividerPresenterTest.java +++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/DividerPresenterTest.java @@ -660,108 +660,241 @@ public class DividerPresenterTest { // Divider position is less than minPosition and the velocity is enough to be dismissed assertEquals( 0, // Closed position - DividerPresenter.dividerPositionWithDraggingToFullscreenAllowed( + DividerPresenter.dividerPositionWithPositionOptions( 10 /* dividerPosition */, 30 /* minPosition */, 900 /* maxPosition */, 1200 /* fullyExpandedPosition */, -dismissVelocity, - displayDensity)); + displayDensity, + true /* isDraggingToFullscreenAllowed */)); // Divider position is greater than maxPosition and the velocity is enough to be dismissed assertEquals( 1200, // Fully expanded position - DividerPresenter.dividerPositionWithDraggingToFullscreenAllowed( + DividerPresenter.dividerPositionWithPositionOptions( 1000 /* dividerPosition */, 30 /* minPosition */, 900 /* maxPosition */, 1200 /* fullyExpandedPosition */, dismissVelocity, - displayDensity)); + displayDensity, + true /* isDraggingToFullscreenAllowed */)); // Divider position is returned when the velocity is not fast enough for fling and is in // between minPosition and maxPosition assertEquals( 500, // dividerPosition is not snapped - DividerPresenter.dividerPositionWithDraggingToFullscreenAllowed( + DividerPresenter.dividerPositionWithPositionOptions( 500 /* dividerPosition */, 30 /* minPosition */, 900 /* maxPosition */, 1200 /* fullyExpandedPosition */, nonFlingVelocity, - displayDensity)); + displayDensity, + true /* isDraggingToFullscreenAllowed */)); // Divider position is snapped when the velocity is not fast enough for fling and larger // than maxPosition assertEquals( 900, // Closest position is maxPosition - DividerPresenter.dividerPositionWithDraggingToFullscreenAllowed( + DividerPresenter.dividerPositionWithPositionOptions( 950 /* dividerPosition */, 30 /* minPosition */, 900 /* maxPosition */, 1200 /* fullyExpandedPosition */, nonFlingVelocity, - displayDensity)); + displayDensity, + true /* isDraggingToFullscreenAllowed */)); // Divider position is snapped when the velocity is not fast enough for fling and smaller // than minPosition assertEquals( 30, // Closest position is minPosition - DividerPresenter.dividerPositionWithDraggingToFullscreenAllowed( + DividerPresenter.dividerPositionWithPositionOptions( 20 /* dividerPosition */, 30 /* minPosition */, 900 /* maxPosition */, 1200 /* fullyExpandedPosition */, nonFlingVelocity, - displayDensity)); + displayDensity, + true /* isDraggingToFullscreenAllowed */)); // Divider position is in the closed to maxPosition bounds and the velocity is enough for // backward fling assertEquals( 2000, // maxPosition - DividerPresenter.dividerPositionWithDraggingToFullscreenAllowed( + DividerPresenter.dividerPositionWithPositionOptions( 2200 /* dividerPosition */, 1000 /* minPosition */, 2000 /* maxPosition */, 2500 /* fullyExpandedPosition */, -flingVelocity, - displayDensity)); + displayDensity, + true /* isDraggingToFullscreenAllowed */)); // Divider position is not in the closed to maxPosition bounds and the velocity is enough // for backward fling assertEquals( 1000, // minPosition - DividerPresenter.dividerPositionWithDraggingToFullscreenAllowed( + DividerPresenter.dividerPositionWithPositionOptions( 1200 /* dividerPosition */, 1000 /* minPosition */, 2000 /* maxPosition */, 2500 /* fullyExpandedPosition */, -flingVelocity, - displayDensity)); + displayDensity, + true /* isDraggingToFullscreenAllowed */)); // Divider position is in the closed to minPosition bounds and the velocity is enough for // forward fling assertEquals( 1000, // minPosition - DividerPresenter.dividerPositionWithDraggingToFullscreenAllowed( + DividerPresenter.dividerPositionWithPositionOptions( 500 /* dividerPosition */, 1000 /* minPosition */, 2000 /* maxPosition */, 2500 /* fullyExpandedPosition */, flingVelocity, - displayDensity)); + displayDensity, + true /* isDraggingToFullscreenAllowed */)); // Divider position is not in the closed to minPosition bounds and the velocity is enough // for forward fling assertEquals( 2000, // maxPosition - DividerPresenter.dividerPositionWithDraggingToFullscreenAllowed( + DividerPresenter.dividerPositionWithPositionOptions( 1200 /* dividerPosition */, 1000 /* minPosition */, 2000 /* maxPosition */, 2500 /* fullyExpandedPosition */, flingVelocity, - displayDensity)); + displayDensity, + true /* isDraggingToFullscreenAllowed */)); + } + + @Test + public void testDividerPositionWithDraggingToFullscreenNotAllowed() { + final float displayDensity = 600F; + final float nonFlingVelocity = MIN_FLING_VELOCITY_DP_PER_SECOND * displayDensity - 10f; + final float flingVelocity = MIN_FLING_VELOCITY_DP_PER_SECOND * displayDensity + 10f; + + // Divider position is returned when the velocity is not fast enough for fling and is in + // between minPosition and maxPosition + assertEquals( + 500, // dividerPosition is not snapped + DividerPresenter.dividerPositionWithPositionOptions( + 500 /* dividerPosition */, + 30 /* minPosition */, + 900 /* maxPosition */, + 1200 /* fullyExpandedPosition */, + nonFlingVelocity, + displayDensity, + false /* isDraggingToFullscreenAllowed */)); + + // Divider position is snapped when the velocity is not fast enough for fling and larger + // than maxPosition + assertEquals( + 900, // Closest position is maxPosition + DividerPresenter.dividerPositionWithPositionOptions( + 950 /* dividerPosition */, + 30 /* minPosition */, + 900 /* maxPosition */, + 1200 /* fullyExpandedPosition */, + nonFlingVelocity, + displayDensity, + false /* isDraggingToFullscreenAllowed */)); + + // Divider position is snapped when the velocity is not fast enough for fling and smaller + // than minPosition + assertEquals( + 30, // Closest position is minPosition + DividerPresenter.dividerPositionWithPositionOptions( + 20 /* dividerPosition */, + 30 /* minPosition */, + 900 /* maxPosition */, + 1200 /* fullyExpandedPosition */, + nonFlingVelocity, + displayDensity, + false /* isDraggingToFullscreenAllowed */)); + + // Divider position is snapped when the velocity is not fast enough for fling and at the + // closed position + assertEquals( + 30, // Closest position is minPosition + DividerPresenter.dividerPositionWithPositionOptions( + 0 /* dividerPosition */, + 30 /* minPosition */, + 900 /* maxPosition */, + 1200 /* fullyExpandedPosition */, + nonFlingVelocity, + displayDensity, + false /* isDraggingToFullscreenAllowed */)); + + // Divider position is snapped when the velocity is not fast enough for fling and at the + // fully expanded position + assertEquals( + 900, // Closest position is maxPosition + DividerPresenter.dividerPositionWithPositionOptions( + 1200 /* dividerPosition */, + 30 /* minPosition */, + 900 /* maxPosition */, + 1200 /* fullyExpandedPosition */, + nonFlingVelocity, + displayDensity, + false /* isDraggingToFullscreenAllowed */)); + + // Divider position is in the closed to maxPosition bounds and the velocity is enough for + // backward fling + assertEquals( + 2000, // maxPosition + DividerPresenter.dividerPositionWithPositionOptions( + 2200 /* dividerPosition */, + 1000 /* minPosition */, + 2000 /* maxPosition */, + 2500 /* fullyExpandedPosition */, + -flingVelocity, + displayDensity, + false /* isDraggingToFullscreenAllowed */)); + + // Divider position is not in the closed to maxPosition bounds and the velocity is enough + // for backward fling + assertEquals( + 1000, // minPosition + DividerPresenter.dividerPositionWithPositionOptions( + 1200 /* dividerPosition */, + 1000 /* minPosition */, + 2000 /* maxPosition */, + 2500 /* fullyExpandedPosition */, + -flingVelocity, + displayDensity, + false /* isDraggingToFullscreenAllowed */)); + + // Divider position is in the closed to minPosition bounds and the velocity is enough for + // forward fling + assertEquals( + 1000, // minPosition + DividerPresenter.dividerPositionWithPositionOptions( + 500 /* dividerPosition */, + 1000 /* minPosition */, + 2000 /* maxPosition */, + 2500 /* fullyExpandedPosition */, + flingVelocity, + displayDensity, + false /* isDraggingToFullscreenAllowed */)); + + // Divider position is not in the closed to minPosition bounds and the velocity is enough + // for forward fling + assertEquals( + 2000, // maxPosition + DividerPresenter.dividerPositionWithPositionOptions( + 1200 /* dividerPosition */, + 1000 /* minPosition */, + 2000 /* maxPosition */, + 2500 /* fullyExpandedPosition */, + flingVelocity, + displayDensity, + false /* isDraggingToFullscreenAllowed */)); } private TaskFragmentContainer createMockTaskFragmentContainer( diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java index 0972d40f33e3..7a0b9a0ece6b 100644 --- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java +++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java @@ -409,6 +409,22 @@ public class OverlayPresentationTest { } @Test + public void testSanitizeBounds_taskInSplitScreen() { + final TaskFragmentContainer overlayContainer = + createTestOverlayContainer(TASK_ID, "test1"); + TaskContainer taskContainer = overlayContainer.getTaskContainer(); + spyOn(taskContainer); + doReturn(new Rect(TASK_BOUNDS.left + TASK_BOUNDS.width() / 2, TASK_BOUNDS.top, + TASK_BOUNDS.right, TASK_BOUNDS.bottom)).when(taskContainer).getBounds(); + final Rect taskBounds = taskContainer.getBounds(); + final Rect bounds = new Rect(taskBounds.width() / 2, 0, taskBounds.width(), + taskBounds.height()); + + assertThat(sanitizeBounds(bounds, null, overlayContainer) + .isEmpty()).isFalse(); + } + + @Test public void testCreateOrUpdateOverlayTaskFragmentIfNeeded_createOverlay() { final Rect bounds = new Rect(0, 0, 100, 100); mSplitController.setActivityStackAttributesCalculator(params -> diff --git a/libs/WindowManager/Shell/Android.bp b/libs/WindowManager/Shell/Android.bp index 89781fd650a4..25d3067a34bc 100644 --- a/libs/WindowManager/Shell/Android.bp +++ b/libs/WindowManager/Shell/Android.bp @@ -51,6 +51,7 @@ filegroup { "src/com/android/wm/shell/common/split/SplitScreenConstants.java", "src/com/android/wm/shell/common/TransactionPool.java", "src/com/android/wm/shell/common/TriangleShape.java", + "src/com/android/wm/shell/common/desktopmode/*.kt", "src/com/android/wm/shell/draganddrop/DragAndDropConstants.java", "src/com/android/wm/shell/pip/PipContentOverlay.java", "src/com/android/wm/shell/startingsurface/SplashScreenExitAnimationUtils.java", @@ -205,6 +206,7 @@ android_library { "androidx.core_core-animation", "androidx.core_core-ktx", "androidx.arch.core_core-runtime", + "androidx.compose.material3_material3", "androidx-constraintlayout_constraintlayout", "androidx.dynamicanimation_dynamicanimation", "androidx.recyclerview_recyclerview", 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 0efdbdc9376c..327e2059557c 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 @@ -456,5 +456,7 @@ class BubbleStackViewTest { override fun isStackExpanded(): Boolean = false override fun isShowingAsBubbleBar(): Boolean = false + + override fun hideCurrentInputMethod() {} } } diff --git a/libs/WindowManager/Shell/res/layout/desktop_mode_focused_window_decor.xml b/libs/WindowManager/Shell/res/layout/desktop_mode_app_handle.xml index c0ff1922edc8..c0ff1922edc8 100644 --- a/libs/WindowManager/Shell/res/layout/desktop_mode_focused_window_decor.xml +++ b/libs/WindowManager/Shell/res/layout/desktop_mode_app_handle.xml diff --git a/libs/WindowManager/Shell/res/layout/desktop_mode_app_controls_window_decor.xml b/libs/WindowManager/Shell/res/layout/desktop_mode_app_header.xml index 84e144972f38..7b31c1420a7c 100644 --- a/libs/WindowManager/Shell/res/layout/desktop_mode_app_controls_window_decor.xml +++ b/libs/WindowManager/Shell/res/layout/desktop_mode_app_header.xml @@ -19,7 +19,6 @@ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/desktop_mode_caption" - android:background="@drawable/desktop_mode_header_background" android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="center_horizontal" diff --git a/libs/WindowManager/Shell/res/values-cs/strings.xml b/libs/WindowManager/Shell/res/values-cs/strings.xml index aafb2e16b703..150a6e642529 100644 --- a/libs/WindowManager/Shell/res/values-cs/strings.xml +++ b/libs/WindowManager/Shell/res/values-cs/strings.xml @@ -84,7 +84,7 @@ <string name="notification_bubble_title" msgid="6082910224488253378">"Bublina"</string> <string name="manage_bubbles_text" msgid="7730624269650594419">"Spravovat"</string> <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bublina byla zavřena."</string> - <string name="restart_button_description" msgid="4564728020654658478">"Klepnutím tuto aplikaci kvůli lepšímu zobrazení restartujete"</string> + <string name="restart_button_description" msgid="4564728020654658478">"Klepnutím tuto aplikaci restartujete kvůli lepšímu zobrazení"</string> <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Změnit v Nastavení poměr stran této aplikace"</string> <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Změnit poměr stran"</string> <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Problémy s fotoaparátem?\nKlepnutím vyřešíte"</string> diff --git a/libs/WindowManager/Shell/res/values-de/strings.xml b/libs/WindowManager/Shell/res/values-de/strings.xml index bcdc2a9c8539..7b91559e9179 100644 --- a/libs/WindowManager/Shell/res/values-de/strings.xml +++ b/libs/WindowManager/Shell/res/values-de/strings.xml @@ -48,8 +48,8 @@ <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"50 % oben"</string> <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"30 % oben"</string> <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"Vollbild unten"</string> - <string name="accessibility_split_left" msgid="1713683765575562458">"Links teilen"</string> - <string name="accessibility_split_right" msgid="8441001008181296837">"Rechts teilen"</string> + <string name="accessibility_split_left" msgid="1713683765575562458">"Links positionieren"</string> + <string name="accessibility_split_right" msgid="8441001008181296837">"Rechts positionieren"</string> <string name="accessibility_split_top" msgid="2789329702027147146">"Oben teilen"</string> <string name="accessibility_split_bottom" msgid="8694551025220868191">"Unten teilen"</string> <string name="one_handed_tutorial_title" msgid="4583241688067426350">"Einhandmodus wird verwendet"</string> diff --git a/libs/WindowManager/Shell/res/values-ko/strings.xml b/libs/WindowManager/Shell/res/values-ko/strings.xml index e8b5522838b7..caa114fd6f88 100644 --- a/libs/WindowManager/Shell/res/values-ko/strings.xml +++ b/libs/WindowManager/Shell/res/values-ko/strings.xml @@ -95,7 +95,7 @@ <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"앱 위치를 조정하려면 앱 외부를 두 번 탭합니다."</string> <string name="letterbox_education_got_it" msgid="4057634570866051177">"확인"</string> <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"추가 정보는 펼쳐서 확인하세요."</string> - <string name="letterbox_restart_dialog_title" msgid="8543049527871033505">"화면에 맞게 보도록 다시 시작할까요?"</string> + <string name="letterbox_restart_dialog_title" msgid="8543049527871033505">"화면에 맞게 보이도록 다시 시작할까요?"</string> <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> diff --git a/libs/WindowManager/Shell/res/values-nb/strings.xml b/libs/WindowManager/Shell/res/values-nb/strings.xml index 9f03d8b5b178..6005be4fb5c9 100644 --- a/libs/WindowManager/Shell/res/values-nb/strings.xml +++ b/libs/WindowManager/Shell/res/values-nb/strings.xml @@ -85,7 +85,7 @@ <string name="manage_bubbles_text" msgid="7730624269650594419">"Administrer"</string> <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Boblen er avvist."</string> <string name="restart_button_description" msgid="4564728020654658478">"Trykk for å starte denne appen på nytt og få en bedre visning"</string> - <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Endre høyde/bredde-forholdet for denne appen i innstillingene"</string> + <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Endre høyde/bredde-forholdet for denne appen i Innstillinger"</string> <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Endre høyde/bredde-forholdet"</string> <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Har du kameraproblemer?\nTrykk for å tilpasse"</string> <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Ble ikke problemet løst?\nTrykk for å gå tilbake"</string> diff --git a/libs/WindowManager/Shell/res/values/dimen.xml b/libs/WindowManager/Shell/res/values/dimen.xml index f27f46c07de6..89cddc3a14d1 100644 --- a/libs/WindowManager/Shell/res/values/dimen.xml +++ b/libs/WindowManager/Shell/res/values/dimen.xml @@ -459,6 +459,12 @@ start of this area. --> <dimen name="desktop_mode_customizable_caption_margin_end">152dp</dimen> + <!-- The default minimum allowed window width when resizing a window in desktop mode. --> + <dimen name="desktop_mode_minimum_window_width">386dp</dimen> + + <!-- The default minimum allowed window height when resizing a window in desktop mode. --> + <dimen name="desktop_mode_minimum_window_height">352dp</dimen> + <!-- The width of the maximize menu in desktop mode. --> <dimen name="desktop_mode_maximize_menu_width">228dp</dimen> diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/DesktopModeStatus.java b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/DesktopModeStatus.java index 8d8655addc65..4876f327a650 100644 --- a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/DesktopModeStatus.java +++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/DesktopModeStatus.java @@ -67,6 +67,10 @@ public class DesktopModeStatus { private static final boolean ENFORCE_DEVICE_RESTRICTIONS = SystemProperties.getBoolean( "persist.wm.debug.desktop_mode_enforce_device_restrictions", true); + /** Whether the desktop density override is enabled. */ + public static final boolean DESKTOP_DENSITY_OVERRIDE_ENABLED = + SystemProperties.getBoolean("persist.wm.debug.desktop_mode_density_enabled", false); + /** Override density for tasks when they're inside the desktop. */ public static final int DESKTOP_DENSITY_OVERRIDE = SystemProperties.getInt("persist.wm.debug.desktop_mode_density", 284); @@ -157,9 +161,23 @@ public class DesktopModeStatus { } /** - * Return {@code true} if the override desktop density is set. + * Return {@code true} if the override desktop density is enabled and valid. + */ + public static boolean useDesktopOverrideDensity() { + return isDesktopDensityOverrideEnabled() && isValidDesktopDensityOverrideSet(); + } + + /** + * Return {@code true} if the override desktop density is enabled. + */ + private static boolean isDesktopDensityOverrideEnabled() { + return DESKTOP_DENSITY_OVERRIDE_ENABLED; + } + + /** + * Return {@code true} if the override desktop density is set and within a valid range. */ - public static boolean isDesktopDensityOverrideSet() { + private static boolean isValidDesktopDensityOverrideSet() { return DESKTOP_DENSITY_OVERRIDE >= DESKTOP_DENSITY_MIN && DESKTOP_DENSITY_OVERRIDE <= DESKTOP_DENSITY_MAX; } 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 5600664a8f47..0fd21f3798ac 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 @@ -18,7 +18,6 @@ package com.android.wm.shell.back; import static com.android.internal.jank.InteractionJankMonitor.CUJ_PREDICTIVE_BACK_HOME; import static com.android.window.flags.Flags.predictiveBackSystemAnims; -import static com.android.wm.shell.common.ExecutorUtils.executeRemoteCallWithTaskPermission; import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_BACK_PREVIEW; import static com.android.wm.shell.sysui.ShellSharedConstants.KEY_EXTRA_SHELL_BACK_ANIMATION; @@ -31,6 +30,7 @@ import android.content.ContentResolver; import android.content.Context; import android.content.res.Configuration; import android.database.ContentObserver; +import android.graphics.Rect; import android.hardware.input.InputManager; import android.net.Uri; import android.os.Bundle; @@ -41,7 +41,6 @@ import android.os.SystemClock; import android.os.SystemProperties; import android.os.UserHandle; import android.provider.Settings.Global; -import android.util.DisplayMetrics; import android.util.Log; import android.view.IRemoteAnimationRunner; import android.view.InputDevice; @@ -49,6 +48,7 @@ import android.view.KeyCharacterMap; import android.view.KeyEvent; import android.view.MotionEvent; import android.view.RemoteAnimationTarget; +import android.view.WindowManager; import android.window.BackAnimationAdapter; import android.window.BackEvent; import android.window.BackMotionEvent; @@ -63,7 +63,6 @@ import com.android.internal.protolog.common.ProtoLog; import com.android.internal.util.LatencyTracker; import com.android.internal.view.AppearanceRegion; import com.android.wm.shell.R; -import com.android.wm.shell.animation.FlingAnimationUtils; import com.android.wm.shell.common.ExternalInterfaceBinder; import com.android.wm.shell.common.RemoteCallable; import com.android.wm.shell.common.ShellExecutor; @@ -88,15 +87,6 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont public static final boolean IS_ENABLED = SystemProperties.getInt("persist.wm.debug.predictive_back", SETTING_VALUE_ON) == SETTING_VALUE_ON; - public static final float FLING_MAX_LENGTH_SECONDS = 0.1f; // 100ms - public static final float FLING_SPEED_UP_FACTOR = 0.6f; - - /** - * The maximum additional progress in case of fling gesture. - * The end animation starts after the user lifts the finger from the screen, we continue to - * fire {@link BackEvent}s until the velocity reaches 0. - */ - private static final float MAX_FLING_PROGRESS = 0.3f; /* 30% of the screen */ /** Predictive back animation developer option */ private final AtomicBoolean mEnableAnimations = new AtomicBoolean(false); @@ -119,8 +109,6 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont private boolean mPointersPilfered = false; private final boolean mRequirePointerPilfer; - private final FlingAnimationUtils mFlingAnimationUtils; - /** Registry for the back animations */ private final ShellBackAnimationRegistry mShellBackAnimationRegistry; @@ -133,6 +121,9 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont private final ShellCommandHandler mShellCommandHandler; private final ShellExecutor mShellExecutor; private final Handler mBgHandler; + private final WindowManager mWindowManager; + @VisibleForTesting + final Rect mTouchableArea = new Rect(); /** * Tracks the current user back gesture. @@ -233,14 +224,11 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont mBgHandler = bgHandler; shellInit.addInitCallback(this::onInit, this); mAnimationBackground = backAnimationBackground; - DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics(); - mFlingAnimationUtils = new FlingAnimationUtils.Builder(displayMetrics) - .setMaxLengthSeconds(FLING_MAX_LENGTH_SECONDS) - .setSpeedUpFactor(FLING_SPEED_UP_FACTOR) - .build(); mShellBackAnimationRegistry = shellBackAnimationRegistry; mLatencyTracker = LatencyTracker.getInstance(mContext); mShellCommandHandler = shellCommandHandler; + mWindowManager = context.getSystemService(WindowManager.class); + updateTouchableArea(); } private void onInit() { @@ -302,6 +290,11 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont @Override public void onConfigurationChanged(Configuration newConfig) { mShellBackAnimationRegistry.onConfigurationChanged(newConfig); + updateTouchableArea(); + } + + private void updateTouchableArea() { + mTouchableArea.set(mWindowManager.getCurrentWindowMetrics().getBounds()); } @Override @@ -435,11 +428,18 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont if (!shouldDispatchToAnimator && mActiveCallback != null) { mCurrentTracker.updateStartLocation(); tryDispatchOnBackStarted(mActiveCallback, mCurrentTracker.createStartEvent(null)); + if (mBackNavigationInfo != null && !isAppProgressGenerationAllowed()) { + tryPilferPointers(); + } } else if (shouldDispatchToAnimator) { tryPilferPointers(); } } + private boolean isAppProgressGenerationAllowed() { + return mBackNavigationInfo.getTouchableRegion().equals(mTouchableArea); + } + /** * Called when a new motion event needs to be transferred to this * {@link BackAnimationController} @@ -555,6 +555,9 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont // App is handling back animation. Cancel system animation latency tracking. cancelLatencyTracking(); tryDispatchOnBackStarted(mActiveCallback, touchTracker.createStartEvent(null)); + if (!isAppProgressGenerationAllowed()) { + tryPilferPointers(); + } } } @@ -661,7 +664,8 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont private void dispatchOnBackProgressed(IOnBackInvokedCallback callback, BackMotionEvent backEvent) { - if (callback == null || !shouldDispatchToAnimator()) { + if (callback == null || (!shouldDispatchToAnimator() && mBackNavigationInfo != null + && isAppProgressGenerationAllowed())) { return; } try { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java index da530d740d48..1279fc42c066 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java @@ -305,6 +305,7 @@ public class Bubble implements BubbleViewProvider { getUser().getIdentifier(), getPackageName(), getTitle(), + getAppName(), isImportantConversation()); } 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 317e00a44bce..644907361cd7 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 @@ -518,7 +518,7 @@ public class BubbleController implements ConfigurationChangeListener, } private ExternalInterfaceBinder createExternalInterface() { - return new BubbleController.IBubblesImpl(this); + return new IBubblesImpl(this); } @VisibleForTesting @@ -592,11 +592,12 @@ public class BubbleController implements ConfigurationChangeListener, * Hides the current input method, wherever it may be focused, via InputMethodManagerInternal. */ void hideCurrentInputMethod() { + mBubblePositioner.setImeVisible(false /* visible */, 0 /* height */); int displayId = mWindowManager.getDefaultDisplay().getDisplayId(); try { mBarService.hideCurrentInputMethodForBubbles(displayId); } catch (RemoteException e) { - e.printStackTrace(); + Log.e(TAG, "Failed to hide IME", e); } } @@ -1457,8 +1458,9 @@ public class BubbleController implements ConfigurationChangeListener, SynchronousScreenCaptureListener screenCaptureListener) { try { ScreenCapture.CaptureArgs args = null; - if (mStackView != null) { - ViewRootImpl viewRoot = mStackView.getViewRootImpl(); + View viewToUse = mStackView != null ? mStackView : mLayerView; + if (viewToUse != null) { + ViewRootImpl viewRoot = viewToUse.getViewRootImpl(); if (viewRoot != null) { SurfaceControl bubbleLayer = viewRoot.getSurfaceControl(); if (bubbleLayer != null) { @@ -1550,6 +1552,12 @@ public class BubbleController implements ConfigurationChangeListener, Log.w(TAG, "Tried to add a bubble to the stack but the stack is null"); } }; + } else if (mBubbleData.isExpanded() && mBubbleData.getSelectedBubble() != null) { + callback = b -> { + if (b.getKey().equals(mBubbleData.getSelectedBubbleKey())) { + mLayerView.showExpandedView(b); + } + }; } for (int i = mBubbleData.getBubbles().size() - 1; i >= 0; i--) { Bubble bubble = mBubbleData.getBubbles().get(i); @@ -2354,6 +2362,8 @@ public class BubbleController implements ConfigurationChangeListener, @Override public void invalidate() { mController = null; + // Unregister the listeners to ensure any binder death recipients are unlinked + mListener.unregister(); } @Override @@ -2531,17 +2541,6 @@ public class BubbleController implements ConfigurationChangeListener, private CachedState mCachedState = new CachedState(); - private IBubblesImpl mIBubbles; - - @Override - public IBubbles createExternalInterface() { - if (mIBubbles != null) { - mIBubbles.invalidate(); - } - mIBubbles = new IBubblesImpl(BubbleController.this); - return mIBubbles; - } - @Override public boolean isBubbleNotificationSuppressedFromShade(String key, String groupKey) { return mCachedState.isBubbleNotificationSuppressedFromShade(key, groupKey); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java index 874102c20925..81e7d1fc4d87 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java @@ -256,11 +256,15 @@ public class BubbleData { } /** - * Returns a bubble bar update populated with the current list of active bubbles. + * Returns a bubble bar update populated with the current list of active bubbles, expanded, + * and selected state. */ public BubbleBarUpdate getInitialStateForBubbleBar() { BubbleBarUpdate initialState = mStateChange.getInitialState(); initialState.bubbleBarLocation = mPositioner.getBubbleBarLocation(); + initialState.expanded = mExpanded; + initialState.expandedChanged = mExpanded; // only matters if we're expanded + initialState.selectedBubbleKey = getSelectedBubbleKey(); return initialState; } @@ -598,7 +602,7 @@ public class BubbleData { List<Bubble> removedBubbles = filterAllBubbles(bubble -> userId == bubble.getUser().getIdentifier()); for (Bubble b : removedBubbles) { - doRemove(b.getKey(), Bubbles.DISMISS_USER_REMOVED); + doRemove(b.getKey(), Bubbles.DISMISS_USER_ACCOUNT_REMOVED); } if (!removedBubbles.isEmpty()) { dispatchPendingChanges(); @@ -674,7 +678,7 @@ public class BubbleData { || reason == Bubbles.DISMISS_SHORTCUT_REMOVED || reason == Bubbles.DISMISS_PACKAGE_REMOVED || reason == Bubbles.DISMISS_USER_CHANGED - || reason == Bubbles.DISMISS_USER_REMOVED; + || reason == Bubbles.DISMISS_USER_ACCOUNT_REMOVED; int indexToRemove = indexForKey(key); if (indexToRemove == -1) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java index 4e8afccee40f..c7ccd50af550 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java @@ -482,31 +482,38 @@ public class BubbleExpandedView extends LinearLayout { mPointerWidth, mPointerHeight, true /* pointLeft */)); mRightPointer = new ShapeDrawable(TriangleShape.createHorizontal( mPointerWidth, mPointerHeight, false /* pointLeft */)); - if (mPointerView != null) { - updatePointerView(); - } + updatePointerViewIfExists(); + updateManageButtonIfExists(); + } - if (mManageButton != null) { - int visibility = mManageButton.getVisibility(); - removeView(mManageButton); - ContextThemeWrapper ctw = new ContextThemeWrapper(getContext(), - com.android.internal.R.style.Theme_DeviceDefault_DayNight); - mManageButton = (AlphaOptimizedButton) LayoutInflater.from(ctw).inflate( - R.layout.bubble_manage_button, this /* parent */, false /* attach */); - addView(mManageButton); - mManageButton.setVisibility(visibility); - post(() -> { - int touchAreaHeight = - getResources().getDimensionPixelSize( - R.dimen.bubble_manage_button_touch_area_height); - Rect r = new Rect(); - mManageButton.getHitRect(r); - int extraTouchArea = (touchAreaHeight - r.height()) / 2; - r.top -= extraTouchArea; - r.bottom += extraTouchArea; - setTouchDelegate(new TouchDelegate(r, mManageButton)); - }); + + /** + * Reinflate manage button if {@link #mManageButton} is initialized. + * Does nothing otherwise. + */ + private void updateManageButtonIfExists() { + if (mManageButton == null) { + return; } + int visibility = mManageButton.getVisibility(); + removeView(mManageButton); + ContextThemeWrapper ctw = new ContextThemeWrapper(getContext(), + com.android.internal.R.style.Theme_DeviceDefault_DayNight); + mManageButton = (AlphaOptimizedButton) LayoutInflater.from(ctw).inflate( + R.layout.bubble_manage_button, this /* parent */, false /* attach */); + addView(mManageButton); + mManageButton.setVisibility(visibility); + post(() -> { + int touchAreaHeight = + getResources().getDimensionPixelSize( + R.dimen.bubble_manage_button_touch_area_height); + Rect r = new Rect(); + mManageButton.getHitRect(r); + int extraTouchArea = (touchAreaHeight - r.height()) / 2; + r.top -= extraTouchArea; + r.bottom += extraTouchArea; + setTouchDelegate(new TouchDelegate(r, mManageButton)); + }); } void updateFontSize() { @@ -548,11 +555,18 @@ public class BubbleExpandedView extends LinearLayout { if (mTaskView != null) { mTaskView.setCornerRadius(mCornerRadius); } - updatePointerView(); + updatePointerViewIfExists(); + updateManageButtonIfExists(); } - /** Updates the size and visuals of the pointer. **/ - private void updatePointerView() { + /** + * Updates the size and visuals of the pointer if {@link #mPointerView} is initialized. + * Does nothing otherwise. + */ + private void updatePointerViewIfExists() { + if (mPointerView == null) { + return; + } LayoutParams lp = (LayoutParams) mPointerView.getLayoutParams(); if (mCurrentPointer == mLeftPointer || mCurrentPointer == mRightPointer) { lp.width = mPointerHeight; @@ -1055,7 +1069,7 @@ public class BubbleExpandedView extends LinearLayout { // Post because we need the width of the view post(() -> { mCurrentPointer = showVertically ? onLeft ? mLeftPointer : mRightPointer : mTopPointer; - updatePointerView(); + updatePointerViewIfExists(); if (showVertically) { mPointerPos.y = bubbleCenter - (mPointerWidth / 2f); if (!isRtl) { 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 b0d3cc4a5d5c..3d9bf032c1b0 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 @@ -29,6 +29,7 @@ interface BubbleExpandedViewManager { fun setAppBubbleTaskId(key: String, taskId: Int) fun isStackExpanded(): Boolean fun isShowingAsBubbleBar(): Boolean + fun hideCurrentInputMethod() companion object { /** @@ -73,6 +74,10 @@ interface BubbleExpandedViewManager { override fun isStackExpanded(): Boolean = controller.isStackExpanded override fun isShowingAsBubbleBar(): Boolean = controller.isShowingAsBubbleBar + + override fun hideCurrentInputMethod() { + controller.hideCurrentInputMethod() + } } } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java index fac9bf6e2a4b..ed904e2ff766 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java @@ -2324,7 +2324,6 @@ public class BubbleStackView extends FrameLayout * not. */ void hideCurrentInputMethod() { - mPositioner.setImeVisible(false, 0); mManager.hideCurrentInputMethod(); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTaskViewHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTaskViewHelper.java index 21b70b8e32da..0b66bcb6930e 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTaskViewHelper.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTaskViewHelper.java @@ -161,6 +161,11 @@ public class BubbleTaskViewHelper { // The taskId is saved to use for removeTask, preventing appearance in recent tasks. mTaskId = taskId; + if (mBubble != null && mBubble.isAppBubble()) { + // Let the controller know sooner what the taskId is. + mExpandedViewManager.setAppBubbleTaskId(mBubble.getKey(), mTaskId); + } + // With the task org, the taskAppeared callback will only happen once the task has // already drawn mListener.onTaskCreated(); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java index 1d053f9aab35..82af88d03b19 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java @@ -61,7 +61,7 @@ public interface Bubbles { DISMISS_NOTIF_CANCEL, DISMISS_ACCESSIBILITY_ACTION, DISMISS_NO_LONGER_BUBBLE, DISMISS_USER_CHANGED, DISMISS_GROUP_CANCELLED, DISMISS_INVALID_INTENT, DISMISS_OVERFLOW_MAX_REACHED, DISMISS_SHORTCUT_REMOVED, DISMISS_PACKAGE_REMOVED, - DISMISS_NO_BUBBLE_UP, DISMISS_RELOAD_FROM_DISK, DISMISS_USER_REMOVED, + DISMISS_NO_BUBBLE_UP, DISMISS_RELOAD_FROM_DISK, DISMISS_USER_ACCOUNT_REMOVED, DISMISS_SWITCH_TO_STACK}) @Target({FIELD, LOCAL_VARIABLE, PARAMETER}) @interface DismissReason { @@ -82,7 +82,7 @@ public interface Bubbles { int DISMISS_PACKAGE_REMOVED = 13; int DISMISS_NO_BUBBLE_UP = 14; int DISMISS_RELOAD_FROM_DISK = 15; - int DISMISS_USER_REMOVED = 16; + int DISMISS_USER_ACCOUNT_REMOVED = 16; int DISMISS_SWITCH_TO_STACK = 17; /** Returns a binder that can be passed to an external process to manipulate Bubbles. */ 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 271fb9abce6a..972dce51e02b 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 @@ -82,6 +82,7 @@ public class BubbleBarExpandedView extends FrameLayout implements BubbleTaskView private static final int INVALID_TASK_ID = -1; private BubbleExpandedViewManager mManager; + private BubblePositioner mPositioner; private boolean mIsOverflow; private BubbleTaskViewHelper mBubbleTaskViewHelper; private BubbleBarMenuViewController mMenuViewController; @@ -160,6 +161,7 @@ public class BubbleBarExpandedView extends FrameLayout implements BubbleTaskView boolean isOverflow, @Nullable BubbleTaskView bubbleTaskView) { mManager = expandedViewManager; + mPositioner = positioner; mIsOverflow = isOverflow; if (mIsOverflow) { @@ -207,7 +209,7 @@ public class BubbleBarExpandedView extends FrameLayout implements BubbleTaskView @Override public void onDismissBubble(Bubble bubble) { - mManager.dismissBubble(bubble, Bubbles.DISMISS_USER_REMOVED); + mManager.dismissBubble(bubble, Bubbles.DISMISS_USER_GESTURE); } }); mHandleView.setOnClickListener(view -> { @@ -290,15 +292,27 @@ public class BubbleBarExpandedView extends FrameLayout implements BubbleTaskView } /** - * Hides the current modal menu view or collapses the bubble stack. - * Called from {@link BubbleBarLayerView} + * Hides the current modal menu if it is visible + * @return {@code true} if menu was visible and is hidden */ - public void hideMenuOrCollapse() { + public boolean hideMenuIfVisible() { if (mMenuViewController.isMenuVisible()) { - mMenuViewController.hideMenu(/* animated = */ true); - } else { - mManager.collapseStack(); + mMenuViewController.hideMenu(true /* animated */); + return true; + } + return false; + } + + /** + * Hides the IME if it is visible + * @return {@code true} if IME was visible + */ + public boolean hideImeIfVisible() { + if (mPositioner.isImeVisible()) { + mManager.hideCurrentInputMethod(); + return true; } + return false; } /** Updates the bubble shown in the expanded view. */ 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 123cc7e9d488..badc40997902 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 @@ -132,7 +132,7 @@ public class BubbleBarLayerView extends FrameLayout } }); - setOnClickListener(view -> hideMenuOrCollapse()); + setOnClickListener(view -> hideModalOrCollapse()); } @Override @@ -217,7 +217,7 @@ public class BubbleBarLayerView extends FrameLayout @Override public void onBackPressed() { - hideMenuOrCollapse(); + hideModalOrCollapse(); } }); @@ -344,15 +344,23 @@ public class BubbleBarLayerView extends FrameLayout addView(mDismissView); } - /** Hides the current modal education/menu view, expanded view or collapses the bubble stack */ - private void hideMenuOrCollapse() { + /** Hides the current modal education/menu view, IME or collapses the expanded view */ + private void hideModalOrCollapse() { if (mEducationViewController.isEducationVisible()) { mEducationViewController.hideEducation(/* animated = */ true); - } else if (isExpanded() && mExpandedView != null) { - mExpandedView.hideMenuOrCollapse(); - } else { - mBubbleController.collapseStack(); + return; + } + if (isExpanded() && mExpandedView != null) { + boolean menuHidden = mExpandedView.hideMenuIfVisible(); + if (menuHidden) { + return; + } + boolean imeHidden = mExpandedView.hideImeIfVisible(); + if (imeHidden) { + return; + } } + mBubbleController.collapseStack(); } /** Updates the expanded view size and position. */ diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java index ad01d0fa311a..f4ac5f260fcd 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java @@ -220,6 +220,8 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged final int mDisplayId; final InsetsState mInsetsState = new InsetsState(); @InsetsType int mRequestedVisibleTypes = WindowInsets.Type.defaultVisible(); + boolean mImeRequestedVisible = + (WindowInsets.Type.defaultVisible() & WindowInsets.Type.ime()) != 0; InsetsSourceControl mImeSourceControl = null; int mAnimationDirection = DIRECTION_NONE; ValueAnimator mAnimation = null; @@ -247,8 +249,10 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged return; } - updateImeVisibility(insetsState.isSourceOrDefaultVisible(InsetsSource.ID_IME, - WindowInsets.Type.ime())); + if (!android.view.inputmethod.Flags.refactorInsetsController()) { + updateImeVisibility(insetsState.isSourceOrDefaultVisible(InsetsSource.ID_IME, + WindowInsets.Type.ime())); + } final InsetsSource newSource = insetsState.peekSource(InsetsSource.ID_IME); final Rect newFrame = newSource != null ? newSource.getFrame() : null; @@ -287,32 +291,63 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged dispatchImeControlTargetChanged(mDisplayId, hasImeSourceControl); } - if (hasImeSourceControl) { + boolean pendingImeStartAnimation = false; + boolean canAnimate; + if (android.view.inputmethod.Flags.refactorInsetsController()) { + canAnimate = hasImeSourceControl && imeSourceControl.getLeash() != null; + } else { + canAnimate = hasImeSourceControl; + } + + boolean positionChanged = false; + if (canAnimate) { if (mAnimation != null) { final Point lastSurfacePosition = hadImeSourceControl ? mImeSourceControl.getSurfacePosition() : null; - final boolean positionChanged = - !imeSourceControl.getSurfacePosition().equals(lastSurfacePosition); - if (positionChanged) { - startAnimation(mImeShowing, true /* forceRestart */, - SoftInputShowHideReason.DISPLAY_CONTROLS_CHANGED); - } + positionChanged = !imeSourceControl.getSurfacePosition().equals( + lastSurfacePosition); } else { if (!haveSameLeash(mImeSourceControl, imeSourceControl)) { applyVisibilityToLeash(imeSourceControl); + + if (android.view.inputmethod.Flags.refactorInsetsController()) { + pendingImeStartAnimation = true; + } } if (!mImeShowing) { removeImeSurface(); } } - } else if (mAnimation != null) { + } else if (!android.view.inputmethod.Flags.refactorInsetsController() + && mAnimation != null) { + // we don"t want to cancel the hide animation, when the control is lost, but + // continue the bar to slide to the end (even without visible IME) mAnimation.cancel(); } + if (positionChanged) { + if (android.view.inputmethod.Flags.refactorInsetsController()) { + // For showing the IME, the leash has to be available first. Hiding + // the IME happens directly via {@link #hideInsets} (triggered by + // setImeInputTargetRequestedVisibility) while the leash is not gone + // yet. + pendingImeStartAnimation = true; + } else { + startAnimation(mImeShowing, true /* forceRestart */, + SoftInputShowHideReason.DISPLAY_CONTROLS_CHANGED); + } + } if (hadImeSourceControl && mImeSourceControl != imeSourceControl) { mImeSourceControl.release(SurfaceControl::release); } mImeSourceControl = imeSourceControl; + + if (android.view.inputmethod.Flags.refactorInsetsController()) { + if (pendingImeStartAnimation) { + startAnimation(true, true /* forceRestart */, + null /* statsToken */); + } + } } private void applyVisibilityToLeash(InsetsSourceControl imeSourceControl) { @@ -354,6 +389,20 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged // Do nothing } + @Override + // TODO(b/335404678): pass control target + public void setImeInputTargetRequestedVisibility(boolean visible) { + if (android.view.inputmethod.Flags.refactorInsetsController()) { + mImeRequestedVisible = visible; + // In the case that the IME becomes visible, but we have the control with leash + // already (e.g., when focussing an editText in activity B, while and editText in + // activity A is focussed), we will not get a call of #insetsControlChanged, and + // therefore have to start the show animation from here + startAnimation(mImeRequestedVisible /* show */, false /* forceRestart */, + null /* TODO statsToken */); + } + } + /** * Sends the local visibility state back to window manager. Needed for legacy adjustForIme. */ @@ -402,6 +451,12 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged private void startAnimation(final boolean show, final boolean forceRestart, @NonNull final ImeTracker.Token statsToken) { + if (android.view.inputmethod.Flags.refactorInsetsController()) { + if (mImeSourceControl == null || mImeSourceControl.getLeash() == null) { + if (DEBUG) Slog.d(TAG, "No leash available, not starting the animation."); + return; + } + } final InsetsSource imeSource = mInsetsState.peekSource(InsetsSource.ID_IME); if (imeSource == null || mImeSourceControl == null) { ImeTracker.forLogging().onFailed(statsToken, ImeTracker.PHASE_WM_ANIMATION_CREATE); @@ -463,10 +518,13 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged mAnimation.addUpdateListener(animation -> { SurfaceControl.Transaction t = mTransactionPool.acquire(); float value = (float) animation.getAnimatedValue(); - t.setPosition(mImeSourceControl.getLeash(), x, value); - final float alpha = (mAnimateAlpha || isFloating) - ? (value - hiddenY) / (shownY - hiddenY) : 1.f; - t.setAlpha(mImeSourceControl.getLeash(), alpha); + if (!android.view.inputmethod.Flags.refactorInsetsController() || ( + mImeSourceControl != null && mImeSourceControl.getLeash() != null)) { + t.setPosition(mImeSourceControl.getLeash(), x, value); + final float alpha = (mAnimateAlpha || isFloating) + ? (value - hiddenY) / (shownY - hiddenY) : 1.f; + t.setAlpha(mImeSourceControl.getLeash(), alpha); + } dispatchPositionChanged(mDisplayId, imeTop(value), t); t.apply(); mTransactionPool.release(t); @@ -480,8 +538,10 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged @Override public void onAnimationStart(Animator animation) { + ValueAnimator valueAnimator = (ValueAnimator) animation; + float value = (float) valueAnimator.getAnimatedValue(); SurfaceControl.Transaction t = mTransactionPool.acquire(); - t.setPosition(mImeSourceControl.getLeash(), x, startY); + t.setPosition(mImeSourceControl.getLeash(), x, value); if (DEBUG) { Slog.d(TAG, "onAnimationStart d:" + mDisplayId + " top:" + imeTop(hiddenY) + "->" + imeTop(shownY) @@ -491,7 +551,7 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged imeTop(shownY), mAnimationDirection == DIRECTION_SHOW, isFloating, t); mAnimateAlpha = (flags & ImePositionProcessor.IME_ANIMATION_NO_ALPHA) == 0; final float alpha = (mAnimateAlpha || isFloating) - ? (startY - hiddenY) / (shownY - hiddenY) + ? (value - hiddenY) / (shownY - hiddenY) : 1.f; t.setAlpha(mImeSourceControl.getLeash(), alpha); if (mAnimationDirection == DIRECTION_SHOW) { @@ -502,7 +562,7 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged if (DEBUG_IME_VISIBILITY) { EventLog.writeEvent(IMF_IME_REMOTE_ANIM_START, mStatsToken != null ? mStatsToken.getTag() : ImeTracker.TOKEN_NONE, - mDisplayId, mAnimationDirection, alpha, startY , endY, + mDisplayId, mAnimationDirection, alpha, value, endY, Objects.toString(mImeSourceControl.getLeash()), Objects.toString(mImeSourceControl.getInsetsHint()), Objects.toString(mImeSourceControl.getSurfacePosition()), @@ -525,17 +585,25 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged @Override public void onAnimationEnd(Animator animation) { + boolean hasLeash = + mImeSourceControl != null && mImeSourceControl.getLeash() != null; if (DEBUG) Slog.d(TAG, "onAnimationEnd " + mCancelled); SurfaceControl.Transaction t = mTransactionPool.acquire(); if (!mCancelled) { - t.setPosition(mImeSourceControl.getLeash(), x, endY); - t.setAlpha(mImeSourceControl.getLeash(), 1.f); + if (!android.view.inputmethod.Flags.refactorInsetsController() + || hasLeash) { + t.setPosition(mImeSourceControl.getLeash(), x, endY); + t.setAlpha(mImeSourceControl.getLeash(), 1.f); + } } dispatchEndPositioning(mDisplayId, mCancelled, t); if (mAnimationDirection == DIRECTION_HIDE && !mCancelled) { ImeTracker.forLogging().onProgress(mStatsToken, ImeTracker.PHASE_WM_ANIMATION_RUNNING); - t.hide(mImeSourceControl.getLeash()); + if (!android.view.inputmethod.Flags.refactorInsetsController() + || hasLeash) { + t.hide(mImeSourceControl.getLeash()); + } removeImeSurface(); ImeTracker.forLogging().onHidden(mStatsToken); } else if (mAnimationDirection == DIRECTION_SHOW && !mCancelled) { @@ -548,9 +616,13 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged EventLog.writeEvent(IMF_IME_REMOTE_ANIM_END, mStatsToken != null ? mStatsToken.getTag() : ImeTracker.TOKEN_NONE, mDisplayId, mAnimationDirection, endY, - Objects.toString(mImeSourceControl.getLeash()), - Objects.toString(mImeSourceControl.getInsetsHint()), - Objects.toString(mImeSourceControl.getSurfacePosition()), + Objects.toString( + mImeSourceControl != null ? mImeSourceControl.getLeash() + : "null"), + Objects.toString(mImeSourceControl != null + ? mImeSourceControl.getInsetsHint() : "null"), + Objects.toString(mImeSourceControl != null + ? mImeSourceControl.getSurfacePosition() : "null"), Objects.toString(mImeFrame)); } t.apply(); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayInsetsController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayInsetsController.java index 55dc793cc3b6..1fb0e1745e3e 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayInsetsController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayInsetsController.java @@ -199,6 +199,16 @@ public class DisplayInsetsController implements DisplayController.OnDisplaysChan } } + private void setImeInputTargetRequestedVisibility(boolean visible) { + CopyOnWriteArrayList<OnInsetsChangedListener> listeners = mListeners.get(mDisplayId); + if (listeners == null) { + return; + } + for (OnInsetsChangedListener listener : listeners) { + listener.setImeInputTargetRequestedVisibility(visible); + } + } + @BinderThread private class DisplayWindowInsetsControllerImpl extends IDisplayWindowInsetsController.Stub { @@ -240,6 +250,14 @@ public class DisplayInsetsController implements DisplayController.OnDisplaysChan PerDisplay.this.hideInsets(types, fromIme, statsToken); }); } + + @Override + public void setImeInputTargetRequestedVisibility(boolean visible) + throws RemoteException { + mMainExecutor.execute(() -> { + PerDisplay.this.setImeInputTargetRequestedVisibility(visible); + }); + } } } @@ -291,5 +309,12 @@ public class DisplayInsetsController implements DisplayController.OnDisplaysChan */ default void hideInsets(@InsetsType int types, boolean fromIme, @Nullable ImeTracker.Token statsToken) {} + + /** + * Called to set the requested visibility of the IME in DisplayImeController. Invoked by + * {@link com.android.server.wm.DisplayContent.RemoteInsetsControlTarget}. + * @param visible requested status of the IME + */ + default void setImeInputTargetRequestedVisibility(boolean visible) {} } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/ExecutorUtils.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/ExecutorUtils.java deleted file mode 100644 index b29058b1f204..000000000000 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/ExecutorUtils.java +++ /dev/null @@ -1,64 +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.wm.shell.common; - -import android.Manifest; -import android.util.Slog; - -import java.util.function.Consumer; - -/** - * Helpers for working with executors - */ -public class ExecutorUtils { - - /** - * Checks that the caller has the MANAGE_ACTIVITY_TASKS permission and executes the given - * callback. - */ - public static <T> void executeRemoteCallWithTaskPermission(RemoteCallable<T> controllerInstance, - String log, Consumer<T> callback) { - executeRemoteCallWithTaskPermission(controllerInstance, log, callback, - false /* blocking */); - } - - /** - * Checks that the caller has the MANAGE_ACTIVITY_TASKS permission and executes the given - * callback. - */ - public static <T> void executeRemoteCallWithTaskPermission(RemoteCallable<T> controllerInstance, - String log, Consumer<T> callback, boolean blocking) { - if (controllerInstance == null) return; - - final RemoteCallable<T> controller = controllerInstance; - controllerInstance.getContext().enforceCallingPermission( - Manifest.permission.MANAGE_ACTIVITY_TASKS, log); - if (blocking) { - try { - controllerInstance.getRemoteCallExecutor().executeBlocking(() -> { - callback.accept((T) controller); - }); - } catch (InterruptedException e) { - Slog.e("ExecutorUtils", "Remote call failed", e); - } - } else { - controllerInstance.getRemoteCallExecutor().execute(() -> { - callback.accept((T) controller); - }); - } - } -} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/ExternalInterfaceBinder.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/ExternalInterfaceBinder.java index aa5b0cb628e1..d6f4d81b44f3 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/ExternalInterfaceBinder.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/ExternalInterfaceBinder.java @@ -16,7 +16,11 @@ package com.android.wm.shell.common; +import android.Manifest; import android.os.IBinder; +import android.util.Slog; + +import java.util.function.Consumer; /** * An interface for binders which can be registered to be sent to other processes. @@ -31,4 +35,40 @@ public interface ExternalInterfaceBinder { * Returns the IBinder to send. */ IBinder asBinder(); + + /** + * Checks that the caller has the MANAGE_ACTIVITY_TASKS permission and executes the given + * callback. + */ + default <T> void executeRemoteCallWithTaskPermission(RemoteCallable<T> controllerInstance, + String log, Consumer<T> callback) { + executeRemoteCallWithTaskPermission(controllerInstance, log, callback, + false /* blocking */); + } + + /** + * Checks that the caller has the MANAGE_ACTIVITY_TASKS permission and executes the given + * callback. + */ + default <T> void executeRemoteCallWithTaskPermission(RemoteCallable<T> controllerInstance, + String log, Consumer<T> callback, boolean blocking) { + if (controllerInstance == null) return; + + final RemoteCallable<T> controller = controllerInstance; + controllerInstance.getContext().enforceCallingPermission( + Manifest.permission.MANAGE_ACTIVITY_TASKS, log); + if (blocking) { + try { + controllerInstance.getRemoteCallExecutor().executeBlocking(() -> { + callback.accept((T) controller); + }); + } catch (InterruptedException e) { + Slog.e("ExternalInterfaceBinder", "Remote call failed", e); + } + } else { + controllerInstance.getRemoteCallExecutor().execute(() -> { + callback.accept((T) controller); + }); + } + } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/RemoteCallable.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/RemoteCallable.java index 30f535ba940c..0d90fb7e60fb 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/RemoteCallable.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/RemoteCallable.java @@ -19,7 +19,7 @@ package com.android.wm.shell.common; import android.content.Context; /** - * An interface for controllers that can receive remote calls. + * An interface for controllers (of type T) that can receive remote calls. */ public interface RemoteCallable<T> { /** diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubbleInfo.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubbleInfo.java index 24608d651d06..829af08e612a 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubbleInfo.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubbleInfo.java @@ -45,10 +45,12 @@ public class BubbleInfo implements Parcelable { private Icon mIcon; @Nullable private String mTitle; + @Nullable + private String mAppName; private boolean mIsImportantConversation; public BubbleInfo(String key, int flags, @Nullable String shortcutId, @Nullable Icon icon, - int userId, String packageName, @Nullable String title, + int userId, String packageName, @Nullable String title, @Nullable String appName, boolean isImportantConversation) { mKey = key; mFlags = flags; @@ -57,6 +59,7 @@ public class BubbleInfo implements Parcelable { mUserId = userId; mPackageName = packageName; mTitle = title; + mAppName = appName; mIsImportantConversation = isImportantConversation; } @@ -68,6 +71,7 @@ public class BubbleInfo implements Parcelable { mUserId = source.readInt(); mPackageName = source.readString(); mTitle = source.readString(); + mAppName = source.readString(); mIsImportantConversation = source.readBoolean(); } @@ -102,6 +106,11 @@ public class BubbleInfo implements Parcelable { return mTitle; } + @Nullable + public String getAppName() { + return mAppName; + } + public boolean isImportantConversation() { return mIsImportantConversation; } @@ -161,6 +170,7 @@ public class BubbleInfo implements Parcelable { parcel.writeInt(mUserId); parcel.writeString(mPackageName); parcel.writeString(mTitle); + parcel.writeString(mAppName); parcel.writeBoolean(mIsImportantConversation); } diff --git a/packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingConfig.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/common/desktopmode/DesktopModeTransitionSource.aidl index 23dbc26d40d3..c968e809bf61 100644 --- a/packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingConfig.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/desktopmode/DesktopModeTransitionSource.aidl @@ -14,8 +14,6 @@ * limitations under the License. */ -package com.android.systemui.recordissue +package com.android.wm.shell.common.desktopmode; -import com.android.traceur.TraceUtils.PresetTraceType - -data class IssueRecordingConfig(val screenRecord: Boolean, val traceType: PresetTraceType) +parcelable DesktopModeTransitionSource;
\ No newline at end of file diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/desktopmode/DesktopModeTransitionSource.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/common/desktopmode/DesktopModeTransitionSource.kt new file mode 100644 index 000000000000..dbbf178613b5 --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/desktopmode/DesktopModeTransitionSource.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.wm.shell.common.desktopmode + +import android.os.Parcel +import android.os.Parcelable + +/** Transition source types for Desktop Mode. */ +enum class DesktopModeTransitionSource : Parcelable { + /** Transitions that originated as a consequence of task dragging. */ + TASK_DRAG, + /** Transitions that originated from an app from Overview. */ + APP_FROM_OVERVIEW, + /** Transitions that originated from app handle menu button */ + APP_HANDLE_MENU_BUTTON, + /** Transitions that originated as a result of keyboard shortcuts. */ + KEYBOARD_SHORTCUT, + /** Transitions with source unknown. */ + UNKNOWN; + + override fun describeContents(): Int { + return 0 + } + + override fun writeToParcel(dest: Parcel, flags: Int) { + dest.writeString(name) + } + + companion object { + @JvmField + val CREATOR = + object : Parcelable.Creator<DesktopModeTransitionSource> { + override fun createFromParcel(parcel: Parcel): DesktopModeTransitionSource { + return parcel.readString()?.let { valueOf(it) } ?: UNKNOWN + } + + override fun newArray(size: Int) = arrayOfNulls<DesktopModeTransitionSource>(size) + } + } +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/IPip.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/IPip.aidl index b5f25433f9aa..e77987963b48 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/IPip.aidl +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/IPip.aidl @@ -53,9 +53,11 @@ interface IPip { * @param destinationBounds the destination bounds the PiP window lands into * @param overlay an optional overlay to fade out after entering PiP * @param appBounds the bounds used to set the buffer size of the optional content overlay + * @param sourceRectHint the bounds to show in the transition to PiP */ oneway void stopSwipePipToHome(int taskId, in ComponentName componentName, - in Rect destinationBounds, in SurfaceControl overlay, in Rect appBounds) = 2; + in Rect destinationBounds, in SurfaceControl overlay, in Rect appBounds, + in Rect sourceRectHint) = 2; /** * Notifies the swiping Activity to PiP onto home transition is aborted 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 579a7943829e..a09720dd6a70 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 @@ -22,6 +22,7 @@ import android.app.WindowConfiguration import android.content.ComponentName import android.content.Context import android.content.pm.PackageManager +import android.graphics.Rect import android.os.RemoteException import android.os.SystemProperties import android.util.DisplayMetrics @@ -138,6 +139,30 @@ object PipUtils { } } + + /** + * Returns a fake source rect hint for animation purposes when app-provided one is invalid. + * Resulting adjusted source rect hint lets the app icon in the content overlay to stay visible. + */ + @JvmStatic + fun getEnterPipWithOverlaySrcRectHint(appBounds: Rect, aspectRatio: Float): Rect { + val appBoundsAspRatio = appBounds.width().toFloat() / appBounds.height() + val width: Int + val height: Int + var left = 0 + var top = 0 + if (appBoundsAspRatio < aspectRatio) { + width = appBounds.width() + height = Math.round(width / aspectRatio) + top = (appBounds.height() - height) / 2 + } else { + height = appBounds.height() + width = Math.round(height * aspectRatio) + left = (appBounds.width() - width) / 2 + } + return Rect(left, top, left + width, top + height) + } + private var isPip2ExperimentEnabled: Boolean? = null /** diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java index 2234041b8c9d..c2242a8b87fa 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java @@ -336,6 +336,11 @@ public class DividerView extends FrameLayout implements View.OnTouchListener { setTouching(); mStartPos = touchPos; mMoving = false; + // This triggers initialization of things like the resize veil in preparation for + // showing it when the user moves the divider past the slop, and has to be done + // before onStartDragging() which starts the jank interaction tracing + mSplitLayout.updateDividerBounds(mSplitLayout.getDividerPosition(), + false /* shouldUseParallaxEffect */); mSplitLayout.onStartDragging(); break; case MotionEvent.ACTION_MOVE: diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitWindowManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitWindowManager.java index 8fb9bda539a0..5d121c23c6e1 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitWindowManager.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitWindowManager.java @@ -143,6 +143,8 @@ public final class SplitWindowManager extends WindowlessWindowManager { /** * Releases the surface control of the current {@link DividerView} and tear down the view * hierarchy. + * @param t If supplied, the surface removal will be bundled with this Transaction. If + * called with null, removes the surface immediately. */ void release(@Nullable SurfaceControl.Transaction t) { if (mDividerView != null) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/ReachabilityEduWindowManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/ReachabilityEduWindowManager.java index 835f1af85c51..07082a558744 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/ReachabilityEduWindowManager.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/ReachabilityEduWindowManager.java @@ -53,7 +53,7 @@ class ReachabilityEduWindowManager extends CompatUIWindowManagerAbstract { private final ShellExecutor mMainExecutor; - private boolean mIsActivityLetterboxed; + private boolean mIsLetterboxDoubleTapEnabled; private int mLetterboxVerticalPosition; @@ -91,7 +91,7 @@ class ReachabilityEduWindowManager extends CompatUIWindowManagerAbstract { Function<Integer, Integer> disappearTimeSupplier) { super(context, taskInfo, syncQueue, taskListener, displayLayout); final AppCompatTaskInfo appCompatTaskInfo = taskInfo.appCompatTaskInfo; - mIsActivityLetterboxed = appCompatTaskInfo.isLetterboxDoubleTapEnabled; + mIsLetterboxDoubleTapEnabled = appCompatTaskInfo.isLetterboxDoubleTapEnabled; mLetterboxVerticalPosition = appCompatTaskInfo.topActivityLetterboxVerticalPosition; mLetterboxHorizontalPosition = appCompatTaskInfo.topActivityLetterboxHorizontalPosition; mTopActivityLetterboxWidth = appCompatTaskInfo.topActivityLetterboxWidth; @@ -119,7 +119,7 @@ class ReachabilityEduWindowManager extends CompatUIWindowManagerAbstract { @Override protected boolean eligibleToShowLayout() { - return mIsActivityLetterboxed + return mIsLetterboxDoubleTapEnabled && (mLetterboxVerticalPosition != -1 || mLetterboxHorizontalPosition != -1); } @@ -142,13 +142,13 @@ class ReachabilityEduWindowManager extends CompatUIWindowManagerAbstract { @Override public boolean updateCompatInfo(TaskInfo taskInfo, ShellTaskOrganizer.TaskListener taskListener, boolean canShow) { - final boolean prevIsActivityLetterboxed = mIsActivityLetterboxed; + final boolean prevIsLetterboxDoubleTapEnabled = mIsLetterboxDoubleTapEnabled; final int prevLetterboxVerticalPosition = mLetterboxVerticalPosition; final int prevLetterboxHorizontalPosition = mLetterboxHorizontalPosition; final int prevTopActivityLetterboxWidth = mTopActivityLetterboxWidth; final int prevTopActivityLetterboxHeight = mTopActivityLetterboxHeight; final AppCompatTaskInfo appCompatTaskInfo = taskInfo.appCompatTaskInfo; - mIsActivityLetterboxed = appCompatTaskInfo.isLetterboxDoubleTapEnabled; + mIsLetterboxDoubleTapEnabled = appCompatTaskInfo.isLetterboxDoubleTapEnabled; mLetterboxVerticalPosition = appCompatTaskInfo.topActivityLetterboxVerticalPosition; mLetterboxHorizontalPosition = appCompatTaskInfo.topActivityLetterboxHorizontalPosition; mTopActivityLetterboxWidth = appCompatTaskInfo.topActivityLetterboxWidth; @@ -162,7 +162,7 @@ class ReachabilityEduWindowManager extends CompatUIWindowManagerAbstract { mHasLetterboxSizeChanged = prevTopActivityLetterboxWidth != mTopActivityLetterboxWidth || prevTopActivityLetterboxHeight != mTopActivityLetterboxHeight; - if (mHasUserDoubleTapped || prevIsActivityLetterboxed != mIsActivityLetterboxed + if (mHasUserDoubleTapped || prevIsLetterboxDoubleTapEnabled != mIsLetterboxDoubleTapEnabled || prevLetterboxVerticalPosition != mLetterboxVerticalPosition || prevLetterboxHorizontalPosition != mLetterboxHorizontalPosition || prevTopActivityLetterboxWidth != mTopActivityLetterboxWidth @@ -249,7 +249,7 @@ class ReachabilityEduWindowManager extends CompatUIWindowManagerAbstract { && (mLetterboxVerticalPosition == REACHABILITY_LEFT_OR_UP_POSITION || mLetterboxVerticalPosition == REACHABILITY_RIGHT_OR_BOTTOM_POSITION)); - if (mIsActivityLetterboxed && (eligibleForDisplayHorizontalEducation + if (mIsLetterboxDoubleTapEnabled && (eligibleForDisplayHorizontalEducation || eligibleForDisplayVerticalEducation)) { int availableWidth = getTaskBounds().width() - mTopActivityLetterboxWidth; int availableHeight = getTaskBounds().height() - mTopActivityLetterboxHeight; 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 991fbafed296..609e5af5c5b0 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 @@ -87,6 +87,7 @@ import com.android.wm.shell.performance.PerfHintController; import com.android.wm.shell.recents.RecentTasks; import com.android.wm.shell.recents.RecentTasksController; import com.android.wm.shell.recents.RecentsTransitionHandler; +import com.android.wm.shell.recents.TaskStackTransitionObserver; import com.android.wm.shell.shared.DesktopModeStatus; import com.android.wm.shell.shared.ShellTransitions; import com.android.wm.shell.shared.annotations.ShellAnimationThread; @@ -619,12 +620,13 @@ public abstract class WMShellBaseModule { TaskStackListenerImpl taskStackListener, ActivityTaskManager activityTaskManager, Optional<DesktopModeTaskRepository> desktopModeTaskRepository, + TaskStackTransitionObserver taskStackTransitionObserver, @ShellMainThread ShellExecutor mainExecutor ) { return Optional.ofNullable( RecentTasksController.create(context, shellInit, shellController, shellCommandHandler, taskStackListener, activityTaskManager, - desktopModeTaskRepository, mainExecutor)); + desktopModeTaskRepository, taskStackTransitionObserver, mainExecutor)); } @BindsOptionalOf @@ -924,6 +926,19 @@ public abstract class WMShellBaseModule { } // + // Task Stack + // + + @WMSingleton + @Provides + static TaskStackTransitionObserver provideTaskStackTransitionObserver( + Lazy<Transitions> transitions, + ShellInit shellInit + ) { + return new TaskStackTransitionObserver(transitions, shellInit); + } + + // // Misc // 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 12bbd51b968d..87bd84017dee 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 @@ -121,9 +121,9 @@ import java.util.Optional; */ @Module( includes = { - WMShellBaseModule.class, - PipModule.class, - ShellBackAnimationModule.class, + WMShellBaseModule.class, + PipModule.class, + ShellBackAnimationModule.class, }) public abstract class WMShellModule { @@ -664,7 +664,8 @@ public abstract class WMShellModule { @Provides static Object provideIndependentShellComponentsToCreate( DragAndDropController dragAndDropController, - Optional<DesktopTasksTransitionObserver> desktopTasksTransitionObserverOptional) { + Optional<DesktopTasksTransitionObserver> desktopTasksTransitionObserverOptional + ) { return new Object(); } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopMode.java b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopMode.java index df1b06225fda..31c8f1e45007 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopMode.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopMode.java @@ -18,6 +18,7 @@ package com.android.wm.shell.desktopmode; import android.graphics.Region; +import com.android.wm.shell.common.desktopmode.DesktopModeTransitionSource; import com.android.wm.shell.shared.annotations.ExternalThread; import java.util.concurrent.Executor; @@ -49,10 +50,10 @@ public interface DesktopMode { /** Called when requested to go to desktop mode from the current focused app. */ - void moveFocusedTaskToDesktop(int displayId); + void moveFocusedTaskToDesktop(int displayId, DesktopModeTransitionSource transitionSource); /** Called when requested to go to fullscreen from the current focused desktop app. */ - void moveFocusedTaskToFullscreen(int displayId); + void moveFocusedTaskToFullscreen(int displayId, DesktopModeTransitionSource transitionSource); /** Called when requested to go to split screen from the current focused desktop app. */ void moveFocusedTaskToStageSplit(int displayId, boolean leftOrTop); 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 109868daae7d..9192e6ed3175 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 @@ -187,7 +187,10 @@ class DesktopModeEventLogger { KEYBOARD_SHORTCUT_ENTER( FrameworkStatsLog.DESKTOP_MODE_UICHANGED__ENTER_REASON__KEYBOARD_SHORTCUT_ENTER ), - SCREEN_ON(FrameworkStatsLog.DESKTOP_MODE_UICHANGED__ENTER_REASON__SCREEN_ON) + SCREEN_ON(FrameworkStatsLog.DESKTOP_MODE_UICHANGED__ENTER_REASON__SCREEN_ON), + APP_FROM_OVERVIEW( + FrameworkStatsLog.DESKTOP_MODE_UICHANGED__ENTER_REASON__APP_FROM_OVERVIEW + ), } /** @@ -204,7 +207,7 @@ class DesktopModeEventLogger { FrameworkStatsLog.DESKTOP_MODE_UICHANGED__EXIT_REASON__KEYBOARD_SHORTCUT_EXIT ), RETURN_HOME_OR_OVERVIEW( - FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__RETURN_HOME + FrameworkStatsLog.DESKTOP_MODE_UICHANGED__EXIT_REASON__RETURN_HOME_OR_OVERVIEW ), TASK_FINISHED(FrameworkStatsLog.DESKTOP_MODE_UICHANGED__EXIT_REASON__TASK_FINISHED), SCREEN_OFF(FrameworkStatsLog.DESKTOP_MODE_UICHANGED__EXIT_REASON__SCREEN_OFF) diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserver.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserver.kt index 5d8e34022841..cee2d92244cc 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserver.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserver.kt @@ -38,6 +38,12 @@ import com.android.internal.logging.InstanceIdSequence import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.EnterReason import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.ExitReason import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.TaskUpdate +import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_EXIT_DESKTOP_MODE_HANDLE_MENU_BUTTON +import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_EXIT_DESKTOP_MODE_KEYBOARD_SHORTCUT +import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_EXIT_DESKTOP_MODE_TASK_DRAG +import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_ENTER_DESKTOP_FROM_APP_FROM_OVERVIEW +import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_ENTER_DESKTOP_FROM_APP_HANDLE_MENU_BUTTON +import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_ENTER_DESKTOP_FROM_KEYBOARD_SHORTCUT import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE import com.android.wm.shell.shared.DesktopModeStatus import com.android.wm.shell.shared.TransitionUtil @@ -304,11 +310,12 @@ class DesktopModeLoggerTransitionObserver( /** Get [EnterReason] for this session enter */ private fun getEnterReason(transitionInfo: TransitionInfo): EnterReason { - // TODO(b/326231756) - Add support for missing enter reasons return when (transitionInfo.type) { WindowManager.TRANSIT_WAKE -> EnterReason.SCREEN_ON Transitions.TRANSIT_DESKTOP_MODE_END_DRAG_TO_DESKTOP -> EnterReason.APP_HANDLE_DRAG - Transitions.TRANSIT_MOVE_TO_DESKTOP -> EnterReason.APP_HANDLE_MENU_BUTTON + TRANSIT_ENTER_DESKTOP_FROM_APP_HANDLE_MENU_BUTTON -> EnterReason.APP_HANDLE_MENU_BUTTON + TRANSIT_ENTER_DESKTOP_FROM_APP_FROM_OVERVIEW -> EnterReason.APP_FROM_OVERVIEW + TRANSIT_ENTER_DESKTOP_FROM_KEYBOARD_SHORTCUT -> EnterReason.KEYBOARD_SHORTCUT_ENTER WindowManager.TRANSIT_OPEN -> EnterReason.APP_FREEFORM_INTENT else -> EnterReason.UNKNOWN_ENTER } @@ -316,11 +323,14 @@ class DesktopModeLoggerTransitionObserver( /** Get [ExitReason] for this session exit */ private fun getExitReason(transitionInfo: TransitionInfo): ExitReason { - // TODO(b/326231756) - Add support for missing exit reasons return when { transitionInfo.type == WindowManager.TRANSIT_SLEEP -> ExitReason.SCREEN_OFF transitionInfo.type == WindowManager.TRANSIT_CLOSE -> ExitReason.TASK_FINISHED - transitionInfo.type == Transitions.TRANSIT_EXIT_DESKTOP_MODE -> ExitReason.DRAG_TO_EXIT + transitionInfo.type == TRANSIT_EXIT_DESKTOP_MODE_TASK_DRAG -> ExitReason.DRAG_TO_EXIT + transitionInfo.type == TRANSIT_EXIT_DESKTOP_MODE_HANDLE_MENU_BUTTON -> + ExitReason.APP_HANDLE_MENU_BUTTON_EXIT + transitionInfo.type == TRANSIT_EXIT_DESKTOP_MODE_KEYBOARD_SHORTCUT -> + ExitReason.KEYBOARD_SHORTCUT_EXIT transitionInfo.isRecentsTransition() -> ExitReason.RETURN_HOME_OR_OVERVIEW else -> ExitReason.UNKNOWN_EXIT } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeShellCommandHandler.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeShellCommandHandler.kt index bc27f341b566..1a6ca0efa748 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeShellCommandHandler.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeShellCommandHandler.kt @@ -16,7 +16,7 @@ package com.android.wm.shell.desktopmode -import android.window.WindowContainerTransaction +import com.android.wm.shell.common.desktopmode.DesktopModeTransitionSource.UNKNOWN import com.android.wm.shell.sysui.ShellCommandHandler import java.io.PrintWriter @@ -64,7 +64,7 @@ class DesktopModeShellCommandHandler(private val controller: DesktopTasksControl return false } - return controller.moveToDesktop(taskId, WindowContainerTransaction()) + return controller.moveToDesktop(taskId, transitionSource = UNKNOWN) } private fun runMoveToNextDisplay(args: Array<String>, pw: PrintWriter): Boolean { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTransitionTypes.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTransitionTypes.kt new file mode 100644 index 000000000000..b24bd10eaa0d --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTransitionTypes.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.view.WindowManager.TransitionType +import com.android.wm.shell.common.desktopmode.DesktopModeTransitionSource +import com.android.wm.shell.transition.Transitions.TRANSIT_DESKTOP_MODE_TYPES + +/** + * Contains desktop mode [TransitionType]s (extended from [TRANSIT_DESKTOP_MODE_TYPES]) and helper + * methods. + */ +object DesktopModeTransitionTypes { + + const val TRANSIT_ENTER_DESKTOP_FROM_APP_HANDLE_MENU_BUTTON = TRANSIT_DESKTOP_MODE_TYPES + 1 + const val TRANSIT_ENTER_DESKTOP_FROM_APP_FROM_OVERVIEW = TRANSIT_DESKTOP_MODE_TYPES + 2 + const val TRANSIT_ENTER_DESKTOP_FROM_KEYBOARD_SHORTCUT = TRANSIT_DESKTOP_MODE_TYPES + 3 + const val TRANSIT_ENTER_DESKTOP_FROM_UNKNOWN = TRANSIT_DESKTOP_MODE_TYPES + 4 + const val TRANSIT_EXIT_DESKTOP_MODE_TASK_DRAG = TRANSIT_DESKTOP_MODE_TYPES + 5 + const val TRANSIT_EXIT_DESKTOP_MODE_HANDLE_MENU_BUTTON = TRANSIT_DESKTOP_MODE_TYPES + 6 + const val TRANSIT_EXIT_DESKTOP_MODE_KEYBOARD_SHORTCUT = TRANSIT_DESKTOP_MODE_TYPES + 7 + const val TRANSIT_EXIT_DESKTOP_MODE_UNKNOWN = TRANSIT_DESKTOP_MODE_TYPES + 8 + + /** Return whether the [TransitionType] corresponds to a transition to enter desktop mode. */ + @JvmStatic + fun @receiver:TransitionType Int.isEnterDesktopModeTransition(): Boolean { + return this in + listOf( + TRANSIT_ENTER_DESKTOP_FROM_APP_HANDLE_MENU_BUTTON, + TRANSIT_ENTER_DESKTOP_FROM_APP_FROM_OVERVIEW, + TRANSIT_ENTER_DESKTOP_FROM_KEYBOARD_SHORTCUT, + TRANSIT_ENTER_DESKTOP_FROM_UNKNOWN + ) + } + + /** + * Returns corresponding desktop mode enter [TransitionType] for a + * [DesktopModeTransitionSource]. + */ + @JvmStatic + @TransitionType + fun DesktopModeTransitionSource.getEnterTransitionType(): Int { + return when (this) { + DesktopModeTransitionSource.APP_HANDLE_MENU_BUTTON -> + TRANSIT_ENTER_DESKTOP_FROM_APP_HANDLE_MENU_BUTTON + DesktopModeTransitionSource.APP_FROM_OVERVIEW -> + TRANSIT_ENTER_DESKTOP_FROM_APP_FROM_OVERVIEW + DesktopModeTransitionSource.KEYBOARD_SHORTCUT -> + TRANSIT_ENTER_DESKTOP_FROM_KEYBOARD_SHORTCUT + else -> TRANSIT_ENTER_DESKTOP_FROM_UNKNOWN + } + } + + /** Return whether the [TransitionType] corresponds to a transition to exit desktop mode. */ + @JvmStatic + fun @receiver:TransitionType Int.isExitDesktopModeTransition(): Boolean { + return this in + listOf( + TRANSIT_EXIT_DESKTOP_MODE_TASK_DRAG, + TRANSIT_EXIT_DESKTOP_MODE_HANDLE_MENU_BUTTON, + TRANSIT_EXIT_DESKTOP_MODE_KEYBOARD_SHORTCUT, + TRANSIT_EXIT_DESKTOP_MODE_UNKNOWN + ) + } + + /** + * Returns corresponding desktop mode exit [TransitionType] for a [DesktopModeTransitionSource]. + */ + @JvmStatic + @TransitionType + fun DesktopModeTransitionSource.getExitTransitionType(): Int { + return when (this) { + DesktopModeTransitionSource.TASK_DRAG -> TRANSIT_EXIT_DESKTOP_MODE_TASK_DRAG + DesktopModeTransitionSource.APP_HANDLE_MENU_BUTTON -> + TRANSIT_EXIT_DESKTOP_MODE_HANDLE_MENU_BUTTON + DesktopModeTransitionSource.KEYBOARD_SHORTCUT -> + TRANSIT_EXIT_DESKTOP_MODE_KEYBOARD_SHORTCUT + else -> TRANSIT_EXIT_DESKTOP_MODE_UNKNOWN + } + } +} 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 c9c6bcb59429..0807f7555c09 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 @@ -54,7 +54,6 @@ 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.DisplayLayout -import com.android.wm.shell.common.ExecutorUtils import com.android.wm.shell.common.ExternalInterfaceBinder import com.android.wm.shell.common.LaunchAdjacentController import com.android.wm.shell.common.MultiInstanceHelper @@ -63,6 +62,7 @@ import com.android.wm.shell.common.RemoteCallable import com.android.wm.shell.common.ShellExecutor import com.android.wm.shell.common.SingleInstanceRemoteListener import com.android.wm.shell.common.SyncTransactionQueue +import com.android.wm.shell.common.desktopmode.DesktopModeTransitionSource import com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT import com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT import com.android.wm.shell.compatui.isSingleTopActivityTranslucent @@ -75,7 +75,7 @@ import com.android.wm.shell.recents.RecentsTransitionHandler import com.android.wm.shell.recents.RecentsTransitionStateListener import com.android.wm.shell.shared.DesktopModeStatus import com.android.wm.shell.shared.DesktopModeStatus.DESKTOP_DENSITY_OVERRIDE -import com.android.wm.shell.shared.DesktopModeStatus.isDesktopDensityOverrideSet +import com.android.wm.shell.shared.DesktopModeStatus.useDesktopOverrideDensity import com.android.wm.shell.shared.annotations.ExternalThread import com.android.wm.shell.shared.annotations.ShellMainThread import com.android.wm.shell.splitscreen.SplitScreenController @@ -253,7 +253,7 @@ class DesktopTasksController( } /** Enter desktop by using the focused task in given `displayId` */ - fun moveFocusedTaskToDesktop(displayId: Int) { + fun moveFocusedTaskToDesktop(displayId: Int, transitionSource: DesktopModeTransitionSource) { val allFocusedTasks = shellTaskOrganizer.getRunningTasks(displayId).filter { taskInfo -> taskInfo.isFocused && @@ -272,11 +272,11 @@ class DesktopTasksController( } else { allFocusedTasks[0] } - moveToDesktop(splitFocusedTask) + moveToDesktop(splitFocusedTask, transitionSource = transitionSource) } 1 -> { // Fullscreen case where we move the current focused task. - moveToDesktop(allFocusedTasks[0].taskId) + moveToDesktop(allFocusedTasks[0].taskId, transitionSource = transitionSource) } else -> { KtProtoLog.w( @@ -293,17 +293,20 @@ class DesktopTasksController( /** Move a task with given `taskId` to desktop */ fun moveToDesktop( taskId: Int, - wct: WindowContainerTransaction = WindowContainerTransaction() + wct: WindowContainerTransaction = WindowContainerTransaction(), + transitionSource: DesktopModeTransitionSource, ): Boolean { shellTaskOrganizer.getRunningTaskInfo(taskId)?.let { - moveToDesktop(it, wct) - } ?: moveToDesktopFromNonRunningTask(taskId, wct) + moveToDesktop(it, wct, transitionSource) + } + ?: moveToDesktopFromNonRunningTask(taskId, wct, transitionSource) return true } private fun moveToDesktopFromNonRunningTask( taskId: Int, - wct: WindowContainerTransaction + wct: WindowContainerTransaction, + transitionSource: DesktopModeTransitionSource, ): Boolean { recentTasksController?.findTaskInBackground(taskId)?.let { KtProtoLog.v( @@ -316,10 +319,11 @@ class DesktopTasksController( bringDesktopAppsToFrontBeforeShowingNewTask(DEFAULT_DISPLAY, wct, taskId) addMoveToDesktopChangesNonRunningTask(wct, taskId) // TODO(343149901): Add DPI changes for task launch - val transition = enterDesktopTaskTransitionHandler.moveToDesktop(wct) + val transition = enterDesktopTaskTransitionHandler.moveToDesktop(wct, transitionSource) addPendingMinimizeTransition(transition, taskToMinimize) return true - } ?: return false + } + ?: return false } private fun addMoveToDesktopChangesNonRunningTask( @@ -331,12 +335,11 @@ class DesktopTasksController( wct.startTask(taskId, options.toBundle()) } - /** - * Move a task to desktop - */ + /** Move a task to desktop */ fun moveToDesktop( task: RunningTaskInfo, - wct: WindowContainerTransaction = WindowContainerTransaction() + wct: WindowContainerTransaction = WindowContainerTransaction(), + transitionSource: DesktopModeTransitionSource, ) { if (Flags.enableDesktopWindowingModalsPolicy() && isSingleTopActivityTranslucent(task)) { KtProtoLog.w( @@ -358,7 +361,7 @@ class DesktopTasksController( addMoveToDesktopChanges(wct, task) if (Transitions.ENABLE_SHELL_TRANSITIONS) { - val transition = enterDesktopTaskTransitionHandler.moveToDesktop(wct) + val transition = enterDesktopTaskTransitionHandler.moveToDesktop(wct, transitionSource) addPendingMinimizeTransition(transition, taskToMinimize) } else { shellTaskOrganizer.applyTransaction(wct) @@ -433,16 +436,16 @@ class DesktopTasksController( } /** Move a task with given `taskId` to fullscreen */ - fun moveToFullscreen(taskId: Int) { + fun moveToFullscreen(taskId: Int, transitionSource: DesktopModeTransitionSource) { shellTaskOrganizer.getRunningTaskInfo(taskId)?.let { task -> - moveToFullscreenWithAnimation(task, task.positionInParent) + moveToFullscreenWithAnimation(task, task.positionInParent, transitionSource) } } /** Enter fullscreen by moving the focused freeform task in given `displayId` to fullscreen. */ - fun enterFullscreen(displayId: Int) { + fun enterFullscreen(displayId: Int, transitionSource: DesktopModeTransitionSource) { getFocusedFreeformTask(displayId)?.let { - moveToFullscreenWithAnimation(it, it.positionInParent) + moveToFullscreenWithAnimation(it, it.positionInParent, transitionSource) } } @@ -491,7 +494,11 @@ class DesktopTasksController( ) } - private fun moveToFullscreenWithAnimation(task: RunningTaskInfo, position: Point) { + private fun moveToFullscreenWithAnimation( + task: RunningTaskInfo, + position: Point, + transitionSource: DesktopModeTransitionSource + ) { KtProtoLog.v( WM_SHELL_DESKTOP_MODE, "DesktopTasksController: moveToFullscreen with animation taskId=%d", @@ -502,7 +509,7 @@ class DesktopTasksController( if (Transitions.ENABLE_SHELL_TRANSITIONS) { exitDesktopTaskTransitionHandler.startTransition( - Transitions.TRANSIT_EXIT_DESKTOP_MODE, + transitionSource, wct, position, mOnAnimationFinishedCallback @@ -780,7 +787,6 @@ class DesktopTasksController( val intent = Intent(context, DesktopWallpaperActivity::class.java) val options = ActivityOptions.makeBasic().apply { - setAvoidMoveToFront() isPendingIntentBackgroundActivityLaunchAllowedByPermission = true pendingIntentBackgroundActivityStartMode = ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED @@ -932,7 +938,8 @@ class DesktopTasksController( request.type == TRANSIT_TO_BACK && request.triggerTask?.let { task -> desktopModeTaskRepository.isOnlyActiveTask(task.taskId) - } ?: false + } + ?: false } private fun handleFreeformTaskLaunch( @@ -953,7 +960,7 @@ class DesktopTasksController( } } val wct = WindowContainerTransaction() - if (isDesktopDensityOverrideSet()) { + if (useDesktopOverrideDensity()) { wct.setDensityDpi(task.token, DESKTOP_DENSITY_OVERRIDE) } // Desktop Mode is showing and we're launching a new Task - we might need to minimize @@ -1028,7 +1035,7 @@ class DesktopTasksController( } wct.setWindowingMode(taskInfo.token, targetWindowingMode) wct.reorder(taskInfo.token, true /* onTop */) - if (isDesktopDensityOverrideSet()) { + if (useDesktopOverrideDensity()) { wct.setDensityDpi(taskInfo.token, DESKTOP_DENSITY_OVERRIDE) } } @@ -1048,7 +1055,7 @@ class DesktopTasksController( } wct.setWindowingMode(taskInfo.token, targetWindowingMode) wct.setBounds(taskInfo.token, Rect()) - if (isDesktopDensityOverrideSet()) { + if (useDesktopOverrideDensity()) { wct.setDensityDpi(taskInfo.token, getDefaultDensityDpi()) } } @@ -1220,7 +1227,11 @@ class DesktopTasksController( ) when (indicatorType) { DesktopModeVisualIndicator.IndicatorType.TO_FULLSCREEN_INDICATOR -> { - moveToFullscreenWithAnimation(taskInfo, position) + moveToFullscreenWithAnimation( + taskInfo, + position, + DesktopModeTransitionSource.TASK_DRAG + ) } DesktopModeVisualIndicator.IndicatorType.TO_SPLIT_LEFT_INDICATOR -> { releaseVisualIndicator() @@ -1392,12 +1403,22 @@ class DesktopTasksController( } } - override fun moveFocusedTaskToDesktop(displayId: Int) { - mainExecutor.execute { this@DesktopTasksController.moveFocusedTaskToDesktop(displayId) } + override fun moveFocusedTaskToDesktop( + displayId: Int, + transitionSource: DesktopModeTransitionSource + ) { + mainExecutor.execute { + this@DesktopTasksController.moveFocusedTaskToDesktop(displayId, transitionSource) + } } - override fun moveFocusedTaskToFullscreen(displayId: Int) { - mainExecutor.execute { this@DesktopTasksController.enterFullscreen(displayId) } + override fun moveFocusedTaskToFullscreen( + displayId: Int, + transitionSource: DesktopModeTransitionSource + ) { + mainExecutor.execute { + this@DesktopTasksController.enterFullscreen(displayId, transitionSource) + } } override fun moveFocusedTaskToStageSplit(displayId: Int, leftOrTop: Boolean) { @@ -1449,13 +1470,13 @@ class DesktopTasksController( } override fun showDesktopApps(displayId: Int, remoteTransition: RemoteTransition?) { - ExecutorUtils.executeRemoteCallWithTaskPermission(controller, "showDesktopApps") { c -> + executeRemoteCallWithTaskPermission(controller, "showDesktopApps") { c -> c.showDesktopApps(displayId, remoteTransition) } } override fun showDesktopApp(taskId: Int) { - ExecutorUtils.executeRemoteCallWithTaskPermission(controller, "showDesktopApp") { c -> + executeRemoteCallWithTaskPermission(controller, "showDesktopApp") { c -> c.moveTaskToFront(taskId) } } @@ -1473,7 +1494,7 @@ class DesktopTasksController( override fun getVisibleTaskCount(displayId: Int): Int { val result = IntArray(1) - ExecutorUtils.executeRemoteCallWithTaskPermission( + executeRemoteCallWithTaskPermission( controller, "getVisibleTaskCount", { controller -> result[0] = controller.getVisibleTaskCount(displayId) }, @@ -1483,7 +1504,7 @@ class DesktopTasksController( } override fun onDesktopSplitSelectAnimComplete(taskInfo: RunningTaskInfo) { - ExecutorUtils.executeRemoteCallWithTaskPermission( + executeRemoteCallWithTaskPermission( controller, "onDesktopSplitSelectAnimComplete" ) { c -> @@ -1497,14 +1518,14 @@ class DesktopTasksController( "IDesktopModeImpl: set task listener=%s", listener ?: "null" ) - ExecutorUtils.executeRemoteCallWithTaskPermission(controller, "setTaskListener") { _ -> + executeRemoteCallWithTaskPermission(controller, "setTaskListener") { _ -> listener?.let { remoteListener.register(it) } ?: remoteListener.unregister() } } - override fun moveToDesktop(taskId: Int) { - ExecutorUtils.executeRemoteCallWithTaskPermission(controller, "moveToDesktop") { c -> - c.moveToDesktop(taskId) + override fun moveToDesktop(taskId: Int, transitionSource: DesktopModeTransitionSource) { + executeRemoteCallWithTaskPermission(controller, "moveToDesktop") { c -> + c.moveToDesktop(taskId, transitionSource = transitionSource) } } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/EnterDesktopTaskTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/EnterDesktopTaskTransitionHandler.java index 526cf4d0295b..e5b624f91c54 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/EnterDesktopTaskTransitionHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/EnterDesktopTaskTransitionHandler.java @@ -18,7 +18,8 @@ package com.android.wm.shell.desktopmode; import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; -import static com.android.wm.shell.transition.Transitions.TRANSIT_MOVE_TO_DESKTOP; +import static com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.getEnterTransitionType; +import static com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.isEnterDesktopModeTransition; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; @@ -30,6 +31,7 @@ import android.os.IBinder; import android.util.Slog; import android.view.SurfaceControl; import android.view.WindowManager; +import android.view.WindowManager.TransitionType; import android.window.TransitionInfo; import android.window.TransitionRequestInfo; import android.window.WindowContainerTransaction; @@ -37,6 +39,7 @@ import android.window.WindowContainerTransaction; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import com.android.wm.shell.common.desktopmode.DesktopModeTransitionSource; import com.android.wm.shell.transition.Transitions; import com.android.wm.shell.windowdecor.OnTaskResizeAnimationListener; @@ -82,8 +85,12 @@ public class EnterDesktopTaskTransitionHandler implements Transitions.Transition * @param wct WindowContainerTransaction for transition * @return the token representing the started transition */ - public IBinder moveToDesktop(@NonNull WindowContainerTransaction wct) { - final IBinder token = mTransitions.startTransition(TRANSIT_MOVE_TO_DESKTOP, wct, this); + public IBinder moveToDesktop( + @NonNull WindowContainerTransaction wct, + DesktopModeTransitionSource transitionSource + ) { + final IBinder token = mTransitions.startTransition(getEnterTransitionType(transitionSource), + wct, this); mPendingTransitionTokens.add(token); return token; } @@ -117,7 +124,7 @@ public class EnterDesktopTaskTransitionHandler implements Transitions.Transition private boolean startChangeTransition( @NonNull IBinder transition, - @WindowManager.TransitionType int type, + @TransitionType int type, @NonNull TransitionInfo.Change change, @NonNull SurfaceControl.Transaction startT, @NonNull SurfaceControl.Transaction finishT, @@ -127,7 +134,7 @@ public class EnterDesktopTaskTransitionHandler implements Transitions.Transition } final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo(); - if (type == TRANSIT_MOVE_TO_DESKTOP + if (isEnterDesktopModeTransition(type) && taskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM) { return animateMoveToDesktop(change, startT, finishCallback); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/ExitDesktopTaskTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/ExitDesktopTaskTransitionHandler.java index 9f9e256fc2b7..891f75cfdbda 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/ExitDesktopTaskTransitionHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/ExitDesktopTaskTransitionHandler.java @@ -18,6 +18,9 @@ package com.android.wm.shell.desktopmode; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; +import static com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.getExitTransitionType; +import static com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.isExitDesktopModeTransition; + import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.ValueAnimator; @@ -30,6 +33,7 @@ import android.os.IBinder; import android.util.DisplayMetrics; import android.view.SurfaceControl; import android.view.WindowManager; +import android.view.WindowManager.TransitionType; import android.window.TransitionInfo; import android.window.TransitionRequestInfo; import android.window.WindowContainerTransaction; @@ -38,6 +42,7 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import com.android.internal.annotations.VisibleForTesting; +import com.android.wm.shell.common.desktopmode.DesktopModeTransitionSource; import com.android.wm.shell.transition.Transitions; import java.util.ArrayList; @@ -52,6 +57,7 @@ import java.util.function.Supplier; */ public class ExitDesktopTaskTransitionHandler implements Transitions.TransitionHandler { private static final int FULLSCREEN_ANIMATION_DURATION = 336; + private final Context mContext; private final Transitions mTransitions; private final List<IBinder> mPendingTransitionTokens = new ArrayList<>(); @@ -77,17 +83,18 @@ public class ExitDesktopTaskTransitionHandler implements Transitions.TransitionH /** * Starts Transition of a given type * - * @param type Transition type + * @param transitionSource DesktopModeTransitionSource for transition * @param wct WindowContainerTransaction for transition * @param position Position of the task when transition is started * @param onAnimationEndCallback to be called after animation */ - public void startTransition(@WindowManager.TransitionType int type, + public void startTransition(@NonNull DesktopModeTransitionSource transitionSource, @NonNull WindowContainerTransaction wct, Point position, Consumer<SurfaceControl.Transaction> onAnimationEndCallback) { mPosition = position; mOnAnimationFinishedCallback = onAnimationEndCallback; - final IBinder token = mTransitions.startTransition(type, wct, this); + final IBinder token = mTransitions.startTransition(getExitTransitionType(transitionSource), + wct, this); mPendingTransitionTokens.add(token); } @@ -121,7 +128,7 @@ public class ExitDesktopTaskTransitionHandler implements Transitions.TransitionH @VisibleForTesting boolean startChangeTransition( @NonNull IBinder transition, - @WindowManager.TransitionType int type, + @TransitionType int type, @NonNull TransitionInfo.Change change, @NonNull SurfaceControl.Transaction startT, @NonNull SurfaceControl.Transaction finishT, @@ -130,7 +137,7 @@ public class ExitDesktopTaskTransitionHandler implements Transitions.TransitionH return false; } final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo(); - if (type == Transitions.TRANSIT_EXIT_DESKTOP_MODE + if (isExitDesktopModeTransition(type) && taskInfo.getWindowingMode() == WINDOWING_MODE_FULLSCREEN) { // This Transition animates a task to fullscreen after being dragged to status bar final Resources resources = mContext.getResources(); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopMode.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopMode.aidl index c36f8deb6ecc..a7ec2037706d 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopMode.aidl +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopMode.aidl @@ -18,6 +18,7 @@ package com.android.wm.shell.desktopmode; import android.app.ActivityManager.RunningTaskInfo; import android.window.RemoteTransition; +import com.android.wm.shell.common.desktopmode.DesktopModeTransitionSource; import com.android.wm.shell.desktopmode.IDesktopTaskListener; /** @@ -47,5 +48,5 @@ interface IDesktopMode { oneway void setTaskListener(IDesktopTaskListener listener); /** Move a task with given `taskId` to desktop */ - void moveToDesktop(int taskId); + void moveToDesktop(int taskId, in DesktopModeTransitionSource transitionSource); }
\ No newline at end of file diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/docs/changes.md b/libs/WindowManager/Shell/src/com/android/wm/shell/docs/changes.md index 9aa5f4ffcd78..0acc7df98d1c 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/docs/changes.md +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/docs/changes.md @@ -54,8 +54,8 @@ Specifically, to support calling into a controller from an external process (lik extend `ExternalInterfaceBinder` and implement `invalidate()` to ensure it doesn't hold long references to the outer controller - Make the controller implement `RemoteCallable<T>`, and have all incoming calls use one of - the `ExecutorUtils.executeRemoteCallWithTaskPermission()` calls to verify the caller's identity - and ensure the call happens on the main shell thread and not the binder thread + the `executeRemoteCallWithTaskPermission()` calls to verify the caller's identity and ensure the + call happens on the main shell thread and not the binder thread - Inject `ShellController` and add the instance of the implementation as external interface - In Launcher, update `TouchInteractionService` to pass the interface to `SystemUIProxy`, and then call the SystemUIProxy method as needed in that code diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java index 7e70d6a3debe..c374eb8e8f03 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java @@ -32,7 +32,6 @@ import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMA import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; -import static com.android.wm.shell.common.ExecutorUtils.executeRemoteCallWithTaskPermission; import static com.android.wm.shell.sysui.ShellSharedConstants.KEY_EXTRA_SHELL_DRAG_AND_DROP; import android.app.ActivityManager; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java index 39b9000856f2..962309f7c534 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java @@ -19,7 +19,6 @@ package com.android.wm.shell.onehanded; import static android.os.UserHandle.myUserId; import static android.view.Display.DEFAULT_DISPLAY; -import static com.android.wm.shell.common.ExecutorUtils.executeRemoteCallWithTaskPermission; import static com.android.wm.shell.onehanded.OneHandedState.STATE_ACTIVE; import static com.android.wm.shell.onehanded.OneHandedState.STATE_ENTERING; import static com.android.wm.shell.onehanded.OneHandedState.STATE_EXITING; 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 eb845db409e3..0a3c15b6057f 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 @@ -40,6 +40,7 @@ import com.android.internal.graphics.SfVsyncFrameCallbackProvider; import com.android.internal.protolog.common.ProtoLog; import com.android.launcher3.icons.IconProvider; import com.android.wm.shell.animation.Interpolators; +import com.android.wm.shell.common.pip.PipUtils; import com.android.wm.shell.protolog.ShellProtoLogGroup; import com.android.wm.shell.transition.Transitions; @@ -583,7 +584,7 @@ public class PipAnimationController { } static PipTransitionAnimator<Rect> ofBounds(TaskInfo taskInfo, SurfaceControl leash, - Rect baseValue, Rect startValue, Rect endValue, Rect sourceHintRect, + Rect baseValue, Rect startValue, Rect endValue, Rect sourceRectHint, @PipAnimationController.TransitionDirection int direction, float startingAngle, @Surface.Rotation int rotationDelta) { final boolean isOutPipDirection = isOutPipDirection(direction); @@ -613,14 +614,25 @@ public class PipAnimationController { initialContainerRect = initialSourceValue; } - final Rect sourceHintRectInsets; - if (sourceHintRect == null) { - sourceHintRectInsets = null; + final Rect adjustedSourceRectHint = new Rect(); + if (sourceRectHint == null || sourceRectHint.isEmpty()) { + // Crop a Rect matches the aspect ratio and pivots at the center point. + // This is done for entering case only. + if (isInPipDirection(direction)) { + final float aspectRatio = endValue.width() / (float) endValue.height(); + adjustedSourceRectHint.set(PipUtils.getEnterPipWithOverlaySrcRectHint( + startValue, aspectRatio)); + } } else { - sourceHintRectInsets = new Rect(sourceHintRect.left - initialContainerRect.left, - sourceHintRect.top - initialContainerRect.top, - initialContainerRect.right - sourceHintRect.right, - initialContainerRect.bottom - sourceHintRect.bottom); + adjustedSourceRectHint.set(sourceRectHint); + } + final Rect sourceHintRectInsets = new Rect(); + if (!adjustedSourceRectHint.isEmpty()) { + sourceHintRectInsets.set( + adjustedSourceRectHint.left - initialContainerRect.left, + adjustedSourceRectHint.top - initialContainerRect.top, + initialContainerRect.right - adjustedSourceRectHint.right, + initialContainerRect.bottom - adjustedSourceRectHint.bottom); } final Rect zeroInsets = new Rect(0, 0, 0, 0); @@ -648,7 +660,7 @@ public class PipAnimationController { } float angle = (1.0f - fraction) * startingAngle; setCurrentValue(bounds); - if (inScaleTransition() || sourceHintRect == null) { + if (inScaleTransition() || adjustedSourceRectHint.isEmpty()) { if (isOutPipDirection) { getSurfaceTransactionHelper().crop(tx, leash, end) .scale(tx, leash, end, bounds); @@ -661,7 +673,7 @@ public class PipAnimationController { } else { final Rect insets = computeInsets(fraction); getSurfaceTransactionHelper().scaleAndCrop(tx, leash, - sourceHintRect, initialSourceValue, bounds, insets, + adjustedSourceRectHint, initialSourceValue, bounds, insets, isInPipDirection, fraction); if (shouldApplyCornerRadius()) { final Rect sourceBounds = new Rect(initialContainerRect); @@ -729,9 +741,6 @@ public class PipAnimationController { } private Rect computeInsets(float fraction) { - if (sourceHintRectInsets == null) { - return zeroInsets; - } final Rect startRect = isOutPipDirection ? sourceHintRectInsets : zeroInsets; final Rect endRect = isOutPipDirection ? zeroInsets : sourceHintRectInsets; return mInsetsEvaluator.evaluate(fraction, startRect, endRect); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipContentOverlay.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipContentOverlay.java index e11e8596a7fe..ff2d46e11107 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipContentOverlay.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipContentOverlay.java @@ -226,11 +226,10 @@ public abstract class PipContentOverlay { appBoundsCenterX - mOverlayHalfSize, appBoundsCenterY - mOverlayHalfSize); // Scale back the bitmap with the pivot point at center. - mTmpTransform.postScale( + final float scale = Math.min( (float) mAppBounds.width() / currentBounds.width(), - (float) mAppBounds.height() / currentBounds.height(), - appBoundsCenterX, - appBoundsCenterY); + (float) mAppBounds.height() / currentBounds.height()); + mTmpTransform.postScale(scale, scale, appBoundsCenterX, appBoundsCenterY); atomicTx.setMatrix(mLeash, mTmpTransform, mTmpFloat9) .setAlpha(mLeash, fraction < 0.5f ? 0 : (fraction - 0.5f) * 2); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSurfaceTransactionHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSurfaceTransactionHelper.java index a58d94ecd19b..202f60dad842 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSurfaceTransactionHelper.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSurfaceTransactionHelper.java @@ -137,7 +137,7 @@ public class PipSurfaceTransactionHelper { mTmpDestinationRect.inset(insets); // Scale to the bounds no smaller than the destination and offset such that the top/left // of the scaled inset source rect aligns with the top/left of the destination bounds - final float scale; + final float scale, left, top; if (isInPipDirection && sourceRectHint != null && sourceRectHint.width() < sourceBounds.width()) { // scale by sourceRectHint if it's not edge-to-edge, for entering PiP transition only. @@ -148,12 +148,15 @@ public class PipSurfaceTransactionHelper { ? (float) destinationBounds.width() / sourceBounds.width() : (float) destinationBounds.height() / sourceBounds.height(); scale = (1 - fraction) * startScale + fraction * endScale; + left = destinationBounds.left - insets.left * scale; + top = destinationBounds.top - insets.top * scale; } else { scale = Math.max((float) destinationBounds.width() / sourceBounds.width(), (float) destinationBounds.height() / sourceBounds.height()); + // Work around the rounding error by fix the position at very beginning. + left = scale == 1 ? 0 : destinationBounds.left - insets.left * scale; + top = scale == 1 ? 0 : destinationBounds.top - insets.top * scale; } - final float left = destinationBounds.left - insets.left * scale; - final float top = destinationBounds.top - insets.top * scale; mTmpTransform.setScale(scale, scale); tx.setMatrix(leash, mTmpTransform, mTmpFloat9) .setCrop(leash, mTmpDestinationRect) 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 e1657f99639d..e4420d73886e 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 @@ -373,6 +373,10 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, @NonNull final Rect mAppBounds = new Rect(); + /** The source rect hint from stopSwipePipToHome(). */ + @Nullable + private Rect mSwipeSourceRectHint; + public PipTaskOrganizer(Context context, @NonNull SyncTransactionQueue syncTransactionQueue, @NonNull PipTransitionState pipTransitionState, @@ -504,7 +508,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, * Expect {@link #onTaskAppeared(ActivityManager.RunningTaskInfo, SurfaceControl)} afterwards. */ public void stopSwipePipToHome(int taskId, ComponentName componentName, Rect destinationBounds, - SurfaceControl overlay, Rect appBounds) { + SurfaceControl overlay, Rect appBounds, Rect sourceRectHint) { ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE, "stopSwipePipToHome: %s, stat=%s", componentName, mPipTransitionState); // do nothing if there is no startSwipePipToHome being called before @@ -513,6 +517,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, } mPipBoundsState.setBounds(destinationBounds); setContentOverlay(overlay, appBounds); + mSwipeSourceRectHint = sourceRectHint; if (ENABLE_SHELL_TRANSITIONS && overlay != null) { // With Shell transition, the overlay was attached to the remote transition leash, which // will be removed when the current transition is finished, so we need to reparent it @@ -529,6 +534,20 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, } } + /** + * Returns non-null Rect if the pip is entering from swipe-to-home with a specified source hint. + * This also consumes the rect hint. + */ + @Nullable + Rect takeSwipeSourceRectHint() { + final Rect sourceRectHint = mSwipeSourceRectHint; + if (sourceRectHint == null || sourceRectHint.isEmpty()) { + return null; + } + mSwipeSourceRectHint = null; + return mPipTransitionState.getInSwipePipToHomeTransition() ? sourceRectHint : null; + } + private void mayRemoveContentOverlay(SurfaceControl overlay) { final WeakReference<SurfaceControl> overlayRef = new WeakReference<>(overlay); final long timeoutDuration = (mEnterAnimationDuration @@ -603,6 +622,8 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, // the end of the enter animation and reschedule exitPip to run after enter-PiP // has finished its transition and allowed the client to draw in PiP mode. mPipTransitionController.end(() -> { + // TODO(341627042): force set to entered state to avoid potential stack overflow. + mPipTransitionState.setTransitionState(PipTransitionState.ENTERED_PIP); exitPip(animationDurationMs, requestEnterSplit); }); return; @@ -978,7 +999,6 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, private void onEndOfSwipePipToHomeTransition() { if (Transitions.ENABLE_SHELL_TRANSITIONS) { - mPipTransitionController.setEnterAnimationType(ANIM_TYPE_BOUNDS); return; } 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 b52b0d8dee74..3cae72d89ecc 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 @@ -200,9 +200,6 @@ public class PipTransition extends PipTransitionController { animator.cancel(); } mExitTransition = mTransitions.startTransition(type, out, this); - if (mPipOrganizer.getOutPipWindowingMode() == WINDOWING_MODE_UNDEFINED) { - mHomeTransitionObserver.notifyHomeVisibilityChanged(false /* isVisible */); - } } @Override @@ -659,6 +656,9 @@ public class PipTransition extends PipTransitionController { startTransaction.remove(mPipOrganizer.mPipOverlay); mPipOrganizer.clearContentOverlay(); } + if (mPipOrganizer.getOutPipWindowingMode() == WINDOWING_MODE_UNDEFINED) { + mHomeTransitionObserver.notifyHomeVisibilityChanged(false /* isVisible */); + } if (pipChange == null) { ProtoLog.w(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE, "%s: No window of exiting PIP is found. Can't play expand animation", TAG); @@ -1004,8 +1004,11 @@ public class PipTransition extends PipTransitionController { final Rect currentBounds = pipChange.getStartAbsBounds(); int rotationDelta = deltaRotation(startRotation, endRotation); - Rect sourceHintRect = PipBoundsAlgorithm.getValidSourceHintRect( - taskInfo.pictureInPictureParams, currentBounds, destinationBounds); + Rect sourceHintRect = mPipOrganizer.takeSwipeSourceRectHint(); + if (sourceHintRect == null) { + sourceHintRect = PipBoundsAlgorithm.getValidSourceHintRect( + taskInfo.pictureInPictureParams, currentBounds, destinationBounds); + } if (rotationDelta != Surface.ROTATION_0 && endRotation != mPipDisplayLayoutState.getRotation()) { // Computes the destination bounds in new rotation. @@ -1080,6 +1083,8 @@ public class PipTransition extends PipTransitionController { mSurfaceTransactionHelper .crop(finishTransaction, leash, destinationBounds) .round(finishTransaction, leash, true /* applyCornerRadius */); + // Always reset to bounds animation type afterwards. + setEnterAnimationType(ANIM_TYPE_BOUNDS); } else { throw new RuntimeException("Unrecognized animation type: " + enterAnimationType); } 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 85f9194ac804..8c4bf7620068 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 @@ -22,7 +22,6 @@ import static android.content.pm.PackageManager.FEATURE_PICTURE_IN_PICTURE; import static android.view.WindowManager.INPUT_CONSUMER_PIP; import static com.android.internal.jank.InteractionJankMonitor.CUJ_PIP_TRANSITION; -import static com.android.wm.shell.common.ExecutorUtils.executeRemoteCallWithTaskPermission; import static com.android.wm.shell.pip.PipAnimationController.ANIM_TYPE_ALPHA; import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTION_EXPAND_OR_UNEXPAND; import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTION_LEAVE_PIP; @@ -1002,9 +1001,9 @@ public class PipController implements PipTransitionController.PipTransitionCallb } private void stopSwipePipToHome(int taskId, ComponentName componentName, Rect destinationBounds, - SurfaceControl overlay, Rect appBounds) { + SurfaceControl overlay, Rect appBounds, Rect sourceRectHint) { mPipTaskOrganizer.stopSwipePipToHome(taskId, componentName, destinationBounds, overlay, - appBounds); + appBounds, sourceRectHint); } private void abortSwipePipToHome(int taskId, ComponentName componentName) { @@ -1292,13 +1291,15 @@ public class PipController implements PipTransitionController.PipTransitionCallb @Override public void stopSwipePipToHome(int taskId, ComponentName componentName, - Rect destinationBounds, SurfaceControl overlay, Rect appBounds) { + Rect destinationBounds, SurfaceControl overlay, Rect appBounds, + Rect sourceRectHint) { if (overlay != null) { overlay.setUnreleasedWarningCallSite("PipController.stopSwipePipToHome"); } executeRemoteCallWithTaskPermission(mController, "stopSwipePipToHome", (controller) -> controller.stopSwipePipToHome( - taskId, componentName, destinationBounds, overlay, appBounds)); + taskId, componentName, destinationBounds, overlay, appBounds, + sourceRectHint)); } @Override diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/animation/PipAlphaAnimator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/animation/PipAlphaAnimator.java index 3e298e530415..895c2aeba9ef 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/animation/PipAlphaAnimator.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/animation/PipAlphaAnimator.java @@ -19,11 +19,13 @@ package com.android.wm.shell.pip2.animation; import android.animation.Animator; import android.animation.ValueAnimator; import android.annotation.IntDef; +import android.content.Context; import android.view.SurfaceControl; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import com.android.wm.shell.R; import com.android.wm.shell.pip2.PipSurfaceTransactionHelper; import java.lang.annotation.Retention; @@ -45,6 +47,7 @@ public class PipAlphaAnimator extends ValueAnimator implements ValueAnimator.Ani public static final int FADE_IN = 0; public static final int FADE_OUT = 1; + private final int mEnterAnimationDuration; private final SurfaceControl mLeash; private final SurfaceControl.Transaction mStartTransaction; @@ -55,7 +58,8 @@ public class PipAlphaAnimator extends ValueAnimator implements ValueAnimator.Ani private final PipSurfaceTransactionHelper.SurfaceControlTransactionFactory mSurfaceControlTransactionFactory; - public PipAlphaAnimator(SurfaceControl leash, + public PipAlphaAnimator(Context context, + SurfaceControl leash, SurfaceControl.Transaction tx, @Fade int direction) { mLeash = leash; @@ -67,6 +71,9 @@ public class PipAlphaAnimator extends ValueAnimator implements ValueAnimator.Ani } mSurfaceControlTransactionFactory = new PipSurfaceTransactionHelper.VsyncSurfaceControlTransactionFactory(); + mEnterAnimationDuration = context.getResources() + .getInteger(R.integer.config_pipEnterAnimationDuration); + setDuration(mEnterAnimationDuration); addListener(this); addUpdateListener(this); } 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 f5afeea3eaef..fc0d36d13b2e 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 @@ -19,7 +19,6 @@ package com.android.wm.shell.pip2.phone; import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; import static android.content.pm.PackageManager.FEATURE_PICTURE_IN_PICTURE; -import static com.android.wm.shell.common.ExecutorUtils.executeRemoteCallWithTaskPermission; import static com.android.wm.shell.sysui.ShellSharedConstants.KEY_EXTRA_SHELL_PIP; import android.app.ActivityManager; @@ -286,7 +285,8 @@ public class PipController implements ConfigurationChangeListener, } private void onSwipePipToHomeAnimationStart(int taskId, ComponentName componentName, - Rect destinationBounds, SurfaceControl overlay, Rect appBounds) { + Rect destinationBounds, SurfaceControl overlay, Rect appBounds, + Rect sourceRectHint) { ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE, "onSwipePipToHomeAnimationStart: %s", componentName); Bundle extra = new Bundle(); @@ -391,6 +391,7 @@ public class PipController implements ConfigurationChangeListener, @Override public void invalidate() { mController = null; + // Unregister the listener to ensure any registered binder death recipients are unlinked mListener.unregister(); } @@ -409,13 +410,15 @@ public class PipController implements ConfigurationChangeListener, @Override public void stopSwipePipToHome(int taskId, ComponentName componentName, - Rect destinationBounds, SurfaceControl overlay, Rect appBounds) { + Rect destinationBounds, SurfaceControl overlay, Rect appBounds, + Rect sourceRectHint) { if (overlay != null) { overlay.setUnreleasedWarningCallSite("PipController.stopSwipePipToHome"); } executeRemoteCallWithTaskPermission(mController, "stopSwipePipToHome", (controller) -> controller.onSwipePipToHomeAnimationStart( - taskId, componentName, destinationBounds, overlay, appBounds)); + taskId, componentName, destinationBounds, overlay, appBounds, + sourceRectHint)); } @Override 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 3e215d9bfa34..0b2db6cf3dd1 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 @@ -293,37 +293,32 @@ public class PipTransition extends PipTransitionController implements return false; } + SurfaceControl overlayLeash = mPipTransitionState.getSwipePipToHomeOverlay(); PictureInPictureParams params = pipChange.getTaskInfo().pictureInPictureParams; - Rect srcRectHint = params.getSourceRectHint(); - Rect startBounds = pipChange.getStartAbsBounds(); + + Rect appBounds = mPipTransitionState.getSwipePipToHomeAppBounds(); Rect destinationBounds = pipChange.getEndAbsBounds(); + float aspectRatio = pipChange.getTaskInfo().pictureInPictureParams.getAspectRatioFloat(); + + // We fake the source rect hint when the one prvided by the app is invalid for + // the animation with an app icon overlay. + Rect animationSrcRectHint = overlayLeash == null ? params.getSourceRectHint() + : PipUtils.getEnterPipWithOverlaySrcRectHint(appBounds, aspectRatio); + WindowContainerTransaction finishWct = new WindowContainerTransaction(); SurfaceControl.Transaction tx = new SurfaceControl.Transaction(); - if (PipBoundsAlgorithm.isSourceRectHintValidForEnterPip(srcRectHint, destinationBounds)) { - final float scale = (float) destinationBounds.width() / srcRectHint.width(); - startTransaction.setWindowCrop(pipLeash, srcRectHint); - startTransaction.setPosition(pipLeash, - destinationBounds.left - srcRectHint.left * scale, - destinationBounds.top - srcRectHint.top * scale); - - // Reset the scale in case we are in the multi-activity case. - // TO_FRONT transition already scales down the task in single-activity case, but - // in multi-activity case, reparenting yields new reset scales coming from pinned task. - startTransaction.setScale(pipLeash, scale, scale); - } else { - final float scaleX = (float) destinationBounds.width() / startBounds.width(); - final float scaleY = (float) destinationBounds.height() / startBounds.height(); + final float scale = (float) destinationBounds.width() / animationSrcRectHint.width(); + startTransaction.setWindowCrop(pipLeash, animationSrcRectHint); + startTransaction.setPosition(pipLeash, + destinationBounds.left - animationSrcRectHint.left * scale, + destinationBounds.top - animationSrcRectHint.top * scale); + startTransaction.setScale(pipLeash, scale, scale); + + if (overlayLeash != null) { final int overlaySize = PipContentOverlay.PipAppIconOverlay.getOverlaySize( mPipTransitionState.getSwipePipToHomeAppBounds(), destinationBounds); - SurfaceControl overlayLeash = mPipTransitionState.getSwipePipToHomeOverlay(); - - startTransaction.setPosition(pipLeash, destinationBounds.left, destinationBounds.top) - .setScale(pipLeash, scaleX, scaleY) - .setWindowCrop(pipLeash, startBounds) - .reparent(overlayLeash, pipLeash) - .setLayer(overlayLeash, Integer.MAX_VALUE); // Overlay needs to be adjusted once a new draw comes in resetting surface transform. tx.setScale(overlayLeash, 1f, 1f); @@ -390,15 +385,23 @@ public class PipTransition extends PipTransitionController implements if (pipChange == null) { return false; } - // cache the PiP task token and leash - WindowContainerToken pipTaskToken = pipChange.getContainer(); - Preconditions.checkNotNull(mPipLeash, "Leash is null for alpha transition."); - // start transition with 0 alpha - startTransaction.setAlpha(mPipLeash, 0f); - PipAlphaAnimator animator = new PipAlphaAnimator(mPipLeash, - startTransaction, PipAlphaAnimator.FADE_IN); - animator.setAnimationEndCallback(() -> finishCallback.onTransitionFinished(null)); + Rect destinationBounds = pipChange.getEndAbsBounds(); + SurfaceControl pipLeash = mPipTransitionState.mPinnedTaskLeash; + Preconditions.checkNotNull(pipLeash, "Leash is null for alpha transition."); + + // Start transition with 0 alpha at the entry bounds. + startTransaction.setPosition(pipLeash, destinationBounds.left, destinationBounds.top) + .setWindowCrop(pipLeash, destinationBounds.width(), destinationBounds.height()) + .setAlpha(pipLeash, 0f); + + PipAlphaAnimator animator = new PipAlphaAnimator(mContext, pipLeash, startTransaction, + PipAlphaAnimator.FADE_IN); + animator.setAnimationEndCallback(() -> { + finishCallback.onTransitionFinished(null); + // This should update the pip transition state accordingly after we stop playing. + onClientDrawAtTransitionEnd(); + }); animator.start(); return true; @@ -480,10 +483,10 @@ public class PipTransition extends PipTransitionController implements private boolean isLegacyEnter(@NonNull TransitionInfo info) { TransitionInfo.Change pipChange = getPipChange(info); - // If the only change in the changes list is a TO_FRONT mode PiP task, + // If the only change in the changes list is a opening type PiP task, // then this is legacy-enter PiP. - return pipChange != null && pipChange.getMode() == TRANSIT_TO_FRONT - && info.getChanges().size() == 1; + return pipChange != null && info.getChanges().size() == 1 + && (pipChange.getMode() == TRANSIT_TO_FRONT || pipChange.getMode() == TRANSIT_OPEN); } private boolean isRemovePipTransition(@NonNull TransitionInfo info) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/OWNERS b/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/OWNERS new file mode 100644 index 000000000000..3f3308cfc75a --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/OWNERS @@ -0,0 +1 @@ +include platform/development:/tools/winscope/OWNERS 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 19af3d544b36..497c3f704c82 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 @@ -18,6 +18,8 @@ package com.android.wm.shell.protolog; import com.android.internal.protolog.common.IProtoLogGroup; +import java.util.UUID; + /** * Defines logging groups for ProtoLog. * @@ -116,6 +118,11 @@ public enum ShellProtoLogGroup implements IProtoLogGroup { this.mLogToLogcat = logToLogcat; } + @Override + public int getId() { + return Consts.START_ID + this.ordinal(); + } + private static class Consts { private static final String TAG_WM_SHELL = "WindowManagerShell"; private static final String TAG_WM_STARTING_WINDOW = "ShellStartingWindow"; @@ -124,5 +131,9 @@ public enum ShellProtoLogGroup implements IProtoLogGroup { private static final boolean ENABLE_DEBUG = true; private static final boolean ENABLE_LOG_TO_PROTO_DEBUG = true; + + private static final int START_ID = (int) ( + UUID.nameUUIDFromBytes(ShellProtoLogGroup.class.getName().getBytes()) + .getMostSignificantBits() % Integer.MAX_VALUE); } } 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 62d195efb381..245829ecafb3 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 @@ -42,4 +42,7 @@ oneway interface IRecentTasksListener { * Called when a running task changes. */ void onRunningTaskChanged(in RunningTaskInfo taskInfo); -} + + /** A task has moved to front. */ + oneway void onTaskMovedToFront(in RunningTaskInfo taskInfo); +}
\ No newline at end of file diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/OWNERS b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/OWNERS new file mode 100644 index 000000000000..452644b05a2a --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/OWNERS @@ -0,0 +1,6 @@ +# WM shell sub-module task stack owners +uysalorhan@google.com +samcackett@google.com +alexchau@google.com +silvajordan@google.com +uwaisashraf@google.com
\ No newline at end of file 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 863202d5e1c3..03c8cf8cc795 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 @@ -20,7 +20,7 @@ import static android.app.ActivityTaskManager.INVALID_TASK_ID; import static android.content.pm.PackageManager.FEATURE_PC; import static com.android.window.flags.Flags.enableDesktopWindowingTaskbarRunningApps; -import static com.android.wm.shell.common.ExecutorUtils.executeRemoteCallWithTaskPermission; +import static com.android.window.flags.Flags.enableTaskStackObserverInShell; import static com.android.wm.shell.sysui.ShellSharedConstants.KEY_EXTRA_SHELL_RECENT_TASKS; import android.app.ActivityManager; @@ -58,6 +58,7 @@ import com.android.wm.shell.shared.annotations.ShellMainThread; import com.android.wm.shell.sysui.ShellCommandHandler; import com.android.wm.shell.sysui.ShellController; import com.android.wm.shell.sysui.ShellInit; +import com.android.wm.shell.transition.Transitions; import com.android.wm.shell.util.GroupedRecentTaskInfo; import com.android.wm.shell.util.SplitBounds; @@ -74,7 +75,8 @@ import java.util.function.Consumer; * Manages the recent task list from the system, caching it as necessary. */ public class RecentTasksController implements TaskStackListenerCallback, - RemoteCallable<RecentTasksController>, DesktopModeTaskRepository.ActiveTasksListener { + RemoteCallable<RecentTasksController>, DesktopModeTaskRepository.ActiveTasksListener, + TaskStackTransitionObserver.TaskStackTransitionObserverListener { private static final String TAG = RecentTasksController.class.getSimpleName(); private final Context mContext; @@ -85,6 +87,7 @@ public class RecentTasksController implements TaskStackListenerCallback, private final TaskStackListenerImpl mTaskStackListener; private final RecentTasksImpl mImpl = new RecentTasksImpl(); private final ActivityTaskManager mActivityTaskManager; + private final TaskStackTransitionObserver mTaskStackTransitionObserver; private RecentsTransitionHandler mTransitionHandler = null; private IRecentTasksListener mListener; private final boolean mPcFeatureEnabled; @@ -113,13 +116,15 @@ public class RecentTasksController implements TaskStackListenerCallback, TaskStackListenerImpl taskStackListener, ActivityTaskManager activityTaskManager, Optional<DesktopModeTaskRepository> desktopModeTaskRepository, + TaskStackTransitionObserver taskStackTransitionObserver, @ShellMainThread ShellExecutor mainExecutor ) { if (!context.getResources().getBoolean(com.android.internal.R.bool.config_hasRecents)) { return null; } return new RecentTasksController(context, shellInit, shellController, shellCommandHandler, - taskStackListener, activityTaskManager, desktopModeTaskRepository, mainExecutor); + taskStackListener, activityTaskManager, desktopModeTaskRepository, + taskStackTransitionObserver, mainExecutor); } RecentTasksController(Context context, @@ -129,6 +134,7 @@ public class RecentTasksController implements TaskStackListenerCallback, TaskStackListenerImpl taskStackListener, ActivityTaskManager activityTaskManager, Optional<DesktopModeTaskRepository> desktopModeTaskRepository, + TaskStackTransitionObserver taskStackTransitionObserver, ShellExecutor mainExecutor) { mContext = context; mShellController = shellController; @@ -137,6 +143,7 @@ public class RecentTasksController implements TaskStackListenerCallback, mPcFeatureEnabled = mContext.getPackageManager().hasSystemFeature(FEATURE_PC); mTaskStackListener = taskStackListener; mDesktopModeTaskRepository = desktopModeTaskRepository; + mTaskStackTransitionObserver = taskStackTransitionObserver; mMainExecutor = mainExecutor; shellInit.addInitCallback(this::onInit, this); } @@ -155,6 +162,10 @@ public class RecentTasksController implements TaskStackListenerCallback, mShellCommandHandler.addDumpCallback(this::dump, this); mTaskStackListener.addListener(this); mDesktopModeTaskRepository.ifPresent(it -> it.addActiveTaskListener(this)); + if (Transitions.ENABLE_SHELL_TRANSITIONS) { + mTaskStackTransitionObserver.addTaskStackTransitionObserverListener(this, + mMainExecutor); + } } void setTransitionHandler(RecentsTransitionHandler handler) { @@ -268,6 +279,12 @@ public class RecentTasksController implements TaskStackListenerCallback, notifyRecentTasksChanged(); } + @Override + public void onTaskMovedToFrontThroughTransition( + ActivityManager.RunningTaskInfo runningTaskInfo) { + notifyTaskMovedToFront(runningTaskInfo); + } + @VisibleForTesting void notifyRecentTasksChanged() { ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENT_TASKS, "Notify recent tasks changed"); @@ -329,6 +346,19 @@ public class RecentTasksController implements TaskStackListenerCallback, } } + private void notifyTaskMovedToFront(ActivityManager.RunningTaskInfo taskInfo) { + if (mListener == null + || !enableTaskStackObserverInShell() + || taskInfo.realActivity == null) { + return; + } + try { + mListener.onTaskMovedToFront(taskInfo); + } catch (RemoteException e) { + Slog.w(TAG, "Failed call onTaskMovedToFront", e); + } + } + private boolean shouldEnableRunningTasksForDesktopMode() { return mPcFeatureEnabled || (DesktopModeStatus.canEnterDesktopMode(mContext) @@ -465,6 +495,7 @@ public class RecentTasksController implements TaskStackListenerCallback, } return null; } + public void dump(@NonNull PrintWriter pw, String prefix) { final String innerPrefix = prefix + " "; pw.println(prefix + TAG); @@ -548,6 +579,11 @@ public class RecentTasksController implements TaskStackListenerCallback, public void onRunningTaskChanged(ActivityManager.RunningTaskInfo taskInfo) { mListener.call(l -> l.onRunningTaskChanged(taskInfo)); } + + @Override + public void onTaskMovedToFront(ActivityManager.RunningTaskInfo taskInfo) { + mListener.call(l -> l.onTaskMovedToFront(taskInfo)); + } }; public IRecentTasksImpl(RecentTasksController controller) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/TaskStackTransitionObserver.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/TaskStackTransitionObserver.kt new file mode 100644 index 000000000000..7c5f10a5bcca --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/TaskStackTransitionObserver.kt @@ -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.wm.shell.recents + +import android.app.ActivityManager.RunningTaskInfo +import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM +import android.os.IBinder +import android.util.ArrayMap +import android.view.SurfaceControl +import android.view.WindowManager +import android.window.TransitionInfo +import com.android.window.flags.Flags.enableTaskStackObserverInShell +import com.android.wm.shell.shared.TransitionUtil +import com.android.wm.shell.sysui.ShellInit +import com.android.wm.shell.transition.Transitions +import dagger.Lazy +import java.util.concurrent.Executor + +/** + * A [Transitions.TransitionObserver] that observes shell transitions and sends updates to listeners + * about task stack changes. + * + * TODO(346588978) Move split/pip signals here as well so that launcher don't need to handle it + */ +class TaskStackTransitionObserver( + private val transitions: Lazy<Transitions>, + shellInit: ShellInit +) : Transitions.TransitionObserver { + + private val transitionToTransitionChanges: MutableMap<IBinder, TransitionChanges> = + mutableMapOf() + private val taskStackTransitionObserverListeners = + ArrayMap<TaskStackTransitionObserverListener, Executor>() + + init { + if (Transitions.ENABLE_SHELL_TRANSITIONS) { + shellInit.addInitCallback(::onInit, this) + } + } + + fun onInit() { + transitions.get().registerObserver(this) + } + + override fun onTransitionReady( + transition: IBinder, + info: TransitionInfo, + startTransaction: SurfaceControl.Transaction, + finishTransaction: SurfaceControl.Transaction + ) { + if (enableTaskStackObserverInShell()) { + val taskInfoList = mutableListOf<RunningTaskInfo>() + val transitionTypeList = mutableListOf<Int>() + + for (change in info.changes) { + if (change.flags and TransitionInfo.FLAG_IS_WALLPAPER != 0) { + continue + } + + val taskInfo = change.taskInfo + if (taskInfo == null || taskInfo.taskId == -1) { + continue + } + + if (change.mode == WindowManager.TRANSIT_OPEN) { + change.taskInfo?.let { taskInfoList.add(it) } + transitionTypeList.add(change.mode) + } + } + transitionToTransitionChanges.put( + transition, + TransitionChanges(taskInfoList, transitionTypeList) + ) + } + } + + override fun onTransitionStarting(transition: IBinder) {} + + override fun onTransitionMerged(merged: IBinder, playing: IBinder) {} + + override fun onTransitionFinished(transition: IBinder, aborted: Boolean) { + val taskInfoList = + transitionToTransitionChanges.getOrDefault(transition, TransitionChanges()).taskInfoList + val typeList = + transitionToTransitionChanges + .getOrDefault(transition, TransitionChanges()) + .transitionTypeList + transitionToTransitionChanges.remove(transition) + + for ((index, taskInfo) in taskInfoList.withIndex()) { + if ( + TransitionUtil.isOpeningType(typeList[index]) && + taskInfo.windowingMode == WINDOWING_MODE_FREEFORM + ) { + notifyTaskStackTransitionObserverListeners(taskInfo) + } + } + } + + fun addTaskStackTransitionObserverListener( + taskStackTransitionObserverListener: TaskStackTransitionObserverListener, + executor: Executor + ) { + taskStackTransitionObserverListeners[taskStackTransitionObserverListener] = executor + } + + fun removeTaskStackTransitionObserverListener( + taskStackTransitionObserverListener: TaskStackTransitionObserverListener + ) { + taskStackTransitionObserverListeners.remove(taskStackTransitionObserverListener) + } + + private fun notifyTaskStackTransitionObserverListeners(taskInfo: RunningTaskInfo) { + taskStackTransitionObserverListeners.forEach { (listener, executor) -> + executor.execute { listener.onTaskMovedToFrontThroughTransition(taskInfo) } + } + } + + /** Listener to use to get updates regarding task stack from this observer */ + interface TaskStackTransitionObserverListener { + /** Called when a task is moved to front. */ + fun onTaskMovedToFrontThroughTransition(taskInfo: RunningTaskInfo) {} + } + + private data class TransitionChanges( + val taskInfoList: MutableList<RunningTaskInfo> = ArrayList(), + val transitionTypeList: MutableList<Int> = ArrayList() + ) +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java index b9d70e1a599d..dd219d32bbaa 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java @@ -24,7 +24,6 @@ import static android.content.Intent.FLAG_ACTIVITY_NO_USER_ACTION; import static android.view.Display.DEFAULT_DISPLAY; import static android.view.RemoteAnimationTarget.MODE_OPENING; -import static com.android.wm.shell.common.ExecutorUtils.executeRemoteCallWithTaskPermission; import static com.android.wm.shell.common.MultiInstanceHelper.getComponent; import static com.android.wm.shell.common.MultiInstanceHelper.getShortcutComponent; import static com.android.wm.shell.common.MultiInstanceHelper.samePackage; @@ -1241,8 +1240,9 @@ public class SplitScreenController implements DragAndDropPolicy.Starter, @Override public void invalidate() { mController = null; - // Unregister the listener to ensure any registered binder death recipients are unlinked + // Unregister the listeners to ensure any binder death recipients are unlinked mListener.unregister(); + mSelectListener.unregister(); } @Override diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java index 82ef422f829a..b6a18e537600 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java @@ -1524,6 +1524,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, prepareExitSplitScreen(mTopStageAfterFoldDismiss, wct); mSplitTransitions.startDismissTransition(wct, this, mTopStageAfterFoldDismiss, EXIT_REASON_DEVICE_FOLDED); + setSplitsVisible(false); } else { exitSplitScreen( mTopStageAfterFoldDismiss == STAGE_TYPE_MAIN ? mMainStage : mSideStage, @@ -1846,7 +1847,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, void finishEnterSplitScreen(SurfaceControl.Transaction finishT) { ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "finishEnterSplitScreen"); - mSplitLayout.update(finishT, true /* resetImePosition */); + mSplitLayout.update(null, true /* resetImePosition */); mMainStage.getSplitDecorManager().inflate(mContext, mMainStage.mRootLeash); mSideStage.getSplitDecorManager().inflate(mContext, mSideStage.mRootLeash); setDividerVisibility(true, finishT); @@ -1893,6 +1894,10 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, // will be canceled. options.setPendingIntentBackgroundActivityStartMode(MODE_BACKGROUND_ACTIVITY_START_ALLOWED); options.setPendingIntentBackgroundActivityLaunchAllowedByPermission(true); + + // TODO (b/336477473): Disallow enter PiP when launching a task in split by default; + // this might have to be changed as more split-to-pip cujs are defined. + options.setDisallowEnterPictureInPictureWhileLaunching(true); opts.putAll(options.toBundle()); } @@ -2757,6 +2762,14 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, // cases above and it is not already visible return null; } else { + if (triggerTask.parentTaskId == mMainStage.mRootTaskInfo.taskId + || triggerTask.parentTaskId == mSideStage.mRootTaskInfo.taskId) { + ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "handleRequest: transition=%d " + + "restoring to split", request.getDebugId()); + out = new WindowContainerTransaction(); + mSplitTransitions.setEnterTransition(transition, request.getRemoteTransition(), + TRANSIT_SPLIT_SCREEN_OPEN_TO_SIDE, false /* resizeAnim */); + } if (isOpening && getStageOfTask(triggerTask) != null) { ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "handleRequest: transition=%d enter split", request.getDebugId()); @@ -3103,7 +3116,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, // Includes TRANSIT_CHANGE to cover reparenting top-most task to split. mainChild = change; } else if (sideChild == null && stageType == STAGE_TYPE_SIDE - && isOpeningType(change.getMode())) { + && (isOpeningType(change.getMode()) || change.getMode() == TRANSIT_CHANGE)) { sideChild = change; } else if (stageType != STAGE_TYPE_UNDEFINED && change.getMode() == TRANSIT_TO_BACK) { // Collect all to back task's and evict them when transition finished. @@ -3114,7 +3127,8 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, SplitScreenTransitions.EnterSession pendingEnter = mSplitTransitions.mPendingEnter; if (pendingEnter.mExtraTransitType == TRANSIT_SPLIT_SCREEN_OPEN_TO_SIDE) { - // Open to side should only be used when split already active and foregorund. + // Open to side should only be used when split already active and foregorund or when + // app is restoring to split from fullscreen. if (mainChild == null && sideChild == null) { Log.w(TAG, splitFailureMessage("startPendingEnterAnimation", "Launched a task in split, but didn't receive any task in transition.")); @@ -3201,6 +3215,22 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, mPausingTasks.clear(); }); + if (info.getType() == TRANSIT_CHANGE && !isSplitActive() + && pendingEnter.mExtraTransitType == TRANSIT_SPLIT_SCREEN_OPEN_TO_SIDE) { + if (finalMainChild != null && finalSideChild == null) { + requestEnterSplitSelect(finalMainChild.getTaskInfo(), + new WindowContainerTransaction(), + getMainStagePosition(), finalMainChild.getStartAbsBounds()); + } else if (finalSideChild != null && finalMainChild == null) { + requestEnterSplitSelect(finalSideChild.getTaskInfo(), + new WindowContainerTransaction(), + getSideStagePosition(), finalSideChild.getStartAbsBounds()); + } else { + throw new IllegalStateException( + "Attempting to restore to split but reparenting change not found"); + } + } + finishEnterSplitScreen(finishT); addDividerBarToTransition(info, true /* show */); return true; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingWindowController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingWindowController.java index bec4ba3bf0d1..fa084c585a59 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingWindowController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingWindowController.java @@ -23,7 +23,6 @@ import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_SOLID_COLOR import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_SPLASH_SCREEN; import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_WINDOWLESS; -import static com.android.wm.shell.common.ExecutorUtils.executeRemoteCallWithTaskPermission; import static com.android.wm.shell.sysui.ShellSharedConstants.KEY_EXTRA_SHELL_STARTING_WINDOW; import android.app.ActivityManager.RunningTaskInfo; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/HomeTransitionObserver.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/HomeTransitionObserver.java index b1a1e5999aa9..299da136a5b4 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/HomeTransitionObserver.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/HomeTransitionObserver.java @@ -133,5 +133,7 @@ public class HomeTransitionObserver implements TransitionObserver, */ public void invalidate(Transitions transitions) { transitions.unregisterObserver(this); + // Unregister the listener to ensure any registered binder death recipients are unlinked + mListener.unregister(); } } 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 6ade81c0f3a1..f257e207673d 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 @@ -35,7 +35,6 @@ import static android.window.TransitionInfo.FLAG_NO_ANIMATION; import static android.window.TransitionInfo.FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT; import static com.android.systemui.shared.Flags.returnAnimationFrameworkLibrary; -import static com.android.wm.shell.common.ExecutorUtils.executeRemoteCallWithTaskPermission; import static com.android.wm.shell.shared.TransitionUtil.isClosingType; import static com.android.wm.shell.shared.TransitionUtil.isOpeningType; import static com.android.wm.shell.sysui.ShellSharedConstants.KEY_EXTRA_SHELL_SHELL_TRANSITIONS; @@ -54,6 +53,7 @@ import android.os.IBinder; import android.os.RemoteException; import android.os.SystemProperties; import android.provider.Settings; +import android.util.ArrayMap; import android.util.Log; import android.util.Pair; import android.view.SurfaceControl; @@ -166,9 +166,6 @@ public class Transitions implements RemoteCallable<Transitions>, public static final int TRANSIT_DESKTOP_MODE_END_DRAG_TO_DESKTOP = WindowManager.TRANSIT_FIRST_CUSTOM + 11; - /** Transition type to fullscreen from desktop mode. */ - public static final int TRANSIT_EXIT_DESKTOP_MODE = WindowManager.TRANSIT_FIRST_CUSTOM + 12; - /** Transition type to cancel the drag to desktop mode. */ public static final int TRANSIT_DESKTOP_MODE_CANCEL_DRAG_TO_DESKTOP = WindowManager.TRANSIT_FIRST_CUSTOM + 13; @@ -177,9 +174,6 @@ public class Transitions implements RemoteCallable<Transitions>, public static final int TRANSIT_DESKTOP_MODE_TOGGLE_RESIZE = WindowManager.TRANSIT_FIRST_CUSTOM + 14; - /** Transition to animate task to desktop. */ - public static final int TRANSIT_MOVE_TO_DESKTOP = WindowManager.TRANSIT_FIRST_CUSTOM + 15; - /** Transition to resize PiP task. */ public static final int TRANSIT_RESIZE_PIP = TRANSIT_FIRST_CUSTOM + 16; @@ -190,6 +184,10 @@ public class Transitions implements RemoteCallable<Transitions>, // TRANSIT_FIRST_CUSTOM + 17 TaskFragmentOrganizer.TASK_FRAGMENT_TRANSIT_DRAG_RESIZE; + /** Transition type for desktop mode transitions. */ + public static final int TRANSIT_DESKTOP_MODE_TYPES = + WindowManager.TRANSIT_FIRST_CUSTOM + 100; + private final ShellTaskOrganizer mOrganizer; private final Context mContext; private final ShellExecutor mMainExecutor; @@ -229,7 +227,8 @@ public class Transitions implements RemoteCallable<Transitions>, private boolean mDisableForceSync = false; private static final class ActiveTransition { - IBinder mToken; + final IBinder mToken; + TransitionHandler mHandler; boolean mAborted; TransitionInfo mInfo; @@ -239,6 +238,10 @@ public class Transitions implements RemoteCallable<Transitions>, /** Ordered list of transitions which have been merged into this one. */ private ArrayList<ActiveTransition> mMerged; + ActiveTransition(IBinder token) { + mToken = token; + } + boolean isSync() { return (mInfo.getFlags() & TransitionInfo.FLAG_SYNC) != 0; } @@ -268,6 +271,9 @@ public class Transitions implements RemoteCallable<Transitions>, } } + /** All transitions that we have created, but not yet finished. */ + private final ArrayMap<IBinder, ActiveTransition> mKnownTransitions = new ArrayMap<>(); + /** Keeps track of transitions which have been started, but aren't ready yet. */ private final ArrayList<ActiveTransition> mPendingTransitions = new ArrayList<>(); @@ -657,8 +663,10 @@ public class Transitions implements RemoteCallable<Transitions>, } if (change.hasFlags(FLAG_NO_ANIMATION)) { hasNoAnimation = true; - } else { - // at-least one relevant participant *is* animated, so we need to animate. + } else if (!TransitionUtil.isOrderOnly(change) && !change.hasFlags(FLAG_IS_OCCLUDED)) { + // Ignore the order only or occluded changes since they shouldn't be visible during + // animation. For anything else, we need to animate if at-least one relevant + // participant *is* animated, return false; } } @@ -690,7 +698,7 @@ public class Transitions implements RemoteCallable<Transitions>, info.getDebugId(), transitionToken, info); int activeIdx = findByToken(mPendingTransitions, transitionToken); if (activeIdx < 0) { - final ActiveTransition existing = getKnownTransition(transitionToken); + final ActiveTransition existing = mKnownTransitions.get(transitionToken); if (existing != null) { Log.e(TAG, "Got duplicate transitionReady for " + transitionToken); // The transition is already somewhere else in the pipeline, so just return here. @@ -705,8 +713,8 @@ public class Transitions implements RemoteCallable<Transitions>, + transitionToken + ". expecting one of " + Arrays.toString(mPendingTransitions.stream().map( activeTransition -> activeTransition.mToken).toArray())); - final ActiveTransition fallback = new ActiveTransition(); - fallback.mToken = transitionToken; + final ActiveTransition fallback = new ActiveTransition(transitionToken); + mKnownTransitions.put(transitionToken, fallback); mPendingTransitions.add(fallback); activeIdx = mPendingTransitions.size() - 1; } @@ -746,7 +754,7 @@ public class Transitions implements RemoteCallable<Transitions>, // Sleep starts a process of forcing all prior transitions to finish immediately ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Start finish-for-sync track %d", i); - finishForSync(active, i, null /* forceFinish */); + finishForSync(active.mToken, i, null /* forceFinish */); } if (hadPreceding) { return false; @@ -864,6 +872,7 @@ public class Transitions implements RemoteCallable<Transitions>, } else if (mPendingTransitions.isEmpty()) { ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "All active transition " + "animations finished"); + mKnownTransitions.clear(); // Run all runnables from the run-when-idle queue. for (int i = 0; i < mRunWhenIdleQueue.size(); i++) { mRunWhenIdleQueue.get(i).run(); @@ -884,7 +893,7 @@ public class Transitions implements RemoteCallable<Transitions>, ready.mStartT.apply(); } // finish now since there's nothing to animate. Calls back into processReadyQueue - onFinish(ready, null); + onFinish(ready.mToken, null); return; } playTransition(ready); @@ -943,8 +952,10 @@ public class Transitions implements RemoteCallable<Transitions>, private void playTransition(@NonNull ActiveTransition active) { ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Playing animation for %s", active); + final var token = active.mToken; + for (int i = 0; i < mObservers.size(); ++i) { - mObservers.get(i).onTransitionStarting(active.mToken); + mObservers.get(i).onTransitionStarting(token); } setupAnimHierarchy(active.mInfo, active.mStartT, active.mFinishT); @@ -953,8 +964,8 @@ public class Transitions implements RemoteCallable<Transitions>, if (active.mHandler != null) { ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " try firstHandler %s", active.mHandler); - boolean consumed = active.mHandler.startAnimation(active.mToken, active.mInfo, - active.mStartT, active.mFinishT, (wct) -> onFinish(active, wct)); + boolean consumed = active.mHandler.startAnimation(token, active.mInfo, + active.mStartT, active.mFinishT, (wct) -> onFinish(token, wct)); if (consumed) { ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " animated by firstHandler"); mTransitionTracer.logDispatched(active.mInfo.getDebugId(), active.mHandler); @@ -962,8 +973,8 @@ public class Transitions implements RemoteCallable<Transitions>, } } // Otherwise give every other handler a chance - active.mHandler = dispatchTransition(active.mToken, active.mInfo, active.mStartT, - active.mFinishT, (wct) -> onFinish(active, wct), active.mHandler); + active.mHandler = dispatchTransition(token, active.mInfo, active.mStartT, + active.mFinishT, (wct) -> onFinish(token, wct), active.mHandler); } /** @@ -1039,10 +1050,15 @@ public class Transitions implements RemoteCallable<Transitions>, info.releaseAnimSurfaces(); } - private void onFinish(ActiveTransition active, + private void onFinish(IBinder token, @Nullable WindowContainerTransaction wct) { + final ActiveTransition active = mKnownTransitions.get(token); + if (active == null) { + Log.e(TAG, "Trying to finish a non-existent transition: " + token); + return; + } final Track track = mTracks.get(active.getTrack()); - if (track.mActiveTransition != active) { + if (track == null || track.mActiveTransition != active) { Log.e(TAG, "Trying to finish a non-running transition. Either remote crashed or " + " a handler didn't properly deal with a merge. " + active, new RuntimeException()); @@ -1095,54 +1111,25 @@ public class Transitions implements RemoteCallable<Transitions>, ActiveTransition merged = active.mMerged.get(iM); mOrganizer.finishTransition(merged.mToken, null /* wct */); releaseSurfaces(merged.mInfo); + mKnownTransitions.remove(merged.mToken); } active.mMerged.clear(); } + mKnownTransitions.remove(token); // Now that this is done, check the ready queue for more work. processReadyQueue(track); } - /** - * Checks to see if the transition specified by `token` is already known. If so, it will be - * returned. - */ - @Nullable - private ActiveTransition getKnownTransition(IBinder token) { - for (int i = 0; i < mPendingTransitions.size(); ++i) { - final ActiveTransition active = mPendingTransitions.get(i); - if (active.mToken == token) return active; - } - for (int i = 0; i < mReadyDuringSync.size(); ++i) { - final ActiveTransition active = mReadyDuringSync.get(i); - if (active.mToken == token) return active; - } - for (int t = 0; t < mTracks.size(); ++t) { - final Track tr = mTracks.get(t); - for (int i = 0; i < tr.mReadyTransitions.size(); ++i) { - final ActiveTransition active = tr.mReadyTransitions.get(i); - if (active.mToken == token) return active; - } - final ActiveTransition active = tr.mActiveTransition; - if (active == null) continue; - if (active.mToken == token) return active; - if (active.mMerged == null) continue; - for (int m = 0; m < active.mMerged.size(); ++m) { - final ActiveTransition merged = active.mMerged.get(m); - if (merged.mToken == token) return merged; - } - } - return null; - } - void requestStartTransition(@NonNull IBinder transitionToken, @Nullable TransitionRequestInfo request) { ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Transition requested (#%d): %s %s", request.getDebugId(), transitionToken, request); - if (getKnownTransition(transitionToken) != null) { + if (mKnownTransitions.containsKey(transitionToken)) { throw new RuntimeException("Transition already started " + transitionToken); } - final ActiveTransition active = new ActiveTransition(); + final ActiveTransition active = new ActiveTransition(transitionToken); + mKnownTransitions.put(transitionToken, active); WindowContainerTransaction wct = null; // If we have sleep, we use a special handler and we try to finish everything ASAP. @@ -1182,7 +1169,6 @@ public class Transitions implements RemoteCallable<Transitions>, wct.setBounds(request.getTriggerTask().token, null); } mOrganizer.startTransition(transitionToken, wct != null && wct.isEmpty() ? null : wct); - active.mToken = transitionToken; // Currently, WMCore only does one transition at a time. If it makes a requestStart, it // is already collecting that transition on core-side, so it will be the next one to // become ready. There may already be pending transitions added as part of direct @@ -1201,9 +1187,10 @@ public class Transitions implements RemoteCallable<Transitions>, @NonNull WindowContainerTransaction wct, @Nullable TransitionHandler handler) { ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Directly starting a new transition " + "type=%d wct=%s handler=%s", type, wct, handler); - final ActiveTransition active = new ActiveTransition(); + final ActiveTransition active = + new ActiveTransition(mOrganizer.startNewTransition(type, wct)); active.mHandler = handler; - active.mToken = mOrganizer.startNewTransition(type, wct); + mKnownTransitions.put(active.mToken, active); mPendingTransitions.add(active); return active.mToken; } @@ -1243,14 +1230,14 @@ public class Transitions implements RemoteCallable<Transitions>, * * This is then repeated until there are no more pending sleep transitions. * - * @param reason The SLEEP transition that triggered this round of finishes. We will continue - * looping round finishing transitions as long as this is still waiting. + * @param reason The token for the SLEEP transition that triggered this round of finishes. + * We will continue looping round finishing transitions until this is ready. * @param forceFinish When non-null, this is the transition that we last sent the SLEEP merge * signal to -- so it will be force-finished if it's still running. */ - private void finishForSync(ActiveTransition reason, + private void finishForSync(IBinder reason, int trackIdx, @Nullable ActiveTransition forceFinish) { - if (getKnownTransition(reason.mToken) == null) { + if (!mKnownTransitions.containsKey(reason)) { Log.d(TAG, "finishForSleep: already played sync transition " + reason); return; } @@ -1270,7 +1257,7 @@ public class Transitions implements RemoteCallable<Transitions>, forceFinish.mHandler.onTransitionConsumed( forceFinish.mToken, true /* aborted */, null /* finishTransaction */); } - onFinish(forceFinish, null); + onFinish(forceFinish.mToken, null); } } if (track.isIdle() || mReadyDuringSync.isEmpty()) { 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 37cdbb47bfe8..eeb3662dd45a 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 @@ -83,6 +83,7 @@ import com.android.wm.shell.common.DisplayInsetsController; import com.android.wm.shell.common.DisplayLayout; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.SyncTransactionQueue; +import com.android.wm.shell.common.desktopmode.DesktopModeTransitionSource; import com.android.wm.shell.common.split.SplitScreenConstants.SplitPosition; import com.android.wm.shell.desktopmode.DesktopModeVisualIndicator; import com.android.wm.shell.desktopmode.DesktopTasksController; @@ -438,7 +439,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { } else if (id == R.id.caption_handle || id == R.id.open_menu_button) { if (!decoration.isHandleMenuActive()) { moveTaskToFront(decoration.mTaskInfo); - decoration.createHandleMenu(); + decoration.createHandleMenu(mSplitScreenController); } else { decoration.closeHandleMenu(); } @@ -447,7 +448,8 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { // App sometimes draws before the insets from WindowDecoration#relayout have // been added, so they must be added here mWindowDecorByTaskId.get(mTaskId).addCaptionInset(wct); - mDesktopTasksController.moveToDesktop(mTaskId, wct); + mDesktopTasksController.moveToDesktop(mTaskId, wct, + DesktopModeTransitionSource.APP_HANDLE_MENU_BUTTON); decoration.closeHandleMenu(); } else if (id == R.id.fullscreen_button) { decoration.closeHandleMenu(); @@ -455,7 +457,8 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { mSplitScreenController.moveTaskToFullscreen(mTaskId, SplitScreenController.EXIT_REASON_DESKTOP_MODE); } else { - mDesktopTasksController.moveToFullscreen(mTaskId); + mDesktopTasksController.moveToFullscreen(mTaskId, + DesktopModeTransitionSource.APP_HANDLE_MENU_BUTTON); } } else if (id == R.id.split_screen_button) { decoration.closeHandleMenu(); 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 eced07831ff7..4d597cac889e 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 @@ -68,10 +68,11 @@ 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.shared.DesktopModeStatus; +import com.android.wm.shell.splitscreen.SplitScreenController; import com.android.wm.shell.windowdecor.extension.TaskInfoKt; -import com.android.wm.shell.windowdecor.viewholder.DesktopModeAppControlsWindowDecorationViewHolder; -import com.android.wm.shell.windowdecor.viewholder.DesktopModeFocusedWindowDecorationViewHolder; -import com.android.wm.shell.windowdecor.viewholder.DesktopModeWindowDecorationViewHolder; +import com.android.wm.shell.windowdecor.viewholder.AppHandleViewHolder; +import com.android.wm.shell.windowdecor.viewholder.AppHeaderViewHolder; +import com.android.wm.shell.windowdecor.viewholder.WindowDecorationViewHolder; import kotlin.Unit; @@ -90,7 +91,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin private final Choreographer mChoreographer; private final SyncTransactionQueue mSyncQueue; - private DesktopModeWindowDecorationViewHolder mWindowDecorViewHolder; + private WindowDecorationViewHolder mWindowDecorViewHolder; private View.OnClickListener mOnCaptionButtonClickListener; private View.OnTouchListener mOnCaptionTouchListener; private View.OnLongClickListener mOnCaptionLongClickListener; @@ -98,10 +99,12 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin private DragPositioningCallback mDragPositioningCallback; private DragResizeInputListener mDragResizeListener; private DragDetector mDragDetector; - + private Runnable mCurrentViewHostRunnable = null; private RelayoutParams mRelayoutParams = new RelayoutParams(); private final WindowDecoration.RelayoutResult<WindowDecorLinearLayout> mResult = new WindowDecoration.RelayoutResult<>(); + private final Runnable mViewHostRunnable = + () -> updateViewHost(mRelayoutParams, null /* onDrawTransaction */, mResult); private final Point mPositionInParent = new Point(); private HandleMenu mHandleMenu; @@ -193,17 +196,88 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin // position and crop are set. final boolean shouldSetTaskPositionAndCrop = !DesktopModeStatus.isVeiledResizeEnabled() && mTaskDragResizer.isResizingOrAnimating(); - // Use |applyStartTransactionOnDraw| so that the transaction (that applies task crop) is - // 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 */, - shouldSetTaskPositionAndCrop); + // For headers only (i.e. in freeform): use |applyStartTransactionOnDraw| so that the + // transaction (that applies task crop) is 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. + final boolean applyTransactionOnDraw = + taskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM; + relayout(taskInfo, t, t, applyTransactionOnDraw, shouldSetTaskPositionAndCrop); + if (!applyTransactionOnDraw) { + t.apply(); + } } void relayout(ActivityManager.RunningTaskInfo taskInfo, SurfaceControl.Transaction startT, SurfaceControl.Transaction finishT, boolean applyStartTransactionOnDraw, boolean shouldSetTaskPositionAndCrop) { Trace.beginSection("DesktopModeWindowDecoration#relayout"); + if (taskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM) { + // 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, + shouldSetTaskPositionAndCrop); + } 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, + shouldSetTaskPositionAndCrop); + } + Trace.endSection(); + } + + /** Run the whole relayout phase immediately without delay. */ + private void relayoutInSync(ActivityManager.RunningTaskInfo taskInfo, + SurfaceControl.Transaction startT, SurfaceControl.Transaction finishT, + boolean applyStartTransactionOnDraw, boolean shouldSetTaskPositionAndCrop) { + // Clear the current ViewHost runnable as we will update the ViewHost here + clearCurrentViewHostRunnable(); + updateRelayoutParamsAndSurfaces(taskInfo, startT, finishT, applyStartTransactionOnDraw, + shouldSetTaskPositionAndCrop); + if (mResult.mRootView != null) { + updateViewHost(mRelayoutParams, startT, mResult); + } + } + + /** + * Clear the current ViewHost runnable - to ensure it doesn't run once relayout params have been + * updated. + */ + private void clearCurrentViewHostRunnable() { + if (mCurrentViewHostRunnable != null) { + mHandler.removeCallbacks(mCurrentViewHostRunnable); + mCurrentViewHostRunnable = null; + } + } + + /** + * Relayout the window decoration but repost some of the work, to unblock the current callstack. + */ + private void relayoutWithDelayedViewHost(ActivityManager.RunningTaskInfo taskInfo, + SurfaceControl.Transaction startT, SurfaceControl.Transaction finishT, + boolean applyStartTransactionOnDraw, boolean shouldSetTaskPositionAndCrop) { + if (applyStartTransactionOnDraw) { + throw new IllegalArgumentException( + "We cannot both sync viewhost ondraw and delay viewhost creation."); + } + // Clear the current ViewHost runnable as we will update the ViewHost here + clearCurrentViewHostRunnable(); + updateRelayoutParamsAndSurfaces(taskInfo, startT, finishT, + false /* applyStartTransactionOnDraw */, shouldSetTaskPositionAndCrop); + 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. + return; + } + // Store the current runnable so it can be removed if we start a new relayout. + mCurrentViewHostRunnable = mViewHostRunnable; + mHandler.post(mCurrentViewHostRunnable); + } + + private void updateRelayoutParamsAndSurfaces(ActivityManager.RunningTaskInfo taskInfo, + SurfaceControl.Transaction startT, SurfaceControl.Transaction finishT, + boolean applyStartTransactionOnDraw, boolean shouldSetTaskPositionAndCrop) { + Trace.beginSection("DesktopModeWindowDecoration#updateRelayoutParamsAndSurfaces"); if (isHandleMenuActive()) { mHandleMenu.relayout(startT); } @@ -215,8 +289,8 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin final SurfaceControl oldDecorationSurface = mDecorationContainerSurface; final WindowContainerTransaction wct = new WindowContainerTransaction(); - Trace.beginSection("DesktopModeWindowDecoration#relayout-inner"); - relayout(mRelayoutParams, startT, finishT, wct, oldRootView, mResult); + Trace.beginSection("DesktopModeWindowDecoration#relayout-updateViewsAndSurfaces"); + updateViewsAndSurfaces(mRelayoutParams, startT, finishT, wct, oldRootView, mResult); Trace.endSection(); // After this line, mTaskInfo is up-to-date and should be used instead of taskInfo @@ -227,37 +301,12 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin 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. - Trace.endSection(); // DesktopModeWindowDecoration#relayout + Trace.endSection(); // DesktopModeWindowDecoration#updateRelayoutParamsAndSurfaces return; } if (oldRootView != mResult.mRootView) { - if (mRelayoutParams.mLayoutResId == R.layout.desktop_mode_focused_window_decor) { - mWindowDecorViewHolder = new DesktopModeFocusedWindowDecorationViewHolder( - mResult.mRootView, - mOnCaptionTouchListener, - mOnCaptionButtonClickListener - ); - } else if (mRelayoutParams.mLayoutResId - == R.layout.desktop_mode_app_controls_window_decor) { - loadAppInfoIfNeeded(); - mWindowDecorViewHolder = new DesktopModeAppControlsWindowDecorationViewHolder( - mResult.mRootView, - mOnCaptionTouchListener, - mOnCaptionButtonClickListener, - mOnCaptionLongClickListener, - mOnCaptionGenericMotionListener, - mAppName, - mAppIconBitmap, - () -> { - if (!isMaximizeMenuActive()) { - createMaximizeMenu(); - } - return Unit.INSTANCE; - }); - } else { - throw new IllegalArgumentException("Unexpected layout resource id"); - } + mWindowDecorViewHolder = createViewHolder(); } Trace.beginSection("DesktopModeWindowDecoration#relayout-binding"); mWindowDecorViewHolder.bindData(mTaskInfo); @@ -268,16 +317,18 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin closeMaximizeMenu(); } - final boolean isFreeform = - taskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM; - final boolean isDragResizeable = isFreeform && taskInfo.isResizeable; - if (!isDragResizeable) { + updateDragResizeListener(oldDecorationSurface); + updateMaximizeMenu(startT); + Trace.endSection(); // DesktopModeWindowDecoration#updateRelayoutParamsAndSurfaces + } + + private void updateDragResizeListener(SurfaceControl oldDecorationSurface) { + if (!isDragResizable(mTaskInfo)) { if (!mTaskInfo.positionInParent.equals(mPositionInParent)) { // We still want to track caption bar's exclusion region on a non-resizeable task. updateExclusionRegion(); } closeDragResizeListener(); - Trace.endSection(); // DesktopModeWindowDecoration#relayout return; } @@ -311,15 +362,51 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin || !mTaskInfo.positionInParent.equals(mPositionInParent)) { updateExclusionRegion(); } + } - if (isMaximizeMenuActive()) { - if (!mTaskInfo.isVisible()) { - closeMaximizeMenu(); - } else { - mMaximizeMenu.positionMenu(calculateMaximizeMenuPosition(), startT); - } + private static boolean isDragResizable(ActivityManager.RunningTaskInfo taskInfo) { + final boolean isFreeform = + taskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM; + return isFreeform && taskInfo.isResizeable; + } + + private void updateMaximizeMenu(SurfaceControl.Transaction startT) { + if (!isDragResizable(mTaskInfo) || !isMaximizeMenuActive()) { + return; + } + if (!mTaskInfo.isVisible()) { + closeMaximizeMenu(); + } else { + mMaximizeMenu.positionMenu(calculateMaximizeMenuPosition(), startT); + } + } + + private WindowDecorationViewHolder createViewHolder() { + if (mRelayoutParams.mLayoutResId == R.layout.desktop_mode_app_handle) { + return new AppHandleViewHolder( + mResult.mRootView, + mOnCaptionTouchListener, + mOnCaptionButtonClickListener + ); + } else if (mRelayoutParams.mLayoutResId + == R.layout.desktop_mode_app_header) { + loadAppInfoIfNeeded(); + return new AppHeaderViewHolder( + mResult.mRootView, + mOnCaptionTouchListener, + mOnCaptionButtonClickListener, + mOnCaptionLongClickListener, + mOnCaptionGenericMotionListener, + mAppName, + mAppIconBitmap, + () -> { + if (!isMaximizeMenuActive()) { + createMaximizeMenu(); + } + return Unit.INSTANCE; + }); } - Trace.endSection(); // DesktopModeWindowDecoration#relayout + throw new IllegalArgumentException("Unexpected layout resource id"); } @VisibleForTesting @@ -331,8 +418,8 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin boolean shouldSetTaskPositionAndCrop) { final int captionLayoutId = getDesktopModeWindowDecorLayoutId(taskInfo.getWindowingMode()); final boolean isAppHeader = - captionLayoutId == R.layout.desktop_mode_app_controls_window_decor; - final boolean isAppHandle = captionLayoutId == R.layout.desktop_mode_focused_window_decor; + captionLayoutId == R.layout.desktop_mode_app_header; + final boolean isAppHandle = captionLayoutId == R.layout.desktop_mode_app_handle; relayoutParams.reset(); relayoutParams.mRunningTaskInfo = taskInfo; relayoutParams.mLayoutResId = captionLayoutId; @@ -385,7 +472,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin // Should match the density of the task. The task may have had its density overridden // to be different that SysUI's. windowDecorConfig.setTo(taskInfo.configuration); - } else if (DesktopModeStatus.isDesktopDensityOverrideSet()) { + } else if (DesktopModeStatus.useDesktopOverrideDensity()) { // The task has had its density overridden, but keep using the system's density to // layout the header. windowDecorConfig.setTo(context.getResources().getConfiguration()); @@ -405,7 +492,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin * resource. Otherwise, return ID_NULL and caption width be set to task width. */ private static int getCaptionWidthId(int layoutResId) { - if (layoutResId == R.layout.desktop_mode_focused_window_decor) { + if (layoutResId == R.layout.desktop_mode_app_handle) { return R.dimen.desktop_mode_fullscreen_decor_caption_width; } return Resources.ID_NULL; @@ -515,8 +602,8 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin private void createResizeVeilIfNeeded() { if (mResizeVeil != null) return; loadAppInfoIfNeeded(); - mResizeVeil = new ResizeVeil(mContext, mDisplayController, mResizeVeilBitmap, mTaskInfo, - mTaskSurface, mSurfaceControlTransactionSupplier); + mResizeVeil = new ResizeVeil(mContext, mDisplayController, mResizeVeilBitmap, + mTaskSurface, mSurfaceControlTransactionSupplier, mTaskInfo); } /** @@ -524,7 +611,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin */ public void showResizeVeil(Rect taskBounds) { createResizeVeilIfNeeded(); - mResizeVeil.showVeil(mTaskSurface, taskBounds); + mResizeVeil.showVeil(mTaskSurface, taskBounds, mTaskInfo); } /** @@ -532,7 +619,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin */ public void showResizeVeil(SurfaceControl.Transaction tx, Rect taskBounds) { createResizeVeilIfNeeded(); - mResizeVeil.showVeil(tx, mTaskSurface, taskBounds, false /* fadeIn */); + mResizeVeil.showVeil(tx, mTaskSurface, taskBounds, mTaskInfo, false /* fadeIn */); } /** @@ -568,7 +655,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin @Override @NonNull Rect calculateValidDragArea() { - final int appTextWidth = ((DesktopModeAppControlsWindowDecorationViewHolder) + final int appTextWidth = ((AppHeaderViewHolder) mWindowDecorViewHolder).getAppNameTextWidth(); final int leftButtonsWidth = loadDimensionPixelSize(mContext.getResources(), R.dimen.desktop_mode_app_details_width_minus_text) + appTextWidth; @@ -650,7 +737,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin /** * Create and display handle menu window. */ - void createHandleMenu() { + void createHandleMenu(SplitScreenController splitScreenController) { loadAppInfoIfNeeded(); mHandleMenu = new HandleMenu.Builder(this) .setAppIcon(mAppIconBitmap) @@ -660,6 +747,8 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin .setLayoutId(mRelayoutParams.mLayoutResId) .setWindowingButtonsVisible(DesktopModeStatus.canEnterDesktopMode(mContext)) .setCaptionHeight(mResult.mCaptionHeight) + .setDisplayController(mDisplayController) + .setSplitScreenController(splitScreenController) .build(); mWindowDecorViewHolder.onHandleMenuOpened(); mHandleMenu.show(); @@ -747,7 +836,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin */ boolean checkTouchEventInFocusedCaptionHandle(MotionEvent ev) { if (isHandleMenuActive() || !(mWindowDecorViewHolder - instanceof DesktopModeFocusedWindowDecorationViewHolder)) { + instanceof AppHandleViewHolder)) { return false; } @@ -815,11 +904,15 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin // We want handle to remain pressed if the pointer moves outside of it during a drag. handle.setPressed((inHandle && action == ACTION_DOWN) || (handle.isPressed() && action != ACTION_UP && action != ACTION_CANCEL)); - if (isHandleMenuActive()) { + if (isHandleMenuActive() && !isHandleMenuAboveStatusBar()) { mHandleMenu.checkMotionEvent(ev); } } + private boolean isHandleMenuAboveStatusBar() { + return Flags.enableAdditionalWindowsAboveStatusBar() && !mTaskInfo.isFreeform(); + } + private boolean pointInView(View v, float x, float y) { return v != null && v.getLeft() <= x && v.getRight() >= x && v.getTop() <= y && v.getBottom() >= y; @@ -831,13 +924,14 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin closeHandleMenu(); mExclusionRegionListener.onExclusionRegionDismissed(mTaskInfo.taskId); disposeResizeVeil(); + clearCurrentViewHostRunnable(); super.close(); } private static int getDesktopModeWindowDecorLayoutId(@WindowingMode int windowingMode) { return windowingMode == WINDOWING_MODE_FREEFORM - ? R.layout.desktop_mode_app_controls_window_decor - : R.layout.desktop_mode_focused_window_decor; + ? R.layout.desktop_mode_app_header + : R.layout.desktop_mode_app_handle; } private void updatePositionInParent() { @@ -868,6 +962,10 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin return exclusionRegion; } + int getCaptionX() { + return mResult.mCaptionX; + } + @Override int getCaptionHeightId(@WindowingMode int windowingMode) { return getCaptionHeightIdStatic(windowingMode); @@ -889,20 +987,20 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin } void setAnimatingTaskResize(boolean animatingTaskResize) { - if (mRelayoutParams.mLayoutResId == R.layout.desktop_mode_focused_window_decor) return; - ((DesktopModeAppControlsWindowDecorationViewHolder) mWindowDecorViewHolder) + if (mRelayoutParams.mLayoutResId == R.layout.desktop_mode_app_handle) return; + ((AppHeaderViewHolder) mWindowDecorViewHolder) .setAnimatingTaskResize(animatingTaskResize); } /** Called when there is a {@Link ACTION_HOVER_EXIT} on the maximize window button. */ void onMaximizeWindowHoverExit() { - ((DesktopModeAppControlsWindowDecorationViewHolder) mWindowDecorViewHolder) + ((AppHeaderViewHolder) mWindowDecorViewHolder) .onMaximizeWindowHoverExit(); } /** Called when there is a {@Link ACTION_HOVER_ENTER} on the maximize window button. */ void onMaximizeWindowHoverEnter() { - ((DesktopModeAppControlsWindowDecorationViewHolder) mWindowDecorViewHolder) + ((AppHeaderViewHolder) mWindowDecorViewHolder) .onMaximizeWindowHoverEnter(); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragPositioningCallbackUtility.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragPositioningCallbackUtility.java index 82c399ad8152..fe1c9c3cce66 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragPositioningCallbackUtility.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragPositioningCallbackUtility.java @@ -22,12 +22,16 @@ import static com.android.wm.shell.windowdecor.DragPositioningCallback.CTRL_TYPE import static com.android.wm.shell.windowdecor.DragPositioningCallback.CTRL_TYPE_TOP; import static com.android.wm.shell.windowdecor.DragPositioningCallback.CTRL_TYPE_UNDEFINED; +import android.content.Context; import android.graphics.PointF; import android.graphics.Rect; import android.util.DisplayMetrics; import android.view.SurfaceControl; +import com.android.window.flags.Flags; +import com.android.wm.shell.R; import com.android.wm.shell.common.DisplayController; +import com.android.wm.shell.shared.DesktopModeStatus; /** * Utility class that contains logic common to classes implementing {@link DragPositioningCallback} @@ -35,11 +39,11 @@ import com.android.wm.shell.common.DisplayController; * and applying that change to the task bounds when applicable. */ public class DragPositioningCallbackUtility { - /** * Determine the delta between input's current point and the input start point. - * @param inputX current input x coordinate - * @param inputY current input y coordinate + * + * @param inputX current input x coordinate + * @param inputY current input y coordinate * @param repositionStartPoint initial input coordinate * @return delta between these two points */ @@ -52,13 +56,14 @@ public class DragPositioningCallbackUtility { /** * Based on type of resize and delta provided, calculate the new bounds to display for this * task. - * @param ctrlType type of drag being performed - * @param repositionTaskBounds the bounds the task is being repositioned to + * + * @param ctrlType type of drag being performed + * @param repositionTaskBounds the bounds the task is being repositioned to * @param taskBoundsAtDragStart the bounds of the task on the first drag input event - * @param stableBounds bounds that represent the resize limit of this task - * @param delta difference between start input and current input in x/y coordinates - * @param displayController task's display controller - * @param windowDecoration window decoration of the task being dragged + * @param stableBounds bounds that represent the resize limit of this task + * @param delta difference between start input and current input in x/y + * coordinates + * @param windowDecoration window decoration of the task being dragged * @return whether this method changed repositionTaskBounds */ static boolean changeBounds(int ctrlType, Rect repositionTaskBounds, Rect taskBoundsAtDragStart, @@ -142,8 +147,9 @@ public class DragPositioningCallbackUtility { /** * If task bounds are outside of provided drag area, snap the bounds to be just inside the * drag area. + * * @param repositionTaskBounds bounds determined by task positioner - * @param validDragArea the area that task must be positioned inside + * @param validDragArea the area that task must be positioned inside * @return whether bounds were modified */ public static boolean snapTaskBoundsIfNecessary(Rect repositionTaskBounds, Rect validDragArea) { @@ -170,18 +176,38 @@ public class DragPositioningCallbackUtility { private static float getMinWidth(DisplayController displayController, WindowDecoration windowDecoration) { - return windowDecoration.mTaskInfo.minWidth < 0 ? getDefaultMinSize(displayController, + return windowDecoration.mTaskInfo.minWidth < 0 ? getDefaultMinWidth(displayController, windowDecoration) : windowDecoration.mTaskInfo.minWidth; } private static float getMinHeight(DisplayController displayController, WindowDecoration windowDecoration) { - return windowDecoration.mTaskInfo.minHeight < 0 ? getDefaultMinSize(displayController, + return windowDecoration.mTaskInfo.minHeight < 0 ? getDefaultMinHeight(displayController, windowDecoration) : windowDecoration.mTaskInfo.minHeight; } + private static float getDefaultMinWidth(DisplayController displayController, + WindowDecoration windowDecoration) { + if (isSizeConstraintForDesktopModeEnabled(windowDecoration.mDecorWindowContext)) { + return WindowDecoration.loadDimensionPixelSize( + windowDecoration.mDecorWindowContext.getResources(), + R.dimen.desktop_mode_minimum_window_width); + } + return getDefaultMinSize(displayController, windowDecoration); + } + + private static float getDefaultMinHeight(DisplayController displayController, + WindowDecoration windowDecoration) { + if (isSizeConstraintForDesktopModeEnabled(windowDecoration.mDecorWindowContext)) { + return WindowDecoration.loadDimensionPixelSize( + windowDecoration.mDecorWindowContext.getResources(), + R.dimen.desktop_mode_minimum_window_height); + } + return getDefaultMinSize(displayController, windowDecoration); + } + private static float getDefaultMinSize(DisplayController displayController, WindowDecoration windowDecoration) { float density = displayController.getDisplayLayout(windowDecoration.mTaskInfo.displayId) @@ -189,9 +215,15 @@ public class DragPositioningCallbackUtility { return windowDecoration.mTaskInfo.defaultMinSize * density; } + private static boolean isSizeConstraintForDesktopModeEnabled(Context context) { + return DesktopModeStatus.canEnterDesktopMode(context) + && Flags.enableDesktopWindowingSizeConstraints(); + } + interface DragStartListener { /** * Inform the implementing class that a drag resize has started + * * @param taskId id of this positioner's {@link WindowDecoration} */ void onDragStart(int taskId); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java index badce6e93d67..635f96be3d70 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java @@ -285,6 +285,9 @@ class DragResizeInputListener implements AutoCloseable { private boolean mShouldHandleEvents; private int mLastCursorType = PointerIcon.TYPE_DEFAULT; private Rect mDragStartTaskBounds; + // The id of the particular pointer in a MotionEvent that we are listening to for drag + // resize events. For example, if multiple fingers are touching the screen, then each one + // has a separate pointer id, but we only accept drag input from one. private int mDragPointerId = -1; private TaskResizeInputEventReceiver(@NonNull Context context, @@ -395,6 +398,8 @@ class DragResizeInputListener implements AutoCloseable { mShouldHandleEvents = mDragResizeWindowGeometry.shouldHandleEvent(e, isTouch, new Point() /* offset */); if (mShouldHandleEvents) { + // Save the id of the pointer for this drag interaction; we will use the + // same pointer for all subsequent MotionEvents in this interaction. mDragPointerId = e.getPointerId(0); float x = e.getX(0); float y = e.getY(0); @@ -420,9 +425,16 @@ class DragResizeInputListener implements AutoCloseable { break; } mInputManager.pilferPointers(mInputChannel.getToken()); - int dragPointerIndex = e.findPointerIndex(mDragPointerId); - float rawX = e.getRawX(dragPointerIndex); - float rawY = e.getRawY(dragPointerIndex); + final int dragPointerIndex = e.findPointerIndex(mDragPointerId); + if (dragPointerIndex < 0) { + ProtoLog.d(WM_SHELL_DESKTOP_MODE, + "%s: Handling action move, but ignore event due to invalid " + + "pointer index", + TAG); + break; + } + final float rawX = e.getRawX(dragPointerIndex); + final float rawY = e.getRawY(dragPointerIndex); final Rect taskBounds = mCallback.onDragPositioningMove(rawX, rawY); updateInputSinkRegionForDrag(taskBounds); result = true; @@ -431,7 +443,14 @@ class DragResizeInputListener implements AutoCloseable { case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: { if (mShouldHandleEvents) { - int dragPointerIndex = e.findPointerIndex(mDragPointerId); + final int dragPointerIndex = e.findPointerIndex(mDragPointerId); + if (dragPointerIndex < 0) { + ProtoLog.d(WM_SHELL_DESKTOP_MODE, + "%s: Handling action %d, but ignore event due to invalid " + + "pointer index", + TAG, e.getActionMasked()); + break; + } final Rect taskBounds = mCallback.onDragPositioningEnd( e.getRawX(dragPointerIndex), e.getRawY(dragPointerIndex)); // If taskBounds has changed, setGeometry will be called and update the diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.java index 65adcee1567c..df0836c1121d 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.java @@ -23,6 +23,9 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; import static android.view.MotionEvent.ACTION_DOWN; import static android.view.MotionEvent.ACTION_UP; +import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT; +import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT; + import android.annotation.NonNull; import android.annotation.Nullable; import android.app.ActivityManager.RunningTaskInfo; @@ -33,7 +36,9 @@ import android.content.res.Resources; import android.content.res.TypedArray; import android.graphics.Bitmap; import android.graphics.Color; +import android.graphics.Point; import android.graphics.PointF; +import android.graphics.Rect; import android.view.MotionEvent; import android.view.SurfaceControl; import android.view.View; @@ -42,7 +47,15 @@ import android.widget.ImageView; import android.widget.TextView; import android.window.SurfaceSyncGroup; +import androidx.annotation.VisibleForTesting; + +import com.android.window.flags.Flags; import com.android.wm.shell.R; +import com.android.wm.shell.common.DisplayController; +import com.android.wm.shell.common.DisplayLayout; +import com.android.wm.shell.splitscreen.SplitScreenController; +import com.android.wm.shell.windowdecor.additionalviewcontainer.AdditionalSystemViewContainer; +import com.android.wm.shell.windowdecor.additionalviewcontainer.AdditionalViewContainer; /** * Handle menu opened when the appropriate button is clicked on. @@ -56,15 +69,25 @@ class HandleMenu { private static final String TAG = "HandleMenu"; private static final boolean SHOULD_SHOW_MORE_ACTIONS_PILL = false; private final Context mContext; - private final WindowDecoration mParentDecor; - private WindowDecoration.AdditionalWindow mHandleMenuWindow; - private final PointF mHandleMenuPosition = new PointF(); + private final DesktopModeWindowDecoration mParentDecor; + @VisibleForTesting + AdditionalViewContainer mHandleMenuViewContainer; + // Position of the handle menu used for laying out the handle view. + @VisibleForTesting + final PointF mHandleMenuPosition = new PointF(); + // With the introduction of {@link AdditionalSystemViewContainer}, {@link mHandleMenuPosition} + // may be in a different coordinate space than the input coordinates. Therefore, we still care + // about the menu's coordinates relative to the display as a whole, so we need to maintain + // those as well. + final Point mGlobalMenuPosition = new Point(); private final boolean mShouldShowWindowingPill; private final Bitmap mAppIconBitmap; private final CharSequence mAppName; private final View.OnClickListener mOnClickListener; private final View.OnTouchListener mOnTouchListener; private final RunningTaskInfo mTaskInfo; + private final DisplayController mDisplayController; + private final SplitScreenController mSplitScreenController; private final int mLayoutResId; private int mMarginMenuTop; private int mMarginMenuStart; @@ -74,12 +97,16 @@ class HandleMenu { private HandleMenuAnimator mHandleMenuAnimator; - HandleMenu(WindowDecoration parentDecor, int layoutResId, View.OnClickListener onClickListener, - View.OnTouchListener onTouchListener, Bitmap appIcon, CharSequence appName, - boolean shouldShowWindowingPill, int captionHeight) { + HandleMenu(DesktopModeWindowDecoration parentDecor, int layoutResId, + View.OnClickListener onClickListener, View.OnTouchListener onTouchListener, + Bitmap appIcon, CharSequence appName, DisplayController displayController, + SplitScreenController splitScreenController, boolean shouldShowWindowingPill, + int captionHeight) { mParentDecor = parentDecor; mContext = mParentDecor.mDecorWindowContext; mTaskInfo = mParentDecor.mTaskInfo; + mDisplayController = displayController; + mSplitScreenController = splitScreenController; mLayoutResId = layoutResId; mOnClickListener = onClickListener; mOnTouchListener = onTouchListener; @@ -95,20 +122,27 @@ class HandleMenu { final SurfaceSyncGroup ssg = new SurfaceSyncGroup(TAG); SurfaceControl.Transaction t = new SurfaceControl.Transaction(); - createHandleMenuWindow(t, ssg); + createHandleMenuViewContainer(t, ssg); ssg.addTransaction(t); ssg.markSyncReady(); setupHandleMenu(); animateHandleMenu(); } - private void createHandleMenuWindow(SurfaceControl.Transaction t, SurfaceSyncGroup ssg) { + private void createHandleMenuViewContainer(SurfaceControl.Transaction t, + SurfaceSyncGroup ssg) { final int x = (int) mHandleMenuPosition.x; final int y = (int) mHandleMenuPosition.y; - mHandleMenuWindow = mParentDecor.addWindow( - R.layout.desktop_mode_window_decor_handle_menu, "Handle Menu", - t, ssg, x, y, mMenuWidth, mMenuHeight); - final View handleMenuView = mHandleMenuWindow.mWindowViewHost.getView(); + if (!mTaskInfo.isFreeform() && Flags.enableAdditionalWindowsAboveStatusBar()) { + mHandleMenuViewContainer = new AdditionalSystemViewContainer(mContext, + R.layout.desktop_mode_window_decor_handle_menu, mTaskInfo.taskId, + x, y, mMenuWidth, mMenuHeight); + } else { + mHandleMenuViewContainer = mParentDecor.addWindow( + R.layout.desktop_mode_window_decor_handle_menu, "Handle Menu", + t, ssg, x, y, mMenuWidth, mMenuHeight); + } + final View handleMenuView = mHandleMenuViewContainer.getView(); mHandleMenuAnimator = new HandleMenuAnimator(handleMenuView, mMenuWidth, mCaptionHeight); } @@ -129,7 +163,7 @@ class HandleMenu { * pill. */ private void setupHandleMenu() { - final View handleMenu = mHandleMenuWindow.mWindowViewHost.getView(); + final View handleMenu = mHandleMenuViewContainer.getView(); handleMenu.setOnTouchListener(mOnTouchListener); setupAppInfoPill(handleMenu); if (mShouldShowWindowingPill) { @@ -147,6 +181,7 @@ class HandleMenu { final ImageView appIcon = handleMenu.findViewById(R.id.application_icon); final TextView appName = handleMenu.findViewById(R.id.application_name); collapseBtn.setOnClickListener(mOnClickListener); + collapseBtn.setTaskInfo(mTaskInfo); appIcon.setImageBitmap(mAppIconBitmap); appName.setText(mAppName); } @@ -215,33 +250,69 @@ class HandleMenu { * Updates handle menu's position variables to reflect its next position. */ private void updateHandleMenuPillPositions() { - final int menuX, menuY; - final int captionWidth = mTaskInfo.getConfiguration() - .windowConfiguration.getBounds().width(); - if (mLayoutResId - == R.layout.desktop_mode_app_controls_window_decor) { - // Align the handle menu to the left of the caption. + int menuX; + final int menuY; + final Rect taskBounds = mTaskInfo.getConfiguration().windowConfiguration.getBounds(); + updateGlobalMenuPosition(taskBounds); + if (mLayoutResId == R.layout.desktop_mode_app_header) { + // Align the handle menu to the left side of the caption. menuX = mMarginMenuStart; menuY = mMarginMenuTop; } else { - // Position the handle menu at the center of the caption. - menuX = (captionWidth / 2) - (mMenuWidth / 2); - menuY = mMarginMenuStart; + if (Flags.enableAdditionalWindowsAboveStatusBar()) { + // 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. + final DisplayLayout layout = mDisplayController + .getDisplayLayout(mTaskInfo.displayId); + menuX = mGlobalMenuPosition.x + ((mMenuWidth - layout.width()) / 2); + menuY = mGlobalMenuPosition.y + ((mMenuHeight - layout.height()) / 2); + } else { + menuX = (taskBounds.width() / 2) - (mMenuWidth / 2); + menuY = mMarginMenuTop; + } } - // Handle Menu position setup. mHandleMenuPosition.set(menuX, menuY); + } + private void updateGlobalMenuPosition(Rect taskBounds) { + if (mTaskInfo.isFreeform()) { + mGlobalMenuPosition.set(taskBounds.left + mMarginMenuStart, + taskBounds.top + mMarginMenuTop); + } else if (mTaskInfo.getWindowingMode() == WINDOWING_MODE_FULLSCREEN) { + mGlobalMenuPosition.set( + (taskBounds.width() / 2) - (mMenuWidth / 2) + mMarginMenuStart, + mMarginMenuTop + ); + } else if (mTaskInfo.getWindowingMode() == WINDOWING_MODE_MULTI_WINDOW) { + final int splitPosition = mSplitScreenController.getSplitPosition(mTaskInfo.taskId); + final Rect leftOrTopStageBounds = new Rect(); + final Rect rightOrBottomStageBounds = new Rect(); + mSplitScreenController.getStageBounds(leftOrTopStageBounds, + rightOrBottomStageBounds); + // TODO(b/343561161): This needs to be calculated differently if the task is in + // top/bottom split. + if (splitPosition == SPLIT_POSITION_BOTTOM_OR_RIGHT) { + mGlobalMenuPosition.set(leftOrTopStageBounds.width() + + (rightOrBottomStageBounds.width() / 2) + - (mMenuWidth / 2) + mMarginMenuStart, + mMarginMenuTop); + } else if (splitPosition == SPLIT_POSITION_TOP_OR_LEFT) { + mGlobalMenuPosition.set((leftOrTopStageBounds.width() / 2) + - (mMenuWidth / 2) + mMarginMenuStart, + mMarginMenuTop); + } + } } /** * Update pill layout, in case task changes have caused positioning to change. */ void relayout(SurfaceControl.Transaction t) { - if (mHandleMenuWindow != null) { + if (mHandleMenuViewContainer != null) { updateHandleMenuPillPositions(); - t.setPosition(mHandleMenuWindow.mWindowSurface, - mHandleMenuPosition.x, mHandleMenuPosition.y); + mHandleMenuViewContainer.setPosition(t, mHandleMenuPosition.x, mHandleMenuPosition.y); } } @@ -253,7 +324,9 @@ class HandleMenu { * @param ev the MotionEvent to compare against. */ void checkMotionEvent(MotionEvent ev) { - final View handleMenu = mHandleMenuWindow.mWindowViewHost.getView(); + // If the menu view is above status bar, we can let the views handle input directly. + if (isViewAboveStatusBar()) return; + final View handleMenu = mHandleMenuViewContainer.getView(); final HandleMenuImageButton collapse = handleMenu.findViewById(R.id.collapse_menu_button); final PointF inputPoint = translateInputToLocalSpace(ev); final boolean inputInCollapseButton = pointInView(collapse, inputPoint.x, inputPoint.y); @@ -265,6 +338,11 @@ class HandleMenu { } } + private boolean isViewAboveStatusBar() { + return Flags.enableAdditionalWindowsAboveStatusBar() + && !mTaskInfo.isFreeform(); + } + // Translate the input point from display coordinates to the same space as the handle menu. private PointF translateInputToLocalSpace(MotionEvent ev) { return new PointF(ev.getX() - mHandleMenuPosition.x, @@ -280,10 +358,33 @@ class HandleMenu { */ boolean isValidMenuInput(PointF inputPoint) { if (!viewsLaidOut()) return true; - return pointInView( - mHandleMenuWindow.mWindowViewHost.getView(), - inputPoint.x - mHandleMenuPosition.x, - inputPoint.y - mHandleMenuPosition.y); + if (!isViewAboveStatusBar()) { + return pointInView( + mHandleMenuViewContainer.getView(), + inputPoint.x - mHandleMenuPosition.x, + inputPoint.y - mHandleMenuPosition.y); + } else { + // Handle menu exists in a different coordinate space when added to WindowManager. + // Therefore we must compare the provided input coordinates to global menu coordinates. + // This includes factoring for split stage as input coordinates are relative to split + // stage position, not relative to the display as a whole. + PointF inputRelativeToMenu = new PointF( + inputPoint.x - mGlobalMenuPosition.x, + inputPoint.y - mGlobalMenuPosition.y + ); + if (mSplitScreenController.getSplitPosition(mTaskInfo.taskId) + == SPLIT_POSITION_BOTTOM_OR_RIGHT) { + // TODO(b/343561161): This also needs to be calculated differently if + // the task is in top/bottom split. + Rect leftStageBounds = new Rect(); + mSplitScreenController.getStageBounds(leftStageBounds, new Rect()); + inputRelativeToMenu.x += leftStageBounds.width(); + } + return pointInView( + mHandleMenuViewContainer.getView(), + inputRelativeToMenu.x, + inputRelativeToMenu.y); + } } private boolean pointInView(View v, float x, float y) { @@ -295,7 +396,7 @@ class HandleMenu { * Check if the views for handle menu can be seen. */ private boolean viewsLaidOut() { - return mHandleMenuWindow.mWindowViewHost.getView().isLaidOut(); + return mHandleMenuViewContainer.getView().isLaidOut(); } private void loadHandleMenuDimensions() { @@ -334,8 +435,8 @@ class HandleMenu { void close() { final Runnable after = () -> { - mHandleMenuWindow.releaseView(); - mHandleMenuWindow = null; + mHandleMenuViewContainer.releaseView(); + mHandleMenuViewContainer = null; }; if (mTaskInfo.getWindowingMode() == WINDOWING_MODE_FULLSCREEN || mTaskInfo.getWindowingMode() == WINDOWING_MODE_MULTI_WINDOW) { @@ -346,7 +447,7 @@ class HandleMenu { } static final class Builder { - private final WindowDecoration mParent; + private final DesktopModeWindowDecoration mParent; private CharSequence mName; private Bitmap mAppIcon; private View.OnClickListener mOnClickListener; @@ -354,9 +455,10 @@ class HandleMenu { private int mLayoutId; private boolean mShowWindowingPill; private int mCaptionHeight; + private DisplayController mDisplayController; + private SplitScreenController mSplitScreenController; - - Builder(@NonNull WindowDecoration parent) { + Builder(@NonNull DesktopModeWindowDecoration parent) { mParent = parent; } @@ -395,9 +497,20 @@ class HandleMenu { return this; } + Builder setDisplayController(DisplayController displayController) { + mDisplayController = displayController; + return this; + } + + Builder setSplitScreenController(SplitScreenController splitScreenController) { + mSplitScreenController = splitScreenController; + return this; + } + HandleMenu build() { - return new HandleMenu(mParent, mLayoutId, mOnClickListener, mOnTouchListener, - mAppIcon, mName, mShowWindowingPill, mCaptionHeight); + return new HandleMenu(mParent, mLayoutId, mOnClickListener, + mOnTouchListener, mAppIcon, mName, mDisplayController, mSplitScreenController, + mShowWindowingPill, mCaptionHeight); } } } 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 7898567b70e9..18757ef6ff40 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 @@ -15,6 +15,10 @@ */ 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 @@ -25,10 +29,20 @@ import android.widget.ImageButton * This is due to the hover events being handled by [DesktopModeWindowDecorViewModel] * in order to take the status bar layer into account. Handling it in both classes results in a * flicker when the hover moves from outside to inside status bar layer. + * TODO(b/342229481): Remove this and all uses of it once [AdditionalSystemViewContainer] is no longer + * guarded by a flag. */ -class HandleMenuImageButton(context: Context?, attrs: AttributeSet?) : - ImageButton(context, attrs) { +class HandleMenuImageButton( + context: Context?, + attrs: AttributeSet? +) : ImageButton(context, attrs) { + lateinit var taskInfo: RunningTaskInfo + override fun onHoverEvent(motionEvent: MotionEvent): Boolean { - return false + if (Flags.enableAdditionalWindowsAboveStatusBar() || taskInfo.isFreeform) { + return super.onHoverEvent(motionEvent) + } else { + return false + } } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MaximizeMenu.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MaximizeMenu.kt index 22f0adc42f5d..c903d3b2a858 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MaximizeMenu.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MaximizeMenu.kt @@ -50,7 +50,7 @@ import com.android.wm.shell.RootTaskDisplayAreaOrganizer import com.android.wm.shell.animation.Interpolators.EMPHASIZED_DECELERATE import com.android.wm.shell.common.DisplayController import com.android.wm.shell.common.SyncTransactionQueue -import com.android.wm.shell.windowdecor.WindowDecoration.AdditionalWindow +import com.android.wm.shell.windowdecor.additionalviewcontainer.AdditionalViewHostViewContainer import java.util.function.Supplier @@ -70,7 +70,7 @@ class MaximizeMenu( private val menuPosition: PointF, private val transactionSupplier: Supplier<Transaction> = Supplier { Transaction() } ) { - private var maximizeMenu: AdditionalWindow? = null + private var maximizeMenu: AdditionalViewHostViewContainer? = null private lateinit var viewHost: SurfaceControlViewHost private lateinit var leash: SurfaceControl private val openMenuAnimatorSet = AnimatorSet() @@ -145,7 +145,8 @@ class MaximizeMenu( .setPosition(leash, menuPosition.x, menuPosition.y) .setCornerRadius(leash, cornerRadius) .show(leash) - maximizeMenu = AdditionalWindow(leash, viewHost, transactionSupplier) + maximizeMenu = + AdditionalViewHostViewContainer(leash, viewHost, transactionSupplier) syncQueue.runInSync { transaction -> transaction.merge(t) @@ -154,8 +155,7 @@ class MaximizeMenu( } private fun animateOpenMenu() { - val viewHost = maximizeMenu?.mWindowViewHost - val maximizeMenuView = viewHost?.view ?: return + val maximizeMenuView = maximizeMenu?.view ?: return val maximizeWindowText = maximizeMenuView.requireViewById<TextView>( R.id.maximize_menu_maximize_window_text) val snapWindowText = maximizeMenuView.requireViewById<TextView>( @@ -233,7 +233,7 @@ class MaximizeMenu( } private fun setupMaximizeMenu() { - val maximizeMenuView = maximizeMenu?.mWindowViewHost?.view ?: return + val maximizeMenuView = maximizeMenu?.view ?: return maximizeMenuView.setOnGenericMotionListener(onGenericMotionListener) maximizeMenuView.setOnTouchListener(onTouchListener) @@ -275,7 +275,7 @@ class MaximizeMenu( * Check if the views for maximize menu can be seen. */ private fun viewsLaidOut(): Boolean { - return maximizeMenu?.mWindowViewHost?.view?.isLaidOut ?: false + return maximizeMenu?.view?.isLaidOut ?: false } fun onMaximizeMenuHoverEnter(viewId: Int, ev: MotionEvent) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/ResizeVeil.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/ResizeVeil.kt index 4f2d945e49f9..cd2dac806a7f 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/ResizeVeil.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/ResizeVeil.kt @@ -20,7 +20,6 @@ import android.animation.AnimatorListenerAdapter import android.animation.ValueAnimator import android.app.ActivityManager.RunningTaskInfo import android.content.Context -import android.content.res.Configuration import android.graphics.Bitmap import android.graphics.Color import android.graphics.PixelFormat @@ -36,10 +35,15 @@ import android.view.WindowManager import android.view.WindowlessWindowManager import android.widget.ImageView import android.window.TaskConstants +import androidx.compose.material3.dynamicDarkColorScheme +import androidx.compose.material3.dynamicLightColorScheme +import androidx.compose.ui.graphics.toArgb import com.android.wm.shell.R import com.android.wm.shell.common.DisplayController import com.android.wm.shell.common.DisplayController.OnDisplaysChangedListener import com.android.wm.shell.windowdecor.WindowDecoration.SurfaceControlViewHostFactory +import com.android.wm.shell.windowdecor.common.DecorThemeUtil +import com.android.wm.shell.windowdecor.common.Theme import java.util.function.Supplier /** @@ -49,14 +53,18 @@ class ResizeVeil @JvmOverloads constructor( private val context: Context, private val displayController: DisplayController, private val appIcon: Bitmap, - private val taskInfo: RunningTaskInfo, private var parentSurface: SurfaceControl, private val surfaceControlTransactionSupplier: Supplier<SurfaceControl.Transaction>, private val surfaceControlBuilderFactory: SurfaceControlBuilderFactory = object : SurfaceControlBuilderFactory {}, private val surfaceControlViewHostFactory: SurfaceControlViewHostFactory = - object : SurfaceControlViewHostFactory {} + object : SurfaceControlViewHostFactory {}, + taskInfo: RunningTaskInfo, ) { + private val decorThemeUtil = DecorThemeUtil(context) + private val lightColors = dynamicLightColorScheme(context) + private val darkColors = dynamicDarkColorScheme(context) + private val surfaceSession = SurfaceSession() private lateinit var iconView: ImageView private var iconSize = 0 @@ -86,21 +94,10 @@ class ResizeVeil @JvmOverloads constructor( return } displayController.removeDisplayWindowListener(this) - setupResizeVeil() + setupResizeVeil(taskInfo) } } - private val backgroundColorId: Int - get() { - val configuration = context.resources.configuration - return if (configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK - == Configuration.UI_MODE_NIGHT_YES) { - R.color.desktop_mode_resize_veil_dark - } else { - R.color.desktop_mode_resize_veil_light - } - } - /** * Whether the resize veil is ready to be shown. */ @@ -108,14 +105,14 @@ class ResizeVeil @JvmOverloads constructor( get() = viewHost != null init { - setupResizeVeil() + setupResizeVeil(taskInfo) } /** * Create the veil in its default invisible state. */ - private fun setupResizeVeil() { - if (!obtainDisplayOrRegisterListener()) { + private fun setupResizeVeil(taskInfo: RunningTaskInfo) { + if (!obtainDisplayOrRegisterListener(taskInfo.displayId)) { // Display may not be available yet, skip this until then. return } @@ -162,8 +159,8 @@ class ResizeVeil @JvmOverloads constructor( Trace.endSection() } - private fun obtainDisplayOrRegisterListener(): Boolean { - display = displayController.getDisplay(taskInfo.displayId) + private fun obtainDisplayOrRegisterListener(displayId: Int): Boolean { + display = displayController.getDisplay(displayId) if (display == null) { displayController.addDisplayWindowListener(onDisplaysChangedListener) return false @@ -184,7 +181,8 @@ class ResizeVeil @JvmOverloads constructor( t: SurfaceControl.Transaction, parent: SurfaceControl, taskBounds: Rect, - fadeIn: Boolean + taskInfo: RunningTaskInfo, + fadeIn: Boolean, ) { if (!isReady || isVisible) { t.apply() @@ -202,13 +200,15 @@ class ResizeVeil @JvmOverloads constructor( parentSurface = parent } - + val backgroundColor = when (decorThemeUtil.getAppTheme(taskInfo)) { + Theme.LIGHT -> lightColors.surfaceContainer + Theme.DARK -> darkColors.surfaceContainer + } t.show(veil) .setLayer(veil, VEIL_CONTAINER_LAYER) .setLayer(icon, VEIL_ICON_LAYER) .setLayer(background, VEIL_BACKGROUND_LAYER) - .setColor(background, - Color.valueOf(context.getColor(backgroundColorId)).components) + .setColor(background, Color.valueOf(backgroundColor.toArgb()).components) relayout(taskBounds, t) if (fadeIn) { cancelAnimation() @@ -270,12 +270,12 @@ class ResizeVeil @JvmOverloads constructor( /** * Animate veil's alpha to 1, fading it in. */ - fun showVeil(parentSurface: SurfaceControl, taskBounds: Rect) { + fun showVeil(parentSurface: SurfaceControl, taskBounds: Rect, taskInfo: RunningTaskInfo) { if (!isReady || isVisible) { return } val t = surfaceControlTransactionSupplier.get() - showVeil(t, parentSurface, taskBounds, true /* fadeIn */) + showVeil(t, parentSurface, taskBounds, taskInfo, true /* fadeIn */) } /** 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 2ae3cb9ef3c0..216990c35247 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 @@ -22,6 +22,7 @@ import static android.content.res.Configuration.DENSITY_DPI_UNDEFINED; import static android.view.WindowInsets.Type.captionBar; import static android.view.WindowInsets.Type.mandatorySystemGestures; import static android.view.WindowInsets.Type.statusBars; +import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION; import android.annotation.NonNull; import android.annotation.Nullable; @@ -56,6 +57,7 @@ import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.common.DisplayController; import com.android.wm.shell.shared.DesktopModeStatus; import com.android.wm.shell.windowdecor.WindowDecoration.RelayoutParams.OccludingCaptionElement; +import com.android.wm.shell.windowdecor.additionalviewcontainer.AdditionalViewHostViewContainer; import java.util.ArrayList; import java.util.Arrays; @@ -197,8 +199,16 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer> void relayout(RelayoutParams params, SurfaceControl.Transaction startT, SurfaceControl.Transaction finishT, WindowContainerTransaction wct, T rootView, RelayoutResult<T> outResult) { - outResult.reset(); + updateViewsAndSurfaces(params, startT, finishT, wct, rootView, outResult); + if (outResult.mRootView != null) { + updateViewHost(params, startT, outResult); + } + } + protected void updateViewsAndSurfaces(RelayoutParams params, + SurfaceControl.Transaction startT, SurfaceControl.Transaction finishT, + WindowContainerTransaction wct, T rootView, RelayoutResult<T> outResult) { + outResult.reset(); if (params.mRunningTaskInfo != null) { mTaskInfo = params.mRunningTaskInfo; } @@ -211,13 +221,38 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer> return; } + inflateIfNeeded(params, wct, rootView, oldLayoutResId, outResult); + if (outResult.mRootView == null) { + // Didn't manage to create a root view, early out. + return; + } + rootView = null; // Clear it just in case we use it accidentally + + updateCaptionVisibility(outResult.mRootView, mTaskInfo.displayId); + + final Rect taskBounds = mTaskInfo.getConfiguration().windowConfiguration.getBounds(); + outResult.mWidth = taskBounds.width(); + outResult.mHeight = taskBounds.height(); + outResult.mRootView.setTaskFocusState(mTaskInfo.isFocused); + final Resources resources = mDecorWindowContext.getResources(); + outResult.mCaptionHeight = loadDimensionPixelSize(resources, params.mCaptionHeightId); + outResult.mCaptionWidth = params.mCaptionWidthId != Resources.ID_NULL + ? loadDimensionPixelSize(resources, params.mCaptionWidthId) : taskBounds.width(); + outResult.mCaptionX = (outResult.mWidth - outResult.mCaptionWidth) / 2; + + updateDecorationContainerSurface(startT, outResult); + updateCaptionContainerSurface(startT, outResult); + updateCaptionInsets(params, wct, outResult, taskBounds); + updateTaskSurface(params, startT, finishT, outResult); + } + + private void inflateIfNeeded(RelayoutParams params, WindowContainerTransaction wct, + T rootView, int oldLayoutResId, RelayoutResult<T> outResult) { if (rootView == null && params.mLayoutResId == 0) { throw new IllegalArgumentException("layoutResId and rootView can't both be invalid."); } outResult.mRootView = rootView; - rootView = null; // Clear it just in case we use it accidentally - final int oldDensityDpi = mWindowDecorConfig != null ? mWindowDecorConfig.densityDpi : DENSITY_DPI_UNDEFINED; final int oldNightMode = mWindowDecorConfig != null @@ -251,25 +286,17 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer> outResult.mRootView = (T) LayoutInflater.from(mDecorWindowContext) .inflate(params.mLayoutResId, null); } + } - updateCaptionVisibility(outResult.mRootView, mTaskInfo.displayId); - - final Resources resources = mDecorWindowContext.getResources(); - final Configuration taskConfig = mTaskInfo.getConfiguration(); - final Rect taskBounds = taskConfig.windowConfiguration.getBounds(); - final boolean isFullscreen = taskConfig.windowConfiguration.getWindowingMode() - == WINDOWING_MODE_FULLSCREEN; - outResult.mWidth = taskBounds.width(); - outResult.mHeight = taskBounds.height(); - - // DecorationContainerSurface + private void updateDecorationContainerSurface( + SurfaceControl.Transaction startT, RelayoutResult<T> outResult) { if (mDecorationContainerSurface == null) { final SurfaceControl.Builder builder = mSurfaceControlBuilderSupplier.get(); mDecorationContainerSurface = builder .setName("Decor container of Task=" + mTaskInfo.taskId) .setContainerLayer() .setParent(mTaskSurface) - .setCallsite("WindowDecoration.relayout_1") + .setCallsite("WindowDecoration.updateDecorationContainerSurface") .build(); startT.setTrustedOverlay(mDecorationContainerSurface, true) @@ -279,101 +306,101 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer> startT.setWindowCrop(mDecorationContainerSurface, outResult.mWidth, outResult.mHeight) .show(mDecorationContainerSurface); + } - // CaptionContainerSurface, CaptionWindowManager + private void updateCaptionContainerSurface( + SurfaceControl.Transaction startT, RelayoutResult<T> outResult) { if (mCaptionContainerSurface == null) { final SurfaceControl.Builder builder = mSurfaceControlBuilderSupplier.get(); mCaptionContainerSurface = builder .setName("Caption container of Task=" + mTaskInfo.taskId) .setContainerLayer() .setParent(mDecorationContainerSurface) - .setCallsite("WindowDecoration.relayout_2") + .setCallsite("WindowDecoration.updateCaptionContainerSurface") .build(); } - outResult.mCaptionHeight = loadDimensionPixelSize(resources, params.mCaptionHeightId); - outResult.mCaptionWidth = params.mCaptionWidthId != Resources.ID_NULL - ? loadDimensionPixelSize(resources, params.mCaptionWidthId) : taskBounds.width(); - outResult.mCaptionX = (outResult.mWidth - outResult.mCaptionWidth) / 2; - startT.setWindowCrop(mCaptionContainerSurface, outResult.mCaptionWidth, outResult.mCaptionHeight) .setPosition(mCaptionContainerSurface, outResult.mCaptionX, 0 /* y */) .setLayer(mCaptionContainerSurface, CAPTION_LAYER_Z_ORDER) .show(mCaptionContainerSurface); + } - outResult.mRootView.setTaskFocusState(mTaskInfo.isFocused); - - // Caption insets - if (mIsCaptionVisible) { - // Caption inset is the full width of the task with the |captionHeight| and - // positioned at the top of the task bounds, also in absolute coordinates. - // So just reuse the task bounds and adjust the bottom coordinate. - final Rect captionInsetsRect = new Rect(taskBounds); - captionInsetsRect.bottom = captionInsetsRect.top + outResult.mCaptionHeight; - - // Caption bounding rectangles: these are optional, and are used to present finer - // insets than traditional |Insets| to apps about where their content is occluded. - // These are also in absolute coordinates. - final Rect[] boundingRects; - final int numOfElements = params.mOccludingCaptionElements.size(); - if (numOfElements == 0) { - boundingRects = null; - } else { - // The customizable region can at most be equal to the caption bar. - if (params.hasInputFeatureSpy()) { - outResult.mCustomizableCaptionRegion.set(captionInsetsRect); - } - boundingRects = new Rect[numOfElements]; - for (int i = 0; i < numOfElements; i++) { - final OccludingCaptionElement element = - params.mOccludingCaptionElements.get(i); - final int elementWidthPx = - resources.getDimensionPixelSize(element.mWidthResId); - boundingRects[i] = - calculateBoundingRect(element, elementWidthPx, captionInsetsRect); - // Subtract the regions used by the caption elements, the rest is - // customizable. - if (params.hasInputFeatureSpy()) { - outResult.mCustomizableCaptionRegion.op(boundingRects[i], - Region.Op.DIFFERENCE); - } - } - } - - final WindowDecorationInsets newInsets = new WindowDecorationInsets( - mTaskInfo.token, mOwner, captionInsetsRect, boundingRects); - if (!newInsets.equals(mWindowDecorationInsets)) { - // Add or update this caption as an insets source. - mWindowDecorationInsets = newInsets; - mWindowDecorationInsets.addOrUpdate(wct); - } - } else { + private void updateCaptionInsets(RelayoutParams params, WindowContainerTransaction wct, + RelayoutResult<T> outResult, Rect taskBounds) { + if (!mIsCaptionVisible) { if (mWindowDecorationInsets != null) { mWindowDecorationInsets.remove(wct); mWindowDecorationInsets = null; } + return; } - - // Task surface itself - float shadowRadius; - final Point taskPosition = mTaskInfo.positionInParent; - if (isFullscreen) { - // Shadow is not needed for fullscreen tasks - shadowRadius = 0; + // Caption inset is the full width of the task with the |captionHeight| and + // positioned at the top of the task bounds, also in absolute coordinates. + // So just reuse the task bounds and adjust the bottom coordinate. + final Rect captionInsetsRect = new Rect(taskBounds); + captionInsetsRect.bottom = captionInsetsRect.top + outResult.mCaptionHeight; + + // Caption bounding rectangles: these are optional, and are used to present finer + // insets than traditional |Insets| to apps about where their content is occluded. + // These are also in absolute coordinates. + final Rect[] boundingRects; + final int numOfElements = params.mOccludingCaptionElements.size(); + if (numOfElements == 0) { + boundingRects = null; } else { - shadowRadius = loadDimension(resources, params.mShadowRadiusId); + // The customizable region can at most be equal to the caption bar. + if (params.hasInputFeatureSpy()) { + outResult.mCustomizableCaptionRegion.set(captionInsetsRect); + } + final Resources resources = mDecorWindowContext.getResources(); + boundingRects = new Rect[numOfElements]; + for (int i = 0; i < numOfElements; i++) { + final OccludingCaptionElement element = + params.mOccludingCaptionElements.get(i); + final int elementWidthPx = + resources.getDimensionPixelSize(element.mWidthResId); + boundingRects[i] = + calculateBoundingRect(element, elementWidthPx, captionInsetsRect); + // Subtract the regions used by the caption elements, the rest is + // customizable. + if (params.hasInputFeatureSpy()) { + outResult.mCustomizableCaptionRegion.op(boundingRects[i], + Region.Op.DIFFERENCE); + } + } } + final WindowDecorationInsets newInsets = new WindowDecorationInsets( + mTaskInfo.token, mOwner, captionInsetsRect, boundingRects); + if (!newInsets.equals(mWindowDecorationInsets)) { + // Add or update this caption as an insets source. + mWindowDecorationInsets = newInsets; + mWindowDecorationInsets.addOrUpdate(wct); + } + } + + private void updateTaskSurface(RelayoutParams params, SurfaceControl.Transaction startT, + SurfaceControl.Transaction finishT, RelayoutResult<T> outResult) { if (params.mSetTaskPositionAndCrop) { + final Point taskPosition = mTaskInfo.positionInParent; startT.setWindowCrop(mTaskSurface, outResult.mWidth, outResult.mHeight); finishT.setWindowCrop(mTaskSurface, outResult.mWidth, outResult.mHeight) .setPosition(mTaskSurface, taskPosition.x, taskPosition.y); } - startT.setShadowRadius(mTaskSurface, shadowRadius) - .show(mTaskSurface); + float shadowRadius; + if (mTaskInfo.getWindowingMode() == WINDOWING_MODE_FULLSCREEN) { + // Shadow is not needed for fullscreen tasks + shadowRadius = 0; + } else { + shadowRadius = + loadDimension(mDecorWindowContext.getResources(), params.mShadowRadiusId); + } + startT.setShadowRadius(mTaskSurface, shadowRadius).show(mTaskSurface); finishT.setShadowRadius(mTaskSurface, shadowRadius); + if (mTaskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM) { if (!DesktopModeStatus.isVeiledResizeEnabled()) { // When fluid resize is enabled, add a background to freeform tasks @@ -388,7 +415,19 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer> } else if (!DesktopModeStatus.isVeiledResizeEnabled()) { startT.unsetColor(mTaskSurface); } + } + /** + * Updates a {@link SurfaceControlViewHost} to connect the window decoration surfaces with our + * View hierarchy. + * + * @param params parameters to use from the last relayout + * @param onDrawTransaction a transaction to apply in sync with #onDraw + * @param outResult results to use from the last relayout + * + */ + protected void updateViewHost(RelayoutParams params, + SurfaceControl.Transaction onDrawTransaction, RelayoutResult<T> outResult) { Trace.beginSection("CaptionViewHostLayout"); if (mCaptionWindowManager == null) { // Put caption under a container surface because ViewRootImpl sets the destination frame @@ -397,12 +436,10 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer> mTaskInfo.getConfiguration(), mCaptionContainerSurface, null /* hostInputToken */); } - - // Caption view - mCaptionWindowManager.setConfiguration(taskConfig); + mCaptionWindowManager.setConfiguration(mTaskInfo.getConfiguration()); final WindowManager.LayoutParams lp = new WindowManager.LayoutParams(outResult.mCaptionWidth, outResult.mCaptionHeight, - WindowManager.LayoutParams.TYPE_APPLICATION, + TYPE_APPLICATION, WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, PixelFormat.TRANSPARENT); lp.setTitle("Caption of Task=" + mTaskInfo.taskId); lp.setTrustedOverlay(); @@ -412,14 +449,20 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer> mViewHost = mSurfaceControlViewHostFactory.create(mDecorWindowContext, mDisplay, mCaptionWindowManager); if (params.mApplyStartTransactionOnDraw) { - mViewHost.getRootSurfaceControl().applyTransactionOnDraw(startT); + if (onDrawTransaction == null) { + throw new IllegalArgumentException("Trying to sync a null Transaction"); + } + mViewHost.getRootSurfaceControl().applyTransactionOnDraw(onDrawTransaction); } mViewHost.setView(outResult.mRootView, lp); Trace.endSection(); } else { Trace.beginSection("CaptionViewHostLayout-relayout"); if (params.mApplyStartTransactionOnDraw) { - mViewHost.getRootSurfaceControl().applyTransactionOnDraw(startT); + if (onDrawTransaction == null) { + throw new IllegalArgumentException("Trying to sync a null Transaction"); + } + mViewHost.getRootSurfaceControl().applyTransactionOnDraw(onDrawTransaction); } mViewHost.relayout(lp); Trace.endSection(); @@ -569,10 +612,11 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer> * @param yPos y position of new window * @param width width of new window * @param height height of new window - * @return the {@link AdditionalWindow} that was added. + * @return the {@link AdditionalViewHostViewContainer} that was added. */ - AdditionalWindow addWindow(int layoutId, String namePrefix, SurfaceControl.Transaction t, - SurfaceSyncGroup ssg, int xPos, int yPos, int width, int height) { + AdditionalViewHostViewContainer addWindow(int layoutId, String namePrefix, + SurfaceControl.Transaction t, SurfaceSyncGroup ssg, int xPos, int yPos, + int width, int height) { final SurfaceControl.Builder builder = mSurfaceControlBuilderSupplier.get(); SurfaceControl windowSurfaceControl = builder .setName(namePrefix + " of Task=" + mTaskInfo.taskId) @@ -586,9 +630,9 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer> .setWindowCrop(windowSurfaceControl, width, height) .show(windowSurfaceControl); final WindowManager.LayoutParams lp = - new WindowManager.LayoutParams(width, height, - WindowManager.LayoutParams.TYPE_APPLICATION, - WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, PixelFormat.TRANSPARENT); + new WindowManager.LayoutParams(width, height, TYPE_APPLICATION, + WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, + PixelFormat.TRANSPARENT); lp.setTitle("Additional window of Task=" + mTaskInfo.taskId); lp.setTrustedOverlay(); WindowlessWindowManager windowManager = new WindowlessWindowManager(mTaskInfo.configuration, @@ -596,7 +640,7 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer> SurfaceControlViewHost viewHost = mSurfaceControlViewHostFactory .create(mDecorWindowContext, mDisplay, windowManager); ssg.add(viewHost.getSurfacePackage(), () -> viewHost.setView(v, lp)); - return new AdditionalWindow(windowSurfaceControl, viewHost, + return new AdditionalViewHostViewContainer(windowSurfaceControl, viewHost, mSurfaceControlTransactionSupplier); } @@ -739,41 +783,4 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer> return Objects.hash(mToken, mOwner, mFrame, Arrays.hashCode(mBoundingRects)); } } - - /** - * Subclass for additional windows associated with this WindowDecoration - */ - static class AdditionalWindow { - SurfaceControl mWindowSurface; - SurfaceControlViewHost mWindowViewHost; - Supplier<SurfaceControl.Transaction> mTransactionSupplier; - - AdditionalWindow(SurfaceControl surfaceControl, - SurfaceControlViewHost surfaceControlViewHost, - Supplier<SurfaceControl.Transaction> transactionSupplier) { - mWindowSurface = surfaceControl; - mWindowViewHost = surfaceControlViewHost; - mTransactionSupplier = transactionSupplier; - } - - void releaseView() { - WindowlessWindowManager windowManager = mWindowViewHost.getWindowlessWM(); - - if (mWindowViewHost != null) { - mWindowViewHost.release(); - mWindowViewHost = null; - } - windowManager = null; - final SurfaceControl.Transaction t = mTransactionSupplier.get(); - boolean released = false; - if (mWindowSurface != null) { - t.remove(mWindowSurface); - mWindowSurface = null; - released = true; - } - if (released) { - t.apply(); - } - } - } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/additionalviewcontainer/AdditionalSystemViewContainer.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/additionalviewcontainer/AdditionalSystemViewContainer.kt new file mode 100644 index 000000000000..6c2c8fd46bc9 --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/additionalviewcontainer/AdditionalSystemViewContainer.kt @@ -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.wm.shell.windowdecor.additionalviewcontainer + +import android.content.Context +import android.graphics.PixelFormat +import android.view.LayoutInflater +import android.view.SurfaceControl +import android.view.View +import android.view.WindowManager + +/** + * An [AdditionalViewContainer] that uses the system [WindowManager] instance. Intended + * for view containers that should be above the status bar layer. + */ +class AdditionalSystemViewContainer( + private val context: Context, + layoutId: Int, + taskId: Int, + x: Int, + y: Int, + width: Int, + height: Int +) : AdditionalViewContainer() { + override val view: View + + init { + view = LayoutInflater.from(context).inflate(layoutId, null) + val lp = WindowManager.LayoutParams( + width, height, x, y, + WindowManager.LayoutParams.TYPE_STATUS_BAR_ADDITIONAL, + WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, + PixelFormat.TRANSPARENT + ) + lp.title = "Additional view container of Task=$taskId" + lp.setTrustedOverlay() + val wm: WindowManager? = context.getSystemService(WindowManager::class.java) + wm?.addView(view, lp) + } + + override fun releaseView() { + context.getSystemService(WindowManager::class.java)?.removeViewImmediate(view) + } + + override fun setPosition(t: SurfaceControl.Transaction, x: Float, y: Float) { + val lp = (view.layoutParams as WindowManager.LayoutParams).apply { + this.x = x.toInt() + this.y = y.toInt() + } + view.layoutParams = lp + } +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/additionalviewcontainer/AdditionalViewContainer.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/additionalviewcontainer/AdditionalViewContainer.kt new file mode 100644 index 000000000000..2650648a2cde --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/additionalviewcontainer/AdditionalViewContainer.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.wm.shell.windowdecor.additionalviewcontainer + +import android.view.SurfaceControl +import android.view.View +import com.android.wm.shell.windowdecor.WindowDecoration + +/** + * Class for additional view containers associated with a [WindowDecoration]. + */ +abstract class AdditionalViewContainer internal constructor( +) { + abstract val view: View? + + /** Release the view associated with this container and perform needed cleanup. */ + abstract fun releaseView() + + /** Reposition the view container using provided coordinates. */ + abstract fun setPosition(t: SurfaceControl.Transaction, x: Float, y: Float) +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/additionalviewcontainer/AdditionalViewHostViewContainer.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/additionalviewcontainer/AdditionalViewHostViewContainer.kt new file mode 100644 index 000000000000..222761260289 --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/additionalviewcontainer/AdditionalViewHostViewContainer.kt @@ -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 com.android.wm.shell.windowdecor.additionalviewcontainer + +import android.view.SurfaceControl +import android.view.SurfaceControlViewHost +import java.util.function.Supplier + +/** + * An [AdditionalViewContainer] that uses a [SurfaceControlViewHost] to show the window. + * Intended for view containers in freeform tasks that do not extend beyond task bounds. + */ +class AdditionalViewHostViewContainer( + private val windowSurface: SurfaceControl, + private val windowViewHost: SurfaceControlViewHost, + private val transactionSupplier: Supplier<SurfaceControl.Transaction>, +) : AdditionalViewContainer() { + + override val view + get() = windowViewHost.view + + override fun releaseView() { + windowViewHost.release() + val t = transactionSupplier.get() + t.remove(windowSurface) + t.apply() + } + + override fun setPosition(t: SurfaceControl.Transaction, x: Float, y: Float) { + t.setPosition(windowSurface, x, y) + } +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/ThemeUtils.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/ThemeUtils.kt new file mode 100644 index 000000000000..8e6d1ee9d45e --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/ThemeUtils.kt @@ -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.wm.shell.windowdecor.common + +import android.app.ActivityManager.RunningTaskInfo +import android.content.Context +import android.content.res.Configuration +import android.content.res.Configuration.UI_MODE_NIGHT_MASK +import android.graphics.Color + +/** The theme of a window decoration. */ +internal enum class Theme { LIGHT, DARK } + +/** Whether a [Theme] is light. */ +internal fun Theme.isLight(): Boolean = this == Theme.LIGHT + +/** Whether a [Theme] is dark. */ +internal fun Theme.isDark(): Boolean = this == Theme.DARK + +/** + * Utility class for determining themes based on system settings and app's [RunningTaskInfo]. + */ +internal class DecorThemeUtil(private val context: Context) { + + private val systemTheme: Theme + get() = if ((context.resources.configuration.uiMode and UI_MODE_NIGHT_MASK) == + Configuration.UI_MODE_NIGHT_YES) { + Theme.DARK + } else { + Theme.LIGHT + } + + /** + * Returns the [Theme] used by the app with the given [RunningTaskInfo]. + */ + fun getAppTheme(task: RunningTaskInfo): Theme { + // TODO: use app's uiMode to find its actual light/dark value. It needs to be added to the + // TaskInfo/TaskDescription. + val backgroundColor = task.taskDescription?.backgroundColor ?: return systemTheme + return if (Color.valueOf(backgroundColor).luminance() < 0.5) { + Theme.DARK + } else { + Theme.LIGHT + } + } +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/DesktopModeFocusedWindowDecorationViewHolder.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHandleViewHolder.kt index 96bc4a146ebd..8d822c252288 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/DesktopModeFocusedWindowDecorationViewHolder.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHandleViewHolder.kt @@ -1,3 +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 com.android.wm.shell.windowdecor.viewholder import android.animation.ObjectAnimator @@ -12,14 +27,14 @@ import com.android.wm.shell.R import com.android.wm.shell.animation.Interpolators /** - * A desktop mode window decoration used when the window is in full "focus" (i.e. fullscreen). It - * hosts a simple handle bar from which to initiate a drag motion to enter desktop mode. + * A desktop mode window decoration used when the window is in full "focus" (i.e. fullscreen/split). + * It hosts a simple handle bar from which to initiate a drag motion to enter desktop mode. */ -internal class DesktopModeFocusedWindowDecorationViewHolder( +internal class AppHandleViewHolder( rootView: View, onCaptionTouchListener: View.OnTouchListener, onCaptionButtonClickListener: View.OnClickListener -) : DesktopModeWindowDecorationViewHolder(rootView) { +) : WindowDecorationViewHolder(rootView) { companion object { private const val CAPTION_HANDLE_ANIMATION_DURATION: Long = 100 diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/DesktopModeAppControlsWindowDecorationViewHolder.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHeaderViewHolder.kt index 3c12da2d6620..0650154ee6b6 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/DesktopModeAppControlsWindowDecorationViewHolder.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHeaderViewHolder.kt @@ -1,13 +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.wm.shell.windowdecor.viewholder import android.annotation.ColorInt import android.app.ActivityManager.RunningTaskInfo import android.content.res.ColorStateList import android.content.res.Configuration -import android.content.res.Configuration.UI_MODE_NIGHT_MASK import android.graphics.Bitmap import android.graphics.Color -import android.graphics.drawable.GradientDrawable import android.graphics.drawable.LayerDrawable import android.graphics.drawable.RippleDrawable import android.graphics.drawable.ShapeDrawable @@ -17,19 +30,22 @@ import android.view.View.OnLongClickListener import android.widget.ImageButton import android.widget.ImageView import android.widget.TextView +import androidx.compose.material3.dynamicDarkColorScheme +import androidx.compose.material3.dynamicLightColorScheme +import androidx.compose.ui.graphics.toArgb import androidx.core.content.withStyledAttributes import androidx.core.view.isVisible import com.android.internal.R.attr.materialColorOnSecondaryContainer import com.android.internal.R.attr.materialColorOnSurface -import com.android.internal.R.attr.materialColorOnSurfaceInverse import com.android.internal.R.attr.materialColorSecondaryContainer import com.android.internal.R.attr.materialColorSurfaceContainerHigh import com.android.internal.R.attr.materialColorSurfaceContainerLow import com.android.internal.R.attr.materialColorSurfaceDim -import com.android.internal.R.attr.materialColorSurfaceInverse import com.android.window.flags.Flags import com.android.wm.shell.R import com.android.wm.shell.windowdecor.MaximizeButtonView +import com.android.wm.shell.windowdecor.common.DecorThemeUtil +import com.android.wm.shell.windowdecor.common.Theme import com.android.wm.shell.windowdecor.extension.isLightCaptionBarAppearance import com.android.wm.shell.windowdecor.extension.isTransparentCaptionBarAppearance @@ -38,7 +54,7 @@ import com.android.wm.shell.windowdecor.extension.isTransparentCaptionBarAppeara * finer controls such as a close window button and an "app info" section to pull up additional * controls. */ -internal class DesktopModeAppControlsWindowDecorationViewHolder( +internal class AppHeaderViewHolder( rootView: View, onCaptionTouchListener: View.OnTouchListener, onCaptionButtonClickListener: View.OnClickListener, @@ -47,7 +63,11 @@ internal class DesktopModeAppControlsWindowDecorationViewHolder( appName: CharSequence, appIconBitmap: Bitmap, onMaximizeHoverAnimationFinishedListener: () -> Unit -) : DesktopModeWindowDecorationViewHolder(rootView) { +) : WindowDecorationViewHolder(rootView) { + + private val decorThemeUtil = DecorThemeUtil(context) + private val lightColors = dynamicLightColorScheme(context) + private val darkColors = dynamicDarkColorScheme(context) /** * The corner radius to apply to the app chip, maximize and close button's background drawable. @@ -153,19 +173,12 @@ internal class DesktopModeAppControlsWindowDecorationViewHolder( val headerStyle = getHeaderStyle(header) // Caption Background - val headerBackground = captionView.background as LayerDrawable - val backLayer = headerBackground.findDrawableByLayerId(R.id.backLayer) as GradientDrawable - val frontLayer = headerBackground.findDrawableByLayerId(R.id.frontLayer) as GradientDrawable when (headerStyle.background) { is HeaderStyle.Background.Opaque -> { - backLayer.setColor(headerStyle.background.backLayerColor ?: Color.BLACK) - frontLayer.setColor(headerStyle.background.frontLayerColor) - frontLayer.alpha = headerStyle.background.frontLayerOpacity + captionView.setBackgroundColor(headerStyle.background.color) } HeaderStyle.Background.Transparent -> { - backLayer.setColor(Color.TRANSPARENT) - frontLayer.setColor(Color.TRANSPARENT) - frontLayer.alpha = OPACITY_100 + captionView.setBackgroundColor(Color.TRANSPARENT) } } @@ -189,7 +202,7 @@ internal class DesktopModeAppControlsWindowDecorationViewHolder( } // Maximize button. maximizeButtonView.setAnimationTints( - darkMode = header.appTheme == Header.Theme.DARK, + darkMode = header.appTheme == Theme.DARK, iconForegroundColor = colorStateList, baseForegroundColor = foregroundColor, rippleDrawable = createRippleDrawable( @@ -236,186 +249,88 @@ internal class DesktopModeAppControlsWindowDecorationViewHolder( ) } - private fun getHeaderBackground( - header: Header - ): HeaderStyle.Background { - when (header.type) { + private fun getHeaderBackground(header: Header): HeaderStyle.Background { + return when (header.type) { Header.Type.DEFAULT -> { - if (header.systemTheme.isLight() && header.appTheme.isLight() && header.isFocused) { - return HeaderStyle.Background.Opaque( - frontLayerColor = attrToColor(materialColorSecondaryContainer), - frontLayerOpacity = OPACITY_100, - backLayerColor = null - ) - } - if (header.systemTheme.isLight() && header.appTheme.isLight() && - !header.isFocused) { - return HeaderStyle.Background.Opaque( - frontLayerColor = attrToColor(materialColorSurfaceContainerLow), - frontLayerOpacity = OPACITY_100, - backLayerColor = null - ) - } - if (header.systemTheme.isDark() && header.appTheme.isDark() && header.isFocused) { - return HeaderStyle.Background.Opaque( - frontLayerColor = attrToColor(materialColorSurfaceContainerHigh), - frontLayerOpacity = OPACITY_100, - backLayerColor = null - ) - } - if (header.systemTheme.isDark() && header.appTheme.isDark() && !header.isFocused) { - return HeaderStyle.Background.Opaque( - frontLayerColor = attrToColor(materialColorSurfaceDim), - frontLayerOpacity = OPACITY_100, - backLayerColor = null - ) - } - if (header.systemTheme.isLight() && header.appTheme.isDark() && header.isFocused) { - return HeaderStyle.Background.Opaque( - frontLayerColor = attrToColor(materialColorSurfaceInverse), - frontLayerOpacity = OPACITY_100, - backLayerColor = null - ) - } - if (header.systemTheme.isLight() && header.appTheme.isDark() && !header.isFocused) { - return HeaderStyle.Background.Opaque( - frontLayerColor = attrToColor(materialColorSurfaceInverse), - frontLayerOpacity = OPACITY_30, - backLayerColor = Color.BLACK - ) - } - if (header.systemTheme.isDark() && header.appTheme.isLight() && header.isFocused) { - return HeaderStyle.Background.Opaque( - frontLayerColor = attrToColor(materialColorSurfaceInverse), - frontLayerOpacity = OPACITY_100, - backLayerColor = null - ) - } - if (header.systemTheme.isDark() && header.appTheme.isLight() && !header.isFocused) { - return HeaderStyle.Background.Opaque( - frontLayerColor = attrToColor(materialColorSurfaceInverse), - frontLayerOpacity = OPACITY_55, - backLayerColor = Color.WHITE - ) + when (header.appTheme) { + Theme.LIGHT -> { + if (header.isFocused) { + HeaderStyle.Background.Opaque(lightColors.secondaryContainer.toArgb()) + } else { + HeaderStyle.Background.Opaque(lightColors.surfaceContainerLow.toArgb()) + } + } + Theme.DARK -> { + if (header.isFocused) { + HeaderStyle.Background.Opaque(darkColors.surfaceContainerHigh.toArgb()) + } else { + HeaderStyle.Background.Opaque(darkColors.surfaceDim.toArgb()) + } + } } - error("No other combination expected header=$header") } - Header.Type.CUSTOM -> return HeaderStyle.Background.Transparent + Header.Type.CUSTOM -> HeaderStyle.Background.Transparent } } private fun getHeaderForeground(header: Header): HeaderStyle.Foreground { - when (header.type) { + return when (header.type) { Header.Type.DEFAULT -> { - if (header.systemTheme.isLight() && header.appTheme.isLight() && header.isFocused) { - return HeaderStyle.Foreground( - color = attrToColor(materialColorOnSecondaryContainer), - opacity = OPACITY_100 - ) - } - if (header.systemTheme.isLight() && header.appTheme.isLight() && - !header.isFocused) { - return HeaderStyle.Foreground( - color = attrToColor(materialColorOnSecondaryContainer), - opacity = OPACITY_65 - ) - } - if (header.systemTheme.isDark() && header.appTheme.isDark() && header.isFocused) { - return HeaderStyle.Foreground( - color = attrToColor(materialColorOnSurface), - opacity = OPACITY_100 - ) + when (header.appTheme) { + Theme.LIGHT -> { + if (header.isFocused) { + HeaderStyle.Foreground( + color = lightColors.onSecondaryContainer.toArgb(), + opacity = OPACITY_100 + ) + } else { + HeaderStyle.Foreground( + color = lightColors.onSecondaryContainer.toArgb(), + opacity = OPACITY_65 + ) + } + } + Theme.DARK -> { + if (header.isFocused) { + HeaderStyle.Foreground( + color = darkColors.onSurface.toArgb(), + opacity = OPACITY_100 + ) + } else { + HeaderStyle.Foreground( + color = darkColors.onSurface.toArgb(), + opacity = OPACITY_55 + ) + } + } } - if (header.systemTheme.isDark() && header.appTheme.isDark() && !header.isFocused) { - return HeaderStyle.Foreground( - color = attrToColor(materialColorOnSurface), - opacity = OPACITY_55 - ) - } - if (header.systemTheme.isLight() && header.appTheme.isDark() && header.isFocused) { - return HeaderStyle.Foreground( - color = attrToColor(materialColorOnSurfaceInverse), - opacity = OPACITY_100 - ) - } - if (header.systemTheme.isLight() && header.appTheme.isDark() && !header.isFocused) { - return HeaderStyle.Foreground( - color = attrToColor(materialColorOnSurfaceInverse), - opacity = OPACITY_65 - ) - } - if (header.systemTheme.isDark() && header.appTheme.isLight() && header.isFocused) { - return HeaderStyle.Foreground( - color = attrToColor(materialColorOnSurfaceInverse), - opacity = OPACITY_100 - ) - } - if (header.systemTheme.isDark() && header.appTheme.isLight() && !header.isFocused) { - return HeaderStyle.Foreground( - color = attrToColor(materialColorOnSurfaceInverse), - opacity = OPACITY_70 - ) - } - error("No other combination expected header=$header") } - Header.Type.CUSTOM -> { - if (header.systemTheme.isLight() && header.isAppearanceCaptionLight && - header.isFocused) { - return HeaderStyle.Foreground( - color = attrToColor(materialColorOnSecondaryContainer), + Header.Type.CUSTOM -> when { + header.isAppearanceCaptionLight && header.isFocused -> { + HeaderStyle.Foreground( + color = lightColors.onSecondaryContainer.toArgb(), opacity = OPACITY_100 ) } - if (header.systemTheme.isLight() && header.isAppearanceCaptionLight && - !header.isFocused) { - return HeaderStyle.Foreground( - color = attrToColor(materialColorOnSecondaryContainer), + header.isAppearanceCaptionLight && !header.isFocused -> { + HeaderStyle.Foreground( + color = lightColors.onSecondaryContainer.toArgb(), opacity = OPACITY_65 ) } - if (header.systemTheme.isDark() && !header.isAppearanceCaptionLight && - header.isFocused) { - return HeaderStyle.Foreground( - color = attrToColor(materialColorOnSurface), + !header.isAppearanceCaptionLight && header.isFocused -> { + HeaderStyle.Foreground( + color = darkColors.onSurface.toArgb(), opacity = OPACITY_100 ) } - if (header.systemTheme.isDark() && !header.isAppearanceCaptionLight && - !header.isFocused) { - return HeaderStyle.Foreground( - color = attrToColor(materialColorOnSurface), + !header.isAppearanceCaptionLight && !header.isFocused -> { + HeaderStyle.Foreground( + color = darkColors.onSurface.toArgb(), opacity = OPACITY_55 ) } - if (header.systemTheme.isLight() && !header.isAppearanceCaptionLight && - header.isFocused) { - return HeaderStyle.Foreground( - color = attrToColor(materialColorOnSurfaceInverse), - opacity = OPACITY_100 - ) - } - if (header.systemTheme.isLight() && !header.isAppearanceCaptionLight && - !header.isFocused) { - return HeaderStyle.Foreground( - color = attrToColor(materialColorOnSurfaceInverse), - opacity = OPACITY_65 - ) - } - if (header.systemTheme.isDark() && header.isAppearanceCaptionLight && - header.isFocused) { - return HeaderStyle.Foreground( - color = attrToColor(materialColorOnSurfaceInverse), - opacity = OPACITY_100 - ) - } - if (header.systemTheme.isDark() && header.isAppearanceCaptionLight && - !header.isFocused) { - return HeaderStyle.Foreground( - color = attrToColor(materialColorOnSurfaceInverse), - opacity = OPACITY_70 - ) - } - error("No other combination expected header=$header") + else -> error("No other combination expected header=$header") } } } @@ -427,41 +342,12 @@ internal class DesktopModeAppControlsWindowDecorationViewHolder( } else { Header.Type.DEFAULT }, - systemTheme = getSystemTheme(), - appTheme = getAppTheme(taskInfo), + appTheme = decorThemeUtil.getAppTheme(taskInfo), isFocused = taskInfo.isFocused, isAppearanceCaptionLight = taskInfo.isLightCaptionBarAppearance ) } - private fun getSystemTheme(): Header.Theme { - return if ((context.resources.configuration.uiMode and UI_MODE_NIGHT_MASK) == - Configuration.UI_MODE_NIGHT_YES) { - Header.Theme.DARK - } else { - Header.Theme.LIGHT - } - } - - private fun getAppTheme(taskInfo: RunningTaskInfo): Header.Theme { - // TODO: use app's uiMode to find its actual light/dark value. It needs to be added to the - // TaskInfo/TaskDescription. - val backgroundColor = taskInfo.taskDescription?.backgroundColor ?: return getSystemTheme() - return if (Color.valueOf(backgroundColor).luminance() < 0.5) { - Header.Theme.DARK - } else { - Header.Theme.LIGHT - } - } - - @ColorInt - private fun attrToColor(attr: Int): Int { - context.withStyledAttributes(null, intArrayOf(attr), 0, 0) { - return getColor(0, 0) - } - return Color.WHITE - } - @ColorInt private fun replaceColorAlpha(@ColorInt color: Int, alpha: Int): Int { return Color.argb( @@ -515,19 +401,13 @@ internal class DesktopModeAppControlsWindowDecorationViewHolder( private data class Header( val type: Type, - val systemTheme: Theme, val appTheme: Theme, val isFocused: Boolean, val isAppearanceCaptionLight: Boolean, ) { enum class Type { DEFAULT, CUSTOM } - enum class Theme { LIGHT, DARK } } - private fun Header.Theme.isLight(): Boolean = this == Header.Theme.LIGHT - - private fun Header.Theme.isDark(): Boolean = this == Header.Theme.DARK - private data class HeaderStyle( val background: Background, val foreground: Foreground @@ -539,11 +419,7 @@ internal class DesktopModeAppControlsWindowDecorationViewHolder( sealed class Background { data object Transparent : Background() - data class Opaque( - @ColorInt val frontLayerColor: Int, - val frontLayerOpacity: Int, - @ColorInt val backLayerColor: Int? - ) : Background() + data class Opaque(@ColorInt val color: Int) : Background() } } @@ -619,9 +495,7 @@ internal class DesktopModeAppControlsWindowDecorationViewHolder( private const val OPACITY_100 = 255 private const val OPACITY_11 = 28 private const val OPACITY_15 = 38 - private const val OPACITY_30 = 77 private const val OPACITY_55 = 140 private const val OPACITY_65 = 166 - private const val OPACITY_70 = 179 } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/DesktopModeWindowDecorationViewHolder.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/WindowDecorationViewHolder.kt index 81bc34c876b6..5ae8d252a908 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/DesktopModeWindowDecorationViewHolder.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/WindowDecorationViewHolder.kt @@ -1,3 +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 com.android.wm.shell.windowdecor.viewholder import android.app.ActivityManager.RunningTaskInfo @@ -8,7 +23,7 @@ import android.view.View * Encapsulates the root [View] of a window decoration and its children to facilitate looking up * children (via findViewById) and updating to the latest data from [RunningTaskInfo]. */ -internal abstract class DesktopModeWindowDecorationViewHolder(rootView: View) { +internal abstract class WindowDecorationViewHolder(rootView: View) { val context: Context = rootView.context /** diff --git a/libs/WindowManager/Shell/tests/flicker/splitscreen/Android.bp b/libs/WindowManager/Shell/tests/flicker/splitscreen/Android.bp index f813b0d3b0b7..0fe7a16be851 100644 --- a/libs/WindowManager/Shell/tests/flicker/splitscreen/Android.bp +++ b/libs/WindowManager/Shell/tests/flicker/splitscreen/Android.bp @@ -65,9 +65,11 @@ android_test { android_test { name: "WMShellFlickerTestsSplitScreenGroup2", + defaults: ["WMShellFlickerTestsDefault"], manifest: "AndroidManifest.xml", package_name: "com.android.wm.shell.flicker.splitscreen", instrumentation_target_package: "com.android.wm.shell.flicker.splitscreen", + test_config_template: "AndroidTestTemplate.xml", srcs: [ ":WMShellFlickerTestsSplitScreenBase-src", ":WMShellFlickerTestsSplitScreenGroup2-src", diff --git a/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/MultipleShowImeRequestsInSplitScreen.kt b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/MultipleShowImeRequestsInSplitScreen.kt new file mode 100644 index 000000000000..dad5db94d062 --- /dev/null +++ b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/MultipleShowImeRequestsInSplitScreen.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.wm.shell.flicker.splitscreen + +import android.platform.test.annotations.Presubmit +import android.tools.Rotation +import android.tools.flicker.junit.FlickerParametersRunnerFactory +import android.tools.flicker.legacy.FlickerBuilder +import android.tools.flicker.legacy.LegacyFlickerTest +import android.tools.flicker.legacy.LegacyFlickerTestFactory +import android.tools.traces.component.ComponentNameMatcher +import androidx.test.filters.RequiresDevice +import com.android.wm.shell.flicker.splitscreen.benchmark.MultipleShowImeRequestsInSplitScreenBenchmark +import com.android.wm.shell.flicker.utils.ICommonAssertions +import org.junit.FixMethodOrder +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.MethodSorters +import org.junit.runners.Parameterized + +/** + * Test quick switch between two split pairs. + * + * To run this test: `atest WMShellFlickerTestsSplitScreenGroup2:MultipleShowImeRequestsInSplitScreen` + */ +@RequiresDevice +@RunWith(Parameterized::class) +@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +class MultipleShowImeRequestsInSplitScreen(override val flicker: LegacyFlickerTest) : + MultipleShowImeRequestsInSplitScreenBenchmark(flicker), ICommonAssertions { + override val transition: FlickerBuilder.() -> Unit + get() = { + defaultSetup(this) + defaultTeardown(this) + thisTransition(this) + } + + @Presubmit + @Test + fun imeLayerAlwaysVisible() = + flicker.assertLayers { + this.isVisible(ComponentNameMatcher.IME) + } + + companion object { + @Parameterized.Parameters(name = "{0}") + @JvmStatic + fun getParams() = LegacyFlickerTestFactory.nonRotationTests( + supportedRotations = listOf(Rotation.ROTATION_0) + ) + } +} diff --git a/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/UnlockKeyguardToSplitScreen.kt b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/UnlockKeyguardToSplitScreen.kt index 90453640c91a..d34998815fca 100644 --- a/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/UnlockKeyguardToSplitScreen.kt +++ b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/UnlockKeyguardToSplitScreen.kt @@ -47,7 +47,7 @@ import org.junit.runners.Parameterized * To run this test: `atest WMShellFlickerTestsSplitScreen:UnlockKeyguardToSplitScreen` */ @RequiresDevice -@Postsubmit +@FlakyTest(bugId = 293578017) @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) @@ -61,7 +61,6 @@ class UnlockKeyguardToSplitScreen(override val flicker: LegacyFlickerTest) : } @Test - @FlakyTest(bugId = 293578017) override fun visibleLayersShownMoreThanOneConsecutiveEntry() = super.visibleLayersShownMoreThanOneConsecutiveEntry() diff --git a/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/MultipleShowImeRequestsInSplitScreenBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/MultipleShowImeRequestsInSplitScreenBenchmark.kt new file mode 100644 index 000000000000..249253185607 --- /dev/null +++ b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/MultipleShowImeRequestsInSplitScreenBenchmark.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.wm.shell.flicker.splitscreen.benchmark + +import android.tools.flicker.junit.FlickerParametersRunnerFactory +import android.tools.flicker.legacy.FlickerBuilder +import android.tools.flicker.legacy.LegacyFlickerTest +import android.tools.flicker.legacy.LegacyFlickerTestFactory +import androidx.test.filters.RequiresDevice +import com.android.server.wm.flicker.helpers.ImeAppHelper +import com.android.wm.shell.flicker.utils.SplitScreenUtils +import org.junit.FixMethodOrder +import org.junit.runner.RunWith +import org.junit.runners.MethodSorters +import org.junit.runners.Parameterized + +@RequiresDevice +@RunWith(Parameterized::class) +@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +abstract class MultipleShowImeRequestsInSplitScreenBenchmark( + override val flicker: LegacyFlickerTest +) : SplitScreenBase(flicker) { + override val primaryApp = ImeAppHelper(instrumentation) + override val defaultTeardown: FlickerBuilder.() -> Unit + get() = { + teardown { + primaryApp.closeIME(wmHelper) + super.defaultTeardown + } + } + + protected val thisTransition: FlickerBuilder.() -> Unit + get() = { + setup { + SplitScreenUtils.enterSplit( + wmHelper, + tapl, + device, + primaryApp, + secondaryApp, + flicker.scenario.startRotation + ) + // initially open the IME + primaryApp.openIME(wmHelper) + } + transitions { + for (i in 1..OPEN_IME_COUNT) { + primaryApp.openIME(wmHelper) + } + } + } + + companion object { + const val OPEN_IME_COUNT = 30 + + @Parameterized.Parameters(name = "{0}") + @JvmStatic + fun getParams() = LegacyFlickerTestFactory.nonRotationTests() + } +} diff --git a/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/SplitScreenBase.kt b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/SplitScreenBase.kt index 4b106034b2b5..51074f634e30 100644 --- a/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/SplitScreenBase.kt +++ b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/SplitScreenBase.kt @@ -25,7 +25,7 @@ import com.android.wm.shell.flicker.utils.SplitScreenUtils abstract class SplitScreenBase(flicker: LegacyFlickerTest) : BaseBenchmarkTest(flicker) { protected val context: Context = instrumentation.context - protected val primaryApp = SplitScreenUtils.getPrimary(instrumentation) + protected open val primaryApp = SplitScreenUtils.getPrimary(instrumentation) protected val secondaryApp = SplitScreenUtils.getSecondary(instrumentation) protected open val defaultSetup: FlickerBuilder.() -> Unit = { diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java index f6f3aa49bc6e..1903586fc317 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java @@ -123,6 +123,7 @@ public class BackAnimationControllerTest extends ShellTestCase { private DefaultCrossActivityBackAnimation mDefaultCrossActivityBackAnimation; private CrossTaskBackAnimation mCrossTaskBackAnimation; private ShellBackAnimationRegistry mShellBackAnimationRegistry; + private Rect mTouchableRegion; @Before public void setUp() throws Exception { @@ -158,6 +159,8 @@ public class BackAnimationControllerTest extends ShellTestCase { mShellCommandHandler); mShellInit.init(); mShellExecutor.flushAll(); + mTouchableRegion = new Rect(0, 0, 100, 100); + mController.mTouchableArea.set(mTouchableRegion); } private void createNavigationInfo(int backType, @@ -169,7 +172,8 @@ public class BackAnimationControllerTest extends ShellTestCase { .setOnBackNavigationDone(new RemoteCallback((bundle) -> {})) .setOnBackInvokedCallback(mAppCallback) .setPrepareRemoteAnimation(enableAnimation) - .setAnimationCallback(isAnimationCallback); + .setAnimationCallback(isAnimationCallback) + .setTouchableRegion(mTouchableRegion); createNavigationInfo(builder); } @@ -234,7 +238,8 @@ public class BackAnimationControllerTest extends ShellTestCase { .setType(type) .setOnBackInvokedCallback(mAppCallback) .setPrepareRemoteAnimation(true) - .setOnBackNavigationDone(new RemoteCallback(result))); + .setOnBackNavigationDone(new RemoteCallback(result)) + .setTouchableRegion(mTouchableRegion)); triggerBackGesture(); simulateRemoteAnimationStart(); mShellExecutor.flushAll(); @@ -512,7 +517,8 @@ public class BackAnimationControllerTest extends ShellTestCase { .setType(type) .setOnBackInvokedCallback(mAppCallback) .setPrepareRemoteAnimation(true) - .setOnBackNavigationDone(new RemoteCallback(result))); + .setOnBackNavigationDone(new RemoteCallback(result)) + .setTouchableRegion(mTouchableRegion)); triggerBackGesture(); simulateRemoteAnimationStart(); mShellExecutor.flushAll(); @@ -543,7 +549,8 @@ public class BackAnimationControllerTest extends ShellTestCase { createNavigationInfo(new BackNavigationInfo.Builder() .setType(type) .setOnBackInvokedCallback(mAppCallback) - .setOnBackNavigationDone(new RemoteCallback(result))); + .setOnBackNavigationDone(new RemoteCallback(result)) + .setTouchableRegion(mTouchableRegion)); triggerBackGesture(); mShellExecutor.flushAll(); releaseBackGesture(); @@ -570,7 +577,8 @@ public class BackAnimationControllerTest extends ShellTestCase { createNavigationInfo(new BackNavigationInfo.Builder() .setType(type) .setOnBackInvokedCallback(mAppCallback) - .setOnBackNavigationDone(new RemoteCallback(result))); + .setOnBackNavigationDone(new RemoteCallback(result)) + .setTouchableRegion(mTouchableRegion)); doMotionEvent(MotionEvent.ACTION_CANCEL, 0); mShellExecutor.flushAll(); 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 8932e60048e6..4d0348b4f470 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 @@ -19,6 +19,7 @@ package com.android.wm.shell.back; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; import android.os.Handler; import android.os.Looper; @@ -95,16 +96,33 @@ public class BackProgressAnimatorTest { // Trigger animation cancel, the target progress should be 0. mTargetProgress = 0; mTargetProgressCalled = new CountDownLatch(1); - CountDownLatch cancelCallbackCalled = new CountDownLatch(1); + CountDownLatch finishCallbackCalled = new CountDownLatch(1); mMainThreadHandler.post( - () -> mProgressAnimator.onBackCancelled(() -> cancelCallbackCalled.countDown())); - cancelCallbackCalled.await(1, TimeUnit.SECONDS); + () -> mProgressAnimator.onBackCancelled(finishCallbackCalled::countDown)); + finishCallbackCalled.await(1, TimeUnit.SECONDS); mTargetProgressCalled.await(1, TimeUnit.SECONDS); assertNotNull(mReceivedBackEvent); assertEquals(mReceivedBackEvent.getProgress(), mTargetProgress, 0 /* delta */); } @Test + public void testBackInvoked() throws InterruptedException { + // Give the animator some progress. + final BackMotionEvent backEvent = backMotionEventFrom(100, mTargetProgress); + mMainThreadHandler.post( + () -> mProgressAnimator.onBackProgressed(backEvent)); + mTargetProgressCalled.await(1, TimeUnit.SECONDS); + assertNotNull(mReceivedBackEvent); + + // Trigger back invoked animation + CountDownLatch finishCallbackCalled = new CountDownLatch(1); + mMainThreadHandler.post( + () -> mProgressAnimator.onBackInvoked(finishCallbackCalled::countDown)); + assertTrue("onBackInvoked finishCallback never called", + finishCallbackCalled.await(1, TimeUnit.SECONDS)); + } + + @Test public void testResetCallsCancelCallbackImmediately() throws InterruptedException { // Give the animator some progress. final BackMotionEvent backEvent = backMotionEventFrom(100, mTargetProgress); diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java index 0f433770777e..93e405131a58 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java @@ -1170,9 +1170,9 @@ public class BubbleDataTest extends ShellTestCase { // Verify the update has the removals. BubbleData.Update update = mUpdateCaptor.getValue(); assertThat(update.removedBubbles.get(0)).isEqualTo( - Pair.create(mBubbleA2, Bubbles.DISMISS_USER_REMOVED)); + Pair.create(mBubbleA2, Bubbles.DISMISS_USER_ACCOUNT_REMOVED)); assertThat(update.removedBubbles.get(1)).isEqualTo( - Pair.create(mBubbleA1, Bubbles.DISMISS_USER_REMOVED)); + Pair.create(mBubbleA1, Bubbles.DISMISS_USER_ACCOUNT_REMOVED)); // Verify no A bubbles in active or overflow. assertBubbleListContains(mBubbleC1, mBubbleB3); @@ -1203,6 +1203,25 @@ public class BubbleDataTest extends ShellTestCase { assertThat(update.currentBubbleList.get(0).getKey()).isEqualTo(mEntryA2.getKey()); assertThat(update.currentBubbleList.get(1).getKey()).isEqualTo(mEntryA1.getKey()); assertThat(update.bubbleBarLocation).isEqualTo(BubbleBarLocation.LEFT); + assertThat(update.expandedChanged).isFalse(); + assertThat(update.selectedBubbleKey).isEqualTo(mEntryA2.getKey()); + } + + @Test + public void test_getInitialStateForBubbleBar_includesExpandedState() { + sendUpdatedEntryAtTime(mEntryA1, 1000); + sendUpdatedEntryAtTime(mEntryA2, 2000); + mPositioner.setBubbleBarLocation(BubbleBarLocation.LEFT); + mBubbleData.setExpanded(true); + + BubbleBarUpdate update = mBubbleData.getInitialStateForBubbleBar(); + assertThat(update.currentBubbleList).hasSize(2); + assertThat(update.currentBubbleList.get(0).getKey()).isEqualTo(mEntryA2.getKey()); + assertThat(update.currentBubbleList.get(1).getKey()).isEqualTo(mEntryA1.getKey()); + assertThat(update.bubbleBarLocation).isEqualTo(BubbleBarLocation.LEFT); + assertThat(update.expandedChanged).isTrue(); + assertThat(update.expanded).isTrue(); + assertThat(update.selectedBubbleKey).isEqualTo(mEntryA2.getKey()); } @Test diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/bubbles/BubbleInfoTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/bubbles/BubbleInfoTest.kt index 432909f18813..5b22eddcb6ee 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/bubbles/BubbleInfoTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/bubbles/BubbleInfoTest.kt @@ -32,7 +32,17 @@ class BubbleInfoTest : ShellTestCase() { @Test fun bubbleInfo() { val bubbleInfo = - BubbleInfo("key", 0, "shortcut id", null, 6, "com.some.package", "title", true) + BubbleInfo( + "key", + 0, + "shortcut id", + null, + 6, + "com.some.package", + "title", + "Some app", + true + ) val parcel = Parcel.obtain() bubbleInfo.writeToParcel(parcel, PARCELABLE_WRITE_RETURN_VALUE) parcel.setDataPosition(0) @@ -46,6 +56,7 @@ class BubbleInfoTest : ShellTestCase() { assertThat(bubbleInfo.userId).isEqualTo(bubbleInfoFromParcel.userId) assertThat(bubbleInfo.packageName).isEqualTo(bubbleInfoFromParcel.packageName) assertThat(bubbleInfo.title).isEqualTo(bubbleInfoFromParcel.title) + assertThat(bubbleInfo.appName).isEqualTo(bubbleInfoFromParcel.appName) assertThat(bubbleInfo.isImportantConversation) .isEqualTo(bubbleInfoFromParcel.isImportantConversation) } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserverTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserverTest.kt index 2a2483df0792..0d3cd10e90df 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserverTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserverTest.kt @@ -22,7 +22,6 @@ import android.content.Context import android.os.IBinder import android.testing.AndroidTestingRunner import android.view.SurfaceControl -import android.view.WindowManager.TRANSIT_CHANGE import android.view.WindowManager.TRANSIT_CLOSE import android.view.WindowManager.TRANSIT_FLAG_IS_RECENTS import android.view.WindowManager.TRANSIT_NONE @@ -41,6 +40,14 @@ import com.android.modules.utils.testing.ExtendedMockitoRule import com.android.wm.shell.common.ShellExecutor import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.EnterReason import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.ExitReason +import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_ENTER_DESKTOP_FROM_APP_FROM_OVERVIEW +import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_ENTER_DESKTOP_FROM_APP_HANDLE_MENU_BUTTON +import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_ENTER_DESKTOP_FROM_KEYBOARD_SHORTCUT +import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_ENTER_DESKTOP_FROM_UNKNOWN +import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_EXIT_DESKTOP_MODE_HANDLE_MENU_BUTTON +import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_EXIT_DESKTOP_MODE_KEYBOARD_SHORTCUT +import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_EXIT_DESKTOP_MODE_TASK_DRAG +import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_EXIT_DESKTOP_MODE_UNKNOWN import com.android.wm.shell.shared.DesktopModeStatus import com.android.wm.shell.sysui.ShellInit import com.android.wm.shell.transition.TransitionInfoBuilder @@ -60,6 +67,7 @@ import org.mockito.kotlin.eq import org.mockito.kotlin.never import org.mockito.kotlin.same import org.mockito.kotlin.times +import org.mockito.kotlin.verifyZeroInteractions /** * Test class for {@link DesktopModeLoggerTransitionObserver} @@ -70,299 +78,397 @@ import org.mockito.kotlin.times @RunWith(AndroidTestingRunner::class) class DesktopModeLoggerTransitionObserverTest { - @JvmField - @Rule - val extendedMockitoRule = ExtendedMockitoRule.Builder(this) - .mockStatic(DesktopModeEventLogger::class.java) - .mockStatic(DesktopModeStatus::class.java).build()!! - - @Mock - lateinit var testExecutor: ShellExecutor - @Mock - private lateinit var mockShellInit: ShellInit - @Mock - private lateinit var transitions: Transitions - @Mock - private lateinit var context: Context - - private lateinit var transitionObserver: DesktopModeLoggerTransitionObserver - private lateinit var shellInit: ShellInit - private lateinit var desktopModeEventLogger: DesktopModeEventLogger - - @Before - fun setup() { - doReturn(true).`when`{ DesktopModeStatus.canEnterDesktopMode(any()) } - shellInit = Mockito.spy(ShellInit(testExecutor)) - desktopModeEventLogger = mock(DesktopModeEventLogger::class.java) - - transitionObserver = DesktopModeLoggerTransitionObserver( + @JvmField + @Rule + val extendedMockitoRule = + ExtendedMockitoRule.Builder(this) + .mockStatic(DesktopModeEventLogger::class.java) + .mockStatic(DesktopModeStatus::class.java) + .build()!! + + @Mock lateinit var testExecutor: ShellExecutor + @Mock private lateinit var mockShellInit: ShellInit + @Mock private lateinit var transitions: Transitions + @Mock private lateinit var context: Context + + private lateinit var transitionObserver: DesktopModeLoggerTransitionObserver + private lateinit var shellInit: ShellInit + private lateinit var desktopModeEventLogger: DesktopModeEventLogger + + @Before + fun setup() { + doReturn(true).`when` { DesktopModeStatus.canEnterDesktopMode(any()) } + shellInit = Mockito.spy(ShellInit(testExecutor)) + desktopModeEventLogger = mock(DesktopModeEventLogger::class.java) + + transitionObserver = + DesktopModeLoggerTransitionObserver( context, mockShellInit, transitions, desktopModeEventLogger) - if (Transitions.ENABLE_SHELL_TRANSITIONS) { - val initRunnableCaptor = ArgumentCaptor.forClass( - Runnable::class.java) - verify(mockShellInit).addInitCallback(initRunnableCaptor.capture(), - same(transitionObserver)) - initRunnableCaptor.value.run() - } else { - transitionObserver.onInit() - } + if (Transitions.ENABLE_SHELL_TRANSITIONS) { + val initRunnableCaptor = ArgumentCaptor.forClass(Runnable::class.java) + verify(mockShellInit).addInitCallback(initRunnableCaptor.capture(), same(transitionObserver)) + initRunnableCaptor.value.run() + } else { + transitionObserver.onInit() } - - @Test - fun testRegistersObserverAtInit() { - verify(transitions) - .registerObserver(same( - transitionObserver)) - } - - @Test - fun taskCreated_notFreeformWindow_doesNotLogSessionEnterOrTaskAdded() { - val change = createChange(TRANSIT_OPEN, createTaskInfo(1, WINDOWING_MODE_FULLSCREEN)) - val transitionInfo = TransitionInfoBuilder(TRANSIT_OPEN, 0).addChange(change).build() - - callOnTransitionReady(transitionInfo) - - verify(desktopModeEventLogger, never()).logSessionEnter(any(), any()) - verify(desktopModeEventLogger, never()).logTaskAdded(any(), any()) - } - - @Test - fun taskCreated_FreeformWindowOpen_logSessionEnterAndTaskAdded() { - val change = createChange(TRANSIT_OPEN, createTaskInfo(1, WINDOWING_MODE_FREEFORM)) - val transitionInfo = TransitionInfoBuilder(TRANSIT_OPEN, 0).addChange(change).build() - - callOnTransitionReady(transitionInfo) - val sessionId = transitionObserver.getLoggerSessionId() - - assertThat(sessionId).isNotNull() - verify(desktopModeEventLogger, times(1)).logSessionEnter(eq(sessionId!!), - eq(EnterReason.APP_FREEFORM_INTENT)) - verify(desktopModeEventLogger, times(1)).logTaskAdded(eq(sessionId), any()) - } - - @Test - fun taskChanged_taskMovedToDesktopByDrag_logSessionEnterAndTaskAdded() { - val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(1, WINDOWING_MODE_FREEFORM)) - // task change is finalised when drag ends - val transitionInfo = TransitionInfoBuilder( - Transitions.TRANSIT_DESKTOP_MODE_END_DRAG_TO_DESKTOP, 0).addChange(change).build() - - callOnTransitionReady(transitionInfo) - val sessionId = transitionObserver.getLoggerSessionId() - - assertThat(sessionId).isNotNull() - verify(desktopModeEventLogger, times(1)).logSessionEnter(eq(sessionId!!), - eq(EnterReason.APP_HANDLE_DRAG)) - verify(desktopModeEventLogger, times(1)).logTaskAdded(eq(sessionId), any()) - } - - @Test - fun taskChanged_taskMovedToDesktopByButtonTap_logSessionEnterAndTaskAdded() { - val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(1, WINDOWING_MODE_FREEFORM)) - val transitionInfo = TransitionInfoBuilder(Transitions.TRANSIT_MOVE_TO_DESKTOP, 0) - .addChange(change).build() - - callOnTransitionReady(transitionInfo) - val sessionId = transitionObserver.getLoggerSessionId() - - assertThat(sessionId).isNotNull() - verify(desktopModeEventLogger, times(1)).logSessionEnter(eq(sessionId!!), - eq(EnterReason.APP_HANDLE_MENU_BUTTON)) - verify(desktopModeEventLogger, times(1)).logTaskAdded(eq(sessionId), any()) - } - - @Test - fun taskChanged_existingFreeformTaskMadeVisible_logSessionEnterAndTaskAdded() { - val taskInfo = createTaskInfo(1, WINDOWING_MODE_FREEFORM) - taskInfo.isVisibleRequested = true - val change = createChange(TRANSIT_CHANGE, taskInfo) - val transitionInfo = TransitionInfoBuilder(Transitions.TRANSIT_MOVE_TO_DESKTOP, 0) - .addChange(change).build() - - callOnTransitionReady(transitionInfo) - val sessionId = transitionObserver.getLoggerSessionId() - - assertThat(sessionId).isNotNull() - verify(desktopModeEventLogger, times(1)).logSessionEnter(eq(sessionId!!), - eq(EnterReason.APP_HANDLE_MENU_BUTTON)) - verify(desktopModeEventLogger, times(1)).logTaskAdded(eq(sessionId), any()) - } - - @Test - fun taskToFront_screenWake_logSessionStartedAndTaskAdded() { - val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(1, WINDOWING_MODE_FREEFORM)) - val transitionInfo = TransitionInfoBuilder(TRANSIT_WAKE, 0) - .addChange(change).build() - - callOnTransitionReady(transitionInfo) - val sessionId = transitionObserver.getLoggerSessionId() - - assertThat(sessionId).isNotNull() - verify(desktopModeEventLogger, times(1)).logSessionEnter(eq(sessionId!!), - eq(EnterReason.SCREEN_ON)) - verify(desktopModeEventLogger, times(1)).logTaskAdded(eq(sessionId), any()) - } - - @Test - fun freeformTaskVisible_screenTurnOff_logSessionExitAndTaskRemoved_sessionIdNull() { - val sessionId = 1 - // add a freeform task - transitionObserver.addTaskInfosToCachedMap(createTaskInfo(1, WINDOWING_MODE_FREEFORM)) - transitionObserver.setLoggerSessionId(sessionId) - - val transitionInfo = TransitionInfoBuilder(TRANSIT_SLEEP).build() - callOnTransitionReady(transitionInfo) - - verify(desktopModeEventLogger, times(1)).logTaskRemoved(eq(sessionId), any()) - verify(desktopModeEventLogger, times(1)).logSessionExit(eq(sessionId), - eq(ExitReason.SCREEN_OFF)) - assertThat(transitionObserver.getLoggerSessionId()).isNull() - } - - @Test - fun freeformTaskVisible_exitDesktopUsingDrag_logSessionExitAndTaskRemoved_sessionIdNull() { - val sessionId = 1 - // add a freeform task - transitionObserver.addTaskInfosToCachedMap(createTaskInfo(1, WINDOWING_MODE_FREEFORM)) - transitionObserver.setLoggerSessionId(sessionId) - - // window mode changing from FREEFORM to FULLSCREEN - val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(1, WINDOWING_MODE_FULLSCREEN)) - val transitionInfo = TransitionInfoBuilder(Transitions.TRANSIT_EXIT_DESKTOP_MODE) - .addChange(change).build() - callOnTransitionReady(transitionInfo) - - verify(desktopModeEventLogger, times(1)).logTaskRemoved(eq(sessionId), any()) - verify(desktopModeEventLogger, times(1)).logSessionExit(eq(sessionId), - eq(ExitReason.DRAG_TO_EXIT)) - assertThat(transitionObserver.getLoggerSessionId()).isNull() - } - - @Test - fun freeformTaskVisible_exitDesktopBySwipeUp_logSessionExitAndTaskRemoved_sessionIdNull() { - val sessionId = 1 - // add a freeform task - transitionObserver.addTaskInfosToCachedMap(createTaskInfo(1, WINDOWING_MODE_FREEFORM)) - transitionObserver.setLoggerSessionId(sessionId) - - // recents transition - val change = createChange(TRANSIT_TO_BACK, createTaskInfo(1, WINDOWING_MODE_FREEFORM)) - val transitionInfo = TransitionInfoBuilder(TRANSIT_TO_FRONT, TRANSIT_FLAG_IS_RECENTS) - .addChange(change).build() - callOnTransitionReady(transitionInfo) - - verify(desktopModeEventLogger, times(1)).logTaskRemoved(eq(sessionId), any()) - verify(desktopModeEventLogger, times(1)).logSessionExit(eq(sessionId), - eq(ExitReason.RETURN_HOME_OR_OVERVIEW)) - assertThat(transitionObserver.getLoggerSessionId()).isNull() - } - - @Test - fun freeformTaskVisible_taskFinished_logSessionExitAndTaskRemoved_sessionIdNull() { - val sessionId = 1 - // add a freeform task - transitionObserver.addTaskInfosToCachedMap(createTaskInfo(1, WINDOWING_MODE_FREEFORM)) - transitionObserver.setLoggerSessionId(sessionId) - - // task closing - val change = createChange(TRANSIT_CLOSE, createTaskInfo(1, WINDOWING_MODE_FULLSCREEN)) - val transitionInfo = TransitionInfoBuilder(TRANSIT_CLOSE).addChange(change).build() - callOnTransitionReady(transitionInfo) - - verify(desktopModeEventLogger, times(1)).logTaskRemoved(eq(sessionId), any()) - verify(desktopModeEventLogger, times(1)).logSessionExit(eq(sessionId), - eq(ExitReason.TASK_FINISHED)) - assertThat(transitionObserver.getLoggerSessionId()).isNull() - } - - @Test - fun sessionExitByRecents_cancelledAnimation_sessionRestored() { - val sessionId = 1 - // add a freeform task to an existing session - transitionObserver.addTaskInfosToCachedMap(createTaskInfo(1, WINDOWING_MODE_FREEFORM)) - transitionObserver.setLoggerSessionId(sessionId) - - // recents transition sent freeform window to back - val change = createChange(TRANSIT_TO_BACK, createTaskInfo(1, WINDOWING_MODE_FREEFORM)) - val transitionInfo1 = - TransitionInfoBuilder(TRANSIT_TO_FRONT, TRANSIT_FLAG_IS_RECENTS).addChange(change) - .build() - callOnTransitionReady(transitionInfo1) - verify(desktopModeEventLogger, times(1)).logTaskRemoved(eq(sessionId), any()) - verify(desktopModeEventLogger, times(1)).logSessionExit(eq(sessionId), - eq(ExitReason.RETURN_HOME_OR_OVERVIEW)) - assertThat(transitionObserver.getLoggerSessionId()).isNull() - - val transitionInfo2 = TransitionInfoBuilder(TRANSIT_NONE).build() - callOnTransitionReady(transitionInfo2) - - verify(desktopModeEventLogger, times(1)).logSessionEnter(any(), any()) - verify(desktopModeEventLogger, times(1)).logTaskAdded(any(), any()) - } - - @Test - fun sessionAlreadyStarted_newFreeformTaskAdded_logsTaskAdded() { - val sessionId = 1 - // add an existing freeform task - transitionObserver.addTaskInfosToCachedMap(createTaskInfo(1, WINDOWING_MODE_FREEFORM)) - transitionObserver.setLoggerSessionId(sessionId) - - // new freeform task added - val change = createChange(TRANSIT_OPEN, createTaskInfo(2, WINDOWING_MODE_FREEFORM)) - val transitionInfo = TransitionInfoBuilder(TRANSIT_OPEN, 0).addChange(change).build() - callOnTransitionReady(transitionInfo) - - verify(desktopModeEventLogger, times(1)).logTaskAdded(eq(sessionId), any()) - verify(desktopModeEventLogger, never()).logSessionEnter(any(), any()) - } - - @Test - fun sessionAlreadyStarted_freeformTaskRemoved_logsTaskRemoved() { - val sessionId = 1 - // add two existing freeform tasks - transitionObserver.addTaskInfosToCachedMap(createTaskInfo(1, WINDOWING_MODE_FREEFORM)) - transitionObserver.addTaskInfosToCachedMap(createTaskInfo(2, WINDOWING_MODE_FREEFORM)) - transitionObserver.setLoggerSessionId(sessionId) - - // new freeform task added - val change = createChange(TRANSIT_CLOSE, createTaskInfo(2, WINDOWING_MODE_FREEFORM)) - val transitionInfo = TransitionInfoBuilder(TRANSIT_CLOSE, 0).addChange(change).build() - callOnTransitionReady(transitionInfo) - - verify(desktopModeEventLogger, times(1)).logTaskRemoved(eq(sessionId), any()) - verify(desktopModeEventLogger, never()).logSessionExit(any(), any()) - } - - /** - * Simulate calling the onTransitionReady() method - */ - private fun callOnTransitionReady(transitionInfo: TransitionInfo) { - val transition = mock(IBinder::class.java) - val startT = mock( - SurfaceControl.Transaction::class.java) - val finishT = mock( - SurfaceControl.Transaction::class.java) - - transitionObserver.onTransitionReady(transition, transitionInfo, startT, finishT) + } + + @Test + fun testRegistersObserverAtInit() { + verify(transitions).registerObserver(same(transitionObserver)) + } + + @Test + fun transitOpen_notFreeformWindow_doesNotLogTaskAddedOrSessionEnter() { + val change = createChange(TRANSIT_OPEN, createTaskInfo(1, WINDOWING_MODE_FULLSCREEN)) + val transitionInfo = TransitionInfoBuilder(TRANSIT_OPEN, 0).addChange(change).build() + + callOnTransitionReady(transitionInfo) + + verify(desktopModeEventLogger, never()).logSessionEnter(any(), any()) + verify(desktopModeEventLogger, never()).logTaskAdded(any(), any()) + } + + @Test + fun transitOpen_logTaskAddedAndEnterReasonAppFreeformIntent() { + val change = createChange(TRANSIT_OPEN, createTaskInfo(1, WINDOWING_MODE_FREEFORM)) + val transitionInfo = TransitionInfoBuilder(TRANSIT_OPEN, 0).addChange(change).build() + + callOnTransitionReady(transitionInfo) + val sessionId = transitionObserver.getLoggerSessionId() + + assertThat(sessionId).isNotNull() + verify(desktopModeEventLogger, times(1)) + .logSessionEnter(eq(sessionId!!), eq(EnterReason.APP_FREEFORM_INTENT)) + verify(desktopModeEventLogger, times(1)).logTaskAdded(eq(sessionId), any()) + verifyZeroInteractions(desktopModeEventLogger) + } + + @Test + fun transitEndDragToDesktop_logTaskAddedAndEnterReasonAppHandleDrag() { + val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(1, WINDOWING_MODE_FREEFORM)) + // task change is finalised when drag ends + val transitionInfo = + TransitionInfoBuilder(Transitions.TRANSIT_DESKTOP_MODE_END_DRAG_TO_DESKTOP, 0) + .addChange(change) + .build() + + callOnTransitionReady(transitionInfo) + val sessionId = transitionObserver.getLoggerSessionId() + + assertThat(sessionId).isNotNull() + verify(desktopModeEventLogger, times(1)) + .logSessionEnter(eq(sessionId!!), eq(EnterReason.APP_HANDLE_DRAG)) + verify(desktopModeEventLogger, times(1)).logTaskAdded(eq(sessionId), any()) + verifyZeroInteractions(desktopModeEventLogger) + } + + @Test + fun transitEnterDesktopByButtonTap_logTaskAddedAndEnterReasonButtonTap() { + val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(1, WINDOWING_MODE_FREEFORM)) + val transitionInfo = + TransitionInfoBuilder(TRANSIT_ENTER_DESKTOP_FROM_APP_HANDLE_MENU_BUTTON, 0) + .addChange(change) + .build() + + callOnTransitionReady(transitionInfo) + val sessionId = transitionObserver.getLoggerSessionId() + + assertThat(sessionId).isNotNull() + verify(desktopModeEventLogger, times(1)) + .logSessionEnter(eq(sessionId!!), eq(EnterReason.APP_HANDLE_MENU_BUTTON)) + verify(desktopModeEventLogger, times(1)).logTaskAdded(eq(sessionId), any()) + verifyZeroInteractions(desktopModeEventLogger) + } + + @Test + fun transitEnterDesktopFromAppFromOverview_logTaskAddedAndEnterReasonUnknown() { + val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(1, WINDOWING_MODE_FREEFORM)) + val transitionInfo = + TransitionInfoBuilder(TRANSIT_ENTER_DESKTOP_FROM_APP_FROM_OVERVIEW, 0) + .addChange(change) + .build() + + callOnTransitionReady(transitionInfo) + val sessionId = transitionObserver.getLoggerSessionId() + + assertThat(sessionId).isNotNull() + verify(desktopModeEventLogger, times(1)) + .logSessionEnter(eq(sessionId!!), eq(EnterReason.APP_FROM_OVERVIEW)) + verify(desktopModeEventLogger, times(1)).logTaskAdded(eq(sessionId), any()) + verifyZeroInteractions(desktopModeEventLogger) + } + + @Test + fun transitEnterDesktopFromKeyboardShortcut_logTaskAddedAndEnterReasonKeyboardShortcut() { + val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(1, WINDOWING_MODE_FREEFORM)) + val transitionInfo = + TransitionInfoBuilder(TRANSIT_ENTER_DESKTOP_FROM_KEYBOARD_SHORTCUT, 0) + .addChange(change) + .build() + + callOnTransitionReady(transitionInfo) + val sessionId = transitionObserver.getLoggerSessionId() + + assertThat(sessionId).isNotNull() + verify(desktopModeEventLogger, times(1)) + .logSessionEnter(eq(sessionId!!), eq(EnterReason.KEYBOARD_SHORTCUT_ENTER)) + verify(desktopModeEventLogger, times(1)).logTaskAdded(eq(sessionId), any()) + verifyZeroInteractions(desktopModeEventLogger) + } + + @Test + fun transitEnterDesktopFromUnknown_logTaskAddedAndEnterReasonUnknown() { + val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(1, WINDOWING_MODE_FREEFORM)) + val transitionInfo = + TransitionInfoBuilder(TRANSIT_ENTER_DESKTOP_FROM_UNKNOWN, 0).addChange(change).build() + + callOnTransitionReady(transitionInfo) + val sessionId = transitionObserver.getLoggerSessionId() + + assertThat(sessionId).isNotNull() + verify(desktopModeEventLogger, times(1)) + .logSessionEnter(eq(sessionId!!), eq(EnterReason.UNKNOWN_ENTER)) + verify(desktopModeEventLogger, times(1)).logTaskAdded(eq(sessionId), any()) + verifyZeroInteractions(desktopModeEventLogger) + } + + @Test + fun transitWake_logTaskAddedAndEnterReasonScreenOn() { + val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(1, WINDOWING_MODE_FREEFORM)) + val transitionInfo = TransitionInfoBuilder(TRANSIT_WAKE, 0).addChange(change).build() + + callOnTransitionReady(transitionInfo) + val sessionId = transitionObserver.getLoggerSessionId() + + assertThat(sessionId).isNotNull() + verify(desktopModeEventLogger, times(1)) + .logSessionEnter(eq(sessionId!!), eq(EnterReason.SCREEN_ON)) + verify(desktopModeEventLogger, times(1)).logTaskAdded(eq(sessionId), any()) + verifyZeroInteractions(desktopModeEventLogger) + } + + @Test + fun transitSleep_logTaskAddedAndExitReasonScreenOff_sessionIdNull() { + val sessionId = 1 + // add a freeform task + transitionObserver.addTaskInfosToCachedMap(createTaskInfo(1, WINDOWING_MODE_FREEFORM)) + transitionObserver.setLoggerSessionId(sessionId) + + val transitionInfo = TransitionInfoBuilder(TRANSIT_SLEEP).build() + callOnTransitionReady(transitionInfo) + + verify(desktopModeEventLogger, times(1)).logTaskRemoved(eq(sessionId), any()) + verify(desktopModeEventLogger, times(1)) + .logSessionExit(eq(sessionId), eq(ExitReason.SCREEN_OFF)) + verifyZeroInteractions(desktopModeEventLogger) + assertThat(transitionObserver.getLoggerSessionId()).isNull() + } + + @Test + fun transitExitDesktopTaskDrag_logTaskRemovedAndExitReasonDragToExit_sessionIdNull() { + val sessionId = 1 + // add a freeform task + transitionObserver.addTaskInfosToCachedMap(createTaskInfo(1, WINDOWING_MODE_FREEFORM)) + transitionObserver.setLoggerSessionId(sessionId) + + // window mode changing from FREEFORM to FULLSCREEN + val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(1, WINDOWING_MODE_FULLSCREEN)) + val transitionInfo = + TransitionInfoBuilder(TRANSIT_EXIT_DESKTOP_MODE_TASK_DRAG).addChange(change).build() + callOnTransitionReady(transitionInfo) + + verify(desktopModeEventLogger, times(1)).logTaskRemoved(eq(sessionId), any()) + verify(desktopModeEventLogger, times(1)) + .logSessionExit(eq(sessionId), eq(ExitReason.DRAG_TO_EXIT)) + verifyZeroInteractions(desktopModeEventLogger) + assertThat(transitionObserver.getLoggerSessionId()).isNull() + } + + @Test + fun transitExitDesktopAppHandleButton_logTaskRemovedAndExitReasonButton_sessionIdNull() { + val sessionId = 1 + // add a freeform task + transitionObserver.addTaskInfosToCachedMap(createTaskInfo(1, WINDOWING_MODE_FREEFORM)) + transitionObserver.setLoggerSessionId(sessionId) + + // window mode changing from FREEFORM to FULLSCREEN + val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(1, WINDOWING_MODE_FULLSCREEN)) + val transitionInfo = + TransitionInfoBuilder(TRANSIT_EXIT_DESKTOP_MODE_HANDLE_MENU_BUTTON) + .addChange(change) + .build() + callOnTransitionReady(transitionInfo) + + verify(desktopModeEventLogger, times(1)).logTaskRemoved(eq(sessionId), any()) + verify(desktopModeEventLogger, times(1)) + .logSessionExit(eq(sessionId), eq(ExitReason.APP_HANDLE_MENU_BUTTON_EXIT)) + verifyZeroInteractions(desktopModeEventLogger) + assertThat(transitionObserver.getLoggerSessionId()).isNull() + } + + @Test + fun transitExitDesktopUsingKeyboard_logTaskRemovedAndExitReasonKeyboard_sessionIdNull() { + val sessionId = 1 + // add a freeform task + transitionObserver.addTaskInfosToCachedMap(createTaskInfo(1, WINDOWING_MODE_FREEFORM)) + transitionObserver.setLoggerSessionId(sessionId) + + // window mode changing from FREEFORM to FULLSCREEN + val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(1, WINDOWING_MODE_FULLSCREEN)) + val transitionInfo = + TransitionInfoBuilder(TRANSIT_EXIT_DESKTOP_MODE_KEYBOARD_SHORTCUT).addChange(change).build() + callOnTransitionReady(transitionInfo) + + verify(desktopModeEventLogger, times(1)).logTaskRemoved(eq(sessionId), any()) + verify(desktopModeEventLogger, times(1)) + .logSessionExit(eq(sessionId), eq(ExitReason.KEYBOARD_SHORTCUT_EXIT)) + verifyZeroInteractions(desktopModeEventLogger) + assertThat(transitionObserver.getLoggerSessionId()).isNull() + } + + @Test + fun transitExitDesktopUnknown_logTaskRemovedAndExitReasonUnknown_sessionIdNull() { + val sessionId = 1 + // add a freeform task + transitionObserver.addTaskInfosToCachedMap(createTaskInfo(1, WINDOWING_MODE_FREEFORM)) + transitionObserver.setLoggerSessionId(sessionId) + + // window mode changing from FREEFORM to FULLSCREEN + val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(1, WINDOWING_MODE_FULLSCREEN)) + val transitionInfo = + TransitionInfoBuilder(TRANSIT_EXIT_DESKTOP_MODE_UNKNOWN).addChange(change).build() + callOnTransitionReady(transitionInfo) + + verify(desktopModeEventLogger, times(1)).logTaskRemoved(eq(sessionId), any()) + verify(desktopModeEventLogger, times(1)) + .logSessionExit(eq(sessionId), eq(ExitReason.UNKNOWN_EXIT)) + verifyZeroInteractions(desktopModeEventLogger) + assertThat(transitionObserver.getLoggerSessionId()).isNull() + } + + @Test + fun transitToFrontWithFlagRecents_logTaskRemovedAndExitReasonOverview_sessionIdNull() { + val sessionId = 1 + // add a freeform task + transitionObserver.addTaskInfosToCachedMap(createTaskInfo(1, WINDOWING_MODE_FREEFORM)) + transitionObserver.setLoggerSessionId(sessionId) + + // recents transition + val change = createChange(TRANSIT_TO_BACK, createTaskInfo(1, WINDOWING_MODE_FREEFORM)) + val transitionInfo = + TransitionInfoBuilder(TRANSIT_TO_FRONT, TRANSIT_FLAG_IS_RECENTS).addChange(change).build() + callOnTransitionReady(transitionInfo) + + verify(desktopModeEventLogger, times(1)).logTaskRemoved(eq(sessionId), any()) + verify(desktopModeEventLogger, times(1)) + .logSessionExit(eq(sessionId), eq(ExitReason.RETURN_HOME_OR_OVERVIEW)) + verifyZeroInteractions(desktopModeEventLogger) + assertThat(transitionObserver.getLoggerSessionId()).isNull() + } + + @Test + fun transitClose_logTaskRemovedAndExitReasonTaskFinished_sessionIdNull() { + val sessionId = 1 + // add a freeform task + transitionObserver.addTaskInfosToCachedMap(createTaskInfo(1, WINDOWING_MODE_FREEFORM)) + transitionObserver.setLoggerSessionId(sessionId) + + // task closing + val change = createChange(TRANSIT_CLOSE, createTaskInfo(1, WINDOWING_MODE_FULLSCREEN)) + val transitionInfo = TransitionInfoBuilder(TRANSIT_CLOSE).addChange(change).build() + callOnTransitionReady(transitionInfo) + + verify(desktopModeEventLogger, times(1)).logTaskRemoved(eq(sessionId), any()) + verify(desktopModeEventLogger, times(1)) + .logSessionExit(eq(sessionId), eq(ExitReason.TASK_FINISHED)) + verifyZeroInteractions(desktopModeEventLogger) + assertThat(transitionObserver.getLoggerSessionId()).isNull() + } + + @Test + fun sessionExitByRecents_cancelledAnimation_sessionRestored() { + val sessionId = 1 + // add a freeform task to an existing session + transitionObserver.addTaskInfosToCachedMap(createTaskInfo(1, WINDOWING_MODE_FREEFORM)) + transitionObserver.setLoggerSessionId(sessionId) + + // recents transition sent freeform window to back + val change = createChange(TRANSIT_TO_BACK, createTaskInfo(1, WINDOWING_MODE_FREEFORM)) + val transitionInfo1 = + TransitionInfoBuilder(TRANSIT_TO_FRONT, TRANSIT_FLAG_IS_RECENTS).addChange(change).build() + callOnTransitionReady(transitionInfo1) + verify(desktopModeEventLogger, times(1)).logTaskRemoved(eq(sessionId), any()) + verify(desktopModeEventLogger, times(1)) + .logSessionExit(eq(sessionId), eq(ExitReason.RETURN_HOME_OR_OVERVIEW)) + assertThat(transitionObserver.getLoggerSessionId()).isNull() + + val transitionInfo2 = TransitionInfoBuilder(TRANSIT_NONE).build() + callOnTransitionReady(transitionInfo2) + + verify(desktopModeEventLogger, times(1)).logSessionEnter(any(), any()) + verify(desktopModeEventLogger, times(1)).logTaskAdded(any(), any()) + } + + @Test + fun sessionAlreadyStarted_newFreeformTaskAdded_logsTaskAdded() { + val sessionId = 1 + // add an existing freeform task + transitionObserver.addTaskInfosToCachedMap(createTaskInfo(1, WINDOWING_MODE_FREEFORM)) + transitionObserver.setLoggerSessionId(sessionId) + + // new freeform task added + val change = createChange(TRANSIT_OPEN, createTaskInfo(2, WINDOWING_MODE_FREEFORM)) + val transitionInfo = TransitionInfoBuilder(TRANSIT_OPEN, 0).addChange(change).build() + callOnTransitionReady(transitionInfo) + + verify(desktopModeEventLogger, times(1)).logTaskAdded(eq(sessionId), any()) + verify(desktopModeEventLogger, never()).logSessionEnter(any(), any()) + } + + @Test + fun sessionAlreadyStarted_freeformTaskRemoved_logsTaskRemoved() { + val sessionId = 1 + // add two existing freeform tasks + transitionObserver.addTaskInfosToCachedMap(createTaskInfo(1, WINDOWING_MODE_FREEFORM)) + transitionObserver.addTaskInfosToCachedMap(createTaskInfo(2, WINDOWING_MODE_FREEFORM)) + transitionObserver.setLoggerSessionId(sessionId) + + // new freeform task added + val change = createChange(TRANSIT_CLOSE, createTaskInfo(2, WINDOWING_MODE_FREEFORM)) + val transitionInfo = TransitionInfoBuilder(TRANSIT_CLOSE, 0).addChange(change).build() + callOnTransitionReady(transitionInfo) + + verify(desktopModeEventLogger, times(1)).logTaskRemoved(eq(sessionId), any()) + verify(desktopModeEventLogger, never()).logSessionExit(any(), any()) + } + + /** Simulate calling the onTransitionReady() method */ + private fun callOnTransitionReady(transitionInfo: TransitionInfo) { + val transition = mock(IBinder::class.java) + val startT = mock(SurfaceControl.Transaction::class.java) + val finishT = mock(SurfaceControl.Transaction::class.java) + + transitionObserver.onTransitionReady(transition, transitionInfo, startT, finishT) + } + + companion object { + fun createTaskInfo(taskId: Int, windowMode: Int): ActivityManager.RunningTaskInfo { + val taskInfo = ActivityManager.RunningTaskInfo() + taskInfo.taskId = taskId + taskInfo.configuration.windowConfiguration.windowingMode = windowMode + + return taskInfo } - companion object { - fun createTaskInfo(taskId: Int, windowMode: Int): ActivityManager.RunningTaskInfo { - val taskInfo = ActivityManager.RunningTaskInfo() - taskInfo.taskId = taskId - taskInfo.configuration.windowConfiguration.windowingMode = windowMode - - return taskInfo - } - - fun createChange(mode: Int, taskInfo: ActivityManager.RunningTaskInfo): Change { - val change = Change( - WindowContainerToken(mock( - IWindowContainerToken::class.java)), - mock(SurfaceControl::class.java)) - change.mode = mode - change.taskInfo = taskInfo - return change - } + fun createChange(mode: Int, taskInfo: ActivityManager.RunningTaskInfo): Change { + val change = + Change( + WindowContainerToken(mock(IWindowContainerToken::class.java)), + mock(SurfaceControl::class.java)) + change.mode = mode + change.taskInfo = taskInfo + return change } -}
\ No newline at end of file + } +} diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeTransitionTypesTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeTransitionTypesTest.kt new file mode 100644 index 000000000000..518c00d377ad --- /dev/null +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeTransitionTypesTest.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.wm.shell.desktopmode + +import android.testing.AndroidTestingRunner +import androidx.test.filters.SmallTest +import com.android.wm.shell.common.desktopmode.DesktopModeTransitionSource.APP_HANDLE_MENU_BUTTON +import com.android.wm.shell.common.desktopmode.DesktopModeTransitionSource.APP_FROM_OVERVIEW +import com.android.wm.shell.common.desktopmode.DesktopModeTransitionSource.KEYBOARD_SHORTCUT +import com.android.wm.shell.common.desktopmode.DesktopModeTransitionSource.TASK_DRAG +import com.android.wm.shell.common.desktopmode.DesktopModeTransitionSource.UNKNOWN +import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_EXIT_DESKTOP_MODE_HANDLE_MENU_BUTTON +import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_EXIT_DESKTOP_MODE_KEYBOARD_SHORTCUT +import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_EXIT_DESKTOP_MODE_TASK_DRAG +import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_EXIT_DESKTOP_MODE_UNKNOWN +import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_ENTER_DESKTOP_FROM_APP_FROM_OVERVIEW +import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_ENTER_DESKTOP_FROM_APP_HANDLE_MENU_BUTTON +import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_ENTER_DESKTOP_FROM_KEYBOARD_SHORTCUT +import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_ENTER_DESKTOP_FROM_UNKNOWN +import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.getEnterTransitionType +import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.getExitTransitionType +import com.google.common.truth.Truth.assertThat +import org.junit.Test +import org.junit.runner.RunWith + +/** + * Test class for [DesktopModeTransitionTypes] + * + * Usage: atest WMShellUnitTests:DesktopModeTransitionTypesTest + */ +@SmallTest +@RunWith(AndroidTestingRunner::class) +class DesktopModeTransitionTypesTest { + + @Test + fun testGetEnterTransitionType() { + assertThat(UNKNOWN.getEnterTransitionType()).isEqualTo(TRANSIT_ENTER_DESKTOP_FROM_UNKNOWN) + assertThat(APP_HANDLE_MENU_BUTTON.getEnterTransitionType()) + .isEqualTo(TRANSIT_ENTER_DESKTOP_FROM_APP_HANDLE_MENU_BUTTON) + assertThat(APP_FROM_OVERVIEW.getEnterTransitionType()) + .isEqualTo(TRANSIT_ENTER_DESKTOP_FROM_APP_FROM_OVERVIEW) + assertThat(TASK_DRAG.getEnterTransitionType()) + .isEqualTo(TRANSIT_ENTER_DESKTOP_FROM_UNKNOWN) + assertThat(KEYBOARD_SHORTCUT.getEnterTransitionType()) + .isEqualTo(TRANSIT_ENTER_DESKTOP_FROM_KEYBOARD_SHORTCUT) + } + + @Test + fun testGetExitTransitionType() { + assertThat(UNKNOWN.getExitTransitionType()).isEqualTo(TRANSIT_EXIT_DESKTOP_MODE_UNKNOWN) + assertThat(APP_HANDLE_MENU_BUTTON.getExitTransitionType()) + .isEqualTo(TRANSIT_EXIT_DESKTOP_MODE_HANDLE_MENU_BUTTON) + assertThat(APP_FROM_OVERVIEW.getExitTransitionType()) + .isEqualTo(TRANSIT_EXIT_DESKTOP_MODE_UNKNOWN) + assertThat(TASK_DRAG.getExitTransitionType()).isEqualTo(TRANSIT_EXIT_DESKTOP_MODE_TASK_DRAG) + assertThat(KEYBOARD_SHORTCUT.getExitTransitionType()) + .isEqualTo(TRANSIT_EXIT_DESKTOP_MODE_KEYBOARD_SHORTCUT) + } +} 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 d7a3be849a01..748ad3182393 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 @@ -76,6 +76,7 @@ import com.android.wm.shell.common.LaunchAdjacentController 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.common.desktopmode.DesktopModeTransitionSource.UNKNOWN import com.android.wm.shell.common.split.SplitScreenConstants import com.android.wm.shell.desktopmode.DesktopTestHelpers.Companion.createFreeformTask import com.android.wm.shell.desktopmode.DesktopTestHelpers.Companion.createFullscreenTask @@ -94,7 +95,6 @@ import com.android.wm.shell.transition.OneShotRemoteHandler import com.android.wm.shell.transition.TestRemoteTransition import com.android.wm.shell.transition.Transitions import com.android.wm.shell.transition.Transitions.ENABLE_SHELL_TRANSITIONS -import com.android.wm.shell.transition.Transitions.TRANSIT_EXIT_DESKTOP_MODE import com.android.wm.shell.transition.Transitions.TransitionHandler import com.google.common.truth.Truth.assertThat import com.google.common.truth.Truth.assertWithMessage @@ -197,7 +197,7 @@ class DesktopTasksControllerTest : ShellTestCase() { whenever(shellTaskOrganizer.getRunningTasks(anyInt())).thenAnswer { runningTasks } whenever(transitions.startTransition(anyInt(), any(), isNull())).thenAnswer { Binder() } - whenever(enterDesktopTransitionHandler.moveToDesktop(any())).thenAnswer { Binder() } + whenever(enterDesktopTransitionHandler.moveToDesktop(any(), any())).thenAnswer { Binder() } whenever(displayController.getDisplayLayout(anyInt())).thenReturn(displayLayout) whenever(displayLayout.getStableBounds(any())).thenAnswer { i -> (i.arguments.first() as Rect).set(STABLE_BOUNDS) @@ -488,7 +488,7 @@ class DesktopTasksControllerTest : ShellTestCase() { val task = setUpFullscreenTask() setUpLandscapeDisplay() - controller.moveToDesktop(task) + controller.moveToDesktop(task, transitionSource = UNKNOWN) val wct = getLatestMoveToDesktopWct() assertThat(findBoundsChange(wct, task)).isEqualTo(DEFAULT_LANDSCAPE_BOUNDS) } @@ -500,7 +500,7 @@ class DesktopTasksControllerTest : ShellTestCase() { val task = setUpFullscreenTask(screenOrientation = SCREEN_ORIENTATION_LANDSCAPE) setUpLandscapeDisplay() - controller.moveToDesktop(task) + controller.moveToDesktop(task, transitionSource = UNKNOWN) val wct = getLatestMoveToDesktopWct() assertThat(findBoundsChange(wct, task)).isEqualTo(DEFAULT_LANDSCAPE_BOUNDS) } @@ -513,7 +513,7 @@ class DesktopTasksControllerTest : ShellTestCase() { setUpFullscreenTask(screenOrientation = SCREEN_ORIENTATION_PORTRAIT, shouldLetterbox = true) setUpLandscapeDisplay() - controller.moveToDesktop(task) + controller.moveToDesktop(task, transitionSource = UNKNOWN) val wct = getLatestMoveToDesktopWct() assertThat(findBoundsChange(wct, task)).isEqualTo(RESIZABLE_PORTRAIT_BOUNDS) } @@ -526,7 +526,7 @@ class DesktopTasksControllerTest : ShellTestCase() { setUpFullscreenTask(isResizable = false, screenOrientation = SCREEN_ORIENTATION_LANDSCAPE) setUpLandscapeDisplay() - controller.moveToDesktop(task) + controller.moveToDesktop(task, transitionSource = UNKNOWN) val wct = getLatestMoveToDesktopWct() assertThat(findBoundsChange(wct, task)).isEqualTo(DEFAULT_LANDSCAPE_BOUNDS) } @@ -542,7 +542,7 @@ class DesktopTasksControllerTest : ShellTestCase() { shouldLetterbox = true) setUpLandscapeDisplay() - controller.moveToDesktop(task) + controller.moveToDesktop(task, transitionSource = UNKNOWN) val wct = getLatestMoveToDesktopWct() assertThat(findBoundsChange(wct, task)).isEqualTo(UNRESIZABLE_PORTRAIT_BOUNDS) } @@ -554,7 +554,7 @@ class DesktopTasksControllerTest : ShellTestCase() { val task = setUpFullscreenTask(deviceOrientation = ORIENTATION_PORTRAIT) setUpPortraitDisplay() - controller.moveToDesktop(task) + controller.moveToDesktop(task, transitionSource = UNKNOWN) val wct = getLatestMoveToDesktopWct() assertThat(findBoundsChange(wct, task)).isEqualTo(DEFAULT_PORTRAIT_BOUNDS) } @@ -569,7 +569,7 @@ class DesktopTasksControllerTest : ShellTestCase() { screenOrientation = SCREEN_ORIENTATION_PORTRAIT) setUpPortraitDisplay() - controller.moveToDesktop(task) + controller.moveToDesktop(task, transitionSource = UNKNOWN) val wct = getLatestMoveToDesktopWct() assertThat(findBoundsChange(wct, task)).isEqualTo(DEFAULT_PORTRAIT_BOUNDS) } @@ -585,7 +585,7 @@ class DesktopTasksControllerTest : ShellTestCase() { shouldLetterbox = true) setUpPortraitDisplay() - controller.moveToDesktop(task) + controller.moveToDesktop(task, transitionSource = UNKNOWN) val wct = getLatestMoveToDesktopWct() assertThat(findBoundsChange(wct, task)).isEqualTo(RESIZABLE_LANDSCAPE_BOUNDS) } @@ -601,7 +601,7 @@ class DesktopTasksControllerTest : ShellTestCase() { screenOrientation = SCREEN_ORIENTATION_PORTRAIT) setUpPortraitDisplay() - controller.moveToDesktop(task) + controller.moveToDesktop(task, transitionSource = UNKNOWN) val wct = getLatestMoveToDesktopWct() assertThat(findBoundsChange(wct, task)).isEqualTo(DEFAULT_PORTRAIT_BOUNDS) } @@ -618,7 +618,7 @@ class DesktopTasksControllerTest : ShellTestCase() { shouldLetterbox = true) setUpPortraitDisplay() - controller.moveToDesktop(task) + controller.moveToDesktop(task, transitionSource = UNKNOWN) val wct = getLatestMoveToDesktopWct() assertThat(findBoundsChange(wct, task)).isEqualTo(UNRESIZABLE_LANDSCAPE_BOUNDS) } @@ -628,7 +628,7 @@ class DesktopTasksControllerTest : ShellTestCase() { val task = setUpFullscreenTask() val tda = rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY)!! tda.configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FULLSCREEN - controller.moveToDesktop(task) + controller.moveToDesktop(task, transitionSource = UNKNOWN) val wct = getLatestMoveToDesktopWct() assertThat(wct.changes[task.token.asBinder()]?.windowingMode).isEqualTo(WINDOWING_MODE_FREEFORM) } @@ -638,7 +638,7 @@ class DesktopTasksControllerTest : ShellTestCase() { val task = setUpFullscreenTask() val tda = rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY)!! tda.configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FREEFORM - controller.moveToDesktop(task) + controller.moveToDesktop(task, transitionSource = UNKNOWN) val wct = getLatestMoveToDesktopWct() assertThat(wct.changes[task.token.asBinder()]?.windowingMode) .isEqualTo(WINDOWING_MODE_UNDEFINED) @@ -646,7 +646,7 @@ class DesktopTasksControllerTest : ShellTestCase() { @Test fun moveToDesktop_nonExistentTask_doesNothing() { - controller.moveToDesktop(999) + controller.moveToDesktop(999, transitionSource = UNKNOWN) verifyWCTNotExecuted() } @@ -658,7 +658,7 @@ class DesktopTasksControllerTest : ShellTestCase() { whenever(recentTasksController.findTaskInBackground(anyInt())).thenReturn(task) - controller.moveToDesktop(task.taskId) + controller.moveToDesktop(task.taskId, transitionSource = UNKNOWN) with(getLatestMoveToDesktopWct()) { assertLaunchTaskAt(0, task.taskId, WINDOWING_MODE_FREEFORM) } @@ -673,7 +673,7 @@ class DesktopTasksControllerTest : ShellTestCase() { numActivities = 1 } - controller.moveToDesktop(task) + controller.moveToDesktop(task, transitionSource = UNKNOWN) verifyWCTNotExecuted() } @@ -684,7 +684,7 @@ class DesktopTasksControllerTest : ShellTestCase() { // Simulate non compatible device doReturn(false).`when` { DesktopModeStatus.isDesktopModeSupported(any()) } - controller.moveToDesktop(task) + controller.moveToDesktop(task, transitionSource = UNKNOWN) verifyWCTNotExecuted() } @@ -698,7 +698,7 @@ class DesktopTasksControllerTest : ShellTestCase() { // Simulate enforce device restrictions system property overridden to false whenever(DesktopModeStatus.enforceDeviceRestrictions()).thenReturn(false) - controller.moveToDesktop(task) + controller.moveToDesktop(task, transitionSource = UNKNOWN) val wct = getLatestMoveToDesktopWct() assertThat(wct.changes[task.token.asBinder()]?.windowingMode).isEqualTo(WINDOWING_MODE_FREEFORM) @@ -708,7 +708,7 @@ class DesktopTasksControllerTest : ShellTestCase() { fun moveToDesktop_deviceSupported_taskIsMovedToDesktop() { val task = setUpFullscreenTask() - controller.moveToDesktop(task) + controller.moveToDesktop(task, transitionSource = UNKNOWN) val wct = getLatestMoveToDesktopWct() assertThat(wct.changes[task.token.asBinder()]?.windowingMode).isEqualTo(WINDOWING_MODE_FREEFORM) @@ -722,7 +722,7 @@ class DesktopTasksControllerTest : ShellTestCase() { val fullscreenTask = setUpFullscreenTask() markTaskHidden(freeformTask) - controller.moveToDesktop(fullscreenTask) + controller.moveToDesktop(fullscreenTask, transitionSource = UNKNOWN) with(getLatestMoveToDesktopWct()) { // Operations should include home task, freeform task @@ -740,7 +740,7 @@ class DesktopTasksControllerTest : ShellTestCase() { val fullscreenTask = setUpFullscreenTask() markTaskHidden(freeformTask) - controller.moveToDesktop(fullscreenTask) + controller.moveToDesktop(fullscreenTask, transitionSource = UNKNOWN) with(getLatestMoveToDesktopWct()) { // Operations should include wallpaper intent, freeform task, fullscreen task @@ -764,7 +764,7 @@ class DesktopTasksControllerTest : ShellTestCase() { val freeformTaskSecond = setUpFreeformTask(displayId = SECOND_DISPLAY) markTaskHidden(freeformTaskSecond) - controller.moveToDesktop(fullscreenTaskDefault) + controller.moveToDesktop(fullscreenTaskDefault, transitionSource = UNKNOWN) with(getLatestMoveToDesktopWct()) { // Check that hierarchy operations do not include tasks from second display @@ -777,7 +777,7 @@ class DesktopTasksControllerTest : ShellTestCase() { @Test fun moveToDesktop_splitTaskExitsSplit() { val task = setUpSplitScreenTask() - controller.moveToDesktop(task) + controller.moveToDesktop(task, transitionSource = UNKNOWN) val wct = getLatestMoveToDesktopWct() assertThat(wct.changes[task.token.asBinder()]?.windowingMode).isEqualTo(WINDOWING_MODE_FREEFORM) verify(splitScreenController) @@ -787,7 +787,7 @@ class DesktopTasksControllerTest : ShellTestCase() { @Test fun moveToDesktop_fullscreenTaskDoesNotExitSplit() { val task = setUpFullscreenTask() - controller.moveToDesktop(task) + controller.moveToDesktop(task, transitionSource = UNKNOWN) val wct = getLatestMoveToDesktopWct() assertThat(wct.changes[task.token.asBinder()]?.windowingMode).isEqualTo(WINDOWING_MODE_FREEFORM) verify(splitScreenController, never()) @@ -801,7 +801,7 @@ class DesktopTasksControllerTest : ShellTestCase() { val freeformTasks = (1..taskLimit).map { _ -> setUpFreeformTask() } val newTask = setUpFullscreenTask() - controller.moveToDesktop(newTask) + controller.moveToDesktop(newTask, transitionSource = UNKNOWN) val wct = getLatestMoveToDesktopWct() assertThat(wct.hierarchyOps.size).isEqualTo(taskLimit + 1) // visible tasks + home @@ -817,7 +817,7 @@ class DesktopTasksControllerTest : ShellTestCase() { val task = setUpFreeformTask() val tda = rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY)!! tda.configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FULLSCREEN - controller.moveToFullscreen(task.taskId) + controller.moveToFullscreen(task.taskId, transitionSource = UNKNOWN) val wct = getLatestExitDesktopWct() assertThat(wct.changes[task.token.asBinder()]?.windowingMode) .isEqualTo(WINDOWING_MODE_UNDEFINED) @@ -828,7 +828,7 @@ class DesktopTasksControllerTest : ShellTestCase() { val task = setUpFreeformTask() val tda = rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY)!! tda.configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FREEFORM - controller.moveToFullscreen(task.taskId) + controller.moveToFullscreen(task.taskId, transitionSource = UNKNOWN) val wct = getLatestExitDesktopWct() assertThat(wct.changes[task.token.asBinder()]?.windowingMode) .isEqualTo(WINDOWING_MODE_FULLSCREEN) @@ -836,7 +836,7 @@ class DesktopTasksControllerTest : ShellTestCase() { @Test fun moveToFullscreen_nonExistentTask_doesNothing() { - controller.moveToFullscreen(999) + controller.moveToFullscreen(999, transitionSource = UNKNOWN) verifyWCTNotExecuted() } @@ -845,7 +845,7 @@ class DesktopTasksControllerTest : ShellTestCase() { val taskDefaultDisplay = setUpFreeformTask(displayId = DEFAULT_DISPLAY) val taskSecondDisplay = setUpFreeformTask(displayId = SECOND_DISPLAY) - controller.moveToFullscreen(taskDefaultDisplay.taskId) + controller.moveToFullscreen(taskDefaultDisplay.taskId, transitionSource = UNKNOWN) with(getLatestExitDesktopWct()) { assertThat(changes.keys).contains(taskDefaultDisplay.token.asBinder()) @@ -1114,7 +1114,7 @@ class DesktopTasksControllerTest : ShellTestCase() { @Test fun handleRequest_freeformTask_alreadyInDesktop_noOverrideDensity_noConfigDensityChange() { assumeTrue(ENABLE_SHELL_TRANSITIONS) - whenever(DesktopModeStatus.isDesktopDensityOverrideSet()).thenReturn(false) + whenever(DesktopModeStatus.useDesktopOverrideDensity()).thenReturn(false) val freeformTask1 = setUpFreeformTask() markTaskVisible(freeformTask1) @@ -1128,7 +1128,7 @@ class DesktopTasksControllerTest : ShellTestCase() { @Test fun handleRequest_freeformTask_alreadyInDesktop_overrideDensity_hasConfigDensityChange() { assumeTrue(ENABLE_SHELL_TRANSITIONS) - whenever(DesktopModeStatus.isDesktopDensityOverrideSet()).thenReturn(true) + whenever(DesktopModeStatus.useDesktopOverrideDensity()).thenReturn(true) val freeformTask1 = setUpFreeformTask() markTaskVisible(freeformTask1) @@ -1282,7 +1282,7 @@ class DesktopTasksControllerTest : ShellTestCase() { task2.isFocused = false task3.isFocused = false - controller.moveFocusedTaskToDesktop(DEFAULT_DISPLAY) + controller.moveFocusedTaskToDesktop(DEFAULT_DISPLAY, transitionSource = UNKNOWN) val wct = getLatestMoveToDesktopWct() assertThat(wct.changes[task1.token.asBinder()]?.windowingMode) @@ -1303,7 +1303,7 @@ class DesktopTasksControllerTest : ShellTestCase() { task4.parentTaskId = task1.taskId - controller.moveFocusedTaskToDesktop(DEFAULT_DISPLAY) + controller.moveFocusedTaskToDesktop(DEFAULT_DISPLAY, transitionSource = UNKNOWN) val wct = getLatestMoveToDesktopWct() assertThat(wct.changes[task4.token.asBinder()]?.windowingMode) @@ -1322,7 +1322,7 @@ class DesktopTasksControllerTest : ShellTestCase() { task2.isFocused = true task3.isFocused = false - controller.enterFullscreen(DEFAULT_DISPLAY) + controller.enterFullscreen(DEFAULT_DISPLAY, transitionSource = UNKNOWN) val wct = getLatestExitDesktopWct() assertThat(wct.changes[task2.token.asBinder()]?.windowingMode) @@ -1751,7 +1751,7 @@ class DesktopTasksControllerTest : ShellTestCase() { private fun getLatestMoveToDesktopWct(): WindowContainerTransaction { val arg = ArgumentCaptor.forClass(WindowContainerTransaction::class.java) if (ENABLE_SHELL_TRANSITIONS) { - verify(enterDesktopTransitionHandler).moveToDesktop(arg.capture()) + verify(enterDesktopTransitionHandler).moveToDesktop(arg.capture(), any()) } else { verify(shellTaskOrganizer).applyTransaction(arg.capture()) } @@ -1772,8 +1772,7 @@ class DesktopTasksControllerTest : ShellTestCase() { private fun getLatestExitDesktopWct(): WindowContainerTransaction { val arg = ArgumentCaptor.forClass(WindowContainerTransaction::class.java) if (ENABLE_SHELL_TRANSITIONS) { - verify(exitDesktopTransitionHandler) - .startTransition(eq(TRANSIT_EXIT_DESKTOP_MODE), arg.capture(), any(), any()) + verify(exitDesktopTransitionHandler).startTransition(any(), arg.capture(), any(), any()) } else { verify(shellTaskOrganizer).applyTransaction(arg.capture()) } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/ExitDesktopTaskTransitionHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/ExitDesktopTaskTransitionHandlerTest.java index 0d0a08cb0ffb..b2467e9a62cf 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/ExitDesktopTaskTransitionHandlerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/ExitDesktopTaskTransitionHandlerTest.java @@ -21,6 +21,8 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static androidx.test.internal.runner.junit4.statement.UiThreadStatement.runOnUiThread; +import static com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_EXIT_DESKTOP_MODE_UNKNOWN; + import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; @@ -45,6 +47,7 @@ import androidx.test.filters.SmallTest; import com.android.wm.shell.ShellTestCase; import com.android.wm.shell.common.ShellExecutor; +import com.android.wm.shell.common.desktopmode.DesktopModeTransitionSource; import com.android.wm.shell.transition.Transitions; import org.junit.Before; @@ -97,18 +100,18 @@ public class ExitDesktopTaskTransitionHandlerTest extends ShellTestCase { @Test public void testTransitExitDesktopModeAnimation() throws Throwable { - final int transitionType = Transitions.TRANSIT_EXIT_DESKTOP_MODE; + final int transitionType = TRANSIT_EXIT_DESKTOP_MODE_UNKNOWN; final int taskId = 1; WindowContainerTransaction wct = new WindowContainerTransaction(); doReturn(mToken).when(mTransitions) .startTransition(transitionType, wct, mExitDesktopTaskTransitionHandler); - mExitDesktopTaskTransitionHandler.startTransition(transitionType, wct, mPoint, - null); + mExitDesktopTaskTransitionHandler.startTransition(DesktopModeTransitionSource.UNKNOWN, + wct, mPoint, null); TransitionInfo.Change change = createChange(WindowManager.TRANSIT_CHANGE, taskId, WINDOWING_MODE_FULLSCREEN); - TransitionInfo info = createTransitionInfo(Transitions.TRANSIT_EXIT_DESKTOP_MODE, change); + TransitionInfo info = createTransitionInfo(TRANSIT_EXIT_DESKTOP_MODE_UNKNOWN, change); ArrayList<Exception> exceptions = new ArrayList<>(); runOnUiThread(() -> { try { 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 56c4ceacc8ab..e291c0e1a151 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 @@ -113,6 +113,8 @@ public class RecentTasksControllerTest extends ShellTestCase { private DisplayInsetsController mDisplayInsetsController; @Mock private IRecentTasksListener mRecentTasksListener; + @Mock + private TaskStackTransitionObserver mTaskStackTransitionObserver; @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); @@ -139,7 +141,8 @@ public class RecentTasksControllerTest extends ShellTestCase { mDisplayInsetsController, mMainExecutor)); mRecentTasksControllerReal = new RecentTasksController(mContext, mShellInit, mShellController, mShellCommandHandler, mTaskStackListener, mActivityTaskManager, - Optional.of(mDesktopModeTaskRepository), mMainExecutor); + Optional.of(mDesktopModeTaskRepository), mTaskStackTransitionObserver, + mMainExecutor); mRecentTasksController = spy(mRecentTasksControllerReal); mShellTaskOrganizer = new ShellTaskOrganizer(mShellInit, mShellCommandHandler, null /* sizeCompatUI */, Optional.empty(), Optional.of(mRecentTasksController), @@ -557,6 +560,30 @@ public class RecentTasksControllerTest extends ShellTestCase { } @Test + @EnableFlags(Flags.FLAG_ENABLE_TASK_STACK_OBSERVER_IN_SHELL) + public void onTaskMovedToFront_TaskStackObserverEnabled_triggersOnTaskMovedToFront() + throws Exception { + mRecentTasksControllerReal.registerRecentTasksListener(mRecentTasksListener); + ActivityManager.RunningTaskInfo taskInfo = makeRunningTaskInfo(/* taskId= */10); + + mRecentTasksControllerReal.onTaskMovedToFrontThroughTransition(taskInfo); + + verify(mRecentTasksListener).onTaskMovedToFront(taskInfo); + } + + @Test + @DisableFlags(Flags.FLAG_ENABLE_TASK_STACK_OBSERVER_IN_SHELL) + public void onTaskMovedToFront_TaskStackObserverEnabled_doesNotTriggersOnTaskMovedToFront() + throws Exception { + mRecentTasksControllerReal.registerRecentTasksListener(mRecentTasksListener); + ActivityManager.RunningTaskInfo taskInfo = makeRunningTaskInfo(/* taskId= */10); + + mRecentTasksControllerReal.onTaskMovedToFront(taskInfo); + + verify(mRecentTasksListener, never()).onTaskMovedToFront(any()); + } + + @Test public void getNullSplitBoundsNonSplitTask() { SplitBounds sb = mRecentTasksController.getSplitBoundsForTaskId(3); assertNull("splitBounds should be null for non-split task", sb); diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/TaskStackTransitionObserverTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/TaskStackTransitionObserverTest.kt new file mode 100644 index 000000000000..f9599702e763 --- /dev/null +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/TaskStackTransitionObserverTest.kt @@ -0,0 +1,217 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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.recents + +import android.app.ActivityManager +import android.app.WindowConfiguration +import android.os.IBinder +import android.platform.test.annotations.EnableFlags +import android.platform.test.flag.junit.SetFlagsRule +import android.testing.AndroidTestingRunner +import android.view.SurfaceControl +import android.view.WindowManager +import android.window.IWindowContainerToken +import android.window.TransitionInfo +import android.window.WindowContainerToken +import androidx.test.filters.SmallTest +import com.android.window.flags.Flags +import com.android.wm.shell.TestShellExecutor +import com.android.wm.shell.common.ShellExecutor +import com.android.wm.shell.sysui.ShellInit +import com.android.wm.shell.transition.TransitionInfoBuilder +import com.android.wm.shell.transition.Transitions +import com.google.common.truth.Truth.assertThat +import dagger.Lazy +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.Mockito +import org.mockito.MockitoAnnotations +import org.mockito.kotlin.same +import org.mockito.kotlin.verify +import org.mockito.kotlin.whenever + + +/** + * Test class for {@link TaskStackTransitionObserver} + * + * Usage: atest WMShellUnitTests:TaskStackTransitionObserverTest + */ +@SmallTest +@RunWith(AndroidTestingRunner::class) +class TaskStackTransitionObserverTest { + + @JvmField @Rule val setFlagsRule = SetFlagsRule() + + @Mock private lateinit var shellInit: ShellInit + @Mock lateinit var testExecutor: ShellExecutor + @Mock private lateinit var transitionsLazy: Lazy<Transitions> + @Mock private lateinit var transitions: Transitions + @Mock private lateinit var mockTransitionBinder: IBinder + + private lateinit var transitionObserver: TaskStackTransitionObserver + + @Before + fun setUp() { + MockitoAnnotations.initMocks(this) + shellInit = Mockito.spy(ShellInit(testExecutor)) + whenever(transitionsLazy.get()).thenReturn(transitions) + transitionObserver = TaskStackTransitionObserver(transitionsLazy, shellInit) + if (Transitions.ENABLE_SHELL_TRANSITIONS) { + val initRunnableCaptor = ArgumentCaptor.forClass(Runnable::class.java) + verify(shellInit) + .addInitCallback(initRunnableCaptor.capture(), same(transitionObserver)) + initRunnableCaptor.value.run() + } else { + transitionObserver.onInit() + } + } + + @Test + @EnableFlags(Flags.FLAG_ENABLE_TASK_STACK_OBSERVER_IN_SHELL) + fun testRegistersObserverAtInit() { + verify(transitions).registerObserver(same(transitionObserver)) + } + + @Test + @EnableFlags(Flags.FLAG_ENABLE_TASK_STACK_OBSERVER_IN_SHELL) + fun taskCreated_freeformWindow_listenerNotified() { + val listener = TestListener() + val executor = TestShellExecutor() + transitionObserver.addTaskStackTransitionObserverListener(listener, executor) + val change = + createChange( + WindowManager.TRANSIT_OPEN, + createTaskInfo(1, WindowConfiguration.WINDOWING_MODE_FREEFORM) + ) + val transitionInfo = + TransitionInfoBuilder(WindowManager.TRANSIT_OPEN, 0).addChange(change).build() + + callOnTransitionReady(transitionInfo) + callOnTransitionFinished() + executor.flushAll() + + assertThat(listener.taskInfoToBeNotified.taskId).isEqualTo(change.taskInfo?.taskId) + assertThat(listener.taskInfoToBeNotified.windowingMode) + .isEqualTo(change.taskInfo?.windowingMode) + } + + @Test + @EnableFlags(Flags.FLAG_ENABLE_TASK_STACK_OBSERVER_IN_SHELL) + fun taskCreated_fullscreenWindow_listenerNotNotified() { + val listener = TestListener() + val executor = TestShellExecutor() + transitionObserver.addTaskStackTransitionObserverListener(listener, executor) + val change = + createChange( + WindowManager.TRANSIT_OPEN, + createTaskInfo(1, WindowConfiguration.WINDOWING_MODE_FULLSCREEN) + ) + val transitionInfo = + TransitionInfoBuilder(WindowManager.TRANSIT_OPEN, 0).addChange(change).build() + + callOnTransitionReady(transitionInfo) + callOnTransitionFinished() + executor.flushAll() + + assertThat(listener.taskInfoToBeNotified.taskId).isEqualTo(0) + assertThat(listener.taskInfoToBeNotified.windowingMode) + .isEqualTo(WindowConfiguration.WINDOWING_MODE_UNDEFINED) + } + + @Test + @EnableFlags(Flags.FLAG_ENABLE_TASK_STACK_OBSERVER_IN_SHELL) + fun taskCreated_freeformWindowOnTopOfFreeform_listenerNotified() { + val listener = TestListener() + val executor = TestShellExecutor() + transitionObserver.addTaskStackTransitionObserverListener(listener, executor) + val freeformOpenChange = + createChange( + WindowManager.TRANSIT_OPEN, + createTaskInfo(1, WindowConfiguration.WINDOWING_MODE_FREEFORM) + ) + val freeformReorderChange = + createChange( + WindowManager.TRANSIT_TO_BACK, + createTaskInfo(2, WindowConfiguration.WINDOWING_MODE_FREEFORM) + ) + val transitionInfo = + TransitionInfoBuilder(WindowManager.TRANSIT_OPEN, 0) + .addChange(freeformOpenChange) + .addChange(freeformReorderChange) + .build() + + callOnTransitionReady(transitionInfo) + callOnTransitionFinished() + executor.flushAll() + + assertThat(listener.taskInfoToBeNotified.taskId) + .isEqualTo(freeformOpenChange.taskInfo?.taskId) + assertThat(listener.taskInfoToBeNotified.windowingMode) + .isEqualTo(freeformOpenChange.taskInfo?.windowingMode) + } + + class TestListener : TaskStackTransitionObserver.TaskStackTransitionObserverListener { + var taskInfoToBeNotified = ActivityManager.RunningTaskInfo() + + override fun onTaskMovedToFrontThroughTransition( + taskInfo: ActivityManager.RunningTaskInfo + ) { + taskInfoToBeNotified = taskInfo + } + } + + /** Simulate calling the onTransitionReady() method */ + private fun callOnTransitionReady(transitionInfo: TransitionInfo) { + val startT = Mockito.mock(SurfaceControl.Transaction::class.java) + val finishT = Mockito.mock(SurfaceControl.Transaction::class.java) + + transitionObserver.onTransitionReady(mockTransitionBinder, transitionInfo, startT, finishT) + } + + /** Simulate calling the onTransitionFinished() method */ + private fun callOnTransitionFinished() { + transitionObserver.onTransitionFinished(mockTransitionBinder, false) + } + + companion object { + fun createTaskInfo(taskId: Int, windowingMode: Int): ActivityManager.RunningTaskInfo { + val taskInfo = ActivityManager.RunningTaskInfo() + taskInfo.taskId = taskId + taskInfo.configuration.windowConfiguration.windowingMode = windowingMode + + return taskInfo + } + + fun createChange( + mode: Int, + taskInfo: ActivityManager.RunningTaskInfo + ): TransitionInfo.Change { + val change = + TransitionInfo.Change( + WindowContainerToken(Mockito.mock(IWindowContainerToken::class.java)), + Mockito.mock(SurfaceControl::class.java) + ) + change.mode = mode + change.taskInfo = taskInfo + return change + } + } +} 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 a731e5394bdf..46c158908226 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 @@ -22,11 +22,16 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW; import static android.platform.test.flag.junit.SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT; import static android.view.WindowInsetsController.APPEARANCE_TRANSPARENT_CAPTION_BAR_BACKGROUND; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession; +import static com.android.wm.shell.MockSurfaceControlHelper.createMockSurfaceControlTransaction; + import static com.google.common.truth.Truth.assertThat; import static org.mockito.Mockito.any; import static org.mockito.Mockito.anyInt; import static org.mockito.Mockito.doReturn; +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; @@ -35,7 +40,7 @@ import android.app.ActivityManager; import android.content.ComponentName; import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; -import android.content.res.Configuration; +import android.content.pm.PackageManager; import android.content.res.Resources; import android.content.res.TypedArray; import android.os.Handler; @@ -45,15 +50,22 @@ import android.platform.test.annotations.EnableFlags; import android.platform.test.flag.junit.SetFlagsRule; import android.testing.AndroidTestingRunner; import android.testing.TestableContext; +import android.view.AttachedSurfaceControl; import android.view.Choreographer; import android.view.Display; +import android.view.GestureDetector; +import android.view.InsetsState; +import android.view.MotionEvent; import android.view.SurfaceControl; import android.view.SurfaceControlViewHost; +import android.view.View; import android.view.WindowManager; import android.window.WindowContainerTransaction; +import androidx.annotation.Nullable; import androidx.test.filters.SmallTest; +import com.android.dx.mockito.inline.extended.StaticMockitoSession; import com.android.internal.R; import com.android.window.flags.Flags; import com.android.wm.shell.RootTaskDisplayAreaOrganizer; @@ -62,14 +74,18 @@ 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.SyncTransactionQueue; +import com.android.wm.shell.shared.DesktopModeStatus; import com.android.wm.shell.windowdecor.WindowDecoration.RelayoutParams; +import org.junit.After; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; import org.mockito.Mock; +import org.mockito.quality.Strictness; import java.util.function.Supplier; @@ -106,18 +122,26 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase { @Mock private Supplier<SurfaceControl.Transaction> mMockTransactionSupplier; @Mock - private SurfaceControl.Transaction mMockTransaction; - @Mock private SurfaceControl mMockSurfaceControl; @Mock private SurfaceControlViewHost mMockSurfaceControlViewHost; @Mock + private AttachedSurfaceControl mMockRootSurfaceControl; + @Mock private WindowDecoration.SurfaceControlViewHostFactory mMockSurfaceControlViewHostFactory; @Mock private TypedArray mMockRoundedCornersRadiusArray; - private final Configuration mConfiguration = new Configuration(); + @Mock + private TestTouchEventListener mMockTouchEventListener; + @Mock + private DesktopModeWindowDecoration.ExclusionRegionListener mMockExclusionRegionListener; + @Mock + private PackageManager mMockPackageManager; + private final InsetsState mInsetsState = new InsetsState(); + private SurfaceControl.Transaction mMockTransaction; + private StaticMockitoSession mMockitoSession; private TestableContext mTestableContext; /** Set up run before test class. */ @@ -131,11 +155,29 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase { @Before public void setUp() { + mMockitoSession = mockitoSession() + .strictness(Strictness.LENIENT) + .spyStatic(DesktopModeStatus.class) + .startMocking(); + when(DesktopModeStatus.useDesktopOverrideDensity()).thenReturn(false); doReturn(mMockSurfaceControlViewHost).when(mMockSurfaceControlViewHostFactory).create( any(), any(), any()); + when(mMockSurfaceControlViewHost.getRootSurfaceControl()) + .thenReturn(mMockRootSurfaceControl); + mMockTransaction = createMockSurfaceControlTransaction(); doReturn(mMockTransaction).when(mMockTransactionSupplier).get(); mTestableContext = new TestableContext(mContext); mTestableContext.ensureTestableResources(); + mContext.setMockPackageManager(mMockPackageManager); + when(mMockPackageManager.getApplicationLabel(any())).thenReturn("applicationLabel"); + final Display defaultDisplay = mock(Display.class); + doReturn(defaultDisplay).when(mMockDisplayController).getDisplay(Display.DEFAULT_DISPLAY); + doReturn(mInsetsState).when(mMockDisplayController).getInsetsState(anyInt()); + } + + @After + public void tearDown() { + mMockitoSession.finishMocking(); } @Test @@ -206,6 +248,7 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase { @Test @DisableFlags(Flags.FLAG_ENABLE_APP_HEADER_WITH_TASK_DENSITY) public void updateRelayoutParams_appHeader_usesSystemDensity() { + when(DesktopModeStatus.useDesktopOverrideDensity()).thenReturn(true); final int systemDensity = mTestableContext.getOrCreateTestableResources().getResources() .getConfiguration().densityDpi; final int customTaskDensity = systemDensity + 300; @@ -323,6 +366,99 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase { assertThat(hasNoInputChannelFeature(relayoutParams)).isTrue(); } + @Test + public void relayout_fullscreenTask_appliesTransactionImmediately() { + final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true); + final DesktopModeWindowDecoration spyWindowDecor = spy(createWindowDecoration(taskInfo)); + taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN); + + spyWindowDecor.relayout(taskInfo); + + verify(mMockTransaction).apply(); + verify(mMockRootSurfaceControl, never()).applyTransactionOnDraw(any()); + } + + @Test + public void relayout_freeformTask_appliesTransactionOnDraw() { + final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true); + final DesktopModeWindowDecoration spyWindowDecor = spy(createWindowDecoration(taskInfo)); + taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FREEFORM); + // Make non-resizable to avoid dealing with input-permissions (MONITOR_INPUT) + taskInfo.isResizeable = false; + + spyWindowDecor.relayout(taskInfo); + + verify(mMockTransaction, never()).apply(); + verify(mMockRootSurfaceControl).applyTransactionOnDraw(mMockTransaction); + } + + @Test + public void relayout_fullscreenTask_doesNotCreateViewHostImmediately() { + final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true); + final DesktopModeWindowDecoration spyWindowDecor = spy(createWindowDecoration(taskInfo)); + taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN); + + spyWindowDecor.relayout(taskInfo); + + verify(mMockSurfaceControlViewHostFactory, never()).create(any(), any(), any()); + } + + @Test + public void relayout_fullscreenTask_postsViewHostCreation() { + final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true); + final DesktopModeWindowDecoration spyWindowDecor = spy(createWindowDecoration(taskInfo)); + taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN); + + ArgumentCaptor<Runnable> runnableArgument = ArgumentCaptor.forClass(Runnable.class); + spyWindowDecor.relayout(taskInfo); + + verify(mMockHandler).post(runnableArgument.capture()); + runnableArgument.getValue().run(); + verify(mMockSurfaceControlViewHostFactory).create(any(), any(), any()); + } + + @Test + public void relayout_freeformTask_createsViewHostImmediately() { + final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true); + final DesktopModeWindowDecoration spyWindowDecor = spy(createWindowDecoration(taskInfo)); + taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FREEFORM); + // Make non-resizable to avoid dealing with input-permissions (MONITOR_INPUT) + taskInfo.isResizeable = false; + + spyWindowDecor.relayout(taskInfo); + + verify(mMockSurfaceControlViewHostFactory).create(any(), any(), any()); + verify(mMockHandler, never()).post(any()); + } + + @Test + public void relayout_removesExistingHandlerCallback() { + final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true); + final DesktopModeWindowDecoration spyWindowDecor = spy(createWindowDecoration(taskInfo)); + taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN); + ArgumentCaptor<Runnable> runnableArgument = ArgumentCaptor.forClass(Runnable.class); + spyWindowDecor.relayout(taskInfo); + verify(mMockHandler).post(runnableArgument.capture()); + + spyWindowDecor.relayout(taskInfo); + + verify(mMockHandler).removeCallbacks(runnableArgument.getValue()); + } + + @Test + public void close_removesExistingHandlerCallback() { + final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true); + final DesktopModeWindowDecoration spyWindowDecor = spy(createWindowDecoration(taskInfo)); + taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN); + ArgumentCaptor<Runnable> runnableArgument = ArgumentCaptor.forClass(Runnable.class); + spyWindowDecor.relayout(taskInfo); + verify(mMockHandler).post(runnableArgument.capture()); + + spyWindowDecor.close(); + + verify(mMockHandler).removeCallbacks(runnableArgument.getValue()); + } + private void fillRoundedCornersResources(int fillValue) { when(mMockRoundedCornersRadiusArray.getDimensionPixelSize(anyInt(), anyInt())) .thenReturn(fillValue); @@ -343,12 +479,16 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase { private DesktopModeWindowDecoration createWindowDecoration( ActivityManager.RunningTaskInfo taskInfo) { - return new DesktopModeWindowDecoration(mContext, mMockDisplayController, - mMockShellTaskOrganizer, taskInfo, mMockSurfaceControl, + DesktopModeWindowDecoration windowDecor = new DesktopModeWindowDecoration(mContext, + mMockDisplayController, mMockShellTaskOrganizer, taskInfo, mMockSurfaceControl, mMockHandler, mMockChoreographer, mMockSyncQueue, mMockRootTaskDisplayAreaOrganizer, SurfaceControl.Builder::new, mMockTransactionSupplier, WindowContainerTransaction::new, SurfaceControl::new, mMockSurfaceControlViewHostFactory); + windowDecor.setCaptionListeners(mMockTouchEventListener, mMockTouchEventListener, + mMockTouchEventListener, mMockTouchEventListener); + windowDecor.setExclusionRegionListener(mMockExclusionRegionListener); + return windowDecor; } private ActivityManager.RunningTaskInfo createTaskInfo(boolean visible) { @@ -373,4 +513,32 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase { return (params.mInputFeatures & WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) != 0; } + + private static class TestTouchEventListener extends GestureDetector.SimpleOnGestureListener + implements View.OnClickListener, View.OnTouchListener, View.OnLongClickListener, + View.OnGenericMotionListener, DragDetector.MotionEventHandler { + + @Override + public void onClick(View v) {} + + @Override + public boolean onGenericMotion(View v, MotionEvent event) { + return false; + } + + @Override + public boolean onLongClick(View v) { + return false; + } + + @Override + public boolean onTouch(View v, MotionEvent event) { + return false; + } + + @Override + public boolean handleMotionEvent(@Nullable View v, MotionEvent ev) { + return false; + } + } } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DragPositioningCallbackUtilityTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DragPositioningCallbackUtilityTest.kt index e6fabcfec58a..f750e6b9a6fe 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DragPositioningCallbackUtilityTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DragPositioningCallbackUtilityTest.kt @@ -16,14 +16,20 @@ package com.android.wm.shell.windowdecor import android.app.ActivityManager +import android.content.Context +import android.content.res.Resources import android.graphics.PointF import android.graphics.Rect import android.os.IBinder +import android.platform.test.annotations.EnableFlags import android.testing.AndroidTestingRunner import android.view.Display import android.window.WindowContainerToken +import com.android.window.flags.Flags +import com.android.wm.shell.R import com.android.wm.shell.common.DisplayController import com.android.wm.shell.common.DisplayLayout +import com.android.wm.shell.shared.DesktopModeStatus import com.android.wm.shell.windowdecor.DragPositioningCallback.CTRL_TYPE_BOTTOM import com.android.wm.shell.windowdecor.DragPositioningCallback.CTRL_TYPE_RIGHT import com.android.wm.shell.windowdecor.DragPositioningCallback.CTRL_TYPE_TOP @@ -33,8 +39,8 @@ import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mock -import org.mockito.Mockito.`when` as whenever import org.mockito.Mockito.any +import org.mockito.Mockito.`when` as whenever import org.mockito.MockitoAnnotations /** @@ -57,6 +63,10 @@ class DragPositioningCallbackUtilityTest { private lateinit var mockDisplayLayout: DisplayLayout @Mock private lateinit var mockDisplay: Display + @Mock + private lateinit var mockContext: Context + @Mock + private lateinit var mockResources: Resources @Before fun setup() { @@ -69,16 +79,15 @@ class DragPositioningCallbackUtilityTest { (i.arguments.first() as Rect).set(STABLE_BOUNDS) } - mockWindowDecoration.mTaskInfo = ActivityManager.RunningTaskInfo().apply { - taskId = TASK_ID - token = taskToken - minWidth = MIN_WIDTH - minHeight = MIN_HEIGHT - defaultMinSize = DEFAULT_MIN - displayId = DISPLAY_ID - configuration.windowConfiguration.setBounds(STARTING_BOUNDS) - } + initializeTaskInfo() mockWindowDecoration.mDisplay = mockDisplay + mockWindowDecoration.mDecorWindowContext = mockContext + whenever(mockContext.getResources()).thenReturn(mockResources) + whenever(mockWindowDecoration.mDecorWindowContext.resources).thenReturn(mockResources) + whenever(mockResources.getDimensionPixelSize(R.dimen.desktop_mode_minimum_window_width)) + .thenReturn(DESKTOP_MODE_MIN_WIDTH) + whenever(mockResources.getDimensionPixelSize(R.dimen.desktop_mode_minimum_window_height)) + .thenReturn(DESKTOP_MODE_MIN_HEIGHT) whenever(mockDisplay.displayId).thenAnswer { DISPLAY_ID } } @@ -93,8 +102,8 @@ class DragPositioningCallbackUtilityTest { val delta = DragPositioningCallbackUtility.calculateDelta(newX, newY, startingPoint) DragPositioningCallbackUtility.changeBounds(CTRL_TYPE_RIGHT or CTRL_TYPE_TOP, - repositionTaskBounds, STARTING_BOUNDS, STABLE_BOUNDS, delta, - mockDisplayController, mockWindowDecoration) + repositionTaskBounds, STARTING_BOUNDS, STABLE_BOUNDS, delta, mockDisplayController, + mockWindowDecoration) assertThat(repositionTaskBounds.left).isEqualTo(STARTING_BOUNDS.left) assertThat(repositionTaskBounds.top).isEqualTo(STARTING_BOUNDS.top) @@ -113,8 +122,8 @@ class DragPositioningCallbackUtilityTest { val delta = DragPositioningCallbackUtility.calculateDelta(newX, newY, startingPoint) DragPositioningCallbackUtility.changeBounds(CTRL_TYPE_RIGHT or CTRL_TYPE_TOP, - repositionTaskBounds, STARTING_BOUNDS, STABLE_BOUNDS, delta, - mockDisplayController, mockWindowDecoration) + repositionTaskBounds, STARTING_BOUNDS, STABLE_BOUNDS, delta, mockDisplayController, + mockWindowDecoration) assertThat(repositionTaskBounds.left).isEqualTo(STARTING_BOUNDS.left) assertThat(repositionTaskBounds.top).isEqualTo(STARTING_BOUNDS.top + 5) @@ -127,14 +136,14 @@ class DragPositioningCallbackUtilityTest { val startingPoint = PointF(STARTING_BOUNDS.right.toFloat(), STARTING_BOUNDS.top.toFloat()) val repositionTaskBounds = Rect(STARTING_BOUNDS) - // Resize to width of 95px and width of -5px with minimum of 10px + // Resize to width of 95px and height of -5px with minimum of 10px val newX = STARTING_BOUNDS.right.toFloat() - 5 val newY = STARTING_BOUNDS.top.toFloat() + 105 val delta = DragPositioningCallbackUtility.calculateDelta(newX, newY, startingPoint) DragPositioningCallbackUtility.changeBounds(CTRL_TYPE_RIGHT or CTRL_TYPE_TOP, - repositionTaskBounds, STARTING_BOUNDS, STABLE_BOUNDS, delta, - mockDisplayController, mockWindowDecoration) + repositionTaskBounds, STARTING_BOUNDS, STABLE_BOUNDS, delta, mockDisplayController, + mockWindowDecoration) assertThat(repositionTaskBounds.left).isEqualTo(STARTING_BOUNDS.left) assertThat(repositionTaskBounds.top).isEqualTo(STARTING_BOUNDS.top) @@ -153,8 +162,8 @@ class DragPositioningCallbackUtilityTest { val delta = DragPositioningCallbackUtility.calculateDelta(newX, newY, startingPoint) DragPositioningCallbackUtility.changeBounds(CTRL_TYPE_RIGHT or CTRL_TYPE_TOP, - repositionTaskBounds, STARTING_BOUNDS, STABLE_BOUNDS, delta, - mockDisplayController, mockWindowDecoration) + repositionTaskBounds, STARTING_BOUNDS, STABLE_BOUNDS, delta, mockDisplayController, + mockWindowDecoration) assertThat(repositionTaskBounds.left).isEqualTo(STARTING_BOUNDS.left) assertThat(repositionTaskBounds.top).isEqualTo(STARTING_BOUNDS.top + 80) assertThat(repositionTaskBounds.right).isEqualTo(STARTING_BOUNDS.right - 80) @@ -172,8 +181,8 @@ class DragPositioningCallbackUtilityTest { val delta = DragPositioningCallbackUtility.calculateDelta(newX, newY, startingPoint) DragPositioningCallbackUtility.changeBounds(CTRL_TYPE_RIGHT or CTRL_TYPE_TOP, - repositionTaskBounds, STARTING_BOUNDS, STABLE_BOUNDS, delta, - mockDisplayController, mockWindowDecoration) + repositionTaskBounds, STARTING_BOUNDS, STABLE_BOUNDS, delta, mockDisplayController, + mockWindowDecoration) assertThat(repositionTaskBounds.left).isEqualTo(STARTING_BOUNDS.left) assertThat(repositionTaskBounds.top).isEqualTo(STARTING_BOUNDS.top) assertThat(repositionTaskBounds.right).isEqualTo(STARTING_BOUNDS.right) @@ -196,14 +205,13 @@ class DragPositioningCallbackUtilityTest { assertThat(repositionTaskBounds.left).isEqualTo(validDragArea.left) assertThat(repositionTaskBounds.top).isEqualTo(validDragArea.bottom) assertThat(repositionTaskBounds.right) - .isEqualTo(validDragArea.left + STARTING_BOUNDS.width()) + .isEqualTo(validDragArea.left + STARTING_BOUNDS.width()) assertThat(repositionTaskBounds.bottom) - .isEqualTo(validDragArea.bottom + STARTING_BOUNDS.height()) + .isEqualTo(validDragArea.bottom + STARTING_BOUNDS.height()) } @Test fun testChangeBounds_toDisallowedBounds_freezesAtLimit() { - var hasMoved = false val startingPoint = PointF(STARTING_BOUNDS.right.toFloat(), STARTING_BOUNDS.bottom.toFloat()) val repositionTaskBounds = Rect(STARTING_BOUNDS) @@ -212,26 +220,127 @@ class DragPositioningCallbackUtilityTest { var newY = STARTING_BOUNDS.bottom.toFloat() + 10 var delta = DragPositioningCallbackUtility.calculateDelta(newX, newY, startingPoint) assertTrue(DragPositioningCallbackUtility.changeBounds(CTRL_TYPE_RIGHT or CTRL_TYPE_BOTTOM, - repositionTaskBounds, STARTING_BOUNDS, STABLE_BOUNDS, delta, - mockDisplayController, mockWindowDecoration)) - hasMoved = true + repositionTaskBounds, STARTING_BOUNDS, STABLE_BOUNDS, delta, mockDisplayController, + mockWindowDecoration)) // Resize width to 120px, height to disallowed area which should not result in a change. newX += 10 newY = DISALLOWED_RESIZE_AREA.top.toFloat() delta = DragPositioningCallbackUtility.calculateDelta(newX, newY, startingPoint) assertTrue(DragPositioningCallbackUtility.changeBounds(CTRL_TYPE_RIGHT or CTRL_TYPE_BOTTOM, - repositionTaskBounds, STARTING_BOUNDS, STABLE_BOUNDS, delta, - mockDisplayController, mockWindowDecoration)) + repositionTaskBounds, STARTING_BOUNDS, STABLE_BOUNDS, delta, mockDisplayController, + mockWindowDecoration)) assertThat(repositionTaskBounds.left).isEqualTo(STARTING_BOUNDS.left) assertThat(repositionTaskBounds.top).isEqualTo(STARTING_BOUNDS.top) assertThat(repositionTaskBounds.right).isEqualTo(STARTING_BOUNDS.right + 20) assertThat(repositionTaskBounds.bottom).isEqualTo(STARTING_BOUNDS.bottom + 10) } + @Test + @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_SIZE_CONSTRAINTS) + fun taskMinWidthHeightUndefined_changeBoundsInDesktopModeLessThanMin_shouldNotChangeBounds() { + whenever(DesktopModeStatus.canEnterDesktopMode(mockContext)).thenReturn(true) + initializeTaskInfo(taskMinWidth = -1, taskMinHeight = -1) + val startingPoint = + PointF(STARTING_BOUNDS.right.toFloat(), STARTING_BOUNDS.bottom.toFloat()) + val repositionTaskBounds = Rect(STARTING_BOUNDS) + // Shrink height and width to 1px. The default allowed width and height are defined in + // R.dimen.desktop_mode_minimum_window_width and R.dimen.desktop_mode_minimum_window_height + val newX = STARTING_BOUNDS.right.toFloat() - 99 + val newY = STARTING_BOUNDS.bottom.toFloat() - 99 + val delta = DragPositioningCallbackUtility.calculateDelta(newX, newY, startingPoint) + + DragPositioningCallbackUtility.changeBounds(CTRL_TYPE_RIGHT or CTRL_TYPE_BOTTOM, + repositionTaskBounds, STARTING_BOUNDS, STABLE_BOUNDS, delta, mockDisplayController, + mockWindowDecoration) + assertThat(repositionTaskBounds.left).isEqualTo(STARTING_BOUNDS.left) + assertThat(repositionTaskBounds.top).isEqualTo(STARTING_BOUNDS.top) + assertThat(repositionTaskBounds.right).isEqualTo(STARTING_BOUNDS.right) + assertThat(repositionTaskBounds.bottom).isEqualTo(STARTING_BOUNDS.bottom) + } + + @Test + @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_SIZE_CONSTRAINTS) + fun taskMinWidthHeightUndefined_changeBoundsInDesktopModeAllowedSize_shouldChangeBounds() { + whenever(DesktopModeStatus.canEnterDesktopMode(mockContext)).thenReturn(true) + initializeTaskInfo(taskMinWidth = -1, taskMinHeight = -1) + val startingPoint = + PointF(STARTING_BOUNDS.right.toFloat(), STARTING_BOUNDS.bottom.toFloat()) + val repositionTaskBounds = Rect(STARTING_BOUNDS) + // Shrink height and width to 20px. The default allowed width and height are defined in + // R.dimen.desktop_mode_minimum_window_width and R.dimen.desktop_mode_minimum_window_height + val newX = STARTING_BOUNDS.right.toFloat() - 80 + val newY = STARTING_BOUNDS.bottom.toFloat() - 80 + val delta = DragPositioningCallbackUtility.calculateDelta(newX, newY, startingPoint) + + DragPositioningCallbackUtility.changeBounds(CTRL_TYPE_RIGHT or CTRL_TYPE_BOTTOM, + repositionTaskBounds, STARTING_BOUNDS, STABLE_BOUNDS, delta, mockDisplayController, + mockWindowDecoration) + assertThat(repositionTaskBounds.left).isEqualTo(STARTING_BOUNDS.left) + assertThat(repositionTaskBounds.top).isEqualTo(STARTING_BOUNDS.top) + assertThat(repositionTaskBounds.right).isEqualTo(STARTING_BOUNDS.right - 80) + assertThat(repositionTaskBounds.bottom).isEqualTo(STARTING_BOUNDS.bottom - 80) + } + + @Test + fun taskMinWidthHeightUndefined_changeBoundsLessThanDefaultMinSize_shouldNotChangeBounds() { + initializeTaskInfo(taskMinWidth = -1, taskMinHeight = -1) + val startingPoint = + PointF(STARTING_BOUNDS.right.toFloat(), STARTING_BOUNDS.bottom.toFloat()) + val repositionTaskBounds = Rect(STARTING_BOUNDS) + // Shrink height and width to 1px. The default allowed width and height are defined in the + // defaultMinSize of the TaskInfo. + val newX = STARTING_BOUNDS.right.toFloat() - 99 + val newY = STARTING_BOUNDS.bottom.toFloat() - 99 + val delta = DragPositioningCallbackUtility.calculateDelta(newX, newY, startingPoint) + + DragPositioningCallbackUtility.changeBounds(CTRL_TYPE_RIGHT or CTRL_TYPE_BOTTOM, + repositionTaskBounds, STARTING_BOUNDS, STABLE_BOUNDS, delta, mockDisplayController, + mockWindowDecoration) + assertThat(repositionTaskBounds.left).isEqualTo(STARTING_BOUNDS.left) + assertThat(repositionTaskBounds.top).isEqualTo(STARTING_BOUNDS.top) + assertThat(repositionTaskBounds.right).isEqualTo(STARTING_BOUNDS.right) + assertThat(repositionTaskBounds.bottom).isEqualTo(STARTING_BOUNDS.bottom) + } + + @Test + fun taskMinWidthHeightUndefined_changeBoundsToAnAllowedSize_shouldChangeBounds() { + initializeTaskInfo(taskMinWidth = -1, taskMinHeight = -1) + val startingPoint = + PointF(STARTING_BOUNDS.right.toFloat(), STARTING_BOUNDS.bottom.toFloat()) + val repositionTaskBounds = Rect(STARTING_BOUNDS) + // Shrink height and width to 50px. The default allowed width and height are defined in the + // defaultMinSize of the TaskInfo. + val newX = STARTING_BOUNDS.right.toFloat() - 50 + val newY = STARTING_BOUNDS.bottom.toFloat() - 50 + val delta = DragPositioningCallbackUtility.calculateDelta(newX, newY, startingPoint) + + DragPositioningCallbackUtility.changeBounds(CTRL_TYPE_RIGHT or CTRL_TYPE_BOTTOM, + repositionTaskBounds, STARTING_BOUNDS, STABLE_BOUNDS, delta, mockDisplayController, + mockWindowDecoration) + assertThat(repositionTaskBounds.left).isEqualTo(STARTING_BOUNDS.left) + assertThat(repositionTaskBounds.top).isEqualTo(STARTING_BOUNDS.top) + assertThat(repositionTaskBounds.right).isEqualTo(STARTING_BOUNDS.right - 50) + assertThat(repositionTaskBounds.bottom).isEqualTo(STARTING_BOUNDS.bottom - 50) + } + + private fun initializeTaskInfo(taskMinWidth: Int = MIN_WIDTH, taskMinHeight: Int = MIN_HEIGHT) { + mockWindowDecoration.mTaskInfo = ActivityManager.RunningTaskInfo().apply { + taskId = TASK_ID + token = taskToken + minWidth = taskMinWidth + minHeight = taskMinHeight + defaultMinSize = DEFAULT_MIN + displayId = DISPLAY_ID + configuration.windowConfiguration.setBounds(STARTING_BOUNDS) + } + } + companion object { private const val TASK_ID = 5 private const val MIN_WIDTH = 10 private const val MIN_HEIGHT = 10 + private const val DESKTOP_MODE_MIN_WIDTH = 20 + private const val DESKTOP_MODE_MIN_HEIGHT = 20 private const val DENSITY_DPI = 20 private const val DEFAULT_MIN = 40 private const val DISPLAY_ID = 1 diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/FluidResizeTaskPositionerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/FluidResizeTaskPositionerTest.kt index 9174556d091b..666750485ef2 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/FluidResizeTaskPositionerTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/FluidResizeTaskPositionerTest.kt @@ -2,6 +2,8 @@ package com.android.wm.shell.windowdecor import android.app.ActivityManager import android.app.WindowConfiguration +import android.content.Context +import android.content.res.Resources import android.graphics.Point import android.graphics.Rect import android.os.IBinder @@ -17,6 +19,7 @@ import android.window.WindowContainerToken import android.window.WindowContainerTransaction import android.window.WindowContainerTransaction.Change.CHANGE_DRAG_RESIZING import androidx.test.filters.SmallTest +import com.android.wm.shell.R import com.android.wm.shell.ShellTaskOrganizer import com.android.wm.shell.ShellTestCase import com.android.wm.shell.common.DisplayController @@ -83,7 +86,10 @@ class FluidResizeTaskPositionerTest : ShellTestCase() { private lateinit var mockTransaction: SurfaceControl.Transaction @Mock private lateinit var mockTransitionBinder: IBinder - + @Mock + private lateinit var mockContext: Context + @Mock + private lateinit var mockResources: Resources private lateinit var taskPositioner: FluidResizeTaskPositioner @Before @@ -119,6 +125,12 @@ class FluidResizeTaskPositionerTest : ShellTestCase() { } `when`(mockWindowDecoration.calculateValidDragArea()).thenReturn(VALID_DRAG_AREA) mockWindowDecoration.mDisplay = mockDisplay + mockWindowDecoration.mDecorWindowContext = mockContext + whenever(mockWindowDecoration.mDecorWindowContext.resources).thenReturn(mockResources) + whenever(mockResources.getDimensionPixelSize(R.dimen.desktop_mode_minimum_window_width)) + .thenReturn(DESKTOP_MODE_MIN_WIDTH) + whenever(mockResources.getDimensionPixelSize(R.dimen.desktop_mode_minimum_window_height)) + .thenReturn(DESKTOP_MODE_MIN_HEIGHT) whenever(mockDisplay.displayId).thenAnswer { DISPLAY_ID } whenever(mockTransitions.startTransition(anyInt(), any(), any())) .doReturn(mockTransitionBinder) @@ -788,6 +800,8 @@ class FluidResizeTaskPositionerTest : ShellTestCase() { private const val TASK_ID = 5 private const val MIN_WIDTH = 10 private const val MIN_HEIGHT = 10 + private const val DESKTOP_MODE_MIN_WIDTH = 20 + private const val DESKTOP_MODE_MIN_HEIGHT = 20 private const val DENSITY_DPI = 20 private const val DEFAULT_MIN = 40 private const val DISPLAY_ID = 1 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 new file mode 100644 index 000000000000..5582e0f46321 --- /dev/null +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/HandleMenuTest.kt @@ -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.wm.shell.windowdecor + +import android.app.ActivityManager +import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM +import android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN +import android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW +import android.graphics.Bitmap +import android.graphics.Color +import android.graphics.Rect +import android.platform.test.annotations.RequiresFlagsEnabled +import android.platform.test.flag.junit.CheckFlagsRule +import android.platform.test.flag.junit.DeviceFlagsValueProvider +import android.testing.AndroidTestingRunner +import android.testing.TestableLooper +import android.view.Display +import android.view.LayoutInflater +import android.view.SurfaceControl +import android.view.SurfaceControlViewHost +import android.view.View +import androidx.test.filters.SmallTest +import com.android.window.flags.Flags +import com.android.wm.shell.R +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.DisplayLayout +import com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT +import com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT +import com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_UNDEFINED +import com.android.wm.shell.splitscreen.SplitScreenController +import com.android.wm.shell.windowdecor.additionalviewcontainer.AdditionalSystemViewContainer +import com.android.wm.shell.windowdecor.additionalviewcontainer.AdditionalViewHostViewContainer +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.anyInt +import org.mockito.Mock +import org.mockito.Mockito.mock +import org.mockito.kotlin.any +import org.mockito.kotlin.whenever + +/** + * Tests for [HandleMenu]. + * + * Build/Install/Run: + * atest WMShellUnitTests:HandleMenuTest + */ +@SmallTest +@TestableLooper.RunWithLooper +@RunWith(AndroidTestingRunner::class) +class HandleMenuTest : ShellTestCase() { + @JvmField + @Rule + val mCheckFlagsRule: CheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule() + + @Mock + private lateinit var mockDesktopWindowDecoration: DesktopModeWindowDecoration + @Mock + private lateinit var onClickListener: View.OnClickListener + @Mock + private lateinit var onTouchListener: View.OnTouchListener + @Mock + private lateinit var appIcon: Bitmap + @Mock + private lateinit var appName: CharSequence + @Mock + private lateinit var displayController: DisplayController + @Mock + private lateinit var splitScreenController: SplitScreenController + @Mock + private lateinit var displayLayout: DisplayLayout + @Mock + private lateinit var mockSurfaceControlViewHost: SurfaceControlViewHost + + private lateinit var handleMenu: HandleMenu + + @Before + fun setUp() { + val mockAdditionalViewHostViewContainer = AdditionalViewHostViewContainer( + mock(SurfaceControl::class.java), + mockSurfaceControlViewHost, + ) { + SurfaceControl.Transaction() + } + val menuView = LayoutInflater.from(context).inflate( + R.layout.desktop_mode_window_decor_handle_menu, null) + whenever(mockDesktopWindowDecoration.addWindow( + anyInt(), any(), any(), any(), anyInt(), anyInt(), anyInt(), anyInt()) + ).thenReturn(mockAdditionalViewHostViewContainer) + whenever(mockAdditionalViewHostViewContainer.view).thenReturn(menuView) + whenever(displayController.getDisplayLayout(anyInt())).thenReturn(displayLayout) + whenever(displayLayout.width()).thenReturn(DISPLAY_BOUNDS.width()) + whenever(displayLayout.height()).thenReturn(DISPLAY_BOUNDS.height()) + whenever(displayLayout.isLandscape).thenReturn(true) + mockDesktopWindowDecoration.mDecorWindowContext = context + } + + @Test + @RequiresFlagsEnabled(Flags.FLAG_ENABLE_ADDITIONAL_WINDOWS_ABOVE_STATUS_BAR) + fun testFullscreenMenuUsesSystemViewContainer() { + createTaskInfo(WINDOWING_MODE_FULLSCREEN, SPLIT_POSITION_UNDEFINED) + val handleMenu = createAndShowHandleMenu() + assertTrue(handleMenu.mHandleMenuViewContainer is AdditionalSystemViewContainer) + // Verify menu is created at coordinates that, when added to WindowManager, + // show at the top-center of display. + assertTrue(handleMenu.mHandleMenuPosition.equals(16f, -512f)) + } + + @Test + @RequiresFlagsEnabled(Flags.FLAG_ENABLE_ADDITIONAL_WINDOWS_ABOVE_STATUS_BAR) + fun testFreeformMenu_usesViewHostViewContainer() { + createTaskInfo(WINDOWING_MODE_FREEFORM, SPLIT_POSITION_UNDEFINED) + handleMenu = createAndShowHandleMenu() + assertTrue(handleMenu.mHandleMenuViewContainer is AdditionalViewHostViewContainer) + // Verify menu is created near top-left of task. + assertTrue(handleMenu.mHandleMenuPosition.equals(12f, 8f)) + } + + @Test + @RequiresFlagsEnabled(Flags.FLAG_ENABLE_ADDITIONAL_WINDOWS_ABOVE_STATUS_BAR) + fun testSplitLeftMenu_usesSystemViewContainer() { + createTaskInfo(WINDOWING_MODE_MULTI_WINDOW, SPLIT_POSITION_TOP_OR_LEFT) + handleMenu = createAndShowHandleMenu() + assertTrue(handleMenu.mHandleMenuViewContainer is AdditionalSystemViewContainer) + // Verify menu is created at coordinates that, when added to WindowManager, + // show at the top of split left task. + assertTrue(handleMenu.mHandleMenuPosition.equals(-624f, -512f)) + } + + @Test + @RequiresFlagsEnabled(Flags.FLAG_ENABLE_ADDITIONAL_WINDOWS_ABOVE_STATUS_BAR) + fun testSplitRightMenu_usesSystemViewContainer() { + createTaskInfo(WINDOWING_MODE_MULTI_WINDOW, SPLIT_POSITION_BOTTOM_OR_RIGHT) + handleMenu = createAndShowHandleMenu() + assertTrue(handleMenu.mHandleMenuViewContainer is AdditionalSystemViewContainer) + // Verify menu is created at coordinates that, when added to WindowManager, + // show at the top of split right task. + assertTrue(handleMenu.mHandleMenuPosition.equals(656f, -512f)) + } + + private fun createTaskInfo(windowingMode: Int, splitPosition: Int) { + val taskDescriptionBuilder = ActivityManager.TaskDescription.Builder() + .setBackgroundColor(Color.YELLOW) + val bounds = when (windowingMode) { + WINDOWING_MODE_FULLSCREEN -> DISPLAY_BOUNDS + WINDOWING_MODE_FREEFORM -> FREEFORM_BOUNDS + WINDOWING_MODE_MULTI_WINDOW -> { + if (splitPosition == SPLIT_POSITION_TOP_OR_LEFT) { + SPLIT_LEFT_BOUNDS + } else { + SPLIT_RIGHT_BOUNDS + } + } + else -> error("Unsupported windowing mode") + } + mockDesktopWindowDecoration.mTaskInfo = TestRunningTaskInfoBuilder() + .setDisplayId(Display.DEFAULT_DISPLAY) + .setTaskDescriptionBuilder(taskDescriptionBuilder) + .setWindowingMode(windowingMode) + .setBounds(bounds) + .setVisible(true) + .build() + // Calculate captionX similar to how WindowDecoration calculates it. + whenever(mockDesktopWindowDecoration.captionX).thenReturn( + (mockDesktopWindowDecoration.mTaskInfo.configuration.windowConfiguration + .bounds.width() - context.resources.getDimensionPixelSize( + R.dimen.desktop_mode_fullscreen_decor_caption_width)) / 2) + whenever(splitScreenController.getSplitPosition(any())).thenReturn(splitPosition) + whenever(splitScreenController.getStageBounds(any(), any())).thenAnswer { + (it.arguments.first() as Rect).set(SPLIT_LEFT_BOUNDS) + } + } + + private fun createAndShowHandleMenu(): HandleMenu { + val layoutId = if (mockDesktopWindowDecoration.mTaskInfo.isFreeform) { + R.layout.desktop_mode_app_header + } else { + R.layout.desktop_mode_app_header + } + val handleMenu = HandleMenu(mockDesktopWindowDecoration, layoutId, + onClickListener, onTouchListener, appIcon, appName, displayController, + splitScreenController, true /* shouldShowWindowingPill */, + 50 /* captionHeight */ ) + handleMenu.show() + return handleMenu + } + + companion object { + private val DISPLAY_BOUNDS = Rect(0, 0, 2560, 1600) + private val FREEFORM_BOUNDS = Rect(500, 500, 2000, 1200) + private val SPLIT_LEFT_BOUNDS = Rect(0, 0, 1280, 1600) + private val SPLIT_RIGHT_BOUNDS = Rect(1280, 0, 2560, 1600) + } +} diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/ResizeVeilTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/ResizeVeilTest.kt index 5da57c50e6c1..a07be79579eb 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/ResizeVeilTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/ResizeVeilTest.kt @@ -150,7 +150,7 @@ class ResizeVeilTest : ShellTestCase() { fun showVeil() { val veil = createResizeVeil() - veil.showVeil(mockTransaction, mock(), Rect(0, 0, 100, 100), false /* fadeIn */) + veil.showVeil(mockTransaction, mock(), Rect(0, 0, 100, 100), taskInfo, false /* fadeIn */) verify(mockTransaction).show(mockResizeVeilSurface) verify(mockTransaction).show(mockBackgroundSurface) @@ -162,7 +162,7 @@ class ResizeVeilTest : ShellTestCase() { fun showVeil_displayUnavailable_doesNotShow() { val veil = createResizeVeil(withDisplayAvailable = false) - veil.showVeil(mockTransaction, mock(), Rect(0, 0, 100, 100), false /* fadeIn */) + veil.showVeil(mockTransaction, mock(), Rect(0, 0, 100, 100), taskInfo, false /* fadeIn */) verify(mockTransaction, never()).show(mockResizeVeilSurface) verify(mockTransaction, never()).show(mockBackgroundSurface) @@ -174,8 +174,8 @@ class ResizeVeilTest : ShellTestCase() { fun showVeil_alreadyVisible_doesNotShowAgain() { val veil = createResizeVeil() - veil.showVeil(mockTransaction, mock(), Rect(0, 0, 100, 100), false /* fadeIn */) - veil.showVeil(mockTransaction, mock(), Rect(0, 0, 100, 100), false /* fadeIn */) + veil.showVeil(mockTransaction, mock(), Rect(0, 0, 100, 100), taskInfo, false /* fadeIn */) + veil.showVeil(mockTransaction, mock(), Rect(0, 0, 100, 100), taskInfo, false /* fadeIn */) verify(mockTransaction, times(1)).show(mockResizeVeilSurface) verify(mockTransaction, times(1)).show(mockBackgroundSurface) @@ -188,7 +188,13 @@ class ResizeVeilTest : ShellTestCase() { val veil = createResizeVeil(parent = mock()) val newParent = mock<SurfaceControl>() - veil.showVeil(mockTransaction, newParent, Rect(0, 0, 100, 100), false /* fadeIn */) + veil.showVeil( + mockTransaction, + newParent, + Rect(0, 0, 100, 100), + taskInfo, + false /* fadeIn */ + ) verify(mockTransaction).reparent(mockResizeVeilSurface, newParent) } @@ -212,11 +218,11 @@ class ResizeVeilTest : ShellTestCase() { context, mockDisplayController, mockAppIcon, - taskInfo, parent, { mockTransaction }, mockSurfaceControlBuilderFactory, - mockSurfaceControlViewHostFactory + mockSurfaceControlViewHostFactory, + taskInfo ) } } 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 48310810e8c9..f3603e1d9b46 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 @@ -32,6 +32,7 @@ import static junit.framework.Assert.assertTrue; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertThrows; import static org.mockito.ArgumentMatchers.anyFloat; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.Mockito.any; @@ -76,6 +77,7 @@ import com.android.wm.shell.TestRunningTaskInfoBuilder; import com.android.wm.shell.common.DisplayController; import com.android.wm.shell.shared.DesktopModeStatus; import com.android.wm.shell.tests.R; +import com.android.wm.shell.windowdecor.additionalviewcontainer.AdditionalViewContainer; import org.junit.Before; import org.junit.Test; @@ -371,7 +373,7 @@ public class WindowDecorationTests extends ShellTestCase { } @Test - public void testAddWindow() { + public void testAddViewHostViewContainer() { final Display defaultDisplay = mock(Display.class); doReturn(defaultDisplay).when(mMockDisplayController) .getDisplay(Display.DEFAULT_DISPLAY); @@ -393,6 +395,7 @@ public class WindowDecorationTests extends ShellTestCase { final ActivityManager.RunningTaskInfo taskInfo = new TestRunningTaskInfoBuilder() .setDisplayId(Display.DEFAULT_DISPLAY) .setTaskDescriptionBuilder(taskDescriptionBuilder) + .setWindowingMode(WINDOWING_MODE_FREEFORM) .setBounds(TASK_BOUNDS) .setPositionInParent(TASK_POSITION_IN_PARENT.x, TASK_POSITION_IN_PARENT.y) .setVisible(true) @@ -407,7 +410,7 @@ public class WindowDecorationTests extends ShellTestCase { createMockSurfaceControlBuilder(additionalWindowSurface); mMockSurfaceControlBuilders.add(additionalWindowSurfaceBuilder); - WindowDecoration.AdditionalWindow additionalWindow = windowDecor.addTestWindow(); + windowDecor.addTestViewContainer(); verify(additionalWindowSurfaceBuilder).setContainerLayer(); verify(additionalWindowSurfaceBuilder).setParent(decorContainerSurface); @@ -421,12 +424,6 @@ public class WindowDecorationTests extends ShellTestCase { verify(mMockSurfaceControlAddWindowT).show(additionalWindowSurface); verify(mMockSurfaceControlViewHostFactory, Mockito.times(2)) .create(any(), eq(defaultDisplay), any()); - assertThat(additionalWindow.mWindowViewHost).isNotNull(); - - additionalWindow.releaseView(); - - assertThat(additionalWindow.mWindowViewHost).isNull(); - assertThat(additionalWindow.mWindowSurface).isNull(); } @Test @@ -832,6 +829,36 @@ public class WindowDecorationTests extends ShellTestCase { eq(mMockTaskSurface), anyInt(), anyInt()); } + @Test + public void updateViewHost_applyTransactionOnDrawIsTrue_surfaceControlIsUpdated() { + final TestWindowDecoration windowDecor = createWindowDecoration( + new TestRunningTaskInfoBuilder().build()); + mRelayoutParams.mApplyStartTransactionOnDraw = true; + + windowDecor.updateViewHost(mRelayoutParams, mMockSurfaceControlStartT, mRelayoutResult); + + verify(mMockRootSurfaceControl).applyTransactionOnDraw(mMockSurfaceControlStartT); + } + + @Test + public void updateViewHost_nullDrawTransaction_applyTransactionOnDrawIsTrue_throwsException() { + final TestWindowDecoration windowDecor = createWindowDecoration( + new TestRunningTaskInfoBuilder().build()); + mRelayoutParams.mApplyStartTransactionOnDraw = true; + + assertThrows(IllegalArgumentException.class, + () -> windowDecor.updateViewHost( + mRelayoutParams, null /* onDrawTransaction */, mRelayoutResult)); + } + + @Test + public void updateViewHost_nullDrawTransaction_applyTransactionOnDrawIsFalse_doesNotThrow() { + final TestWindowDecoration windowDecor = createWindowDecoration( + new TestRunningTaskInfoBuilder().build()); + mRelayoutParams.mApplyStartTransactionOnDraw = false; + + windowDecor.updateViewHost(mRelayoutParams, null /* onDrawTransaction */, mRelayoutResult); + } private TestWindowDecoration createWindowDecoration(ActivityManager.RunningTaskInfo taskInfo) { return new TestWindowDecoration(mContext, mMockDisplayController, mMockShellTaskOrganizer, @@ -905,16 +932,16 @@ public class WindowDecorationTests extends ShellTestCase { mMockWindowContainerTransaction, mMockView, mRelayoutResult); } - private WindowDecoration.AdditionalWindow addTestWindow() { + private AdditionalViewContainer addTestViewContainer() { final Resources resources = mDecorWindowContext.getResources(); - int width = loadDimensionPixelSize(resources, mCaptionMenuWidthId); - int height = loadDimensionPixelSize(resources, mRelayoutParams.mCaptionHeightId); - String name = "Test Window"; - WindowDecoration.AdditionalWindow additionalWindow = + final int width = loadDimensionPixelSize(resources, mCaptionMenuWidthId); + final int height = loadDimensionPixelSize(resources, mRelayoutParams.mCaptionHeightId); + final String name = "Test Window"; + final AdditionalViewContainer additionalViewContainer = addWindow(R.layout.desktop_mode_window_decor_handle_menu, name, mMockSurfaceControlAddWindowT, mMockSurfaceSyncGroup, 0 /* x */, 0 /* y */, width, height); - return additionalWindow; + return additionalViewContainer; } } } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/additionalviewcontainer/AdditionalSystemViewContainerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/additionalviewcontainer/AdditionalSystemViewContainerTest.kt new file mode 100644 index 000000000000..d3e996b12e1f --- /dev/null +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/additionalviewcontainer/AdditionalSystemViewContainerTest.kt @@ -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.wm.shell.windowdecor.additionalviewcontainer + +import android.content.Context +import android.testing.AndroidTestingRunner +import android.testing.TestableLooper +import android.view.LayoutInflater +import android.view.View +import android.view.WindowManager +import androidx.test.filters.SmallTest +import com.android.wm.shell.R +import com.android.wm.shell.ShellTestCase +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mock +import org.mockito.kotlin.any +import org.mockito.kotlin.eq +import org.mockito.kotlin.verify +import org.mockito.kotlin.whenever + +/** + * Tests for [AdditionalSystemViewContainer]. + * + * Build/Install/Run: + * atest WMShellUnitTests:AdditionalSystemViewContainerTest + */ +@SmallTest +@TestableLooper.RunWithLooper +@RunWith(AndroidTestingRunner::class) +class AdditionalSystemViewContainerTest : ShellTestCase() { + @Mock + private lateinit var mockView: View + @Mock + private lateinit var mockLayoutInflater: LayoutInflater + @Mock + private lateinit var mockContext: Context + @Mock + private lateinit var mockWindowManager: WindowManager + private lateinit var viewContainer: AdditionalSystemViewContainer + + @Before + fun setUp() { + whenever(mockContext.getSystemService(WindowManager::class.java)) + .thenReturn(mockWindowManager) + whenever(mockContext.getSystemService(Context + .LAYOUT_INFLATER_SERVICE)).thenReturn(mockLayoutInflater) + whenever(mockLayoutInflater.inflate( + R.layout.desktop_mode_window_decor_handle_menu, null)).thenReturn(mockView) + } + + @Test + fun testReleaseView_ViewRemoved() { + viewContainer = AdditionalSystemViewContainer( + mockContext, + R.layout.desktop_mode_window_decor_handle_menu, + TASK_ID, + X, + Y, + WIDTH, + HEIGHT + ) + verify(mockWindowManager).addView(eq(mockView), any()) + viewContainer.releaseView() + verify(mockWindowManager).removeViewImmediate(mockView) + } + + companion object { + private const val X = 500 + private const val Y = 50 + private const val WIDTH = 400 + private const val HEIGHT = 600 + private const val TASK_ID = 5 + } +} diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/additionalviewcontainer/AdditionalViewHostViewContainerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/additionalviewcontainer/AdditionalViewHostViewContainerTest.kt new file mode 100644 index 000000000000..82d557a28f52 --- /dev/null +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/additionalviewcontainer/AdditionalViewHostViewContainerTest.kt @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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.windowdecor.additionalviewcontainer + +import android.testing.AndroidTestingRunner +import android.view.SurfaceControl +import android.view.SurfaceControlViewHost +import androidx.test.filters.SmallTest +import com.android.wm.shell.ShellTestCase +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mock +import org.mockito.kotlin.verify +import org.mockito.kotlin.whenever +import java.util.function.Supplier + +/** + * Tests for [AdditionalViewHostViewContainer]. + * + * Build/Install/Run: + * atest WMShellUnitTests:AdditionalViewHostViewContainerTest + */ +@SmallTest +@RunWith(AndroidTestingRunner::class) +class AdditionalViewHostViewContainerTest : ShellTestCase() { + @Mock + private lateinit var mockTransactionSupplier: Supplier<SurfaceControl.Transaction> + @Mock + private lateinit var mockTransaction: SurfaceControl.Transaction + @Mock + private lateinit var mockSurface: SurfaceControl + @Mock + private lateinit var mockViewHost: SurfaceControlViewHost + private lateinit var viewContainer: AdditionalViewHostViewContainer + + @Before + fun setUp() { + whenever(mockTransactionSupplier.get()).thenReturn(mockTransaction) + } + + @Test + fun testReleaseView_ViewRemoved() { + viewContainer = AdditionalViewHostViewContainer( + mockSurface, + mockViewHost, + mockTransactionSupplier + ) + viewContainer.releaseView() + verify(mockViewHost).release() + verify(mockTransaction).remove(mockSurface) + verify(mockTransaction).apply() + } +} diff --git a/libs/androidfw/AssetManager.cpp b/libs/androidfw/AssetManager.cpp index 68befffecf2f..e6182454ad8a 100644 --- a/libs/androidfw/AssetManager.cpp +++ b/libs/androidfw/AssetManager.cpp @@ -926,8 +926,8 @@ Asset* AssetManager::openAssetFromZipLocked(const ZipFileRO* pZipFile, //printf("USING Zip '%s'\n", pEntry->getFileName()); - if (!pZipFile->getEntryInfo(entry, &method, &uncompressedLen, NULL, NULL, - NULL, NULL)) + if (!pZipFile->getEntryInfo(entry, &method, &uncompressedLen, nullptr, nullptr, + nullptr, nullptr, nullptr)) { ALOGW("getEntryInfo failed\n"); return NULL; diff --git a/libs/androidfw/ZipFileRO.cpp b/libs/androidfw/ZipFileRO.cpp index 839c7b6fef37..10651cdaff33 100644 --- a/libs/androidfw/ZipFileRO.cpp +++ b/libs/androidfw/ZipFileRO.cpp @@ -119,14 +119,6 @@ ZipEntryRO ZipFileRO::findEntryByName(const char* entryName) const * appear to be bogus. */ bool ZipFileRO::getEntryInfo(ZipEntryRO entry, uint16_t* pMethod, - uint32_t* pUncompLen, uint32_t* pCompLen, off64_t* pOffset, - uint32_t* pModWhen, uint32_t* pCrc32) const -{ - return getEntryInfo(entry, pMethod, pUncompLen, pCompLen, pOffset, pModWhen, - pCrc32, nullptr); -} - -bool ZipFileRO::getEntryInfo(ZipEntryRO entry, uint16_t* pMethod, uint32_t* pUncompLen, uint32_t* pCompLen, off64_t* pOffset, uint32_t* pModWhen, uint32_t* pCrc32, uint16_t* pExtraFieldSize) const { diff --git a/libs/androidfw/fuzz/resxmlparser_fuzzer/resxmlparser_fuzzer.cpp b/libs/androidfw/fuzz/resxmlparser_fuzzer/resxmlparser_fuzzer.cpp index 829a39617012..a218a1ff1eb6 100644 --- a/libs/androidfw/fuzz/resxmlparser_fuzzer/resxmlparser_fuzzer.cpp +++ b/libs/androidfw/fuzz/resxmlparser_fuzzer/resxmlparser_fuzzer.cpp @@ -52,10 +52,11 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { // Populate the DynamicRefTable with fuzzed data populateDynamicRefTableWithFuzzedData(*dynamic_ref_table, fuzzedDataProvider); + std::vector<uint8_t> xmlData = fuzzedDataProvider.ConsumeRemainingBytes<uint8_t>(); + // Make sure the object here outlives the vector it's set to, otherwise it will try + // accessing an already freed buffer and crash. auto tree = android::ResXMLTree(std::move(dynamic_ref_table)); - - std::vector<uint8_t> xmlData = fuzzedDataProvider.ConsumeRemainingBytes<uint8_t>(); if (tree.setTo(xmlData.data(), xmlData.size()) != android::NO_ERROR) { return 0; // Exit early if unable to parse XML data } diff --git a/libs/androidfw/include/androidfw/ZipFileRO.h b/libs/androidfw/include/androidfw/ZipFileRO.h index f7c5007c80d2..0f3f19c91ed1 100644 --- a/libs/androidfw/include/androidfw/ZipFileRO.h +++ b/libs/androidfw/include/androidfw/ZipFileRO.h @@ -147,10 +147,6 @@ public: * Returns "false" if "entry" is bogus or if the data in the Zip file * appears to be bad. */ - bool getEntryInfo(ZipEntryRO entry, uint16_t* pMethod, uint32_t* pUncompLen, - uint32_t* pCompLen, off64_t* pOffset, uint32_t* pModWhen, - uint32_t* pCrc32) const; - bool getEntryInfo(ZipEntryRO entry, uint16_t* pMethod, uint32_t* pUncompLen, uint32_t* pCompLen, off64_t* pOffset, uint32_t* pModWhen, uint32_t* pCrc32, uint16_t* pExtraFieldSize) const; diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp index 7c1c5b4e7e5f..341599e79662 100644 --- a/libs/hwui/Android.bp +++ b/libs/hwui/Android.bp @@ -115,6 +115,7 @@ cc_defaults { "libharfbuzz_ng", "libminikin", "server_configurable_flags", + "libaconfig_storage_read_api_cc" ], static_libs: [ diff --git a/libs/hwui/Properties.cpp b/libs/hwui/Properties.cpp index 325bdd63ab22..5d3bc89b40dd 100644 --- a/libs/hwui/Properties.cpp +++ b/libs/hwui/Properties.cpp @@ -39,6 +39,9 @@ constexpr bool clip_surfaceviews() { constexpr bool hdr_10bit_plus() { return false; } +constexpr bool initialize_gl_always() { + return false; +} } // namespace hwui_flags #endif @@ -257,5 +260,9 @@ bool Properties::isDrawingEnabled() { return drawingEnabled == DrawingEnabled::On; } +bool Properties::initializeGlAlways() { + return base::GetBoolProperty(PROPERTY_INITIALIZE_GL_ALWAYS, hwui_flags::initialize_gl_always()); +} + } // namespace uirenderer } // namespace android diff --git a/libs/hwui/Properties.h b/libs/hwui/Properties.h index c1510d96461f..d3176f6879d2 100644 --- a/libs/hwui/Properties.h +++ b/libs/hwui/Properties.h @@ -229,6 +229,11 @@ enum DebugLevel { #define PROPERTY_8BIT_HDR_HEADROOM "debug.hwui.8bit_hdr_headroom" +/** + * Whether to initialize GL even when HWUI is running Vulkan. + */ +#define PROPERTY_INITIALIZE_GL_ALWAYS "debug.hwui.initialize_gl_always" + /////////////////////////////////////////////////////////////////////////////// // Misc /////////////////////////////////////////////////////////////////////////////// @@ -368,6 +373,8 @@ public: static bool isDrawingEnabled(); static void setDrawingEnabled(bool enable); + static bool initializeGlAlways(); + private: static StretchEffectBehavior stretchEffectBehavior; static ProfileType sProfileType; diff --git a/libs/hwui/aconfig/hwui_flags.aconfig b/libs/hwui/aconfig/hwui_flags.aconfig index 50f8b3929e1e..cd3ae5342f4e 100644 --- a/libs/hwui/aconfig/hwui_flags.aconfig +++ b/libs/hwui/aconfig/hwui_flags.aconfig @@ -90,3 +90,10 @@ flag { description: "Add canvas#drawRegion API" bug: "318612129" } + +flag { + name: "initialize_gl_always" + namespace: "core_graphics" + description: "Initialize GL even when HWUI is set to use Vulkan. This improves app startup time for apps using GL." + bug: "335172671" +} diff --git a/libs/hwui/apex/jni_runtime.cpp b/libs/hwui/apex/jni_runtime.cpp index 6ace3967ecf3..15b2bac50c79 100644 --- a/libs/hwui/apex/jni_runtime.cpp +++ b/libs/hwui/apex/jni_runtime.cpp @@ -192,5 +192,14 @@ void zygote_preload_graphics() { // Preload Vulkan driver if HWUI renders with Vulkan backend. uint32_t apiVersion; vkEnumerateInstanceVersion(&apiVersion); + + if (Properties::initializeGlAlways()) { + // Even though HWUI is rendering with Vulkan, some apps still use + // GL. Preload GL driver just in case. Since this happens prior to + // forking from the zygote, apps that do not use GL are unaffected. + // Any memory that (E)GL uses for this call is in shared memory, + // and this call only happens once. + eglGetDisplay(EGL_DEFAULT_DISPLAY); + } } } diff --git a/libs/hwui/effects/GainmapRenderer.cpp b/libs/hwui/effects/GainmapRenderer.cpp index 0a30c6c14c4c..eac03609d72f 100644 --- a/libs/hwui/effects/GainmapRenderer.cpp +++ b/libs/hwui/effects/GainmapRenderer.cpp @@ -96,6 +96,7 @@ void DrawGainmapBitmap(SkCanvas* c, const sk_sp<const SkImage>& image, const SkR #ifdef __ANDROID__ static constexpr char gGainmapSKSL[] = R"SKSL( + uniform shader linearBase; uniform shader base; uniform shader gainmap; uniform colorFilter workingSpaceToLinearSrgb; @@ -117,7 +118,11 @@ static constexpr char gGainmapSKSL[] = R"SKSL( } half4 main(float2 coord) { - half4 S = base.eval(coord); + if (W == 0.0) { + return base.eval(coord); + } + + half4 S = linearBase.eval(coord); half4 G = gainmap.eval(coord); if (gainmapIsAlpha == 1) { G = half4(G.a, G.a, G.a, 1.0); @@ -186,8 +191,10 @@ private: SkColorFilterPriv::MakeColorSpaceXform(baseColorSpace, gainmapMathColorSpace); // The base image shader will convert into the color space in which the gainmap is applied. - auto baseImageShader = baseImage->makeRawShader(tileModeX, tileModeY, samplingOptions) - ->makeWithColorFilter(colorXformSdrToGainmap); + auto linearBaseImageShader = baseImage->makeRawShader(tileModeX, tileModeY, samplingOptions) + ->makeWithColorFilter(colorXformSdrToGainmap); + + auto baseImageShader = baseImage->makeShader(tileModeX, tileModeY, samplingOptions); // The gainmap image shader will ignore any color space that the gainmap has. const SkMatrix gainmapRectToDstRect = @@ -201,6 +208,7 @@ private: auto colorXformGainmapToDst = SkColorFilterPriv::MakeColorSpaceXform( gainmapMathColorSpace, SkColorSpace::MakeSRGBLinear()); + mBuilder.child("linearBase") = std::move(linearBaseImageShader); mBuilder.child("base") = std::move(baseImageShader); mBuilder.child("gainmap") = std::move(gainmapImageShader); mBuilder.child("workingSpaceToLinearSrgb") = std::move(colorXformGainmapToDst); diff --git a/libs/hwui/jni/Graphics.cpp b/libs/hwui/jni/Graphics.cpp index 07e97f85d588..a88139d6b5d6 100644 --- a/libs/hwui/jni/Graphics.cpp +++ b/libs/hwui/jni/Graphics.cpp @@ -583,6 +583,16 @@ jobject GraphicsJNI::getColorSpace(JNIEnv* env, SkColorSpace* decodeColorSpace, transferParams.a, transferParams.b, transferParams.c, transferParams.d, transferParams.e, transferParams.f, transferParams.g); + // Some transfer functions that are considered valid by Skia are not + // accepted by android.graphics. + if (hasException(env)) { + // Callers (e.g. Bitmap#getColorSpace) are not expected to throw an + // Exception, so clear it and return null, which is a documented + // possibility. + env->ExceptionClear(); + return nullptr; + } + jfloatArray xyzArray = env->NewFloatArray(9); jfloat xyz[9] = { xyzMatrix.vals[0][0], diff --git a/libs/input/Android.bp b/libs/input/Android.bp index 7b7ccf51aa1a..7a82938435af 100644 --- a/libs/input/Android.bp +++ b/libs/input/Android.bp @@ -46,7 +46,6 @@ cc_library_shared { "liblog", "libutils", "libgui", - "libui", "libinput", ], diff --git a/location/lib/java/com/android/location/provider/SignificantPlaceProvider.java b/location/lib/java/com/android/location/provider/SignificantPlaceProvider.java index 0b39a9a8bc4e..df4b90393fbd 100644 --- a/location/lib/java/com/android/location/provider/SignificantPlaceProvider.java +++ b/location/lib/java/com/android/location/provider/SignificantPlaceProvider.java @@ -21,17 +21,22 @@ import android.app.trust.TrustManager; import android.hardware.location.ISignificantPlaceProvider; import android.hardware.location.ISignificantPlaceProviderManager; import android.os.Binder; +import android.os.Handler; import android.os.IBinder; +import android.os.Looper; import android.os.Process; import android.os.RemoteException; +import android.util.Log; import com.android.internal.annotations.GuardedBy; /** @hide */ -public class SignificantPlaceProvider { +public abstract class SignificantPlaceProvider { public static final String ACTION = TrustManager.ACTION_BIND_SIGNIFICANT_PLACE_PROVIDER; + private static final String TAG = "SignificantPlaceProvider"; + private final IBinder mBinder; // write locked on mBinder, read lock is optional depending on atomicity requirements @@ -69,6 +74,9 @@ public class SignificantPlaceProvider { } } + /** Invoked when some client has checked whether the device is in a significant place. */ + public abstract void onSignificantPlaceCheck(); + private final class Service extends ISignificantPlaceProvider.Stub { Service() {} @@ -76,7 +84,7 @@ public class SignificantPlaceProvider { @Override public void setSignificantPlaceProviderManager(ISignificantPlaceProviderManager manager) { if (Binder.getCallingUid() != Process.SYSTEM_UID) { - return; + throw new SecurityException(); } synchronized (mBinder) { @@ -91,5 +99,22 @@ public class SignificantPlaceProvider { mManager = manager; } } + + @Override + public void onSignificantPlaceCheck() { + if (Binder.getCallingUid() != Process.SYSTEM_UID) { + throw new SecurityException(); + } + + try { + SignificantPlaceProvider.this.onSignificantPlaceCheck(); + } catch (RuntimeException e) { + // exceptions on one-way binder threads are dropped - move to a different thread + Log.w(TAG, e); + new Handler(Looper.getMainLooper()).post(() -> { + throw new AssertionError(e); + }); + } + } } } diff --git a/media/java/android/media/IMediaRouterService.aidl b/media/java/android/media/IMediaRouterService.aidl index efbf8da6d17c..eeb4853afadc 100644 --- a/media/java/android/media/IMediaRouterService.aidl +++ b/media/java/android/media/IMediaRouterService.aidl @@ -52,6 +52,7 @@ interface IMediaRouterService { // Methods for MediaRouter2 List<MediaRoute2Info> getSystemRoutes(String callerPackageName, boolean isProxyRouter); RoutingSessionInfo getSystemSessionInfo(); + boolean showMediaOutputSwitcherWithRouter2(String packageName); void registerRouter2(IMediaRouter2 router, String packageName); void unregisterRouter2(IMediaRouter2 router); @@ -97,5 +98,5 @@ interface IMediaRouterService { void setSessionVolumeWithManager(IMediaRouter2Manager manager, int requestId, String sessionId, int volume); void releaseSessionWithManager(IMediaRouter2Manager manager, int requestId, String sessionId); - boolean showMediaOutputSwitcher(String packageName); + boolean showMediaOutputSwitcherWithProxyRouter(IMediaRouter2Manager manager); } diff --git a/media/java/android/media/MediaRouter2.java b/media/java/android/media/MediaRouter2.java index 5672cd54e369..0667bfda7596 100644 --- a/media/java/android/media/MediaRouter2.java +++ b/media/java/android/media/MediaRouter2.java @@ -2666,8 +2666,11 @@ public final class MediaRouter2 { @Override public boolean showSystemOutputSwitcher() { - throw new UnsupportedOperationException( - "Cannot show system output switcher from a privileged router."); + try { + return mMediaRouterService.showMediaOutputSwitcherWithProxyRouter(mClient); + } catch (RemoteException ex) { + throw ex.rethrowFromSystemServer(); + } } /** Gets the list of all discovered routes. */ @@ -3539,7 +3542,7 @@ public final class MediaRouter2 { public boolean showSystemOutputSwitcher() { synchronized (mLock) { try { - return mMediaRouterService.showMediaOutputSwitcher(mImpl.getPackageName()); + return mMediaRouterService.showMediaOutputSwitcherWithRouter2(mPackageName); } catch (RemoteException ex) { ex.rethrowFromSystemServer(); } diff --git a/media/java/android/media/tv/ITvInputService.aidl b/media/java/android/media/tv/ITvInputService.aidl index be73c0cdf43a..be73c0cdf43a 100755..100644 --- a/media/java/android/media/tv/ITvInputService.aidl +++ b/media/java/android/media/tv/ITvInputService.aidl diff --git a/media/java/android/media/tv/interactive/TvInteractiveAppManager.java b/media/java/android/media/tv/interactive/TvInteractiveAppManager.java index 7cf32ec16638..7cf32ec16638 100755..100644 --- a/media/java/android/media/tv/interactive/TvInteractiveAppManager.java +++ b/media/java/android/media/tv/interactive/TvInteractiveAppManager.java diff --git a/media/java/android/media/tv/interactive/TvInteractiveAppService.java b/media/java/android/media/tv/interactive/TvInteractiveAppService.java index 84d08db1b9c8..84d08db1b9c8 100755..100644 --- a/media/java/android/media/tv/interactive/TvInteractiveAppService.java +++ b/media/java/android/media/tv/interactive/TvInteractiveAppService.java diff --git a/media/java/android/media/tv/interactive/TvInteractiveAppView.java b/media/java/android/media/tv/interactive/TvInteractiveAppView.java index 635572d12cc5..635572d12cc5 100755..100644 --- a/media/java/android/media/tv/interactive/TvInteractiveAppView.java +++ b/media/java/android/media/tv/interactive/TvInteractiveAppView.java diff --git a/media/java/android/mtp/MtpDatabase.java b/media/java/android/mtp/MtpDatabase.java index 20d711cf4c54..20d711cf4c54 100755..100644 --- a/media/java/android/mtp/MtpDatabase.java +++ b/media/java/android/mtp/MtpDatabase.java diff --git a/native/android/input.cpp b/native/android/input.cpp index 0a223142954f..9a8cda357774 100644 --- a/native/android/input.cpp +++ b/native/android/input.cpp @@ -100,7 +100,8 @@ int32_t AMotionEvent_getAction(const AInputEvent* motion_event) { } int32_t AMotionEvent_getFlags(const AInputEvent* motion_event) { - return static_cast<const MotionEvent*>(motion_event)->getFlags(); + return static_cast<const MotionEvent*>(motion_event)->getFlags() & + ~AMOTION_EVENT_PRIVATE_FLAG_MASK; } int32_t AMotionEvent_getMetaState(const AInputEvent* motion_event) { diff --git a/native/android/performance_hint.cpp b/native/android/performance_hint.cpp index 02d72ad7d693..44fa677b59f7 100644 --- a/native/android/performance_hint.cpp +++ b/native/android/performance_hint.cpp @@ -51,6 +51,9 @@ struct APerformanceHintSession; constexpr int64_t SEND_HINT_TIMEOUT = std::chrono::nanoseconds(100ms).count(); struct AWorkDuration : public hal::WorkDuration {}; +// Shared lock for the whole PerformanceHintManager and sessions +static std::mutex sHintMutex = std::mutex{}; + struct APerformanceHintManager { public: static APerformanceHintManager* getInstance(); @@ -192,6 +195,7 @@ APerformanceHintSession* APerformanceHintManager::createSession( } auto out = new APerformanceHintSession(mHintManager, std::move(session), mPreferredRateNanos, initialTargetWorkDurationNanos, sessionConfig); + std::scoped_lock lock(sHintMutex); out->traceThreads(tids); out->traceTargetDuration(initialTargetWorkDurationNanos); out->tracePowerEfficient(false); @@ -219,6 +223,7 @@ APerformanceHintSession::APerformanceHintSession(std::shared_ptr<IHintManager> h if (sessionConfig->id > INT32_MAX) { ALOGE("Session ID too large, must fit 32-bit integer"); } + std::scoped_lock lock(sHintMutex); constexpr int numEnums = ndk::enum_range<hal::SessionHint>().end() - ndk::enum_range<hal::SessionHint>().begin(); mLastHintSentTimestamp = std::vector<int64_t>(numEnums, 0); @@ -244,6 +249,7 @@ int APerformanceHintSession::updateTargetWorkDuration(int64_t targetDurationNano ret.getMessage()); return EPIPE; } + std::scoped_lock lock(sHintMutex); mTargetDurationNanos = targetDurationNanos; /** * Most of the workload is target_duration dependent, so now clear the cached samples @@ -267,6 +273,7 @@ int APerformanceHintSession::reportActualWorkDuration(int64_t actualDurationNano } int APerformanceHintSession::sendHint(SessionHint hint) { + std::scoped_lock lock(sHintMutex); if (hint < 0 || hint >= static_cast<int32_t>(mLastHintSentTimestamp.size())) { ALOGE("%s: invalid session hint %d", __FUNCTION__, hint); return EINVAL; @@ -305,6 +312,7 @@ int APerformanceHintSession::setThreads(const int32_t* threadIds, size_t size) { return EPIPE; } + std::scoped_lock lock(sHintMutex); traceThreads(tids); return 0; @@ -343,6 +351,7 @@ int APerformanceHintSession::setPreferPowerEfficiency(bool enabled) { ret.getMessage()); return EPIPE; } + std::scoped_lock lock(sHintMutex); tracePowerEfficient(enabled); return OK; } @@ -355,6 +364,7 @@ int APerformanceHintSession::reportActualWorkDurationInternal(AWorkDuration* wor int64_t actualTotalDurationNanos = workDuration->durationNanos; int64_t now = uptimeNanos(); workDuration->timeStampNanos = now; + std::scoped_lock lock(sHintMutex); traceActualDuration(workDuration->durationNanos); mActualWorkDurations.push_back(std::move(*workDuration)); diff --git a/native/graphics/jni/Android.bp b/native/graphics/jni/Android.bp index 8ea46329af58..746c280edc70 100644 --- a/native/graphics/jni/Android.bp +++ b/native/graphics/jni/Android.bp @@ -111,6 +111,7 @@ cc_defaults { "allocator_may_return_null = 1", ], }, + dictionary: "fuzz/imagedecoder_fuzzer.dict", host_supported: true, } diff --git a/native/graphics/jni/fuzz/fuzz_imagedecoder.cpp b/native/graphics/jni/fuzz/fuzz_imagedecoder.cpp index 838bf3f30a11..6743997fb152 100644 --- a/native/graphics/jni/fuzz/fuzz_imagedecoder.cpp +++ b/native/graphics/jni/fuzz/fuzz_imagedecoder.cpp @@ -15,32 +15,15 @@ */ #include <android/imagedecoder.h> - #include <binder/IPCThreadState.h> -#include <stddef.h> -#include <stdint.h> -#include <cstdlib> -#include <memory> +#include <fuzzer/FuzzedDataProvider.h> #ifdef PNG_MUTATOR_DEFINE_LIBFUZZER_CUSTOM_MUTATOR #include <fuzz/png_mutator.h> #endif -struct DecoderDeleter { - void operator()(AImageDecoder* decoder) const { AImageDecoder_delete(decoder); } -}; - -using DecoderPointer = std::unique_ptr<AImageDecoder, DecoderDeleter>; - -static DecoderPointer makeDecoder(const uint8_t* data, size_t size) { - AImageDecoder* decoder = nullptr; - int result = AImageDecoder_createFromBuffer(data, size, &decoder); - if (result != ANDROID_IMAGE_DECODER_SUCCESS) { - // This was not a valid image. - return nullptr; - } - return DecoderPointer(decoder); -} +constexpr int32_t kMaxDimension = 5000; +constexpr int32_t kMinDimension = 0; struct PixelFreer { void operator()(void* pixels) const { std::free(pixels); } @@ -48,41 +31,113 @@ struct PixelFreer { using PixelPointer = std::unique_ptr<void, PixelFreer>; -extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { - // Without this call, decoding HEIF may time out on binder IPC calls. - android::ProcessState::self()->startThreadPool(); - - DecoderPointer decoder = makeDecoder(data, size); - if (!decoder) { - return 0; +AImageDecoder* init(const uint8_t* data, size_t size, bool useFileDescriptor) { + AImageDecoder* decoder = nullptr; + if (useFileDescriptor) { + constexpr char testFd[] = "tempFd"; + int32_t fileDesc = open(testFd, O_RDWR | O_CREAT | O_TRUNC); + write(fileDesc, data, size); + AImageDecoder_createFromFd(fileDesc, &decoder); + close(fileDesc); + } else { + AImageDecoder_createFromBuffer(data, size, &decoder); } + return decoder; +} - const AImageDecoderHeaderInfo* info = AImageDecoder_getHeaderInfo(decoder.get()); - int32_t width = AImageDecoderHeaderInfo_getWidth(info); - int32_t height = AImageDecoderHeaderInfo_getHeight(info); - - // Set an arbitrary limit on the size of an image. The fuzzer runs with a - // limited amount of memory, and keeping this allocation small allows the - // fuzzer to continue running to try to find more serious problems. This - // size is large enough to hold a photo taken by a current gen phone. - constexpr int32_t kMaxDimension = 5000; - if (width > kMaxDimension || height > kMaxDimension) { +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + FuzzedDataProvider dataProvider = FuzzedDataProvider(data, size); + /** + * Use maximum of 80% of buffer for creating decoder and save at least + * 20% buffer for fuzzing other APIs + */ + const int32_t dataSize = dataProvider.ConsumeIntegralInRange<int32_t>(0, (size * 80) / 100); + std::vector<uint8_t> inputBuffer = dataProvider.ConsumeBytes<uint8_t>(dataSize); + AImageDecoder* decoder = + init(inputBuffer.data(), inputBuffer.size(), dataProvider.ConsumeBool()); + if (!decoder) { return 0; } - - size_t stride = AImageDecoder_getMinimumStride(decoder.get()); - size_t pixelSize = height * stride; - auto pixels = PixelPointer(std::malloc(pixelSize)); - if (!pixels.get()) { - return 0; + const AImageDecoderHeaderInfo* headerInfo = AImageDecoder_getHeaderInfo(decoder); + AImageDecoderFrameInfo* frameInfo = AImageDecoderFrameInfo_create(); + int32_t height = AImageDecoderHeaderInfo_getHeight(headerInfo); + int32_t width = AImageDecoderHeaderInfo_getWidth(headerInfo); + while (dataProvider.remaining_bytes()) { + auto invokeImageApi = dataProvider.PickValueInArray<const std::function<void()>>({ + [&]() { + int32_t testHeight = + dataProvider.ConsumeIntegralInRange<int32_t>(kMinDimension, + kMaxDimension); + int32_t testWidth = dataProvider.ConsumeIntegralInRange<int32_t>(kMinDimension, + kMaxDimension); + int32_t result = AImageDecoder_setTargetSize(decoder, testHeight, testWidth); + if (result == ANDROID_IMAGE_DECODER_SUCCESS) { + height = testHeight; + width = testWidth; + } + }, + [&]() { + const bool required = dataProvider.ConsumeBool(); + AImageDecoder_setUnpremultipliedRequired(decoder, required); + }, + [&]() { + AImageDecoder_setAndroidBitmapFormat( + decoder, + dataProvider.ConsumeIntegralInRange< + int32_t>(ANDROID_BITMAP_FORMAT_NONE, + ANDROID_BITMAP_FORMAT_RGBA_1010102) /* format */); + }, + [&]() { + AImageDecoder_setDataSpace(decoder, + dataProvider + .ConsumeIntegral<int32_t>() /* dataspace */); + }, + [&]() { + ARect rect{dataProvider.ConsumeIntegral<int32_t>() /* left */, + dataProvider.ConsumeIntegral<int32_t>() /* top */, + dataProvider.ConsumeIntegral<int32_t>() /* right */, + dataProvider.ConsumeIntegral<int32_t>() /* bottom */}; + AImageDecoder_setCrop(decoder, rect); + }, + [&]() { AImageDecoderHeaderInfo_getWidth(headerInfo); }, + [&]() { AImageDecoderHeaderInfo_getMimeType(headerInfo); }, + [&]() { AImageDecoderHeaderInfo_getAlphaFlags(headerInfo); }, + [&]() { AImageDecoderHeaderInfo_getAndroidBitmapFormat(headerInfo); }, + [&]() { + int32_t tempHeight; + int32_t tempWidth; + AImageDecoder_computeSampledSize(decoder, + dataProvider.ConsumeIntegral< + int>() /* sampleSize */, + &tempWidth, &tempHeight); + }, + [&]() { AImageDecoderHeaderInfo_getDataSpace(headerInfo); }, + [&]() { AImageDecoder_getRepeatCount(decoder); }, + [&]() { AImageDecoder_getFrameInfo(decoder, frameInfo); }, + [&]() { AImageDecoderFrameInfo_getDuration(frameInfo); }, + [&]() { AImageDecoderFrameInfo_hasAlphaWithinBounds(frameInfo); }, + [&]() { AImageDecoderFrameInfo_getDisposeOp(frameInfo); }, + [&]() { AImageDecoderFrameInfo_getBlendOp(frameInfo); }, + [&]() { + AImageDecoder_setInternallyHandleDisposePrevious( + decoder, dataProvider.ConsumeBool() /* handle */); + }, + [&]() { AImageDecoder_rewind(decoder); }, + [&]() { AImageDecoder_advanceFrame(decoder); }, + [&]() { + size_t stride = AImageDecoder_getMinimumStride(decoder); + size_t pixelSize = height * stride; + auto pixels = PixelPointer(std::malloc(pixelSize)); + if (!pixels.get()) { + return; + } + AImageDecoder_decodeImage(decoder, pixels.get(), stride, pixelSize); + }, + }); + invokeImageApi(); } - while (true) { - int result = AImageDecoder_decodeImage(decoder.get(), pixels.get(), stride, pixelSize); - if (result != ANDROID_IMAGE_DECODER_SUCCESS) break; - - result = AImageDecoder_advanceFrame(decoder.get()); - if (result != ANDROID_IMAGE_DECODER_SUCCESS) break; - } + AImageDecoderFrameInfo_delete(frameInfo); + AImageDecoder_delete(decoder); return 0; } diff --git a/native/graphics/jni/fuzz/imagedecoder_fuzzer.dict b/native/graphics/jni/fuzz/imagedecoder_fuzzer.dict new file mode 100644 index 000000000000..5b54a0ed115e --- /dev/null +++ b/native/graphics/jni/fuzz/imagedecoder_fuzzer.dict @@ -0,0 +1,7 @@ +kw1="\x89\x50\x4E\x47" +kw2="\xff\xD8\xFF" +kw4="\x52\x49\x46\x46" +kw5="\x00\x00\x01\x00" +kw6="\x47\x49\x46\x08" +kw7="ftyp" +kw8="\x04\x00\x00\x00" diff --git a/nfc/api/system-current.txt b/nfc/api/system-current.txt index 055ccbced58a..3375e18c001d 100644 --- a/nfc/api/system-current.txt +++ b/nfc/api/system-current.txt @@ -57,7 +57,9 @@ package android.nfc { @FlaggedApi("android.nfc.nfc_oem_extension") public final class NfcOemExtension { method @FlaggedApi("android.nfc.nfc_oem_extension") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void clearPreference(); + method @FlaggedApi("android.nfc.nfc_oem_extension") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void maybeTriggerFirmwareUpdate(); method @FlaggedApi("android.nfc.nfc_oem_extension") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void registerCallback(@NonNull java.util.concurrent.Executor, @NonNull android.nfc.NfcOemExtension.Callback); + method @FlaggedApi("android.nfc.nfc_oem_extension") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void synchronizeScreenState(); method @FlaggedApi("android.nfc.nfc_oem_extension") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void unregisterCallback(@NonNull android.nfc.NfcOemExtension.Callback); } diff --git a/nfc/java/android/nfc/INfcAdapter.aidl b/nfc/java/android/nfc/INfcAdapter.aidl index 7150b54cf7f1..fd77820afc81 100644 --- a/nfc/java/android/nfc/INfcAdapter.aidl +++ b/nfc/java/android/nfc/INfcAdapter.aidl @@ -110,4 +110,6 @@ interface INfcAdapter void registerOemExtensionCallback(INfcOemExtensionCallback callbacks); void unregisterOemExtensionCallback(INfcOemExtensionCallback callbacks); void clearPreference(); + void setScreenState(); + void checkFirmware(); } diff --git a/nfc/java/android/nfc/NfcOemExtension.java b/nfc/java/android/nfc/NfcOemExtension.java index 1eff58cb80fd..f6138a63fae4 100644 --- a/nfc/java/android/nfc/NfcOemExtension.java +++ b/nfc/java/android/nfc/NfcOemExtension.java @@ -141,6 +141,34 @@ public final class NfcOemExtension { } } + /** + * Get the screen state from system and set it to current screen state. + */ + @FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION) + @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) + public void synchronizeScreenState() { + try { + NfcAdapter.sService.setScreenState(); + } catch (RemoteException e) { + mAdapter.attemptDeadServiceRecovery(e); + } + } + + /** + * Check if the firmware needs updating. + * + * <p>If an update is needed, a firmware will be triggered when NFC is disabled. + */ + @FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION) + @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) + public void maybeTriggerFirmwareUpdate() { + try { + NfcAdapter.sService.checkFirmware(); + } catch (RemoteException e) { + mAdapter.attemptDeadServiceRecovery(e); + } + } + private final class NfcOemExtensionCallback extends INfcOemExtensionCallback.Stub { @Override public void onTagConnected(boolean connected, Tag tag) throws RemoteException { diff --git a/nfc/java/android/nfc/cardemulation/ApduServiceInfo.java b/nfc/java/android/nfc/cardemulation/ApduServiceInfo.java index a470f93db0c6..3cf0a4dc4873 100644 --- a/nfc/java/android/nfc/cardemulation/ApduServiceInfo.java +++ b/nfc/java/android/nfc/cardemulation/ApduServiceInfo.java @@ -412,7 +412,7 @@ public final class ApduServiceInfo implements Parcelable { false); if (!mOnHost && !autoTransact) { Log.e(TAG, "Ignoring polling-loop-filter " + plf - + " for offhost service that isn't autoTranact"); + + " for offhost service that isn't autoTransact"); } else { mAutoTransact.put(plf, autoTransact); } @@ -429,7 +429,7 @@ public final class ApduServiceInfo implements Parcelable { false); if (!mOnHost && !autoTransact) { Log.e(TAG, "Ignoring polling-loop-filter " + plf - + " for offhost service that isn't autoTranact"); + + " for offhost service that isn't autoTransact"); } else { mAutoTransactPatterns.put(Pattern.compile(plf), autoTransact); } @@ -1028,6 +1028,9 @@ public final class ApduServiceInfo implements Parcelable { pw.println(" Settings Activity: " + mSettingsActivityName); pw.println(" Requires Device Unlock: " + mRequiresDeviceUnlock); pw.println(" Requires Device ScreenOn: " + mRequiresDeviceScreenOn); + pw.println(" Should Default to Observe Mode: " + mShouldDefaultToObserveMode); + pw.println(" Auto-Transact Mapping: " + mAutoTransact); + pw.println(" Auto-Transact Patterns: " + mAutoTransactPatterns); } @@ -1081,6 +1084,27 @@ public final class ApduServiceInfo implements Parcelable { proto.end(token); } proto.write(ApduServiceInfoProto.SETTINGS_ACTIVITY_NAME, mSettingsActivityName); + proto.write(ApduServiceInfoProto.SHOULD_DEFAULT_TO_OBSERVE_MODE, + mShouldDefaultToObserveMode); + { + long token = proto.start(ApduServiceInfoProto.AUTO_TRANSACT_MAPPING); + for (Map.Entry<String, Boolean> entry : mAutoTransact.entrySet()) { + proto.write(ApduServiceInfoProto.AutoTransactMapping.AID, entry.getKey()); + proto.write(ApduServiceInfoProto.AutoTransactMapping.SHOULD_AUTO_TRANSACT, + entry.getValue()); + } + proto.end(token); + } + { + long token = proto.start(ApduServiceInfoProto.AUTO_TRANSACT_PATTERNS); + for (Map.Entry<Pattern, Boolean> entry : mAutoTransactPatterns.entrySet()) { + proto.write(ApduServiceInfoProto.AutoTransactPattern.REGEXP_PATTERN, + entry.getKey().pattern()); + proto.write(ApduServiceInfoProto.AutoTransactPattern.SHOULD_AUTO_TRANSACT, + entry.getValue()); + } + proto.end(token); + } } private static final Pattern AID_PATTERN = Pattern.compile("[0-9A-Fa-f]{10,32}\\*?\\#?"); diff --git a/packages/CarrierDefaultApp/res/values-mn/strings.xml b/packages/CarrierDefaultApp/res/values-mn/strings.xml index 3476ff072ebb..edafaac65cb8 100644 --- a/packages/CarrierDefaultApp/res/values-mn/strings.xml +++ b/packages/CarrierDefaultApp/res/values-mn/strings.xml @@ -10,7 +10,7 @@ <string name="no_mobile_data_connection_title" msgid="7449525772416200578">"Мобайл дата холболт алга"</string> <string name="no_mobile_data_connection" msgid="544980465184147010">"Дата эсвэл роуминг төлөвлөгөөг %s-р нэмнэ үү"</string> <string name="mobile_data_status_notification_channel_name" msgid="833999690121305708">"Мобайл датаны төлөв"</string> - <string name="action_bar_label" msgid="4290345990334377177">"Мобайл сүлжээнд нэвтрэх"</string> + <string name="action_bar_label" msgid="4290345990334377177">"Хөдөлгөөнт холбооны сүлжээнд нэвтрэх"</string> <string name="ssl_error_warning" msgid="3127935140338254180">"Таны холбогдох гэж буй сүлжээ аюулгүй байдлын асуудалтай байна."</string> <string name="ssl_error_example" msgid="6188711843183058764">"Жишээлбэл нэвтрэх хуудас нь харагдаж буй байгууллагынх биш байж болно."</string> <string name="ssl_error_continue" msgid="1138548463994095584">"Ямар ч тохиолдолд хөтчөөр үргэлжлүүлэх"</string> diff --git a/packages/CompanionDeviceManager/res/values-ca/strings.xml b/packages/CompanionDeviceManager/res/values-ca/strings.xml index ae9e23f42c51..a40f2bd57251 100644 --- a/packages/CompanionDeviceManager/res/values-ca/strings.xml +++ b/packages/CompanionDeviceManager/res/values-ca/strings.xml @@ -63,7 +63,7 @@ <string name="permission_storage" msgid="6831099350839392343">"Fotos i contingut multimèdia"</string> <string name="permission_notifications" msgid="4099418516590632909">"Notificacions"</string> <string name="permission_app_streaming" msgid="6009695219091526422">"Aplicacions"</string> - <string name="permission_nearby_device_streaming" msgid="1023325519477349499">"Reproducció en línia"</string> + <string name="permission_nearby_device_streaming" msgid="1023325519477349499">"Estríming"</string> <string name="permission_phone_summary" msgid="8246321093970051702">"Fer i gestionar trucades telefòniques"</string> <string name="permission_call_logs_summary" msgid="7545243592757693321">"Llegir i escriure el registre de trucades del telèfon"</string> <string name="permission_sms_summary" msgid="8499509535410068616">"Enviar i llegir missatges SMS"</string> diff --git a/packages/CompanionDeviceManager/res/values-hr/strings.xml b/packages/CompanionDeviceManager/res/values-hr/strings.xml index b6279980279c..cc1765532d13 100644 --- a/packages/CompanionDeviceManager/res/values-hr/strings.xml +++ b/packages/CompanionDeviceManager/res/values-hr/strings.xml @@ -16,7 +16,7 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="app_label" msgid="4470785958457506021">"Companion Device Manager"</string> + <string name="app_label" msgid="4470785958457506021">"Upravitelj popratnih uređaja"</string> <string name="confirmation_title" msgid="2244241995958340998">"Želite li dopustiti aplikaciji <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> da pristupa <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>?"</string> <string name="profile_name_watch" msgid="576290739483672360">"satom"</string> <string name="chooser_title_non_profile" msgid="6035023914517087400">"Odaberite uređaj kojim će upravljati aplikacija <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>"</string> diff --git a/packages/CompanionDeviceManager/res/values-it/strings.xml b/packages/CompanionDeviceManager/res/values-it/strings.xml index 40d3be55ca71..3575ff31cdfc 100644 --- a/packages/CompanionDeviceManager/res/values-it/strings.xml +++ b/packages/CompanionDeviceManager/res/values-it/strings.xml @@ -59,7 +59,7 @@ <string name="permission_microphone" msgid="2152206421428732949">"Microfono"</string> <string name="permission_call_logs" msgid="5546761417694586041">"Registri chiamate"</string> <string name="permission_nearby_devices" msgid="7530973297737123481">"Dispositivi nelle vicinanze"</string> - <string name="permission_media_routing_control" msgid="5498639511586715253">"Cambia uscita conten. multim."</string> + <string name="permission_media_routing_control" msgid="5498639511586715253">"Cambia uscita multimediale"</string> <string name="permission_storage" msgid="6831099350839392343">"Foto e contenuti multimediali"</string> <string name="permission_notifications" msgid="4099418516590632909">"Notifiche"</string> <string name="permission_app_streaming" msgid="6009695219091526422">"App"</string> diff --git a/packages/CrashRecovery/aconfig/flags.aconfig b/packages/CrashRecovery/aconfig/flags.aconfig index 8cdef38356da..80412321d60b 100644 --- a/packages/CrashRecovery/aconfig/flags.aconfig +++ b/packages/CrashRecovery/aconfig/flags.aconfig @@ -12,15 +12,22 @@ flag { flag { name: "enable_crashrecovery" is_exported: true - namespace: "crashrecovery" + namespace: "modularization" description: "Enables various dependencies of crashrecovery module" bug: "289203818" } flag { name: "allow_rescue_party_flag_resets" - namespace: "crashrecovery" + namespace: "modularization" description: "Enables rescue party flag resets" bug: "287618292" is_fixed_read_only: true } + +flag { + name: "reenable_settings_resets" + namespace: "modularization" + description: "Re-enables settings resets only, deletes flag resets" + bug: "333847376" +} diff --git a/packages/CredentialManager/res/values-kn/strings.xml b/packages/CredentialManager/res/values-kn/strings.xml index bfc31a1b11c2..897f444af2c5 100644 --- a/packages/CredentialManager/res/values-kn/strings.xml +++ b/packages/CredentialManager/res/values-kn/strings.xml @@ -20,7 +20,7 @@ <string name="app_name" msgid="4539824758261855508">"ರುಜುವಾತು ನಿರ್ವಾಹಕ"</string> <string name="string_cancel" msgid="6369133483981306063">"ರದ್ದುಗೊಳಿಸಿ"</string> <string name="string_continue" msgid="1346732695941131882">"ಮುಂದುವರಿಸಿ"</string> - <string name="string_more_options" msgid="2763852250269945472">"ಬೇರೆ ವಿಧಾನದಲ್ಲಿ ಉಳಿಸಿ"</string> + <string name="string_more_options" msgid="2763852250269945472">"ಬೇರೆ ವಿಧಾನದಲ್ಲಿ ಸೇವ್ ಮಾಡಿ"</string> <string name="string_learn_more" msgid="4541600451688392447">"ಇನ್ನಷ್ಟು ತಿಳಿಯಿರಿ"</string> <string name="content_description_show_password" msgid="3283502010388521607">"ಪಾಸ್ವರ್ಡ್ ತೋರಿಸಿ"</string> <string name="content_description_hide_password" msgid="6841375971631767996">"ಪಾಸ್ವರ್ಡ್ ಅನ್ನು ಮರೆಮಾಡಿ"</string> @@ -48,7 +48,7 @@ <string name="passwords" msgid="5419394230391253816">"ಪಾಸ್ವರ್ಡ್ಗಳು"</string> <string name="sign_ins" msgid="4710739369149469208">"ಸೈನ್-ಇನ್ಗಳು"</string> <string name="sign_in_info" msgid="2627704710674232328">"ಸೈನ್-ಇನ್ ಮಾಹಿತಿ"</string> - <string name="save_credential_to_title" msgid="3172811692275634301">"ಇಲ್ಲಿಗೆ <xliff:g id="CREDENTIALTYPES">%1$s</xliff:g> ಅನ್ನು ಉಳಿಸಿ"</string> + <string name="save_credential_to_title" msgid="3172811692275634301">"ಇಲ್ಲಿಗೆ <xliff:g id="CREDENTIALTYPES">%1$s</xliff:g> ಅನ್ನು ಸೇವ್ ಮಾಡಿ"</string> <string name="create_passkey_in_other_device_title" msgid="2360053098931886245">"ಇನ್ನೊಂದು ಸಾಧನದಲ್ಲಿ ಪಾಸ್ಕೀ ಅನ್ನು ರಚಿಸಬೇಕೆ?"</string> <string name="save_password_on_other_device_title" msgid="5829084591948321207">"ಇನ್ನೊಂದು ಸಾಧನದಲ್ಲಿ ಪಾಸ್ವರ್ಡ್ ಉಳಿಸಬೇಕೆ?"</string> <string name="save_sign_in_on_other_device_title" msgid="2827990118560134692">"ಮತ್ತೊಂದು ಸಾಧನದಲ್ಲಿ ಸೈನ್-ಇನ್ ಅನ್ನು ಉಳಿಸಬೇಕೆ?"</string> diff --git a/packages/CredentialManager/res/values-pt-rBR/strings.xml b/packages/CredentialManager/res/values-pt-rBR/strings.xml index bc8a969ff44e..91385c6b8f62 100644 --- a/packages/CredentialManager/res/values-pt-rBR/strings.xml +++ b/packages/CredentialManager/res/values-pt-rBR/strings.xml @@ -48,7 +48,7 @@ <string name="passwords" msgid="5419394230391253816">"senhas"</string> <string name="sign_ins" msgid="4710739369149469208">"logins"</string> <string name="sign_in_info" msgid="2627704710674232328">"informações de login"</string> - <string name="save_credential_to_title" msgid="3172811692275634301">"Salvar <xliff:g id="CREDENTIALTYPES">%1$s</xliff:g> em"</string> + <string name="save_credential_to_title" msgid="3172811692275634301">"Salvar <xliff:g id="CREDENTIALTYPES">%1$s</xliff:g> no"</string> <string name="create_passkey_in_other_device_title" msgid="2360053098931886245">"Criar chave de acesso em outro dispositivo?"</string> <string name="save_password_on_other_device_title" msgid="5829084591948321207">"Salvar senha em outro dispositivo?"</string> <string name="save_sign_in_on_other_device_title" msgid="2827990118560134692">"Salvar credenciais de login em outro dispositivo?"</string> @@ -57,9 +57,9 @@ <string name="set_as_default" msgid="4415328591568654603">"Definir como padrão"</string> <string name="settings" msgid="6536394145760913145">"Configurações"</string> <string name="use_once" msgid="9027366575315399714">"Usar uma vez"</string> - <string name="more_options_usage_passwords_passkeys" msgid="3470113942332934279">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> senhas • <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> chaves de acesso"</string> - <string name="more_options_usage_passwords" msgid="1632047277723187813">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> senhas"</string> - <string name="more_options_usage_passkeys" msgid="5390320437243042237">"<xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g> chaves de acesso"</string> + <string name="more_options_usage_passwords_passkeys" msgid="3470113942332934279">"Senhas (<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g>) • Chaves de acesso (<xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g>)"</string> + <string name="more_options_usage_passwords" msgid="1632047277723187813">"Senhas (<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g>)"</string> + <string name="more_options_usage_passkeys" msgid="5390320437243042237">"Chaves de acesso (<xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g>)"</string> <string name="more_options_usage_credentials" msgid="1785697001787193984">"<xliff:g id="TOTALCREDENTIALSNUMBER">%1$s</xliff:g> credenciais"</string> <string name="passkey_before_subtitle" msgid="2448119456208647444">"Chave de acesso"</string> <string name="another_device" msgid="5147276802037801217">"Outro dispositivo"</string> diff --git a/packages/CredentialManager/res/values-pt/strings.xml b/packages/CredentialManager/res/values-pt/strings.xml index bc8a969ff44e..91385c6b8f62 100644 --- a/packages/CredentialManager/res/values-pt/strings.xml +++ b/packages/CredentialManager/res/values-pt/strings.xml @@ -48,7 +48,7 @@ <string name="passwords" msgid="5419394230391253816">"senhas"</string> <string name="sign_ins" msgid="4710739369149469208">"logins"</string> <string name="sign_in_info" msgid="2627704710674232328">"informações de login"</string> - <string name="save_credential_to_title" msgid="3172811692275634301">"Salvar <xliff:g id="CREDENTIALTYPES">%1$s</xliff:g> em"</string> + <string name="save_credential_to_title" msgid="3172811692275634301">"Salvar <xliff:g id="CREDENTIALTYPES">%1$s</xliff:g> no"</string> <string name="create_passkey_in_other_device_title" msgid="2360053098931886245">"Criar chave de acesso em outro dispositivo?"</string> <string name="save_password_on_other_device_title" msgid="5829084591948321207">"Salvar senha em outro dispositivo?"</string> <string name="save_sign_in_on_other_device_title" msgid="2827990118560134692">"Salvar credenciais de login em outro dispositivo?"</string> @@ -57,9 +57,9 @@ <string name="set_as_default" msgid="4415328591568654603">"Definir como padrão"</string> <string name="settings" msgid="6536394145760913145">"Configurações"</string> <string name="use_once" msgid="9027366575315399714">"Usar uma vez"</string> - <string name="more_options_usage_passwords_passkeys" msgid="3470113942332934279">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> senhas • <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> chaves de acesso"</string> - <string name="more_options_usage_passwords" msgid="1632047277723187813">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> senhas"</string> - <string name="more_options_usage_passkeys" msgid="5390320437243042237">"<xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g> chaves de acesso"</string> + <string name="more_options_usage_passwords_passkeys" msgid="3470113942332934279">"Senhas (<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g>) • Chaves de acesso (<xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g>)"</string> + <string name="more_options_usage_passwords" msgid="1632047277723187813">"Senhas (<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g>)"</string> + <string name="more_options_usage_passkeys" msgid="5390320437243042237">"Chaves de acesso (<xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g>)"</string> <string name="more_options_usage_credentials" msgid="1785697001787193984">"<xliff:g id="TOTALCREDENTIALSNUMBER">%1$s</xliff:g> credenciais"</string> <string name="passkey_before_subtitle" msgid="2448119456208647444">"Chave de acesso"</string> <string name="another_device" msgid="5147276802037801217">"Outro dispositivo"</string> diff --git a/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt b/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt index c477f30a1d2f..08846f0fed96 100644 --- a/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt +++ b/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt @@ -45,8 +45,8 @@ import androidx.credentials.CreateCredentialRequest import androidx.credentials.CreateCustomCredentialRequest import androidx.credentials.CreatePasswordRequest import androidx.credentials.CreatePublicKeyCredentialRequest +import androidx.credentials.CredentialOption import androidx.credentials.PasswordCredential -import androidx.credentials.PriorityHints import androidx.credentials.PublicKeyCredential import androidx.credentials.provider.CreateEntry import androidx.credentials.provider.RemoteEntry @@ -177,9 +177,9 @@ class GetFlowUtils { "androidx.credentials.BUNDLE_KEY_TYPE_PRIORITY_VALUE", when (option.type) { PasswordCredential.TYPE_PASSWORD_CREDENTIAL -> - PriorityHints.PRIORITY_PASSWORD_OR_SIMILAR + CredentialOption.PRIORITY_PASSWORD_OR_SIMILAR PublicKeyCredential.TYPE_PUBLIC_KEY_CREDENTIAL -> 100 - else -> PriorityHints.PRIORITY_DEFAULT + else -> CredentialOption.PRIORITY_DEFAULT } ) typePriorityMap[option.type] = priority @@ -349,8 +349,8 @@ class CreateFlowUtils { } is CreateCustomCredentialRequest -> { // TODO: directly use the display info once made public - val displayInfo = CreateCredentialRequest.DisplayInfo - .parseFromCredentialDataBundle(createCredentialRequest.credentialData) + val displayInfo = CreateCredentialRequest.DisplayInfo.createFrom( + createCredentialRequest.credentialData) ?: return null RequestDisplayInfo( title = displayInfo.userId.toString(), diff --git a/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt b/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt index 0da32bddd928..0b40d11ba224 100644 --- a/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt +++ b/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt @@ -71,8 +71,6 @@ import com.android.credentialmanager.model.CredentialType import java.util.ArrayList import java.util.Objects import java.util.concurrent.Executors -import org.json.JSONException -import org.json.JSONObject class CredentialAutofillService : AutofillService() { @@ -81,13 +79,6 @@ class CredentialAutofillService : AutofillService() { private const val SESSION_ID_KEY = "autofill_session_id" private const val REQUEST_ID_KEY = "autofill_request_id" - private const val CRED_HINT_PREFIX = "credential=" - private const val REQUEST_DATA_KEY = "requestData" - private const val CANDIDATE_DATA_KEY = "candidateQueryData" - private const val SYS_PROVIDER_REQ_KEY = "isSystemProviderRequired" - private const val CRED_OPTIONS_KEY = "credentialOptions" - private const val TYPE_KEY = "type" - private const val REQ_TYPE_KEY = "get" } override fun onFillRequest( @@ -740,7 +731,6 @@ class CredentialAutofillService : AutofillService() { uniqueAutofillIdsForRequest: MutableSet<AutofillId> ) { val traversedViewNodes: MutableSet<AutofillId> = mutableSetOf() - val credentialOptionsFromHints: MutableMap<String, CredentialOption> = mutableMapOf() val windowNodes: List<AssistStructure.WindowNode> = structure.run { (0 until windowNodeCount).map { getWindowNodeAt(it) } @@ -749,7 +739,7 @@ class CredentialAutofillService : AutofillService() { windowNodes.forEach { windowNode: AssistStructure.WindowNode -> traverseNodeForRequest( windowNode.rootViewNode, cmRequests, responseClientState, traversedViewNodes, - credentialOptionsFromHints, sessionId, uniqueAutofillIdsForRequest) + sessionId, uniqueAutofillIdsForRequest) } } @@ -758,7 +748,6 @@ class CredentialAutofillService : AutofillService() { cmRequests: MutableList<CredentialOption>, responseClientState: Bundle, traversedViewNodes: MutableSet<AutofillId>, - credentialOptionsFromHints: MutableMap<String, CredentialOption>, sessionId: Int, uniqueAutofillIdsForRequest: MutableSet<AutofillId> ) { @@ -769,9 +758,8 @@ class CredentialAutofillService : AutofillService() { responseClientState.putBoolean( WEBVIEW_REQUESTED_CREDENTIAL_KEY, true) } - cmRequests.addAll(getCredentialOptionsFromViewNode(viewNode, it, responseClientState, - traversedViewNodes, credentialOptionsFromHints, sessionId, - uniqueAutofillIdsForRequest) + cmRequests.addAll(getCredentialOptionsFromViewNode(viewNode, traversedViewNodes, + sessionId, uniqueAutofillIdsForRequest) ) traversedViewNodes.add(it) } @@ -783,18 +771,15 @@ class CredentialAutofillService : AutofillService() { children.forEach { childNode: AssistStructure.ViewNode -> traverseNodeForRequest( - childNode, cmRequests, responseClientState, traversedViewNodes, - credentialOptionsFromHints, sessionId, uniqueAutofillIdsForRequest + childNode, cmRequests, responseClientState, traversedViewNodes, sessionId, + uniqueAutofillIdsForRequest ) } } private fun getCredentialOptionsFromViewNode( viewNode: AssistStructure.ViewNode, - autofillId: AutofillId, - responseClientState: Bundle, traversedViewNodes: MutableSet<AutofillId>, - credentialOptionsFromHints: MutableMap<String, CredentialOption>, sessionId: Int, uniqueAutofillIdsForRequest: MutableSet<AutofillId> ): MutableList<CredentialOption> { @@ -830,85 +815,6 @@ class CredentialAutofillService : AutofillService() { } } } - // TODO(b/325502552): clean up cred option logic in autofill hint - val credentialHints: MutableList<String> = mutableListOf() - - if (viewNode.autofillHints != null) { - for (hint in viewNode.autofillHints!!) { - if (hint.startsWith(CRED_HINT_PREFIX)) { - credentialHints.add(hint.substringAfter(CRED_HINT_PREFIX)) - if (viewNode.webDomain != null) { - responseClientState.putBoolean(WEBVIEW_REQUESTED_CREDENTIAL_KEY, true) - } - } - } - } - - for (credentialHint in credentialHints) { - try { - convertJsonToCredentialOption( - credentialHint, autofillId, credentialOptionsFromHints) - .let { credentialOptions.addAll(it) } - } catch (e: JSONException) { - Log.i(TAG, "Exception while parsing response: " + e.message) - } - } return credentialOptions } - - private fun convertJsonToCredentialOption( - jsonString: String, - autofillId: AutofillId, - credentialOptionsFromHints: MutableMap<String, CredentialOption> - ): List<CredentialOption> { - val credentialOptions: MutableList<CredentialOption> = mutableListOf() - - val json = JSONObject(jsonString) - val jsonGet = json.getJSONObject(REQ_TYPE_KEY) - val options = jsonGet.getJSONArray(CRED_OPTIONS_KEY) - for (i in 0 until options.length()) { - val option = options.getJSONObject(i) - val optionString = option.toString() - credentialOptionsFromHints[optionString] - ?.let { credentialOption -> - // if the current credential option was seen before, add the current - // viewNode to the credential option, but do not add it to the option list - // again. This will result in the same result as deduping based on - // traversed viewNode. - credentialOption.candidateQueryData.getParcelableArrayList( - CredentialProviderService.EXTRA_AUTOFILL_ID, AutofillId::class.java) - ?.let { - it.add(autofillId) - credentialOption.candidateQueryData.putParcelableArrayList( - CredentialProviderService.EXTRA_AUTOFILL_ID, it) - } - } ?: run { - val candidateBundle = convertJsonToBundle(option.getJSONObject(CANDIDATE_DATA_KEY)) - candidateBundle.putParcelableArrayList( - CredentialProviderService.EXTRA_AUTOFILL_ID, - arrayListOf(autofillId)) - val credentialOption = CredentialOption( - option.getString(TYPE_KEY), - convertJsonToBundle(option.getJSONObject(REQUEST_DATA_KEY)), - candidateBundle, - option.getBoolean(SYS_PROVIDER_REQ_KEY), - ) - credentialOptions.add(credentialOption) - credentialOptionsFromHints[optionString] = credentialOption - } - } - return credentialOptions - } - - private fun convertJsonToBundle(json: JSONObject): Bundle { - val result = Bundle() - json.keys().forEach { - val v = json.get(it) - when (v) { - is String -> result.putString(it, v) - is Boolean -> result.putBoolean(it, v) - } - } - return result - } }
\ No newline at end of file diff --git a/packages/CredentialManager/src/com/android/credentialmanager/common/ui/BottomSheet.kt b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/BottomSheet.kt index f65a1b750e5e..c48e7e40f77f 100644 --- a/packages/CredentialManager/src/com/android/credentialmanager/common/ui/BottomSheet.kt +++ b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/BottomSheet.kt @@ -71,7 +71,7 @@ fun ModalBottomSheet( }, scrimColor = MaterialTheme.colorScheme.scrim.copy(alpha = .32f), shape = EntryShape.TopRoundedCorner, - windowInsets = WindowInsets.navigationBars, + contentWindowInsets = { WindowInsets.navigationBars }, dragHandle = null, // Never take over the full screen. We always want to leave some top scrim space // for exiting and viewing the underlying app to help a user gain context. diff --git a/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetModel.kt b/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetModel.kt index 19f5a99f46fa..314cc0547b89 100644 --- a/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetModel.kt +++ b/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetModel.kt @@ -19,7 +19,7 @@ package com.android.credentialmanager.getflow import android.credentials.flags.Flags.selectorUiImprovementsEnabled import android.credentials.flags.Flags.credmanBiometricApiEnabled import android.graphics.drawable.Drawable -import androidx.credentials.PriorityHints +import androidx.credentials.CredentialOption import com.android.credentialmanager.R import com.android.credentialmanager.model.CredentialType import com.android.credentialmanager.model.get.ProviderInfo @@ -322,10 +322,10 @@ internal class CredentialEntryInfoComparatorByTypeThenTimestamp( // First rank by priorities of each credential type. if (p0.rawCredentialType != p1.rawCredentialType) { val p0Priority = typePriorityMap.getOrDefault( - p0.rawCredentialType, PriorityHints.PRIORITY_DEFAULT + p0.rawCredentialType, CredentialOption.PRIORITY_DEFAULT ) val p1Priority = typePriorityMap.getOrDefault( - p1.rawCredentialType, PriorityHints.PRIORITY_DEFAULT + p1.rawCredentialType, CredentialOption.PRIORITY_DEFAULT ) if (p0Priority < p1Priority) { return -1 diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/CredentialSelectorActivity.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/CredentialSelectorActivity.kt index 652e62cb26b4..5728c8cb9263 100644 --- a/packages/CredentialManager/wear/src/com/android/credentialmanager/CredentialSelectorActivity.kt +++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/CredentialSelectorActivity.kt @@ -18,6 +18,7 @@ package com.android.credentialmanager import android.content.Intent import android.os.Bundle +import android.util.Log import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.activity.viewModels @@ -33,6 +34,7 @@ class CredentialSelectorActivity : Hilt_CredentialSelectorActivity() { @OptIn(ExperimentalHorologistApi::class) override fun onCreate(savedInstanceState: Bundle?) { + Log.d(TAG, "onCreate, intent: $intent") super.onCreate(savedInstanceState) setTheme(android.R.style.Theme_DeviceDefault) setContent { @@ -47,6 +49,7 @@ class CredentialSelectorActivity : Hilt_CredentialSelectorActivity() { } override fun onNewIntent(intent: Intent) { + Log.d(TAG, "onNewIntent, intent: $intent") super.onNewIntent(intent) setIntent(intent) viewModel.updateRequest(intent) diff --git a/packages/CtsShim/apk/arm/CtsShim.apk b/packages/CtsShim/apk/arm/CtsShim.apk Binary files differindex 38a6d131d7e6..5ab190d19514 100644 --- a/packages/CtsShim/apk/arm/CtsShim.apk +++ b/packages/CtsShim/apk/arm/CtsShim.apk diff --git a/packages/CtsShim/apk/arm/CtsShimPriv.apk b/packages/CtsShim/apk/arm/CtsShimPriv.apk Binary files differindex bcd9836733d3..51a8c460e8e7 100644 --- a/packages/CtsShim/apk/arm/CtsShimPriv.apk +++ b/packages/CtsShim/apk/arm/CtsShimPriv.apk diff --git a/packages/CtsShim/apk/x86/CtsShim.apk b/packages/CtsShim/apk/x86/CtsShim.apk Binary files differindex 38a6d131d7e6..5ab190d19514 100644 --- a/packages/CtsShim/apk/x86/CtsShim.apk +++ b/packages/CtsShim/apk/x86/CtsShim.apk diff --git a/packages/CtsShim/apk/x86/CtsShimPriv.apk b/packages/CtsShim/apk/x86/CtsShimPriv.apk Binary files differindex f77890457975..fcd0273abb6e 100644 --- a/packages/CtsShim/apk/x86/CtsShimPriv.apk +++ b/packages/CtsShim/apk/x86/CtsShimPriv.apk diff --git a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java index 635dc420f18c..5a530c4deefb 100644 --- a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java +++ b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java @@ -60,7 +60,6 @@ import android.os.RemoteException; import android.os.image.DynamicSystemClient; import android.os.image.DynamicSystemManager; import android.text.TextUtils; -import android.util.EventLog; import android.util.Log; import android.widget.Toast; @@ -104,14 +103,6 @@ public class DynamicSystemInstallationService extends Service private static final String NOTIFICATION_CHANNEL_ID = "com.android.dynsystem"; private static final int NOTIFICATION_ID = 1; - /* - * Event log tags - */ - private static final int EVENT_DSU_PROGRESS_UPDATE = 120000; - private static final int EVENT_DSU_INSTALL_COMPLETE = 120001; - private static final int EVENT_DSU_INSTALL_FAILED = 120002; - private static final int EVENT_DSU_INSTALL_INSUFFICIENT_SPACE = 120003; - protected static void logEventProgressUpdate( String partitionName, long installedBytes, @@ -119,8 +110,7 @@ public class DynamicSystemInstallationService extends Service int partitionNumber, int totalPartitionNumber, int totalProgressPercentage) { - EventLog.writeEvent( - EVENT_DSU_PROGRESS_UPDATE, + EventLogTags.writeDsuProgressUpdate( partitionName, installedBytes, totalBytes, @@ -130,15 +120,15 @@ public class DynamicSystemInstallationService extends Service } protected static void logEventComplete() { - EventLog.writeEvent(EVENT_DSU_INSTALL_COMPLETE); + EventLogTags.writeDsuInstallComplete(); } protected static void logEventFailed(String cause) { - EventLog.writeEvent(EVENT_DSU_INSTALL_FAILED, cause); + EventLogTags.writeDsuInstallFailed(cause); } protected static void logEventInsufficientSpace() { - EventLog.writeEvent(EVENT_DSU_INSTALL_INSUFFICIENT_SPACE); + EventLogTags.writeDsuInstallInsufficientSpace(); } /* diff --git a/packages/EasterEgg/AndroidManifest.xml b/packages/EasterEgg/AndroidManifest.xml index d1db237966d5..754abb2f76be 100644 --- a/packages/EasterEgg/AndroidManifest.xml +++ b/packages/EasterEgg/AndroidManifest.xml @@ -33,16 +33,36 @@ <uses-permission android:name="android.permission.POST_NOTIFICATIONS" /> <application - android:icon="@drawable/android14_patch_adaptive" + android:icon="@drawable/android15_patch_adaptive" android:label="@string/app_name"> - <!-- Android U easter egg --> + <!-- Android V easter egg: Daydream version of Landroid + (must be enabled by unlocking the egg) --> + <service + android:name=".landroid.DreamUniverse" + android:exported="true" + android:icon="@drawable/android15_patch_adaptive" + android:label="@string/v_egg_name" + android:description="@string/dream_description" + android:enabled="false" + android:permission="android.permission.BIND_DREAM_SERVICE" + > + + <intent-filter> + <action android:name="android.service.dreams.DreamService" /> + <category android:name="android.intent.category.DEFAULT" /> + </intent-filter> + <meta-data + android:name="android.service.dream" + android:resource="@xml/landroid_dream"/> + </service> + <!-- Android U easter egg --> <activity android:name=".landroid.MainActivity" android:exported="true" android:label="@string/u_egg_name" - android:icon="@drawable/android14_patch_adaptive" + android:icon="@drawable/android15_patch_adaptive" android:configChanges="orientation|screenLayout|screenSize|density" android:theme="@android:style/Theme.DeviceDefault.NoActionBar.Fullscreen"> <intent-filter> @@ -52,7 +72,6 @@ </intent-filter> </activity> - <!-- Android Q easter egg --> <activity android:name=".quares.QuaresActivity" diff --git a/packages/EasterEgg/res/drawable/android15_patch_adaptive.xml b/packages/EasterEgg/res/drawable/android15_patch_adaptive.xml new file mode 100644 index 000000000000..d9492000beeb --- /dev/null +++ b/packages/EasterEgg/res/drawable/android15_patch_adaptive.xml @@ -0,0 +1,21 @@ +<?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. +--> +<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android"> + <background android:drawable="@drawable/android15_patch_adaptive_background"/> + <foreground android:drawable="@drawable/android15_patch_adaptive_foreground"/> + <monochrome android:drawable="@drawable/android15_patch_monochrome"/> +</adaptive-icon> diff --git a/packages/EasterEgg/res/drawable/android15_patch_adaptive_background.xml b/packages/EasterEgg/res/drawable/android15_patch_adaptive_background.xml new file mode 100644 index 000000000000..642b30aedf4c --- /dev/null +++ b/packages/EasterEgg/res/drawable/android15_patch_adaptive_background.xml @@ -0,0 +1,75 @@ +<?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="108dp" + android:height="108dp" + android:viewportWidth="108" + android:viewportHeight="108"> + <!-- space --> + <path + android:pathData="M0,0h108v108h-108z" + android:fillColor="#202124"/> + <!-- stars --> + <group> + <path + android:pathData="M32,34 h1v1h-1z" + android:fillColor="#ffffff"/> + <path + android:pathData="M33,61 h1v1h-1z" + android:fillColor="#ffffff"/> + <path + android:pathData="M71,34 h1v1h-1z" + android:fillColor="#ffffff"/> + <path + android:pathData="M62,56 h1v1h-1z" + android:fillColor="#ffffff"/> + <path + android:pathData="M68,47 h1v1h-1z" + android:fillColor="#ffffff"/> + <path + android:pathData="M72,55 h1v1h-1z" + android:fillColor="#ffffff"/> + <path + android:pathData="M39,36 h1v1h-1z" + android:fillColor="#ffffff"/> + + <path + android:pathData="M72,49 h2v2h-2z" + android:fillColor="#ffffff"/> + <path + android:pathData="M46,53 h2v2h-2z" + android:fillColor="#ffffff"/> + <path + android:pathData="M32,45 h2v2h-2z" + android:fillColor="#ffffff"/> + <path + android:pathData="M43,37 h2v2h-2z" + android:fillColor="#ffffff"/> + <path + android:pathData="M78,51 h2v2h-2z" + android:fillColor="#ffffff"/> + <path + android:pathData="M34,51 h2v2h-2z" + android:fillColor="#ffffff"/> + <path + android:pathData="M76,41 h2v2h-2z" + android:fillColor="#ffffff"/> + <path + android:pathData="M39,46 h2v2h-2z" + android:fillColor="#ffffff"/> + </group> +</vector> diff --git a/packages/EasterEgg/res/drawable/android15_patch_adaptive_foreground.xml b/packages/EasterEgg/res/drawable/android15_patch_adaptive_foreground.xml new file mode 100644 index 000000000000..1100eb725780 --- /dev/null +++ b/packages/EasterEgg/res/drawable/android15_patch_adaptive_foreground.xml @@ -0,0 +1,60 @@ +<?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="108dp" + android:height="108dp" + android:viewportWidth="108" + android:viewportHeight="108"> + + <!-- zoomies --> + <group> + <path + android:pathData="M53,42C52.21,50.58 46.46,68.95 32.11,74.63C19.22,79.75 5.77,82.32 1.19,83.19C0.68,83.29 0.28,83.36 0,83.42V108H54H108V83.42C107.72,83.36 107.32,83.29 106.81,83.19C102.23,82.32 88.78,79.75 75.89,74.63C61.54,68.95 55.79,50.58 55,42H54H53Z" + android:fillColor="#C6FF00" + android:fillType="evenOdd"/> + <path + android:pathData="M53.25,42C52.88,50.53 50.44,69.01 43.68,74.67C36.91,80.33 32.65,82.86 31.37,83.41L54,102.87L76.63,83.41C75.35,82.86 71.09,80.33 64.32,74.67C57.56,69.01 55.12,50.53 54.75,42H54H53.25Z" + android:fillColor="#ffffff" + android:fillType="evenOdd"/> + <path + android:pathData="M54,42m-1,0a1,1 0,1 1,2 0a1,1 0,1 1,-2 0" + android:fillColor="#ffffff"/> + </group> + <group> + <!-- head! it's like sputnik --> + <path + android:pathData="M54,94.25m-26.25,0a26.25,26.25 0,1 1,52.5 0a26.25,26.25 0,1 1,-52.5 0" + android:fillColor="#34A853"/> + <!-- ant --> + <path + android:pathData="M38,63.5L44.5,74.5" + android:strokeWidth="5" + android:fillColor="#00000000" + android:strokeColor="#34A853" + android:strokeLineCap="round"/> + <path + android:pathData="M70,63.5L63.5,74.5" + android:strokeWidth="5" + android:fillColor="#00000000" + android:strokeColor="#34A853" + android:strokeLineCap="round"/> + </group> + <!-- spaceship --> + <path + android:pathData="M54,34C52.34,34 51,35.29 51,36.88V40.44C51,40.75 51.25,41 51.56,41C51.87,41 52.13,40.75 52.13,40.44V39.48C52.13,38.87 52.63,38.37 53.25,38.37H54.75C55.37,38.37 55.87,38.87 55.87,39.48V40.44C55.87,40.75 56.13,41 56.44,41C56.75,41 57,40.75 57,40.44V36.88C57,35.29 55.66,34 54,34H54Z" + android:fillColor="#E9F3EB"/> +</vector> diff --git a/packages/EasterEgg/res/drawable/android15_patch_monochrome.xml b/packages/EasterEgg/res/drawable/android15_patch_monochrome.xml new file mode 100644 index 000000000000..a91cc864e7cd --- /dev/null +++ b/packages/EasterEgg/res/drawable/android15_patch_monochrome.xml @@ -0,0 +1,119 @@ +<?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="108dp" + android:height="108dp" + android:viewportWidth="108" + android:viewportHeight="108"> + <group> + <path + android:pathData=" + M54,94.25 + m-26.25,0 + a26.25,26.25 0,1 1,52.5 0 + a26.25,26.25 0,1 1,-52.5 0 + " + android:fillColor="#000000"/> + <path + android:pathData="M38,63.5L44.5,74.5" + android:strokeWidth="5" + android:fillColor="#00000000" + android:strokeColor="#000000" + android:strokeLineCap="round"/> + <path + android:pathData="M70,63.5L63.5,74.5" + android:strokeWidth="5" + android:fillColor="#00000000" + android:strokeColor="#000000" + android:strokeLineCap="round"/> + + <path + android:pathData=" + M54,34 + C52.34,34 51,35.29 51,36.88 + V40.44 + C51,40.75 51.25,41 51.56,41 + C51.87,41 52.13,40.75 52.13,40.44 + V39.48 + C52.13,38.87 52.63,38.37 53.25,38.37 + H54.75 + C55.37,38.37 55.87,38.87 55.87,39.48 + V40.44 + C55.87,40.75 56.13,41 56.44,41 + C56.75,41 57,40.75 57,40.44 + V36.88 + C57,35.29 55.66,34 54,34 + H54 + Z + " + android:fillColor="#34A853"/> + <path + android:strokeWidth="1" + android:pathData="M54,40V67" + android:fillColor="#00000000" + android:strokeColor="#40FFFFFF" + /> + + <path + android:pathData="M32,34 h1v1h-1z" + android:fillColor="#ffffff"/> + <path + android:pathData="M33,61 h1v1h-1z" + android:fillColor="#ffffff"/> + <path + android:pathData="M71,34 h1v1h-1z" + android:fillColor="#ffffff"/> + <path + android:pathData="M62,56 h1v1h-1z" + android:fillColor="#ffffff"/> + <path + android:pathData="M68,47 h1v1h-1z" + android:fillColor="#ffffff"/> + <path + android:pathData="M72,55 h1v1h-1z" + android:fillColor="#ffffff"/> + <path + android:pathData="M39,36 h1v1h-1z" + android:fillColor="#ffffff"/> + + <path + android:pathData="M72,49 h2v2h-2z" + android:fillColor="#ffffff"/> + <path + android:pathData="M46,53 h2v2h-2z" + android:fillColor="#ffffff"/> + <path + android:pathData="M32,45 h2v2h-2z" + android:fillColor="#ffffff"/> + <path + android:pathData="M43,37 h2v2h-2z" + android:fillColor="#ffffff"/> + <path + android:pathData="M78,51 h2v2h-2z" + android:fillColor="#ffffff"/> + <path + android:pathData="M34,51 h2v2h-2z" + android:fillColor="#ffffff"/> + <path + android:pathData="M76,41 h2v2h-2z" + android:fillColor="#ffffff"/> + <path + android:pathData="M39,46 h2v2h-2z" + android:fillColor="#ffffff"/> + + </group> +</vector> diff --git a/packages/EasterEgg/res/values/landroid_strings.xml b/packages/EasterEgg/res/values/landroid_strings.xml index 1394f2f55868..1bbfcca9d899 100644 --- a/packages/EasterEgg/res/values/landroid_strings.xml +++ b/packages/EasterEgg/res/values/landroid_strings.xml @@ -1,21 +1,13 @@ -<?xml version="1.0" encoding="utf-8"?><!-- - 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. ---> - +<?xml version="1.0" encoding="utf-8"?> <resources> - <string name="u_egg_name" translatable="false">Android 14 Easter Egg</string> + + <!-- No Android's Sky --> + <!-- Char Star Field --> + <!-- V-leet: Harmless --> + <!-- Contemplating My Orbital Mechanics --> + <string name="u_egg_name" translatable="false">Landroid</string> + <string name="v_egg_name" translatable="false">Landroid</string> + <string name="dream_description" translatable="false">---- AUTOPILOT ENGAGED ----</string> <string-array name="planet_descriptors" translatable="false"> <item>earthy</item> @@ -365,7 +357,64 @@ <item>relaxed</item> <item>skunky</item> <item>breezy</item> - <item>soup </item> + <item>soup</item> + </string-array> + + <string-array name="fauna_generic_plurals" translatable="false"> + <item>fauna</item> + <item>animals</item> + <item>locals</item> + <item>creatures</item> + <item>critters</item> + <item>wildlife</item> + <item>specimens</item> + <item>life</item> + <item>cells</item> + </string-array> + + <string-array name="flora_generic_plurals" translatable="false"> + <item>flora</item> + <item>plants</item> + <item>flowers</item> + <item>trees</item> + <item>mosses</item> + <item>specimens</item> + <item>life</item> + <item>cells</item> + </string-array> + + <string-array name="atmo_generic_plurals" translatable="false"> + <item>air</item> + <item>atmosphere</item> + <item>clouds</item> + <item>atmo</item> + <item>gases</item> + </string-array> + + <string-array name="activities" translatable="false"> + <item>refueling</item> + <item>sightseeing</item> + <item>vacationing</item> + <item>luncheoning</item> + <item>recharging</item> + <item>taking up space</item> + <item>reticulating space splines</item> + <item>using facilities</item> + <item>spelunking</item> + <item>repairing</item> + <item>herding {fauna}</item> + <item>taming {fauna}</item> + <item>breeding {fauna}</item> + <item>singing lullabies to {fauna}</item> + <item>singing lullabies to {flora}</item> + <item>singing lullabies to the {planet}</item> + <item>gardening {flora}</item> + <item>collecting {flora}</item> + <item>surveying the {planet}</item> + <item>mapping the {planet}</item> + <item>breathing {atmo}</item> + <item>reprocessing {atmo}</item> + <item>bottling {atmo}</item> </string-array> </resources> diff --git a/packages/EasterEgg/res/xml/landroid_dream.xml b/packages/EasterEgg/res/xml/landroid_dream.xml new file mode 100644 index 000000000000..adf82bd9a4cc --- /dev/null +++ b/packages/EasterEgg/res/xml/landroid_dream.xml @@ -0,0 +1,18 @@ +<?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. +--> +<dream xmlns:android="http://schemas.android.com/apk/res/android" + android:previewImage="@*android:drawable/platlogo" /> diff --git a/packages/EasterEgg/src/com/android/egg/ComponentActivationActivity.java b/packages/EasterEgg/src/com/android/egg/ComponentActivationActivity.java index 5820b5a75894..30320d6a8861 100644 --- a/packages/EasterEgg/src/com/android/egg/ComponentActivationActivity.java +++ b/packages/EasterEgg/src/com/android/egg/ComponentActivationActivity.java @@ -18,11 +18,14 @@ package com.android.egg; import android.app.Activity; import android.content.ComponentName; +import android.content.Context; import android.content.pm.PackageManager; import android.provider.Settings; import android.util.Log; import android.widget.Toast; +import com.android.egg.flags.Flags; +import com.android.egg.landroid.DreamUniverse; import com.android.egg.neko.NekoControlsService; import com.android.egg.widget.PaintChipsActivity; import com.android.egg.widget.PaintChipsWidget; @@ -33,7 +36,9 @@ import com.android.egg.widget.PaintChipsWidget; public class ComponentActivationActivity extends Activity { private static final String TAG = "EasterEgg"; + // check PlatLogoActivity.java for these private static final String S_EGG_UNLOCK_SETTING = "egg_mode_s"; + private static final String V_EGG_UNLOCK_SETTING = "egg_mode_v"; private void toastUp(String s) { Toast toast = Toast.makeText(this, s, Toast.LENGTH_SHORT); @@ -44,14 +49,39 @@ public class ComponentActivationActivity extends Activity { public void onStart() { super.onStart(); - final PackageManager pm = getPackageManager(); - final ComponentName[] cns = new ComponentName[] { - new ComponentName(this, NekoControlsService.class), - new ComponentName(this, PaintChipsActivity.class), - new ComponentName(this, PaintChipsWidget.class) - }; - final long unlockValue = Settings.System.getLong(getContentResolver(), - S_EGG_UNLOCK_SETTING, 0); + lockUnlockComponents(this); + + finish(); + } + + /** + * Check easter egg unlock state and update unlockable components to match. + */ + public static void lockUnlockComponents(Context context) { + final PackageManager pm = context.getPackageManager(); + final ComponentName[] cns; + final String unlockSettingsKey; + final boolean shouldReLock; + final long unlockValue; + if (Flags.flagFlag()) { + unlockSettingsKey = V_EGG_UNLOCK_SETTING; + unlockValue = 1; // since we're not toggling we actually don't need to check the setting + shouldReLock = false; + cns = new ComponentName[]{ + new ComponentName(context, DreamUniverse.class) + }; + } else { + unlockSettingsKey = S_EGG_UNLOCK_SETTING; + unlockValue = Settings.System.getLong(context.getContentResolver(), + unlockSettingsKey, 0); + shouldReLock = true; + cns = new ComponentName[]{ + new ComponentName(context, NekoControlsService.class), + new ComponentName(context, PaintChipsActivity.class), + new ComponentName(context, PaintChipsWidget.class), + new ComponentName(context, DreamUniverse.class) + }; + } for (ComponentName cn : cns) { final boolean componentEnabled = pm.getComponentEnabledSetting(cn) == PackageManager.COMPONENT_ENABLED_STATE_ENABLED; @@ -77,7 +107,5 @@ public class ComponentActivationActivity extends Activity { } } } - - finish(); } } diff --git a/packages/EasterEgg/src/com/android/egg/landroid/Autopilot.kt b/packages/EasterEgg/src/com/android/egg/landroid/Autopilot.kt new file mode 100644 index 000000000000..f71abee6fb43 --- /dev/null +++ b/packages/EasterEgg/src/com/android/egg/landroid/Autopilot.kt @@ -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 com.android.egg.landroid + +import kotlin.math.min +import kotlin.math.sign + +class Autopilot(val ship: Spacecraft, val universe: Universe) : Entity { + val BRAKING_TIME = 5f + val SIGHTSEEING_TIME = 10f + val STRATEGY_MIN_TIME = 0.5f + + var enabled = false + + var target: Planet? = null + + var landingAltitude = 0f + + var nextStrategyTime = 0f + + var brakingDistance = 0f + + // used by rendering + var leadingPos = Vec2.Zero + var leadingVector = Vec2.Zero + + val telemetry: String + get() = + listOf( + "---- AUTOPILOT ENGAGED ----", + "TGT: " + (target?.name?.toUpperCase() ?: "SELECTING..."), + "EXE: $strategy" + if (debug.isNotEmpty()) " ($debug)" else "", + ) + .joinToString("\n") + + private var strategy: String = "NONE" + private var debug: String = "" + + override fun update(sim: Simulator, dt: Float) { + if (!enabled) return + + if (sim.now < nextStrategyTime) { + return + } + + val currentStrategy = strategy + + if (ship.landing != null) { + if (target != null) { + strategy = "LANDED" + debug = "" + // we just got here. see the sights. + target = null + landingAltitude = 0f + nextStrategyTime = sim.now + SIGHTSEEING_TIME + } else { + // full power until we blast off + ship.thrust = Vec2.makeWithAngleMag(ship.angle, 1f) + + strategy = "LAUNCHING" + debug = "" + nextStrategyTime = sim.now + 2f + } + } else { + // select new target + + if (target == null) { + // testing: target the first planet + // target = universe.planets[0] + + // target the nearest unexplored planet + target = + universe.planets + .sortedBy { (it.pos - ship.pos).mag() } + .firstOrNull { !it.explored } + brakingDistance = 0f + + // if we've explored them all, pick one at random + if (target == null) target = universe.planets.random() + } + + target?.let { target -> // should be nonnull + val shipV = ship.velocity + val targetV = target.velocity + val targetVector = (target.pos - ship.pos) + val altitude = targetVector.mag() - target.radius + + landingAltitude = min(target.radius, 100f) + + // the following is in the moving reference frame of the target + val relativeV: Vec2 = shipV - targetV + val projection = relativeV.dot(targetVector / targetVector.mag()) + val relativeSpeed = relativeV.mag() * projection.sign + val timeToTarget = if (relativeSpeed != 0f) altitude / relativeSpeed else 1_000f + + val newBrakingDistance = + BRAKING_TIME * if (relativeSpeed > 0) relativeSpeed else MAIN_ENGINE_ACCEL + brakingDistance = + expSmooth(brakingDistance, newBrakingDistance, dt = sim.dt, speed = 5f) + + // We're going to aim at where the target will be, but we want to make sure to + // compute + leadingPos = + target.pos + + Vec2.makeWithAngleMag( + target.velocity.angle(), + min(altitude / 2, target.velocity.mag()) + ) + leadingVector = leadingPos - ship.pos + + if (altitude < landingAltitude) { + strategy = "LANDING" + // Strategy: zero thrust, face away, prepare for landing + + ship.angle = (ship.pos - target.pos).angle() // point away from ground + ship.thrust = Vec2.Zero + } else { + if (relativeSpeed < 0 || altitude > brakingDistance) { + strategy = "CHASING" + // Strategy: Make tracks. We are either a long way away, or falling behind. + ship.angle = leadingVector.angle() + + ship.thrust = Vec2.makeWithAngleMag(ship.angle, 1.0f) + } else { + strategy = "APPROACHING" + // Strategy: Just slow down. If we get caught in the gravity well, it will + // gradually start pulling us more in the direction of the planet, which + // will create a graceful deceleration + ship.angle = (-ship.velocity).angle() + + // We want to bleed off velocity over time. Specifically, relativeSpeed px/s + // over timeToTarget seconds. + val decel = relativeSpeed / timeToTarget + val decelThrust = + decel / MAIN_ENGINE_ACCEL * 0.9f // not quite slowing down enough + ship.thrust = Vec2.makeWithAngleMag(ship.angle, decelThrust) + } + } + debug = ("DV=%.0f D=%.0f T%+.1f").format(relativeSpeed, altitude, timeToTarget) + } + if (strategy != currentStrategy) { + nextStrategyTime = sim.now + STRATEGY_MIN_TIME + } + } + } + + override fun postUpdate(sim: Simulator, dt: Float) { + if (!enabled) return + } +} diff --git a/packages/EasterEgg/src/com/android/egg/landroid/Colors.kt b/packages/EasterEgg/src/com/android/egg/landroid/Colors.kt index f5657ae6c0c3..24c49754276a 100644 --- a/packages/EasterEgg/src/com/android/egg/landroid/Colors.kt +++ b/packages/EasterEgg/src/com/android/egg/landroid/Colors.kt @@ -19,11 +19,22 @@ package com.android.egg.landroid import androidx.compose.ui.graphics.Color /** Various UI colors. */ -object Colors { - val Eigengrau = Color(0xFF16161D) - val Eigengrau2 = Color(0xFF292936) - val Eigengrau3 = Color(0xFF3C3C4F) - val Eigengrau4 = Color(0xFFA7A7CA) +class Colors { + object Android { + val Green = Color(0xFF34A853) + val Blue = Color(0xFF4285F4) + val Mint = Color(0xFFE8F5E9) + val Chartreuse = Color(0xFFC6FF00) + } + companion object { + val Eigengrau = Color(0xFF16161D) + val Eigengrau2 = Color(0xFF292936) + val Eigengrau3 = Color(0xFF3C3C4F) + val Eigengrau4 = Color(0xFFA7A7CA) - val Console = Color(0xFFB7B7FF) + val Console = Color(0xFFB7B7FF) + val Autopilot = Android.Blue + val Track = Android.Green + val Flag = Android.Chartreuse + } } diff --git a/packages/EasterEgg/src/com/android/egg/landroid/DreamUniverse.kt b/packages/EasterEgg/src/com/android/egg/landroid/DreamUniverse.kt new file mode 100644 index 000000000000..8c87c5d4af7b --- /dev/null +++ b/packages/EasterEgg/src/com/android/egg/landroid/DreamUniverse.kt @@ -0,0 +1,120 @@ +/* + * 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.egg.landroid + +import android.service.dreams.DreamService +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.runtime.mutableStateOf +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.ComposeView +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.LifecycleRegistry +import androidx.lifecycle.setViewTreeLifecycleOwner +import androidx.savedstate.SavedStateRegistryController +import androidx.savedstate.SavedStateRegistryOwner +import androidx.savedstate.setViewTreeSavedStateRegistryOwner +import androidx.window.layout.FoldingFeature +import kotlin.random.Random + +class DreamUniverse : DreamService() { + private var foldState = mutableStateOf<FoldingFeature?>(null) // unused + + private val lifecycleOwner = + object : SavedStateRegistryOwner { + override val lifecycle = LifecycleRegistry(this) + override val savedStateRegistry + get() = savedStateRegistryController.savedStateRegistry + + private val savedStateRegistryController = + SavedStateRegistryController.create(this).apply { performAttach() } + + fun onCreate() { + savedStateRegistryController.performRestore(null) + lifecycle.currentState = Lifecycle.State.CREATED + } + + fun onStart() { + lifecycle.currentState = Lifecycle.State.STARTED + } + + fun onStop() { + lifecycle.currentState = Lifecycle.State.CREATED + } + } + + override fun onAttachedToWindow() { + super.onAttachedToWindow() + + val universe = VisibleUniverse(namer = Namer(resources), randomSeed = randomSeed()) + + isInteractive = false + + if (TEST_UNIVERSE) { + universe.initTest() + } else { + universe.initRandom() + + // We actually don't want the deterministic random position of the ship, we want + // true randomness to keep things interesting. So use Random (not universe.rng). + universe.ship.pos = + universe.star.pos + + Vec2.makeWithAngleMag( + Random.nextFloat() * PI2f, + Random.nextFloatInRange( + PLANET_ORBIT_RANGE.start, + PLANET_ORBIT_RANGE.endInclusive + ) + ) + } + + // enable autopilot in screensaver mode + val autopilot = Autopilot(universe.ship, universe) + universe.ship.autopilot = autopilot + universe.add(autopilot) + autopilot.enabled = true + + // much more visually interesting in a screensaver context + DYNAMIC_ZOOM = true + + val composeView = ComposeView(this) + composeView.setContent { + Spaaaace(modifier = Modifier.fillMaxSize(), u = universe, foldState = foldState) + DebugText(DEBUG_TEXT) + Telemetry(universe) + } + + composeView.setViewTreeLifecycleOwner(lifecycleOwner) + composeView.setViewTreeSavedStateRegistryOwner(lifecycleOwner) + + setContentView(composeView) + } + + override fun onCreate() { + super.onCreate() + lifecycleOwner.onCreate() + } + + override fun onDreamingStarted() { + super.onDreamingStarted() + lifecycleOwner.onStart() + } + + override fun onDreamingStopped() { + super.onDreamingStopped() + lifecycleOwner.onStop() + } +} diff --git a/packages/EasterEgg/src/com/android/egg/landroid/MainActivity.kt b/packages/EasterEgg/src/com/android/egg/landroid/MainActivity.kt index 5a9b8141bb40..79f8b5fc6ecd 100644 --- a/packages/EasterEgg/src/com/android/egg/landroid/MainActivity.kt +++ b/packages/EasterEgg/src/com/android/egg/landroid/MainActivity.kt @@ -21,12 +21,10 @@ import android.os.Bundle import android.util.Log import androidx.activity.ComponentActivity import androidx.activity.compose.setContent +import androidx.activity.enableEdgeToEdge import androidx.compose.animation.AnimatedVisibility -import androidx.compose.animation.core.CubicBezierEasing import androidx.compose.animation.core.animateFloatAsState -import androidx.compose.animation.core.tween import androidx.compose.animation.core.withInfiniteAnimationFrameNanos -import androidx.compose.animation.fadeIn import androidx.compose.foundation.Canvas import androidx.compose.foundation.border import androidx.compose.foundation.gestures.awaitFirstDown @@ -34,12 +32,14 @@ import androidx.compose.foundation.gestures.forEachGesture import androidx.compose.foundation.gestures.rememberTransformableState import androidx.compose.foundation.gestures.transformable import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.BoxWithConstraints import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.ColumnScope -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.padding +import androidx.compose.foundation.layout.safeContent +import androidx.compose.foundation.layout.windowInsetsPadding import androidx.compose.material.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect @@ -49,6 +49,7 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.AbsoluteAlignment.Left +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.drawBehind import androidx.compose.ui.geometry.Offset @@ -59,8 +60,10 @@ import androidx.compose.ui.graphics.drawscope.Stroke import androidx.compose.ui.graphics.drawscope.translate import androidx.compose.ui.input.pointer.PointerEvent import androidx.compose.ui.input.pointer.pointerInput +import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.font.FontFamily import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.toUpperCase import androidx.compose.ui.tooling.preview.Devices import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp @@ -94,12 +97,12 @@ const val TEST_UNIVERSE = false val RANDOM_SEED_TYPE = RandomSeedType.Daily const val FIXED_RANDOM_SEED = 5038L -const val DEFAULT_CAMERA_ZOOM = 0.25f +const val DEFAULT_CAMERA_ZOOM = 1f const val MIN_CAMERA_ZOOM = 250f / UNIVERSE_RANGE // 0.0025f const val MAX_CAMERA_ZOOM = 5f -const val TOUCH_CAMERA_PAN = false -const val TOUCH_CAMERA_ZOOM = true -const val DYNAMIC_ZOOM = false // @@@ FIXME +var TOUCH_CAMERA_PAN = false +var TOUCH_CAMERA_ZOOM = false +var DYNAMIC_ZOOM = false fun dailySeed(): Long { val today = GregorianCalendar() @@ -134,38 +137,20 @@ fun DebugText(text: MutableState<String>) { } @Composable -fun ColumnScope.ConsoleText( - modifier: Modifier = Modifier, - visible: Boolean = true, - random: Random = Random.Default, - text: String -) { - AnimatedVisibility( - modifier = modifier, - visible = visible, - enter = - fadeIn( - animationSpec = - tween( - durationMillis = 1000, - easing = flickerFadeEasing(random) * CubicBezierEasing(0f, 1f, 1f, 0f) - ) - ) - ) { - Text( +fun Telemetry(universe: VisibleUniverse) { + var topVisible by remember { mutableStateOf(false) } + var bottomVisible by remember { mutableStateOf(false) } + + var catalogFontSize by remember { mutableStateOf(9.sp) } + + val textStyle = + TextStyle( fontFamily = FontFamily.Monospace, fontWeight = FontWeight.Medium, fontSize = 12.sp, - color = Color(0xFFFF8000), - text = text + letterSpacing = 1.sp, + lineHeight = 12.sp, ) - } -} - -@Composable -fun Telemetry(universe: VisibleUniverse) { - var topVisible by remember { mutableStateOf(false) } - var bottomVisible by remember { mutableStateOf(false) } LaunchedEffect("blah") { delay(1000) @@ -174,65 +159,109 @@ fun Telemetry(universe: VisibleUniverse) { topVisible = true } - Column(modifier = Modifier.fillMaxSize().padding(6.dp)) { - universe.triggerDraw.value // recompose on every frame - val explored = universe.planets.filter { it.explored } + universe.triggerDraw.value // recompose on every frame - AnimatedVisibility(modifier = Modifier, visible = topVisible, enter = flickerFadeIn) { - Text( - fontFamily = FontFamily.Monospace, - fontWeight = FontWeight.Medium, - fontSize = 12.sp, - color = Colors.Console, - modifier = Modifier.align(Left), - text = - with(universe.star) { - " STAR: $name (UDC-${universe.randomSeed % 100_000})\n" + - " CLASS: ${cls.name}\n" + - "RADIUS: ${radius.toInt()}\n" + - " MASS: %.3g\n".format(mass) + - "BODIES: ${explored.size} / ${universe.planets.size}\n" + - "\n" - } + - explored - .map { - " BODY: ${it.name}\n" + - " TYPE: ${it.description.capitalize()}\n" + - " ATMO: ${it.atmosphere.capitalize()}\n" + - " FAUNA: ${it.fauna.capitalize()}\n" + - " FLORA: ${it.flora.capitalize()}\n" - } - .joinToString("\n") + val explored = universe.planets.filter { it.explored } - // TODO: different colors, highlight latest discovery + BoxWithConstraints( + modifier = + Modifier.fillMaxSize().padding(6.dp).windowInsetsPadding(WindowInsets.safeContent), + ) { + val wide = maxWidth > maxHeight + Column( + modifier = + Modifier.align(if (wide) Alignment.BottomEnd else Alignment.BottomStart) + .fillMaxWidth(if (wide) 0.45f else 1.0f) + ) { + universe.ship.autopilot?.let { autopilot -> + if (autopilot.enabled) { + AnimatedVisibility( + modifier = Modifier, + visible = bottomVisible, + enter = flickerFadeIn + ) { + Text( + style = textStyle, + color = Colors.Autopilot, + modifier = Modifier.align(Left), + text = autopilot.telemetry + ) + } + } + } + + AnimatedVisibility( + modifier = Modifier, + visible = bottomVisible, + enter = flickerFadeIn + ) { + Text( + style = textStyle, + color = Colors.Console, + modifier = Modifier.align(Left), + text = + with(universe.ship) { + val closest = universe.closestPlanet() + val distToClosest = ((closest.pos - pos).mag() - closest.radius).toInt() + listOfNotNull( + landing?.let { + "LND: ${it.planet.name.toUpperCase()}\nJOB: ${it.text}" + } + ?: if (distToClosest < 10_000) { + "ALT: $distToClosest" + } else null, + "THR: %.0f%%".format(thrust.mag() * 100f), + "POS: %s".format(pos.str("%+7.0f")), + "VEL: %.0f".format(velocity.mag()) + ) + .joinToString("\n") + } ) + } } - Spacer(modifier = Modifier.weight(1f)) - - AnimatedVisibility(modifier = Modifier, visible = bottomVisible, enter = flickerFadeIn) { + AnimatedVisibility( + modifier = Modifier.align(Alignment.TopStart), + visible = topVisible, + enter = flickerFadeIn + ) { Text( - fontFamily = FontFamily.Monospace, - fontWeight = FontWeight.Medium, - fontSize = 12.sp, + style = textStyle, + fontSize = catalogFontSize, + lineHeight = catalogFontSize, + letterSpacing = 1.sp, color = Colors.Console, - modifier = Modifier.align(Left), + onTextLayout = { textLayoutResult -> + if (textLayoutResult.didOverflowHeight) { + catalogFontSize = 8.sp + } + }, text = - with(universe.ship) { - val closest = universe.closestPlanet() - val distToClosest = (closest.pos - pos).mag().toInt() - listOfNotNull( - landing?.let { "LND: ${it.planet.name}" } - ?: if (distToClosest < 10_000) { - "ALT: $distToClosest" - } else null, - if (thrust != Vec2.Zero) "THR: %.0f%%".format(thrust.mag() * 100f) - else null, - "POS: %s".format(pos.str("%+7.0f")), - "VEL: %.0f".format(velocity.mag()) + (with(universe.star) { + listOf( + " STAR: $name (UDC-${universe.randomSeed % 100_000})", + " CLASS: ${cls.name}", + "RADIUS: ${radius.toInt()}", + " MASS: %.3g".format(mass), + "BODIES: ${explored.size} / ${universe.planets.size}", + "" ) - .joinToString("\n") - } + } + + explored + .map { + listOf( + " BODY: ${it.name}", + " TYPE: ${it.description.capitalize()}", + " ATMO: ${it.atmosphere.capitalize()}", + " FAUNA: ${it.fauna.capitalize()}", + " FLORA: ${it.flora.capitalize()}", + "" + ) + } + .flatten()) + .joinToString("\n") + + // TODO: different colors, highlight latest discovery ) } } @@ -246,6 +275,8 @@ class MainActivity : ComponentActivity() { onWindowLayoutInfoChange() + enableEdgeToEdge() + val universe = VisibleUniverse(namer = Namer(resources), randomSeed = randomSeed()) if (TEST_UNIVERSE) { @@ -254,6 +285,15 @@ class MainActivity : ComponentActivity() { universe.initRandom() } + com.android.egg.ComponentActivationActivity.lockUnlockComponents(applicationContext) + + // for autopilot testing in the activity + // val autopilot = Autopilot(universe.ship, universe) + // universe.ship.autopilot = autopilot + // universe.add(autopilot) + // autopilot.enabled = true + // DYNAMIC_ZOOM = autopilot.enabled + setContent { Spaaaace(modifier = Modifier.fillMaxSize(), u = universe, foldState = foldState) DebugText(DEBUG_TEXT) @@ -437,8 +477,13 @@ fun Spaaaace( val distToNearestSurf = max(0f, (u.ship.pos - closest.pos).mag() - closest.radius * 1.2f) // val normalizedDist = clamp(distToNearestSurf, 50f, 50_000f) / 50_000f if (DYNAMIC_ZOOM) { - // cameraZoom = lerp(0.1f, 5f, smooth(1f-normalizedDist)) - cameraZoom = clamp(500f / distToNearestSurf, MIN_CAMERA_ZOOM, MAX_CAMERA_ZOOM) + cameraZoom = + expSmooth( + cameraZoom, + clamp(500f / distToNearestSurf, MIN_CAMERA_ZOOM, MAX_CAMERA_ZOOM), + dt = u.dt, + speed = 1.5f + ) } else if (!TOUCH_CAMERA_ZOOM) cameraZoom = DEFAULT_CAMERA_ZOOM if (!TOUCH_CAMERA_PAN) cameraOffset = (u.follow?.pos ?: Vec2.Zero) * -1f @@ -478,26 +523,26 @@ fun Spaaaace( "star: '${u.star.name}' designation=UDC-${u.randomSeed % 100_000} " + "class=${u.star.cls.name} r=${u.star.radius.toInt()} m=${u.star.mass}\n" + "planets: ${u.planets.size}\n" + - u.planets.joinToString("\n") { - val range = (u.ship.pos - it.pos).mag() - val vorbit = sqrt(GRAVITATION * it.mass / range) - val vescape = sqrt(2 * GRAVITATION * it.mass / it.radius) - " * ${it.name}:\n" + - if (it.explored) { - " TYPE: ${it.description.capitalize()}\n" + - " ATMO: ${it.atmosphere.capitalize()}\n" + - " FAUNA: ${it.fauna.capitalize()}\n" + - " FLORA: ${it.flora.capitalize()}\n" - } else { - " (Unexplored)\n" - } + - " orbit=${(it.pos - it.orbitCenter).mag().toInt()}" + - " radius=${it.radius.toInt()}" + - " mass=${"%g".format(it.mass)}" + - " vel=${(it.speed).toInt()}" + - " // range=${"%.0f".format(range)}" + - " vorbit=${vorbit.toInt()} vescape=${vescape.toInt()}" - }) + u.planets.joinToString("\n") { + val range = (u.ship.pos - it.pos).mag() + val vorbit = sqrt(GRAVITATION * it.mass / range) + val vescape = sqrt(2 * GRAVITATION * it.mass / it.radius) + " * ${it.name}:\n" + + if (it.explored) { + " TYPE: ${it.description.capitalize()}\n" + + " ATMO: ${it.atmosphere.capitalize()}\n" + + " FAUNA: ${it.fauna.capitalize()}\n" + + " FLORA: ${it.flora.capitalize()}\n" + } else { + " (Unexplored)\n" + } + + " orbit=${(it.pos - it.orbitCenter).mag().toInt()}" + + " radius=${it.radius.toInt()}" + + " mass=${"%g".format(it.mass)}" + + " vel=${(it.speed).toInt()}" + + " // range=${"%.0f".format(range)}" + + " vorbit=${vorbit.toInt()} vescape=${vescape.toInt()}" + }) zoom(cameraZoom) { // All coordinates are space coordinates now. diff --git a/packages/EasterEgg/src/com/android/egg/landroid/Maths.kt b/packages/EasterEgg/src/com/android/egg/landroid/Maths.kt index fdf29f7aa948..a1e82127578d 100644 --- a/packages/EasterEgg/src/com/android/egg/landroid/Maths.kt +++ b/packages/EasterEgg/src/com/android/egg/landroid/Maths.kt @@ -16,6 +16,7 @@ package com.android.egg.landroid +import kotlin.math.exp import kotlin.math.pow /** smoothstep. Ken Perlin's version */ @@ -32,3 +33,8 @@ fun invsmoothish(x: Float): Float { fun lexp(start: Float, end: Float, progress: Float): Float { return (progress - start) / (end - start) } + +/** Exponentially smooth current toward target by a factor of speed. */ +fun expSmooth(current: Float, target: Float, dt: Float = 1f / 60, speed: Float = 5f): Float { + return current + (target - current) * (1 - exp(-dt * speed)) +} diff --git a/packages/EasterEgg/src/com/android/egg/landroid/Namer.kt b/packages/EasterEgg/src/com/android/egg/landroid/Namer.kt index 67d536e0aea1..73318077f47a 100644 --- a/packages/EasterEgg/src/com/android/egg/landroid/Namer.kt +++ b/packages/EasterEgg/src/com/android/egg/landroid/Namer.kt @@ -17,9 +17,8 @@ package com.android.egg.landroid import android.content.res.Resources -import kotlin.random.Random - import com.android.egg.R +import kotlin.random.Random const val SUFFIX_PROB = 0.75f const val LETTER_PROB = 0.3f @@ -62,6 +61,11 @@ class Namer(resources: Resources) { 0.1f to "(^*!%@##!!" ) + private var activities = Bag(resources.getStringArray(R.array.activities)) + private var floraGenericPlurals = Bag(resources.getStringArray(R.array.flora_generic_plurals)) + private var faunaGenericPlurals = Bag(resources.getStringArray(R.array.fauna_generic_plurals)) + private var atmoGenericPlurals = Bag(resources.getStringArray(R.array.atmo_generic_plurals)) + fun describePlanet(rng: Random): String { return planetTable.roll(rng).pull(rng) + " " + planetTypes.pull(rng) } @@ -93,4 +97,30 @@ class Namer(resources: Resources) { fun describeAtmo(rng: Random): String { return atmoTable.roll(rng).pull(rng) } + + fun floraPlural(rng: Random): String { + return floraGenericPlurals.pull(rng) + } + fun faunaPlural(rng: Random): String { + return faunaGenericPlurals.pull(rng) + } + fun atmoPlural(rng: Random): String { + return atmoGenericPlurals.pull(rng) + } + + val TEMPLATE_REGEX = Regex("""\{(flora|fauna|planet|atmo)\}""") + fun describeActivity(rng: Random, target: Planet?): String { + return activities + .pull(rng) + .replace(TEMPLATE_REGEX) { + when (it.groupValues[1]) { + "flora" -> (target?.flora ?: "SOME") + " " + floraPlural(rng) + "fauna" -> (target?.fauna ?: "SOME") + " " + faunaPlural(rng) + "atmo" -> (target?.atmosphere ?: "SOME") + " " + atmoPlural(rng) + "planet" -> (target?.description ?: "SOME BODY") // once told me + else -> "unknown template tag: ${it.groupValues[0]}" + } + } + .toUpperCase() + } } diff --git a/packages/EasterEgg/src/com/android/egg/landroid/PathTools.kt b/packages/EasterEgg/src/com/android/egg/landroid/PathTools.kt index 851064063d19..cd87335ab07b 100644 --- a/packages/EasterEgg/src/com/android/egg/landroid/PathTools.kt +++ b/packages/EasterEgg/src/com/android/egg/landroid/PathTools.kt @@ -32,6 +32,13 @@ fun createPolygon(radius: Float, sides: Int): Path { } } +fun createPolygonPoints(radius: Float, sides: Int): List<Vec2> { + val angleStep = PI2f / sides + return (0 until sides).map { i -> + Vec2(radius * cos(angleStep * i), radius * sin(angleStep * i)) + } +} + fun createStar(radius1: Float, radius2: Float, points: Int): Path { return Path().apply { val angleStep = PI2f / points @@ -46,15 +53,16 @@ fun createStar(radius1: Float, radius2: Float, points: Int): Path { } fun Path.parseSvgPathData(d: String) { - Regex("([A-Z])([-.,0-9e ]+)").findAll(d.trim()).forEach { + Regex("([A-Za-z])\\s*([-.,0-9e ]+)").findAll(d.trim()).forEach { val cmd = it.groups[1]!!.value val args = it.groups[2]?.value?.split(Regex("\\s+"))?.map { v -> v.toFloat() } ?: emptyList() - Log.d("Landroid", "cmd = $cmd, args = " + args.joinToString(",")) + // Log.d("Landroid", "cmd = $cmd, args = " + args.joinToString(",")) when (cmd) { "M" -> moveTo(args[0], args[1]) "C" -> cubicTo(args[0], args[1], args[2], args[3], args[4], args[5]) "L" -> lineTo(args[0], args[1]) + "l" -> relativeLineTo(args[0], args[1]) "Z" -> close() else -> Log.v("Landroid", "unsupported SVG command: $cmd") } diff --git a/packages/EasterEgg/src/com/android/egg/landroid/Physics.kt b/packages/EasterEgg/src/com/android/egg/landroid/Physics.kt index fc66ad6bc2ae..d14234ec66d9 100644 --- a/packages/EasterEgg/src/com/android/egg/landroid/Physics.kt +++ b/packages/EasterEgg/src/com/android/egg/landroid/Physics.kt @@ -20,7 +20,7 @@ import android.util.ArraySet import kotlin.random.Random // artificially speed up or slow down the simulation -const val TIME_SCALE = 1f +const val TIME_SCALE = 1f // simulation seconds per wall clock second // if it's been over 1 real second since our last timestep, don't simulate that elapsed time. // this allows the simulation to "pause" when, for example, the activity pauses @@ -36,6 +36,19 @@ interface Entity { fun postUpdate(sim: Simulator, dt: Float) } +interface Removable { + fun canBeRemoved(): Boolean +} + +class Fuse(var lifetime: Float) : Removable { + fun update(dt: Float) { + lifetime -= dt + } + override fun canBeRemoved(): Boolean { + return lifetime < 0 + } +} + open class Body(var name: String = "Unknown") : Entity { var pos = Vec2.Zero var opos = Vec2.Zero diff --git a/packages/EasterEgg/src/com/android/egg/landroid/Randomness.kt b/packages/EasterEgg/src/com/android/egg/landroid/Randomness.kt index ebbb2bd1270c..2903534b0cb3 100644 --- a/packages/EasterEgg/src/com/android/egg/landroid/Randomness.kt +++ b/packages/EasterEgg/src/com/android/egg/landroid/Randomness.kt @@ -61,6 +61,7 @@ fun Random.nextFloatInRange(from: Float, until: Float): Float = /** Return a random float in the range [start, end). */ fun Random.nextFloatInRange(fromUntil: ClosedFloatingPointRange<Float>): Float = nextFloatInRange(fromUntil.start, fromUntil.endInclusive) + /** Return a random float in the range [first, second). */ fun Random.nextFloatInRange(fromUntil: Pair<Float, Float>): Float = nextFloatInRange(fromUntil.first, fromUntil.second) diff --git a/packages/EasterEgg/src/com/android/egg/landroid/Universe.kt b/packages/EasterEgg/src/com/android/egg/landroid/Universe.kt index 11dce613b0fb..d6fbc119edb5 100644 --- a/packages/EasterEgg/src/com/android/egg/landroid/Universe.kt +++ b/packages/EasterEgg/src/com/android/egg/landroid/Universe.kt @@ -43,11 +43,9 @@ const val CRAFT_SPEED_LIMIT = 5_000f const val MAIN_ENGINE_ACCEL = 1000f // thrust effect, pixels per second squared const val LAUNCH_MECO = 2f // how long to suspend gravity when launching -const val SCALED_THRUST = true +const val LANDING_REMOVAL_TIME = 3600f // one hour of simulation time -interface Removable { - fun canBeRemoved(): Boolean -} +const val SCALED_THRUST = true open class Planet( val orbitCenter: Vec2, @@ -155,10 +153,7 @@ open class Universe(val namer: Namer, randomSeed: Long) : Simulator(randomSeed) speed = speed, color = Colors.Eigengrau4 ) - android.util.Log.v( - "Landroid", - "created planet $p with period $period and vel $speed" - ) + android.util.Log.v("Landroid", "created planet $p with period $period and vel $speed") val num = it + 1 p.description = "TEST PLANET #$num" p.atmosphere = "radius=$radius" @@ -215,10 +210,7 @@ open class Universe(val namer: Namer, randomSeed: Long) : Simulator(randomSeed) speed = speed, color = Colors.Eigengrau4 ) - android.util.Log.v( - "Landroid", - "created planet $p with period $period and vel $speed" - ) + android.util.Log.v("Landroid", "created planet $p with period $period and vel $speed") p.description = namer.describePlanet(rng) p.atmosphere = namer.describeAtmo(rng) p.flora = namer.describeLife(rng) @@ -302,7 +294,7 @@ open class Universe(val namer: Namer, randomSeed: Long) : Simulator(randomSeed) // && // vDiff < 100f ) { - val landing = Landing(ship, planet, a) + val landing = Landing(ship, planet, a, namer.describeActivity(rng, planet)) ship.landing = landing ship.velocity = planet.velocity add(landing) @@ -327,7 +319,7 @@ open class Universe(val namer: Namer, randomSeed: Long) : Simulator(randomSeed) // (1..10).forEach { Spark( - lifetime = rng.nextFloatInRange(0.5f, 2f), + ttl = rng.nextFloatInRange(0.5f, 2f), style = Spark.Style.DOT, color = Color.White, size = 1f @@ -365,28 +357,43 @@ open class Universe(val namer: Namer, randomSeed: Long) : Simulator(randomSeed) entities .filterIsInstance<Removable>() .filter(predicate = Removable::canBeRemoved) - .filterIsInstance<Entity>() - .forEach { remove(it) } + .forEach { remove(it as Entity) } + + constraints + .filterIsInstance<Removable>() + .filter(predicate = Removable::canBeRemoved) + .forEach { remove(it as Constraint) } } } -class Landing(val ship: Spacecraft, val planet: Planet, val angle: Float) : Constraint { - private val landingVector = Vec2.makeWithAngleMag(angle, ship.radius + planet.radius) +class Landing( + var ship: Spacecraft?, + val planet: Planet, + val angle: Float, + val text: String = "", + private val fuse: Fuse = Fuse(LANDING_REMOVAL_TIME) +) : Constraint, Removable by fuse { override fun solve(sim: Simulator, dt: Float) { - val desiredPos = planet.pos + landingVector - ship.pos = (ship.pos * 0.5f) + (desiredPos * 0.5f) // @@@ FIXME - ship.angle = angle + ship?.let { ship -> + val landingVector = Vec2.makeWithAngleMag(angle, ship.radius + planet.radius) + val desiredPos = planet.pos + landingVector + ship.pos = (ship.pos * 0.5f) + (desiredPos * 0.5f) // @@@ FIXME + ship.angle = angle + } + + fuse.update(dt) } } class Spark( - var lifetime: Float, + var ttl: Float, collides: Boolean = false, mass: Float = 0f, val style: Style = Style.LINE, val color: Color = Color.Gray, - val size: Float = 2f -) : Removable, Body() { + val size: Float = 2f, + val fuse: Fuse = Fuse(ttl) +) : Removable by fuse, Body(name = "Spark") { enum class Style { LINE, LINE_ABSOLUTE, @@ -401,10 +408,7 @@ class Spark( } override fun update(sim: Simulator, dt: Float) { super.update(sim, dt) - lifetime -= dt - } - override fun canBeRemoved(): Boolean { - return lifetime < 0 + fuse.update(dt) } } @@ -435,6 +439,7 @@ class Spacecraft : Body() { val track = Track() var landing: Landing? = null + var autopilot: Autopilot? = null init { mass = SPACECRAFT_MASS @@ -448,23 +453,19 @@ class Spacecraft : Body() { var deltaV = MAIN_ENGINE_ACCEL * dt if (SCALED_THRUST) deltaV *= thrustMag.coerceIn(0f, 1f) - if (landing == null) { - // we are free in space, so we attempt to pivot toward the desired direction - // NOTE: no longer required thanks to FlightStick - // angle = thrust.angle() - } else - landing?.let { landing -> - if (launchClock == 0f) launchClock = sim.now + 1f /* @@@ TODO extract */ - - if (sim.now > launchClock) { - // first-stage to orbit has 1000x power - // deltaV *= 1000f - sim.remove(landing) - this.landing = null - } else { - deltaV = 0f - } + // check if we are currently attached to a landing + landing?.let { landing -> + // launch clock is 1 second long + if (launchClock == 0f) launchClock = sim.now + 1f /* @@@ TODO extract */ + + if (sim.now > launchClock) { + // detach from landing site + landing.ship = null + this.landing = null + } else { + deltaV = 0f } + } // this is it. impart thrust to the ship. // note that we always thrust in the forward direction @@ -492,11 +493,11 @@ class Spacecraft : Body() { // exhaust sim.add( Spark( - lifetime = sim.rng.nextFloatInRange(0.5f, 1f), + ttl = sim.rng.nextFloatInRange(0.5f, 1f), collides = true, mass = 1f, style = Spark.Style.RING, - size = 3f, + size = 1f, color = Color(0x40FFFFFF) ) .also { spark -> diff --git a/packages/EasterEgg/src/com/android/egg/landroid/VisibleUniverse.kt b/packages/EasterEgg/src/com/android/egg/landroid/VisibleUniverse.kt index 6baf36ee3c8a..ed3ebc7bf9a5 100644 --- a/packages/EasterEgg/src/com/android/egg/landroid/VisibleUniverse.kt +++ b/packages/EasterEgg/src/com/android/egg/landroid/VisibleUniverse.kt @@ -28,11 +28,11 @@ import androidx.compose.ui.graphics.drawscope.scale import androidx.compose.ui.graphics.drawscope.translate import androidx.compose.ui.util.lerp import androidx.core.math.MathUtils.clamp +import com.android.egg.flags.Flags.flagFlag import java.lang.Float.max +import kotlin.math.exp import kotlin.math.sqrt -import com.android.egg.flags.Flags.flagFlag - const val DRAW_ORBITS = true const val DRAW_GRAVITATIONAL_FIELDS = true const val DRAW_STAR_GRAVITATIONAL_FIELDS = true @@ -71,16 +71,6 @@ fun ZoomedDrawScope.drawUniverse(universe: VisibleUniverse) { with(universe) { triggerDraw.value // Please recompose when this value changes. - // star.drawZoomed(ds, zoom) - // planets.forEach { p -> - // p.drawZoomed(ds, zoom) - // if (p == follow) { - // drawCircle(Color.Red, 20f / zoom, p.pos) - // } - // } - // - // ship.drawZoomed(ds, zoom) - constraints.forEach { when (it) { is Landing -> drawLanding(it) @@ -89,13 +79,14 @@ fun ZoomedDrawScope.drawUniverse(universe: VisibleUniverse) { } drawStar(star) entities.forEach { - if (it === ship || it === star) return@forEach // draw the ship last + if (it === star) return@forEach // don't draw the star as a planet when (it) { - is Spacecraft -> drawSpacecraft(it) is Spark -> drawSpark(it) is Planet -> drawPlanet(it) + else -> Unit // draw these at a different time, or not at all } } + ship.autopilot?.let { drawAutopilot(it) } drawSpacecraft(ship) } } @@ -111,15 +102,6 @@ fun ZoomedDrawScope.drawContainer(container: Container) { pathEffect = PathEffect.dashPathEffect(floatArrayOf(8f / zoom, 8f / zoom), 0f) ) ) - // val path = Path().apply { - // fillType = PathFillType.EvenOdd - // addOval(Rect(center = Vec2.Zero, radius = container.radius)) - // addOval(Rect(center = Vec2.Zero, radius = container.radius + 10_000)) - // } - // drawPath( - // path = path, - // - // ) } fun ZoomedDrawScope.drawGravitationalField(planet: Planet) { @@ -226,23 +208,47 @@ Z """ ) } -val thrustPath = createPolygon(-3f, 3).also { it.translate(Vec2(-4f, 0f)) } +val spaceshipLegs = + Path().apply { + parseSvgPathData( + """ +M-7 -6.5 +l-3.5 0 +l-1 -2 +l 0 4 +l 1 -2 +Z +M-7 6.5 +l-3.5 0 +l-1 -2 +l 0 4 +l 1 -2 +Z +""" + ) + } +val thrustPath = createPolygon(-3f, 3).also { it.translate(Vec2(-5f, 0f)) } fun ZoomedDrawScope.drawSpacecraft(ship: Spacecraft) { with(ship) { rotateRad(angle, pivot = pos) { translate(pos.x, pos.y) { - // drawPath( - // path = createStar(200f, 100f, 3), - // color = Color.White, - // style = Stroke(width = 2f / zoom) - // ) + // new in V: little landing legs + ship.landing?.let { + drawPath( + path = spaceshipLegs, + color = Color(0xFFCCCCCC), + style = Stroke(width = 2f / this@drawSpacecraft.zoom) + ) + } + // draw the ship drawPath(path = spaceshipPath, color = Colors.Eigengrau) // fauxpaque drawPath( path = spaceshipPath, color = if (transit) Color.Black else Color.White, style = Stroke(width = 2f / this@drawSpacecraft.zoom) ) + // draw thrust if (thrust != Vec2.Zero) { drawPath( path = thrustPath, @@ -254,27 +260,8 @@ fun ZoomedDrawScope.drawSpacecraft(ship: Spacecraft) { ) ) } - // drawRect( - // topLeft = Offset(-1f, -1f), - // size = Size(2f, 2f), - // color = Color.Cyan, - // style = Stroke(width = 2f / zoom) - // ) - // drawLine( - // start = Vec2.Zero, - // end = Vec2(20f, 0f), - // color = Color.Cyan, - // strokeWidth = 2f / zoom - // ) } } - // // DEBUG: draw velocity vector - // drawLine( - // start = pos, - // end = pos + velocity, - // color = Color.Red, - // strokeWidth = 3f / zoom - // ) drawTrack(track) } } @@ -287,14 +274,15 @@ fun ZoomedDrawScope.drawLanding(landing: Landing) { val height = 80f rotateRad(landing.angle, pivot = v) { translate(v.x, v.y) { - drawPath( + val flagPath = Path().apply { moveTo(0f, 0f) lineTo(height, 0f) lineTo(height * 0.875f, height * 0.25f) lineTo(height * 0.75f, 0f) close() - }, Color.Yellow, style = Stroke(width = strokeWidth)) + } + drawPath(flagPath, Colors.Flag, style = Stroke(width = strokeWidth)) } } } @@ -302,7 +290,8 @@ fun ZoomedDrawScope.drawLanding(landing: Landing) { fun ZoomedDrawScope.drawSpark(spark: Spark) { with(spark) { - if (lifetime < 0) return + if (fuse.lifetime < 0) return + val life = 1f - fuse.lifetime / ttl when (style) { Spark.Style.LINE -> if (opos != Vec2.Zero) drawLine(color, opos, pos, strokeWidth = size) @@ -310,11 +299,14 @@ fun ZoomedDrawScope.drawSpark(spark: Spark) { if (opos != Vec2.Zero) drawLine(color, opos, pos, strokeWidth = size / zoom) Spark.Style.DOT -> drawCircle(color, size, pos) Spark.Style.DOT_ABSOLUTE -> drawCircle(color, size, pos / zoom) - Spark.Style.RING -> drawCircle(color, size, pos, style = Stroke(width = 1f / zoom)) - // drawPoints(listOf(pos), PointMode.Points, color, strokeWidth = 2f/zoom) - // drawCircle(color, 2f/zoom, pos) + Spark.Style.RING -> + drawCircle( + color = color.copy(alpha = color.alpha * (1f - life)), + radius = exp(lerp(size, 3f * size, life)) - 1f, + center = pos, + style = Stroke(width = 1f / zoom) + ) } - // drawCircle(Color.Gray, center = pos, radius = 1.5f / zoom) } } @@ -324,19 +316,9 @@ fun ZoomedDrawScope.drawTrack(track: Track) { drawPoints( positions, pointMode = PointMode.Lines, - color = Color.Green, + color = Colors.Track, strokeWidth = 1f / zoom ) - // if (positions.size < 2) return - // drawPath(Path() - // .apply { - // val p = positions[positions.size - 1] - // moveTo(p.x, p.y) - // positions.reversed().subList(1, positions.size).forEach { p -> - // lineTo(p.x, p.y) - // } - // }, - // color = Color.Green, style = Stroke(1f/zoom)) } else { if (positions.size < 2) return var prev: Vec2 = positions[positions.size - 1] @@ -349,3 +331,43 @@ fun ZoomedDrawScope.drawTrack(track: Track) { } } } + +fun ZoomedDrawScope.drawAutopilot(autopilot: Autopilot) { + val color = Colors.Autopilot.copy(alpha = 0.5f) + + autopilot.target?.let { target -> + val zoom = zoom + rotateRad(autopilot.universe.now * PI2f / 10f, target.pos) { + translate(target.pos.x, target.pos.y) { + drawPath( + path = + createPolygon( + radius = target.radius + autopilot.brakingDistance, + sides = 15 // Autopilot introduced in Android 15 + ), + color = color, + style = Stroke(1f / zoom) + ) + drawCircle( + color, + radius = target.radius + autopilot.landingAltitude / 2, + center = Vec2.Zero, + alpha = 0.25f, + style = Stroke(autopilot.landingAltitude) + ) + } + } + drawLine( + color, + start = autopilot.ship.pos, + end = autopilot.leadingPos, + strokeWidth = 1f / zoom + ) + drawCircle( + color, + radius = 5f / zoom, + center = autopilot.leadingPos, + style = Stroke(1f / zoom) + ) + } +} diff --git a/packages/InputDevices/res/raw/keyboard_layout_serbian_and_montenegrin_cyrillic.kcm b/packages/InputDevices/res/raw/keyboard_layout_serbian_and_montenegrin_cyrillic.kcm new file mode 100644 index 000000000000..6fa54f9d052f --- /dev/null +++ b/packages/InputDevices/res/raw/keyboard_layout_serbian_and_montenegrin_cyrillic.kcm @@ -0,0 +1,320 @@ +# 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. + +# +# Serbian and Montenegrin (Cyrillic) keyboard layout. +# + +type OVERLAY + +map key 86 PLUS + +### ROW 1 + +key GRAVE { + label: '`' + base: '`' + shift: '~' +} + +key 1 { + label: '1' + base: '1' + shift: '!' +} + +key 2 { + label: '2' + base: '2' + shift: '\u0022' +} + +key 3 { + label: '3' + base: '3' + shift: '#' +} + +key 4 { + label: '4' + base: '4' + shift: '$' +} + +key 5 { + label: '5' + base: '5' + shift: '%' +} + +key 6 { + label: '6' + base: '6' + shift: '&' +} + +key 7 { + label: '7' + base: '7' + shift: '/' +} + +key 8 { + label: '8' + base: '8' + shift: '(' +} + +key 9 { + label: '9' + base: '9' + shift: ')' +} + +key 0 { + label: '0' + base: '0' + shift: '=' +} + +key MINUS { + label: '\'' + base: '\u030d' + shift: '?' +} + +key EQUALS { + label: '+' + base: '+' + shift: '*' +} + +### ROW 2 + +key Q { + label: '\u0409' + base, capslock+shift: '\u0459' + shift, capslock: '\u0409' +} + +key W { + label: '\u040a' + base, capslock+shift: '\u045a' + shift, capslock: '\u040a' +} + +key E { + label: '\u0415' + base, capslock+shift: '\u0435' + shift, capslock: '\u0415' + ralt: '\u20ac' +} + +key R { + label: '\u0420' + base, capslock+shift: '\u0440' + shift, capslock: '\u0420' +} + +key T { + label: '\u0422' + base, capslock+shift: '\u0442' + shift, capslock: '\u0422' +} + +key Y { + label: '\u0417' + base, capslock+shift: '\u0437' + shift, capslock: '\u0417' +} + +key U { + label: '\u0423' + base, capslock+shift: '\u0443' + shift, capslock: '\u0423' +} + +key I { + label: '\u0418' + base, capslock+shift: '\u0438' + shift, capslock: '\u0418' +} + +key O { + label: '\u041e' + base, capslock+shift: '\u043e' + shift, capslock: '\u041e' +} + +key P { + label: '\u041f' + base, capslock+shift: '\u043f' + shift, capslock: '\u041f' +} + +key LEFT_BRACKET { + label: '\u0428' + base, capslock+shift: '\u0448' + shift, capslock: '\u0428' +} + +key RIGHT_BRACKET { + label: '\u0402' + base, capslock+shift: '\u0452' + shift, capslock: '\u0402' +} + +### ROW 3 + +key A { + label: '\u0410' + base, capslock+shift: '\u0430' + shift, capslock: '\u0410' +} + +key S { + label: '\u0421' + base, capslock+shift: '\u0441' + shift, capslock: '\u0421' +} + +key D { + label: '\u0414' + base, capslock+shift: '\u0434' + shift, capslock: '\u0414' +} + +key F { + label: '\u0424' + base, capslock+shift: '\u0444' + shift, capslock: '\u0424' +} + +key G { + label: '\u0413' + base, capslock+shift: '\u0433' + shift, capslock: '\u0413' +} + +key H { + label: '\u0425' + base, capslock+shift: '\u0445' + shift, capslock: '\u0425' +} + +key J { + label: '\u0408' + base, capslock+shift: '\u0458' + shift, capslock: '\u0408' +} + +key K { + label: '\u041a' + base, capslock+shift: '\u043a' + shift, capslock: '\u041a' +} + +key L { + label: '\u041b' + base, capslock+shift: '\u043b' + shift, capslock: '\u041b' +} + +key SEMICOLON { + label: '\u0427' + base, capslock+shift: '\u0447' + shift, capslock: '\u0427' +} + +key APOSTROPHE { + label: '\u040b' + base, capslock+shift: '\u045b' + shift, capslock: '\u040b' +} + +key BACKSLASH { + label: '\u0416' + base, capslock+shift: '\u0436' + shift, capslock: '\u0416' +} + +### ROW 4 + +key PLUS { + label: '<' + base: '<' + shift: '>' +} + +key Z { + label: '\u0405' + base, capslock+shift: '\u0455' + shift, capslock: '\u0405' +} + +key X { + label: '\u040f' + base, capslock+shift: '\u045f' + shift, capslock: '\u040f' +} + +key C { + label: '\u0426' + base, capslock+shift: '\u0446' + shift, capslock: '\u0426' +} + +key V { + label: '\u0412' + base, capslock+shift: '\u0432' + shift, capslock: '\u0412' +} + +key B { + label: '\u0411' + base, capslock+shift: '\u0431' + shift, capslock: '\u0411' +} + +key N { + label: '\u041d' + base, capslock+shift: '\u043d' + shift, capslock: '\u041d' +} + +key M { + label: '\u041c' + base, capslock+shift: '\u043c' + shift, capslock: '\u041c' +} + +key COMMA { + label: ',' + base: ',' + shift: ';' + ralt: '<' +} + +key PERIOD { + label: '.' + base: '.' + shift: ':' + ralt: '>' +} + +key SLASH { + label: '-' + base: '-' + shift: '_' +}
\ No newline at end of file diff --git a/packages/InputDevices/res/raw/keyboard_layout_serbian_latin.kcm b/packages/InputDevices/res/raw/keyboard_layout_serbian_and_montenegrin_latin.kcm index 0ff1dea91d10..8e4d7b147faa 100644 --- a/packages/InputDevices/res/raw/keyboard_layout_serbian_latin.kcm +++ b/packages/InputDevices/res/raw/keyboard_layout_serbian_and_montenegrin_latin.kcm @@ -13,7 +13,7 @@ # limitations under the License. # -# Serbian (Latin) keyboard layout. +# Serbian and Montenegrin (Latin) keyboard layout. # type OVERLAY diff --git a/packages/InputDevices/res/values-af/strings.xml b/packages/InputDevices/res/values-af/strings.xml index 7e2561f1e7db..f7dc4576ade7 100644 --- a/packages/InputDevices/res/values-af/strings.xml +++ b/packages/InputDevices/res/values-af/strings.xml @@ -52,8 +52,6 @@ <string name="keyboard_layout_georgian" msgid="4596185456863747454">"Georgies"</string> <string name="keyboard_layout_thai_kedmanee" msgid="6637147314580760938">"Thai (Kedmanee)"</string> <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"Thais (Pattachote)"</string> - <!-- no translation found for keyboard_layout_serbian_latin (3128791759390046571) --> - <skip /> - <!-- no translation found for keyboard_layout_montenegrin_latin (1467832503378949945) --> - <skip /> + <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"Serwies (Latyns)"</string> + <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"Montenegryns (Latyns)"</string> </resources> diff --git a/packages/InputDevices/res/values-am/strings.xml b/packages/InputDevices/res/values-am/strings.xml index 3053c4485e67..712ca0049ef4 100644 --- a/packages/InputDevices/res/values-am/strings.xml +++ b/packages/InputDevices/res/values-am/strings.xml @@ -52,8 +52,6 @@ <string name="keyboard_layout_georgian" msgid="4596185456863747454">"ጂዮርጂያኛ"</string> <string name="keyboard_layout_thai_kedmanee" msgid="6637147314580760938">"ታይላንድኛ (ኬድማኒ)"</string> <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"ታይላንድኛ (ፓታሾት)"</string> - <!-- no translation found for keyboard_layout_serbian_latin (3128791759390046571) --> - <skip /> - <!-- no translation found for keyboard_layout_montenegrin_latin (1467832503378949945) --> - <skip /> + <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"ሰርቢያኛ (ላቲን)"</string> + <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"ሞንቴኔግሮኛ (ላቲን)"</string> </resources> diff --git a/packages/InputDevices/res/values-as/strings.xml b/packages/InputDevices/res/values-as/strings.xml index 15aa34d31587..c2ea39e431ea 100644 --- a/packages/InputDevices/res/values-as/strings.xml +++ b/packages/InputDevices/res/values-as/strings.xml @@ -52,8 +52,6 @@ <string name="keyboard_layout_georgian" msgid="4596185456863747454">"Georgian"</string> <string name="keyboard_layout_thai_kedmanee" msgid="6637147314580760938">"থাই (কেডমানি)"</string> <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"থাই (পাটাচ’টে)"</string> - <!-- no translation found for keyboard_layout_serbian_latin (3128791759390046571) --> - <skip /> - <!-- no translation found for keyboard_layout_montenegrin_latin (1467832503378949945) --> - <skip /> + <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"ছাৰ্বিয়ান (লেটিন)"</string> + <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"মণ্টেনেগ্ৰিণ (লেটিন)"</string> </resources> diff --git a/packages/InputDevices/res/values-az/strings.xml b/packages/InputDevices/res/values-az/strings.xml index 765d55bd985c..d90c3c848e0e 100644 --- a/packages/InputDevices/res/values-az/strings.xml +++ b/packages/InputDevices/res/values-az/strings.xml @@ -52,8 +52,6 @@ <string name="keyboard_layout_georgian" msgid="4596185456863747454">"Gürcü"</string> <string name="keyboard_layout_thai_kedmanee" msgid="6637147314580760938">"Tay (Kedmanee)"</string> <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"Tay (Pattachote)"</string> - <!-- no translation found for keyboard_layout_serbian_latin (3128791759390046571) --> - <skip /> - <!-- no translation found for keyboard_layout_montenegrin_latin (1467832503378949945) --> - <skip /> + <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"Serb dili (Latın)"</string> + <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"Monteneqro dili (Latın)"</string> </resources> diff --git a/packages/InputDevices/res/values-b+sr+Latn/strings.xml b/packages/InputDevices/res/values-b+sr+Latn/strings.xml index 9b52c346e162..0d9a211e8f62 100644 --- a/packages/InputDevices/res/values-b+sr+Latn/strings.xml +++ b/packages/InputDevices/res/values-b+sr+Latn/strings.xml @@ -52,8 +52,6 @@ <string name="keyboard_layout_georgian" msgid="4596185456863747454">"gruzijska"</string> <string name="keyboard_layout_thai_kedmanee" msgid="6637147314580760938">"tajski (Kedmanee)"</string> <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"tajski (Pattachote)"</string> - <!-- no translation found for keyboard_layout_serbian_latin (3128791759390046571) --> - <skip /> - <!-- no translation found for keyboard_layout_montenegrin_latin (1467832503378949945) --> - <skip /> + <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"srpski (latinica)"</string> + <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"crnogorski (latinica)"</string> </resources> diff --git a/packages/InputDevices/res/values-bg/strings.xml b/packages/InputDevices/res/values-bg/strings.xml index 4d70bf555589..e02fd0cfff74 100644 --- a/packages/InputDevices/res/values-bg/strings.xml +++ b/packages/InputDevices/res/values-bg/strings.xml @@ -52,8 +52,6 @@ <string name="keyboard_layout_georgian" msgid="4596185456863747454">"грузински"</string> <string name="keyboard_layout_thai_kedmanee" msgid="6637147314580760938">"тайландски (Kedmanee)"</string> <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"тайландски (Pattachote)"</string> - <!-- no translation found for keyboard_layout_serbian_latin (3128791759390046571) --> - <skip /> - <!-- no translation found for keyboard_layout_montenegrin_latin (1467832503378949945) --> - <skip /> + <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"сръбски (латиница)"</string> + <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"черногорски (латиница)"</string> </resources> diff --git a/packages/InputDevices/res/values-bn/strings.xml b/packages/InputDevices/res/values-bn/strings.xml index 7c430d3ddef8..3fdbcc19da0f 100644 --- a/packages/InputDevices/res/values-bn/strings.xml +++ b/packages/InputDevices/res/values-bn/strings.xml @@ -52,8 +52,6 @@ <string name="keyboard_layout_georgian" msgid="4596185456863747454">"জর্জিয়ান"</string> <string name="keyboard_layout_thai_kedmanee" msgid="6637147314580760938">"থাই (কেডমানি)"</string> <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"থাই (পাট্টাচোটে)"</string> - <!-- no translation found for keyboard_layout_serbian_latin (3128791759390046571) --> - <skip /> - <!-- no translation found for keyboard_layout_montenegrin_latin (1467832503378949945) --> - <skip /> + <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"সার্বিয়ান (ল্যাটিন)"</string> + <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"মন্টেনেগ্রিন (ল্যাটিন)"</string> </resources> diff --git a/packages/InputDevices/res/values-bs/strings.xml b/packages/InputDevices/res/values-bs/strings.xml index c47dad3c0eea..934da2c3de63 100644 --- a/packages/InputDevices/res/values-bs/strings.xml +++ b/packages/InputDevices/res/values-bs/strings.xml @@ -52,8 +52,6 @@ <string name="keyboard_layout_georgian" msgid="4596185456863747454">"gruzijski"</string> <string name="keyboard_layout_thai_kedmanee" msgid="6637147314580760938">"tajlandski (Kedmanee)"</string> <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"tajlandski (pattachote)"</string> - <!-- no translation found for keyboard_layout_serbian_latin (3128791759390046571) --> - <skip /> - <!-- no translation found for keyboard_layout_montenegrin_latin (1467832503378949945) --> - <skip /> + <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"srpski (latinica)"</string> + <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"crnogorski (latinica)"</string> </resources> diff --git a/packages/InputDevices/res/values-ca/strings.xml b/packages/InputDevices/res/values-ca/strings.xml index fe5a09263539..2082c0535c67 100644 --- a/packages/InputDevices/res/values-ca/strings.xml +++ b/packages/InputDevices/res/values-ca/strings.xml @@ -52,8 +52,6 @@ <string name="keyboard_layout_georgian" msgid="4596185456863747454">"Georgià"</string> <string name="keyboard_layout_thai_kedmanee" msgid="6637147314580760938">"Tai (Kedmanee)"</string> <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"Tai (Pattachote)"</string> - <!-- no translation found for keyboard_layout_serbian_latin (3128791759390046571) --> - <skip /> - <!-- no translation found for keyboard_layout_montenegrin_latin (1467832503378949945) --> - <skip /> + <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"Serbi (llatí)"</string> + <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"Montenegrí (llatí)"</string> </resources> diff --git a/packages/InputDevices/res/values-cs/strings.xml b/packages/InputDevices/res/values-cs/strings.xml index 4e3416c4acad..cd211c91e159 100644 --- a/packages/InputDevices/res/values-cs/strings.xml +++ b/packages/InputDevices/res/values-cs/strings.xml @@ -52,8 +52,6 @@ <string name="keyboard_layout_georgian" msgid="4596185456863747454">"gruzínština"</string> <string name="keyboard_layout_thai_kedmanee" msgid="6637147314580760938">"thajština (Kedmanee)"</string> <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"thajština (Pattachote)"</string> - <!-- no translation found for keyboard_layout_serbian_latin (3128791759390046571) --> - <skip /> - <!-- no translation found for keyboard_layout_montenegrin_latin (1467832503378949945) --> - <skip /> + <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"srbština (latinka)"</string> + <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"černohorština (latinka)"</string> </resources> diff --git a/packages/InputDevices/res/values-da/strings.xml b/packages/InputDevices/res/values-da/strings.xml index c26322453d56..9635810ccede 100644 --- a/packages/InputDevices/res/values-da/strings.xml +++ b/packages/InputDevices/res/values-da/strings.xml @@ -52,8 +52,6 @@ <string name="keyboard_layout_georgian" msgid="4596185456863747454">"Georgisk"</string> <string name="keyboard_layout_thai_kedmanee" msgid="6637147314580760938">"Thai (kedmanee)"</string> <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"Thai (pattachote)"</string> - <!-- no translation found for keyboard_layout_serbian_latin (3128791759390046571) --> - <skip /> - <!-- no translation found for keyboard_layout_montenegrin_latin (1467832503378949945) --> - <skip /> + <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"Serbisk (latinsk)"</string> + <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"Montenegrinsk (latinsk)"</string> </resources> diff --git a/packages/InputDevices/res/values-de/strings.xml b/packages/InputDevices/res/values-de/strings.xml index 587689107811..35db12e98cde 100644 --- a/packages/InputDevices/res/values-de/strings.xml +++ b/packages/InputDevices/res/values-de/strings.xml @@ -52,8 +52,6 @@ <string name="keyboard_layout_georgian" msgid="4596185456863747454">"Georgisch"</string> <string name="keyboard_layout_thai_kedmanee" msgid="6637147314580760938">"Thailändisch (Kedmanee)"</string> <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"Thailändisch (Pattachote)"</string> - <!-- no translation found for keyboard_layout_serbian_latin (3128791759390046571) --> - <skip /> - <!-- no translation found for keyboard_layout_montenegrin_latin (1467832503378949945) --> - <skip /> + <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"Serbisch (lat. Alphabet)"</string> + <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"Montenegrinisch (lat. Alphabet)"</string> </resources> diff --git a/packages/InputDevices/res/values-en-rAU/strings.xml b/packages/InputDevices/res/values-en-rAU/strings.xml index 356ebd48415e..02960673af54 100644 --- a/packages/InputDevices/res/values-en-rAU/strings.xml +++ b/packages/InputDevices/res/values-en-rAU/strings.xml @@ -52,8 +52,6 @@ <string name="keyboard_layout_georgian" msgid="4596185456863747454">"Georgian"</string> <string name="keyboard_layout_thai_kedmanee" msgid="6637147314580760938">"Thai (Kedmanee)"</string> <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"Thai (Pattachote)"</string> - <!-- no translation found for keyboard_layout_serbian_latin (3128791759390046571) --> - <skip /> - <!-- no translation found for keyboard_layout_montenegrin_latin (1467832503378949945) --> - <skip /> + <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"Serbian (Latin)"</string> + <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"Montenegrin (Latin)"</string> </resources> diff --git a/packages/InputDevices/res/values-en-rGB/strings.xml b/packages/InputDevices/res/values-en-rGB/strings.xml index 356ebd48415e..02960673af54 100644 --- a/packages/InputDevices/res/values-en-rGB/strings.xml +++ b/packages/InputDevices/res/values-en-rGB/strings.xml @@ -52,8 +52,6 @@ <string name="keyboard_layout_georgian" msgid="4596185456863747454">"Georgian"</string> <string name="keyboard_layout_thai_kedmanee" msgid="6637147314580760938">"Thai (Kedmanee)"</string> <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"Thai (Pattachote)"</string> - <!-- no translation found for keyboard_layout_serbian_latin (3128791759390046571) --> - <skip /> - <!-- no translation found for keyboard_layout_montenegrin_latin (1467832503378949945) --> - <skip /> + <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"Serbian (Latin)"</string> + <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"Montenegrin (Latin)"</string> </resources> diff --git a/packages/InputDevices/res/values-en-rIN/strings.xml b/packages/InputDevices/res/values-en-rIN/strings.xml index 356ebd48415e..02960673af54 100644 --- a/packages/InputDevices/res/values-en-rIN/strings.xml +++ b/packages/InputDevices/res/values-en-rIN/strings.xml @@ -52,8 +52,6 @@ <string name="keyboard_layout_georgian" msgid="4596185456863747454">"Georgian"</string> <string name="keyboard_layout_thai_kedmanee" msgid="6637147314580760938">"Thai (Kedmanee)"</string> <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"Thai (Pattachote)"</string> - <!-- no translation found for keyboard_layout_serbian_latin (3128791759390046571) --> - <skip /> - <!-- no translation found for keyboard_layout_montenegrin_latin (1467832503378949945) --> - <skip /> + <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"Serbian (Latin)"</string> + <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"Montenegrin (Latin)"</string> </resources> diff --git a/packages/InputDevices/res/values-es-rUS/strings.xml b/packages/InputDevices/res/values-es-rUS/strings.xml index c20d9280fbc4..4d3233391371 100644 --- a/packages/InputDevices/res/values-es-rUS/strings.xml +++ b/packages/InputDevices/res/values-es-rUS/strings.xml @@ -52,8 +52,6 @@ <string name="keyboard_layout_georgian" msgid="4596185456863747454">"Georgiano"</string> <string name="keyboard_layout_thai_kedmanee" msgid="6637147314580760938">"Tailandés (Kedmanee)"</string> <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"Tailandés (Pattachote)"</string> - <!-- no translation found for keyboard_layout_serbian_latin (3128791759390046571) --> - <skip /> - <!-- no translation found for keyboard_layout_montenegrin_latin (1467832503378949945) --> - <skip /> + <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"Serbio (latino)"</string> + <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"Montenegrino (latino)"</string> </resources> diff --git a/packages/InputDevices/res/values-eu/strings.xml b/packages/InputDevices/res/values-eu/strings.xml index 57af1f7cfc15..08d96a9b3f37 100644 --- a/packages/InputDevices/res/values-eu/strings.xml +++ b/packages/InputDevices/res/values-eu/strings.xml @@ -52,8 +52,6 @@ <string name="keyboard_layout_georgian" msgid="4596185456863747454">"Georgiarra"</string> <string name="keyboard_layout_thai_kedmanee" msgid="6637147314580760938">"Thailandiarra (kedmanee-a)"</string> <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"Thailandiarra (pattachote-a)"</string> - <!-- no translation found for keyboard_layout_serbian_latin (3128791759390046571) --> - <skip /> - <!-- no translation found for keyboard_layout_montenegrin_latin (1467832503378949945) --> - <skip /> + <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"Serbiarra (latindarra)"</string> + <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"Montenegroarra (latindarra)"</string> </resources> diff --git a/packages/InputDevices/res/values-fa/strings.xml b/packages/InputDevices/res/values-fa/strings.xml index 6ab841101eb7..8dcae86b12e7 100644 --- a/packages/InputDevices/res/values-fa/strings.xml +++ b/packages/InputDevices/res/values-fa/strings.xml @@ -52,8 +52,6 @@ <string name="keyboard_layout_georgian" msgid="4596185456863747454">"گرجستانی"</string> <string name="keyboard_layout_thai_kedmanee" msgid="6637147314580760938">"تایلندی (کدمانی)"</string> <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"تایلندی (پاتاچوته)"</string> - <!-- no translation found for keyboard_layout_serbian_latin (3128791759390046571) --> - <skip /> - <!-- no translation found for keyboard_layout_montenegrin_latin (1467832503378949945) --> - <skip /> + <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"صربی (لاتین)"</string> + <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"مونتهنگرویی (لاتین)"</string> </resources> diff --git a/packages/InputDevices/res/values-fi/strings.xml b/packages/InputDevices/res/values-fi/strings.xml index 2c69b2929812..8748854e1035 100644 --- a/packages/InputDevices/res/values-fi/strings.xml +++ b/packages/InputDevices/res/values-fi/strings.xml @@ -52,8 +52,6 @@ <string name="keyboard_layout_georgian" msgid="4596185456863747454">"georgia"</string> <string name="keyboard_layout_thai_kedmanee" msgid="6637147314580760938">"thai (kedmanee)"</string> <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"thai (pattachote)"</string> - <!-- no translation found for keyboard_layout_serbian_latin (3128791759390046571) --> - <skip /> - <!-- no translation found for keyboard_layout_montenegrin_latin (1467832503378949945) --> - <skip /> + <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"serbia (latinalainen)"</string> + <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"montenegro (latinalainen)"</string> </resources> diff --git a/packages/InputDevices/res/values-fr-rCA/strings.xml b/packages/InputDevices/res/values-fr-rCA/strings.xml index a4656ffeadca..ee498aad5974 100644 --- a/packages/InputDevices/res/values-fr-rCA/strings.xml +++ b/packages/InputDevices/res/values-fr-rCA/strings.xml @@ -52,8 +52,6 @@ <string name="keyboard_layout_georgian" msgid="4596185456863747454">"Géorgien"</string> <string name="keyboard_layout_thai_kedmanee" msgid="6637147314580760938">"Thaï (Kedmanee)"</string> <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"Thaï (Pattachote)"</string> - <!-- no translation found for keyboard_layout_serbian_latin (3128791759390046571) --> - <skip /> - <!-- no translation found for keyboard_layout_montenegrin_latin (1467832503378949945) --> - <skip /> + <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"Serbe (latin)"</string> + <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"Monténégrin (latin)"</string> </resources> diff --git a/packages/InputDevices/res/values-fr/strings.xml b/packages/InputDevices/res/values-fr/strings.xml index 76c4815b606f..f12e1db266ac 100644 --- a/packages/InputDevices/res/values-fr/strings.xml +++ b/packages/InputDevices/res/values-fr/strings.xml @@ -52,8 +52,6 @@ <string name="keyboard_layout_georgian" msgid="4596185456863747454">"Géorgien"</string> <string name="keyboard_layout_thai_kedmanee" msgid="6637147314580760938">"Thaï (Kedmanee)"</string> <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"Thaï (Pattachote)"</string> - <!-- no translation found for keyboard_layout_serbian_latin (3128791759390046571) --> - <skip /> - <!-- no translation found for keyboard_layout_montenegrin_latin (1467832503378949945) --> - <skip /> + <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"Serbe (latin)"</string> + <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"Monténégrin (latin)"</string> </resources> diff --git a/packages/InputDevices/res/values-gl/strings.xml b/packages/InputDevices/res/values-gl/strings.xml index 133fbf746940..2d96c8919a12 100644 --- a/packages/InputDevices/res/values-gl/strings.xml +++ b/packages/InputDevices/res/values-gl/strings.xml @@ -52,8 +52,6 @@ <string name="keyboard_layout_georgian" msgid="4596185456863747454">"Xeorxiano"</string> <string name="keyboard_layout_thai_kedmanee" msgid="6637147314580760938">"Tailandés (kedmanee)"</string> <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"Tailandés (pattachote)"</string> - <!-- no translation found for keyboard_layout_serbian_latin (3128791759390046571) --> - <skip /> - <!-- no translation found for keyboard_layout_montenegrin_latin (1467832503378949945) --> - <skip /> + <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"Serbio (alfabeto latino)"</string> + <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"Montenegrino (alfabeto latino)"</string> </resources> diff --git a/packages/InputDevices/res/values-gu/strings.xml b/packages/InputDevices/res/values-gu/strings.xml index a3c98ae7a5eb..3c02208ca593 100644 --- a/packages/InputDevices/res/values-gu/strings.xml +++ b/packages/InputDevices/res/values-gu/strings.xml @@ -52,8 +52,6 @@ <string name="keyboard_layout_georgian" msgid="4596185456863747454">"જ્યોર્જિઅન"</string> <string name="keyboard_layout_thai_kedmanee" msgid="6637147314580760938">"થાઇ (કેડમાની)"</string> <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"થાઇ (પટ્ટાશોટે)"</string> - <!-- no translation found for keyboard_layout_serbian_latin (3128791759390046571) --> - <skip /> - <!-- no translation found for keyboard_layout_montenegrin_latin (1467832503378949945) --> - <skip /> + <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"સર્બિયન (લેટિન)"</string> + <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"મોંટેનેગ્રીન (લેટિન)"</string> </resources> diff --git a/packages/InputDevices/res/values-hi/strings.xml b/packages/InputDevices/res/values-hi/strings.xml index fafc42da686e..88729ea173f7 100644 --- a/packages/InputDevices/res/values-hi/strings.xml +++ b/packages/InputDevices/res/values-hi/strings.xml @@ -52,8 +52,6 @@ <string name="keyboard_layout_georgian" msgid="4596185456863747454">"जॉर्जियन कीबोर्ड का लेआउट"</string> <string name="keyboard_layout_thai_kedmanee" msgid="6637147314580760938">"थाई (केडमेनी)"</string> <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"थाई (पटैचोटे)"</string> - <!-- no translation found for keyboard_layout_serbian_latin (3128791759390046571) --> - <skip /> - <!-- no translation found for keyboard_layout_montenegrin_latin (1467832503378949945) --> - <skip /> + <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"सर्बियन (लैटिन)"</string> + <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"मॉन्टेनीग्रिन (लैटिन)"</string> </resources> diff --git a/packages/InputDevices/res/values-hr/strings.xml b/packages/InputDevices/res/values-hr/strings.xml index d8e7ec49be88..39da355a2e01 100644 --- a/packages/InputDevices/res/values-hr/strings.xml +++ b/packages/InputDevices/res/values-hr/strings.xml @@ -52,8 +52,6 @@ <string name="keyboard_layout_georgian" msgid="4596185456863747454">"gruzijski"</string> <string name="keyboard_layout_thai_kedmanee" msgid="6637147314580760938">"tajski (kedmanee)"</string> <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"tajski (pattachote)"</string> - <!-- no translation found for keyboard_layout_serbian_latin (3128791759390046571) --> - <skip /> - <!-- no translation found for keyboard_layout_montenegrin_latin (1467832503378949945) --> - <skip /> + <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"srpski (latinica)"</string> + <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"crnogorski (latinica)"</string> </resources> diff --git a/packages/InputDevices/res/values-hu/strings.xml b/packages/InputDevices/res/values-hu/strings.xml index 88c532e5bf88..edceda27bdba 100644 --- a/packages/InputDevices/res/values-hu/strings.xml +++ b/packages/InputDevices/res/values-hu/strings.xml @@ -52,8 +52,6 @@ <string name="keyboard_layout_georgian" msgid="4596185456863747454">"grúz"</string> <string name="keyboard_layout_thai_kedmanee" msgid="6637147314580760938">"thai (kedmanee)"</string> <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"thai (pattachote)"</string> - <!-- no translation found for keyboard_layout_serbian_latin (3128791759390046571) --> - <skip /> - <!-- no translation found for keyboard_layout_montenegrin_latin (1467832503378949945) --> - <skip /> + <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"szerb (latin betűs)"</string> + <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"montenegrói (latin betűs)"</string> </resources> diff --git a/packages/InputDevices/res/values-hy/strings.xml b/packages/InputDevices/res/values-hy/strings.xml index ef4128e94eea..8f3652aaa62d 100644 --- a/packages/InputDevices/res/values-hy/strings.xml +++ b/packages/InputDevices/res/values-hy/strings.xml @@ -52,8 +52,6 @@ <string name="keyboard_layout_georgian" msgid="4596185456863747454">"վրացերեն"</string> <string name="keyboard_layout_thai_kedmanee" msgid="6637147314580760938">"թայերեն (քեդմանի)"</string> <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"թայերեն (պատաչոտ)"</string> - <!-- no translation found for keyboard_layout_serbian_latin (3128791759390046571) --> - <skip /> - <!-- no translation found for keyboard_layout_montenegrin_latin (1467832503378949945) --> - <skip /> + <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"սերբերեն (լատինատառ)"</string> + <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"չեռնոգորերեն (լատինատառ)"</string> </resources> diff --git a/packages/InputDevices/res/values-it/strings.xml b/packages/InputDevices/res/values-it/strings.xml index 97a2359f7de6..b6b9e8db0362 100644 --- a/packages/InputDevices/res/values-it/strings.xml +++ b/packages/InputDevices/res/values-it/strings.xml @@ -52,8 +52,6 @@ <string name="keyboard_layout_georgian" msgid="4596185456863747454">"Georgiano"</string> <string name="keyboard_layout_thai_kedmanee" msgid="6637147314580760938">"Thai (Kedmanee)"</string> <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"Thai (Pattachote)"</string> - <!-- no translation found for keyboard_layout_serbian_latin (3128791759390046571) --> - <skip /> - <!-- no translation found for keyboard_layout_montenegrin_latin (1467832503378949945) --> - <skip /> + <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"Serbo (latino)"</string> + <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"Montenegrino (latino)"</string> </resources> diff --git a/packages/InputDevices/res/values-iw/strings.xml b/packages/InputDevices/res/values-iw/strings.xml index 0f7a341f8dcd..93f74267ffae 100644 --- a/packages/InputDevices/res/values-iw/strings.xml +++ b/packages/InputDevices/res/values-iw/strings.xml @@ -52,8 +52,6 @@ <string name="keyboard_layout_georgian" msgid="4596185456863747454">"גיאורגית"</string> <string name="keyboard_layout_thai_kedmanee" msgid="6637147314580760938">"תאית (Kedmanee)"</string> <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"תאית (Pattachote)"</string> - <!-- no translation found for keyboard_layout_serbian_latin (3128791759390046571) --> - <skip /> - <!-- no translation found for keyboard_layout_montenegrin_latin (1467832503378949945) --> - <skip /> + <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"סרבית (לטינית)"</string> + <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"מונטנגרית (לטינית)"</string> </resources> diff --git a/packages/InputDevices/res/values-ja/strings.xml b/packages/InputDevices/res/values-ja/strings.xml index f6cfd433b247..bb9fc8c99b3d 100644 --- a/packages/InputDevices/res/values-ja/strings.xml +++ b/packages/InputDevices/res/values-ja/strings.xml @@ -52,8 +52,6 @@ <string name="keyboard_layout_georgian" msgid="4596185456863747454">"ジョージア語"</string> <string name="keyboard_layout_thai_kedmanee" msgid="6637147314580760938">"タイ語(Kedmanee)"</string> <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"タイ語(Pattachote)"</string> - <!-- no translation found for keyboard_layout_serbian_latin (3128791759390046571) --> - <skip /> - <!-- no translation found for keyboard_layout_montenegrin_latin (1467832503378949945) --> - <skip /> + <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"セルビア語(ラテン文字)"</string> + <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"モンテネグロ語(ラテン)"</string> </resources> diff --git a/packages/InputDevices/res/values-ka/strings.xml b/packages/InputDevices/res/values-ka/strings.xml index 4eebe6b30893..e09d65be5f54 100644 --- a/packages/InputDevices/res/values-ka/strings.xml +++ b/packages/InputDevices/res/values-ka/strings.xml @@ -52,8 +52,6 @@ <string name="keyboard_layout_georgian" msgid="4596185456863747454">"ქართული"</string> <string name="keyboard_layout_thai_kedmanee" msgid="6637147314580760938">"ტაილანდური (Kedmanee)"</string> <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"ტაილანდური (Pattachote)"</string> - <!-- no translation found for keyboard_layout_serbian_latin (3128791759390046571) --> - <skip /> - <!-- no translation found for keyboard_layout_montenegrin_latin (1467832503378949945) --> - <skip /> + <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"სერბული (ლათინური)"</string> + <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"მონტენეგრული (ლათინური)"</string> </resources> diff --git a/packages/InputDevices/res/values-kk/strings.xml b/packages/InputDevices/res/values-kk/strings.xml index b1ca40aa89b3..176aa24a3bf1 100644 --- a/packages/InputDevices/res/values-kk/strings.xml +++ b/packages/InputDevices/res/values-kk/strings.xml @@ -52,8 +52,6 @@ <string name="keyboard_layout_georgian" msgid="4596185456863747454">"Грузин"</string> <string name="keyboard_layout_thai_kedmanee" msgid="6637147314580760938">"Тай (кедмани)"</string> <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"Тай (паттачот)"</string> - <!-- no translation found for keyboard_layout_serbian_latin (3128791759390046571) --> - <skip /> - <!-- no translation found for keyboard_layout_montenegrin_latin (1467832503378949945) --> - <skip /> + <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"Серб (латын жазуы)"</string> + <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"Черногор (латын жазуы)"</string> </resources> diff --git a/packages/InputDevices/res/values-kn/strings.xml b/packages/InputDevices/res/values-kn/strings.xml index 94d65bd664e5..3b0def42fef0 100644 --- a/packages/InputDevices/res/values-kn/strings.xml +++ b/packages/InputDevices/res/values-kn/strings.xml @@ -52,8 +52,6 @@ <string name="keyboard_layout_georgian" msgid="4596185456863747454">"ಜಾರ್ಜಿಯನ್"</string> <string name="keyboard_layout_thai_kedmanee" msgid="6637147314580760938">"ಥಾಯ್ (ಕೆಡ್ಮನೀ)"</string> <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"ಥಾಯ್ (ಪಟ್ಟಚೋಟ್)"</string> - <!-- no translation found for keyboard_layout_serbian_latin (3128791759390046571) --> - <skip /> - <!-- no translation found for keyboard_layout_montenegrin_latin (1467832503378949945) --> - <skip /> + <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"ಸೆರ್ಬಿಯನ್ (ಲ್ಯಾಟಿನ್)"</string> + <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"ಮೊಂಟೆನೆಗ್ರಿನ್ (ಲ್ಯಾಟಿನ್)"</string> </resources> diff --git a/packages/InputDevices/res/values-ko/strings.xml b/packages/InputDevices/res/values-ko/strings.xml index fa2d9da87f7f..689d3fffece2 100644 --- a/packages/InputDevices/res/values-ko/strings.xml +++ b/packages/InputDevices/res/values-ko/strings.xml @@ -52,8 +52,6 @@ <string name="keyboard_layout_georgian" msgid="4596185456863747454">"조지아어"</string> <string name="keyboard_layout_thai_kedmanee" msgid="6637147314580760938">"태국어(Kedmanee)"</string> <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"태국어(Pattachote)"</string> - <!-- no translation found for keyboard_layout_serbian_latin (3128791759390046571) --> - <skip /> - <!-- no translation found for keyboard_layout_montenegrin_latin (1467832503378949945) --> - <skip /> + <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"세르비아어(로마자)"</string> + <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"몬테네그로어(로마자)"</string> </resources> diff --git a/packages/InputDevices/res/values-ky/strings.xml b/packages/InputDevices/res/values-ky/strings.xml index 9434840f4ee7..97dbc2a16468 100644 --- a/packages/InputDevices/res/values-ky/strings.xml +++ b/packages/InputDevices/res/values-ky/strings.xml @@ -52,8 +52,6 @@ <string name="keyboard_layout_georgian" msgid="4596185456863747454">"Грузинче"</string> <string name="keyboard_layout_thai_kedmanee" msgid="6637147314580760938">"Тайча (Kedmanee баскычтобу)"</string> <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"Тайча (Pattachote баскычтобу)"</string> - <!-- no translation found for keyboard_layout_serbian_latin (3128791759390046571) --> - <skip /> - <!-- no translation found for keyboard_layout_montenegrin_latin (1467832503378949945) --> - <skip /> + <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"Сербче (Латын)"</string> + <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"Монтенегрочо (Латын)"</string> </resources> diff --git a/packages/InputDevices/res/values-lo/strings.xml b/packages/InputDevices/res/values-lo/strings.xml index 95a8903654f7..da2b868024c2 100644 --- a/packages/InputDevices/res/values-lo/strings.xml +++ b/packages/InputDevices/res/values-lo/strings.xml @@ -52,8 +52,6 @@ <string name="keyboard_layout_georgian" msgid="4596185456863747454">"ຈໍຈຽນ"</string> <string name="keyboard_layout_thai_kedmanee" msgid="6637147314580760938">"ໄທ (ເກດມະນີ)"</string> <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"ໄທ (ປັດຕະໂຊຕິ)"</string> - <!-- no translation found for keyboard_layout_serbian_latin (3128791759390046571) --> - <skip /> - <!-- no translation found for keyboard_layout_montenegrin_latin (1467832503378949945) --> - <skip /> + <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"ເຊີບຽນ (ລາຕິນ)"</string> + <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"ມອນເທເນກຣິນ (ລາຕິນ)"</string> </resources> diff --git a/packages/InputDevices/res/values-lv/strings.xml b/packages/InputDevices/res/values-lv/strings.xml index 3cd4da79e1c7..827480c97be7 100644 --- a/packages/InputDevices/res/values-lv/strings.xml +++ b/packages/InputDevices/res/values-lv/strings.xml @@ -52,8 +52,6 @@ <string name="keyboard_layout_georgian" msgid="4596185456863747454">"Gruzīnu"</string> <string name="keyboard_layout_thai_kedmanee" msgid="6637147314580760938">"Taju valoda (Kedmanee)"</string> <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"Taju (Pattachote)"</string> - <!-- no translation found for keyboard_layout_serbian_latin (3128791759390046571) --> - <skip /> - <!-- no translation found for keyboard_layout_montenegrin_latin (1467832503378949945) --> - <skip /> + <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"Serbu (latīņu)"</string> + <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"Melnkalniešu (latīņu)"</string> </resources> diff --git a/packages/InputDevices/res/values-mk/strings.xml b/packages/InputDevices/res/values-mk/strings.xml index b91fcc19a18b..0aef3240ae2a 100644 --- a/packages/InputDevices/res/values-mk/strings.xml +++ b/packages/InputDevices/res/values-mk/strings.xml @@ -52,8 +52,6 @@ <string name="keyboard_layout_georgian" msgid="4596185456863747454">"грузиски"</string> <string name="keyboard_layout_thai_kedmanee" msgid="6637147314580760938">"тајландски (кедмани)"</string> <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"тајландски (Pattachote)"</string> - <!-- no translation found for keyboard_layout_serbian_latin (3128791759390046571) --> - <skip /> - <!-- no translation found for keyboard_layout_montenegrin_latin (1467832503378949945) --> - <skip /> + <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"српски (латиница)"</string> + <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"црногорски (латиница)"</string> </resources> diff --git a/packages/InputDevices/res/values-ml/strings.xml b/packages/InputDevices/res/values-ml/strings.xml index 408ae13b87fc..b8f4d327f087 100644 --- a/packages/InputDevices/res/values-ml/strings.xml +++ b/packages/InputDevices/res/values-ml/strings.xml @@ -52,8 +52,6 @@ <string name="keyboard_layout_georgian" msgid="4596185456863747454">"ജോര്ജ്ജിയൻ"</string> <string name="keyboard_layout_thai_kedmanee" msgid="6637147314580760938">"തായ് (Kedmanee)"</string> <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"തായ് (Pattachote)"</string> - <!-- no translation found for keyboard_layout_serbian_latin (3128791759390046571) --> - <skip /> - <!-- no translation found for keyboard_layout_montenegrin_latin (1467832503378949945) --> - <skip /> + <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"സെർബിയൻ (ലാറ്റിൻ)"</string> + <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"മോണ്ടിനെഗ്രിൻ (ലാറ്റിൻ)"</string> </resources> diff --git a/packages/InputDevices/res/values-mr/strings.xml b/packages/InputDevices/res/values-mr/strings.xml index 47cebf192408..63c4c906d11c 100644 --- a/packages/InputDevices/res/values-mr/strings.xml +++ b/packages/InputDevices/res/values-mr/strings.xml @@ -52,8 +52,6 @@ <string name="keyboard_layout_georgian" msgid="4596185456863747454">"जॉर्जियन"</string> <string name="keyboard_layout_thai_kedmanee" msgid="6637147314580760938">"थाई (केडमानी)"</string> <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"थाई (पट्टाचोटे)"</string> - <!-- no translation found for keyboard_layout_serbian_latin (3128791759390046571) --> - <skip /> - <!-- no translation found for keyboard_layout_montenegrin_latin (1467832503378949945) --> - <skip /> + <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"सर्बियन (लॅटिन)"</string> + <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"मॉन्टेनेग्रिन (लॅटिन)"</string> </resources> diff --git a/packages/InputDevices/res/values-ms/strings.xml b/packages/InputDevices/res/values-ms/strings.xml index 9a1c4f7569f3..6444ae024fef 100644 --- a/packages/InputDevices/res/values-ms/strings.xml +++ b/packages/InputDevices/res/values-ms/strings.xml @@ -52,8 +52,6 @@ <string name="keyboard_layout_georgian" msgid="4596185456863747454">"Bahasa Georgia"</string> <string name="keyboard_layout_thai_kedmanee" msgid="6637147314580760938">"Thai (Kedmanee)"</string> <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"Thai (Pattachote)"</string> - <!-- no translation found for keyboard_layout_serbian_latin (3128791759390046571) --> - <skip /> - <!-- no translation found for keyboard_layout_montenegrin_latin (1467832503378949945) --> - <skip /> + <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"Serbia (Latin)"</string> + <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"Montenegrin (Latin)"</string> </resources> diff --git a/packages/InputDevices/res/values-nb/strings.xml b/packages/InputDevices/res/values-nb/strings.xml index 25454481fff2..43a172f222a4 100644 --- a/packages/InputDevices/res/values-nb/strings.xml +++ b/packages/InputDevices/res/values-nb/strings.xml @@ -52,8 +52,6 @@ <string name="keyboard_layout_georgian" msgid="4596185456863747454">"Georgisk"</string> <string name="keyboard_layout_thai_kedmanee" msgid="6637147314580760938">"Thai (Kedmanee)"</string> <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"Thai (Pattachote)"</string> - <!-- no translation found for keyboard_layout_serbian_latin (3128791759390046571) --> - <skip /> - <!-- no translation found for keyboard_layout_montenegrin_latin (1467832503378949945) --> - <skip /> + <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"Serbisk (latinsk)"</string> + <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"Montenegrisk (latinsk)"</string> </resources> diff --git a/packages/InputDevices/res/values-ne/strings.xml b/packages/InputDevices/res/values-ne/strings.xml index e85d61571436..ab1ec1de8da1 100644 --- a/packages/InputDevices/res/values-ne/strings.xml +++ b/packages/InputDevices/res/values-ne/strings.xml @@ -52,8 +52,6 @@ <string name="keyboard_layout_georgian" msgid="4596185456863747454">"जर्जियाली"</string> <string name="keyboard_layout_thai_kedmanee" msgid="6637147314580760938">"थाई (केडमानी)"</string> <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"थाई (पत्ताचोते)"</string> - <!-- no translation found for keyboard_layout_serbian_latin (3128791759390046571) --> - <skip /> - <!-- no translation found for keyboard_layout_montenegrin_latin (1467832503378949945) --> - <skip /> + <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"सर्बियाली (ल्याटिन)"</string> + <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"मोन्टेनिग्रिन (ल्याटिन)"</string> </resources> diff --git a/packages/InputDevices/res/values-or/strings.xml b/packages/InputDevices/res/values-or/strings.xml index 8df615efedb7..94fc9b3e3cae 100644 --- a/packages/InputDevices/res/values-or/strings.xml +++ b/packages/InputDevices/res/values-or/strings.xml @@ -52,8 +52,6 @@ <string name="keyboard_layout_georgian" msgid="4596185456863747454">"ଜର୍ଜିଆନ୍"</string> <string name="keyboard_layout_thai_kedmanee" msgid="6637147314580760938">"ଥାଇ (କେଡମାନି)"</string> <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"ଥାଇ (ପାଟ୍ଟାଚୋଟେ)"</string> - <!-- no translation found for keyboard_layout_serbian_latin (3128791759390046571) --> - <skip /> - <!-- no translation found for keyboard_layout_montenegrin_latin (1467832503378949945) --> - <skip /> + <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"ସର୍ବିଆନ (ଲାଟିନ)"</string> + <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"ମଣ୍ଟେନେଗ୍ରିନ (ଲାଟିନ)"</string> </resources> diff --git a/packages/InputDevices/res/values-pa/strings.xml b/packages/InputDevices/res/values-pa/strings.xml index b0a140e8b666..970343fc74ab 100644 --- a/packages/InputDevices/res/values-pa/strings.xml +++ b/packages/InputDevices/res/values-pa/strings.xml @@ -52,8 +52,6 @@ <string name="keyboard_layout_georgian" msgid="4596185456863747454">"ਜਾਰਜੀਆਈ"</string> <string name="keyboard_layout_thai_kedmanee" msgid="6637147314580760938">"ਥਾਈ (ਕੇਦਮਨੀ)"</string> <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"ਥਾਈ (ਪੈਟਾਸ਼ੋਟੇ)"</string> - <!-- no translation found for keyboard_layout_serbian_latin (3128791759390046571) --> - <skip /> - <!-- no translation found for keyboard_layout_montenegrin_latin (1467832503378949945) --> - <skip /> + <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"ਸਰਬੀਆਈ (ਲਾਤੀਨੀ)"</string> + <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"ਮਾਂਟੇਨੀਗਰਿਨ (ਲਾਤੀਨੀ)"</string> </resources> diff --git a/packages/InputDevices/res/values-pt-rBR/strings.xml b/packages/InputDevices/res/values-pt-rBR/strings.xml index 6fa852bc60ae..18c334fb684f 100644 --- a/packages/InputDevices/res/values-pt-rBR/strings.xml +++ b/packages/InputDevices/res/values-pt-rBR/strings.xml @@ -52,8 +52,6 @@ <string name="keyboard_layout_georgian" msgid="4596185456863747454">"Georgiano"</string> <string name="keyboard_layout_thai_kedmanee" msgid="6637147314580760938">"Tailandês (Kedmanee)"</string> <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"Tailandês (pattachote)"</string> - <!-- no translation found for keyboard_layout_serbian_latin (3128791759390046571) --> - <skip /> - <!-- no translation found for keyboard_layout_montenegrin_latin (1467832503378949945) --> - <skip /> + <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"Sérvio (latim)"</string> + <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"Montenegrino (latim)"</string> </resources> diff --git a/packages/InputDevices/res/values-pt-rPT/strings.xml b/packages/InputDevices/res/values-pt-rPT/strings.xml index b7684672bced..ea1d9c1cbe72 100644 --- a/packages/InputDevices/res/values-pt-rPT/strings.xml +++ b/packages/InputDevices/res/values-pt-rPT/strings.xml @@ -52,8 +52,6 @@ <string name="keyboard_layout_georgian" msgid="4596185456863747454">"Georgiano"</string> <string name="keyboard_layout_thai_kedmanee" msgid="6637147314580760938">"Tailandês (Kedmanee)"</string> <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"Tailandês (Pattachote)"</string> - <!-- no translation found for keyboard_layout_serbian_latin (3128791759390046571) --> - <skip /> - <!-- no translation found for keyboard_layout_montenegrin_latin (1467832503378949945) --> - <skip /> + <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"Sérvio (latim)"</string> + <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"Montenegrino (latim)"</string> </resources> diff --git a/packages/InputDevices/res/values-pt/strings.xml b/packages/InputDevices/res/values-pt/strings.xml index 6fa852bc60ae..18c334fb684f 100644 --- a/packages/InputDevices/res/values-pt/strings.xml +++ b/packages/InputDevices/res/values-pt/strings.xml @@ -52,8 +52,6 @@ <string name="keyboard_layout_georgian" msgid="4596185456863747454">"Georgiano"</string> <string name="keyboard_layout_thai_kedmanee" msgid="6637147314580760938">"Tailandês (Kedmanee)"</string> <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"Tailandês (pattachote)"</string> - <!-- no translation found for keyboard_layout_serbian_latin (3128791759390046571) --> - <skip /> - <!-- no translation found for keyboard_layout_montenegrin_latin (1467832503378949945) --> - <skip /> + <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"Sérvio (latim)"</string> + <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"Montenegrino (latim)"</string> </resources> diff --git a/packages/InputDevices/res/values-ro/strings.xml b/packages/InputDevices/res/values-ro/strings.xml index 9dc2841832b1..9fa3017358d0 100644 --- a/packages/InputDevices/res/values-ro/strings.xml +++ b/packages/InputDevices/res/values-ro/strings.xml @@ -52,8 +52,6 @@ <string name="keyboard_layout_georgian" msgid="4596185456863747454">"Georgiană"</string> <string name="keyboard_layout_thai_kedmanee" msgid="6637147314580760938">"Thailandeză (Kedmanee)"</string> <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"Thailandeză (Pattachote)"</string> - <!-- no translation found for keyboard_layout_serbian_latin (3128791759390046571) --> - <skip /> - <!-- no translation found for keyboard_layout_montenegrin_latin (1467832503378949945) --> - <skip /> + <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"Sârbă (caractere latine)"</string> + <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"Muntenegreană (caractere latine)"</string> </resources> diff --git a/packages/InputDevices/res/values-ru/strings.xml b/packages/InputDevices/res/values-ru/strings.xml index 9612717bfe1a..90b2461c7eba 100644 --- a/packages/InputDevices/res/values-ru/strings.xml +++ b/packages/InputDevices/res/values-ru/strings.xml @@ -52,8 +52,6 @@ <string name="keyboard_layout_georgian" msgid="4596185456863747454">"грузинский"</string> <string name="keyboard_layout_thai_kedmanee" msgid="6637147314580760938">"Тайский (Kedmanee)"</string> <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"Тайский (Pattachote)"</string> - <!-- no translation found for keyboard_layout_serbian_latin (3128791759390046571) --> - <skip /> - <!-- no translation found for keyboard_layout_montenegrin_latin (1467832503378949945) --> - <skip /> + <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"Сербский (латиница)"</string> + <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"Черногорский (латиница)"</string> </resources> diff --git a/packages/InputDevices/res/values-si/strings.xml b/packages/InputDevices/res/values-si/strings.xml index 2151f4492fc7..8d4238509341 100644 --- a/packages/InputDevices/res/values-si/strings.xml +++ b/packages/InputDevices/res/values-si/strings.xml @@ -52,8 +52,6 @@ <string name="keyboard_layout_georgian" msgid="4596185456863747454">"ජෝර්ජියානු"</string> <string name="keyboard_layout_thai_kedmanee" msgid="6637147314580760938">"තායි (කෙඩ්මනී)"</string> <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"තායි (පට්ටචෝටේ)"</string> - <!-- no translation found for keyboard_layout_serbian_latin (3128791759390046571) --> - <skip /> - <!-- no translation found for keyboard_layout_montenegrin_latin (1467832503378949945) --> - <skip /> + <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"සර්බියානු (ලතින්)"</string> + <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"මොන්ටෙනේග්රීන් (ලතින්)"</string> </resources> diff --git a/packages/InputDevices/res/values-sk/strings.xml b/packages/InputDevices/res/values-sk/strings.xml index c8b602101761..64b4ef2024da 100644 --- a/packages/InputDevices/res/values-sk/strings.xml +++ b/packages/InputDevices/res/values-sk/strings.xml @@ -52,8 +52,6 @@ <string name="keyboard_layout_georgian" msgid="4596185456863747454">"gruzínske"</string> <string name="keyboard_layout_thai_kedmanee" msgid="6637147314580760938">"thajčina (Kedmanee)"</string> <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"thajčina (Pattachote)"</string> - <!-- no translation found for keyboard_layout_serbian_latin (3128791759390046571) --> - <skip /> - <!-- no translation found for keyboard_layout_montenegrin_latin (1467832503378949945) --> - <skip /> + <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"srbčina (latinka)"</string> + <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"čiernohorčina (latinka)"</string> </resources> diff --git a/packages/InputDevices/res/values-sl/strings.xml b/packages/InputDevices/res/values-sl/strings.xml index 1e04ae11f2c3..fdbbca72e2b7 100644 --- a/packages/InputDevices/res/values-sl/strings.xml +++ b/packages/InputDevices/res/values-sl/strings.xml @@ -52,8 +52,6 @@ <string name="keyboard_layout_georgian" msgid="4596185456863747454">"gruzinščina"</string> <string name="keyboard_layout_thai_kedmanee" msgid="6637147314580760938">"tajščina (Kedmanee)"</string> <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"tajščina (Pattachote)"</string> - <!-- no translation found for keyboard_layout_serbian_latin (3128791759390046571) --> - <skip /> - <!-- no translation found for keyboard_layout_montenegrin_latin (1467832503378949945) --> - <skip /> + <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"srbščina (latinica)"</string> + <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"črnogorščina (latinica)"</string> </resources> diff --git a/packages/InputDevices/res/values-sq/strings.xml b/packages/InputDevices/res/values-sq/strings.xml index 8ad13f442279..06a598c6613a 100644 --- a/packages/InputDevices/res/values-sq/strings.xml +++ b/packages/InputDevices/res/values-sq/strings.xml @@ -52,8 +52,6 @@ <string name="keyboard_layout_georgian" msgid="4596185456863747454">"Gjeorgjisht"</string> <string name="keyboard_layout_thai_kedmanee" msgid="6637147314580760938">"Tajlandisht (Kedmanee)"</string> <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"Tajlandisht (Pattachote)"</string> - <!-- no translation found for keyboard_layout_serbian_latin (3128791759390046571) --> - <skip /> - <!-- no translation found for keyboard_layout_montenegrin_latin (1467832503378949945) --> - <skip /> + <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"Serbisht (latine)"</string> + <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"Malazisht (latine)"</string> </resources> diff --git a/packages/InputDevices/res/values-sr/strings.xml b/packages/InputDevices/res/values-sr/strings.xml index 28cd5caf1c99..d8beac6321f7 100644 --- a/packages/InputDevices/res/values-sr/strings.xml +++ b/packages/InputDevices/res/values-sr/strings.xml @@ -52,8 +52,6 @@ <string name="keyboard_layout_georgian" msgid="4596185456863747454">"грузијска"</string> <string name="keyboard_layout_thai_kedmanee" msgid="6637147314580760938">"тајски (Kedmanee)"</string> <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"тајски (Pattachote)"</string> - <!-- no translation found for keyboard_layout_serbian_latin (3128791759390046571) --> - <skip /> - <!-- no translation found for keyboard_layout_montenegrin_latin (1467832503378949945) --> - <skip /> + <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"српски (латиница)"</string> + <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"црногорски (латиница)"</string> </resources> diff --git a/packages/InputDevices/res/values-sv/strings.xml b/packages/InputDevices/res/values-sv/strings.xml index c24c300f4f6d..f1046bd2636e 100644 --- a/packages/InputDevices/res/values-sv/strings.xml +++ b/packages/InputDevices/res/values-sv/strings.xml @@ -52,8 +52,6 @@ <string name="keyboard_layout_georgian" msgid="4596185456863747454">"georgiska"</string> <string name="keyboard_layout_thai_kedmanee" msgid="6637147314580760938">"Thai (Kedmanee)"</string> <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"thailändska (pattachote)"</string> - <!-- no translation found for keyboard_layout_serbian_latin (3128791759390046571) --> - <skip /> - <!-- no translation found for keyboard_layout_montenegrin_latin (1467832503378949945) --> - <skip /> + <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"serbiska (latinskt)"</string> + <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"montenegrinska (latinskt)"</string> </resources> diff --git a/packages/InputDevices/res/values-sw/strings.xml b/packages/InputDevices/res/values-sw/strings.xml index 0cf002ebdd76..52efd2f38baf 100644 --- a/packages/InputDevices/res/values-sw/strings.xml +++ b/packages/InputDevices/res/values-sw/strings.xml @@ -52,8 +52,6 @@ <string name="keyboard_layout_georgian" msgid="4596185456863747454">"Kijojia"</string> <string name="keyboard_layout_thai_kedmanee" msgid="6637147314580760938">"Kithai (Kedmanee)"</string> <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"Kitai (Kipatachote)"</string> - <!-- no translation found for keyboard_layout_serbian_latin (3128791759390046571) --> - <skip /> - <!-- no translation found for keyboard_layout_montenegrin_latin (1467832503378949945) --> - <skip /> + <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"Kiserbia (Kilatini)"</string> + <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"Kimontenegri (Kilatini)"</string> </resources> diff --git a/packages/InputDevices/res/values-ta/strings.xml b/packages/InputDevices/res/values-ta/strings.xml index 87e9105b0796..14fd630d8fff 100644 --- a/packages/InputDevices/res/values-ta/strings.xml +++ b/packages/InputDevices/res/values-ta/strings.xml @@ -52,8 +52,6 @@ <string name="keyboard_layout_georgian" msgid="4596185456863747454">"ஜார்ஜியன்"</string> <string name="keyboard_layout_thai_kedmanee" msgid="6637147314580760938">"தாய் (கேட்மேனி)"</string> <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"தாய் (பட்டாசொட்டே)"</string> - <!-- no translation found for keyboard_layout_serbian_latin (3128791759390046571) --> - <skip /> - <!-- no translation found for keyboard_layout_montenegrin_latin (1467832503378949945) --> - <skip /> + <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"செர்பியன் (லத்தீன்)"</string> + <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"மாண்டினெக்ரன் (லத்தீன்)"</string> </resources> diff --git a/packages/InputDevices/res/values-te/strings.xml b/packages/InputDevices/res/values-te/strings.xml index 4cf1b1419692..891fd6c00828 100644 --- a/packages/InputDevices/res/values-te/strings.xml +++ b/packages/InputDevices/res/values-te/strings.xml @@ -52,8 +52,6 @@ <string name="keyboard_layout_georgian" msgid="4596185456863747454">"జార్జియన్"</string> <string name="keyboard_layout_thai_kedmanee" msgid="6637147314580760938">"థాయ్ (కెడ్మనీ)"</string> <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"థాయ్ (పత్తచోత్)"</string> - <!-- no translation found for keyboard_layout_serbian_latin (3128791759390046571) --> - <skip /> - <!-- no translation found for keyboard_layout_montenegrin_latin (1467832503378949945) --> - <skip /> + <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"సెర్బియన్ (లాటిన్)"</string> + <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"మాంటెనెగ్రిన్ (లాటిన్)"</string> </resources> diff --git a/packages/InputDevices/res/values-th/strings.xml b/packages/InputDevices/res/values-th/strings.xml index 88cf752ebc16..b58cc295eeb0 100644 --- a/packages/InputDevices/res/values-th/strings.xml +++ b/packages/InputDevices/res/values-th/strings.xml @@ -52,8 +52,6 @@ <string name="keyboard_layout_georgian" msgid="4596185456863747454">"ภาษาจอร์เจีย"</string> <string name="keyboard_layout_thai_kedmanee" msgid="6637147314580760938">"ไทย (เกษมณี)"</string> <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"ไทย (ปัตตะโชติ)"</string> - <!-- no translation found for keyboard_layout_serbian_latin (3128791759390046571) --> - <skip /> - <!-- no translation found for keyboard_layout_montenegrin_latin (1467832503378949945) --> - <skip /> + <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"เซอร์เบีย (ละติน)"</string> + <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"มอนเตเนโกร (ละติน)"</string> </resources> diff --git a/packages/InputDevices/res/values-tl/strings.xml b/packages/InputDevices/res/values-tl/strings.xml index 787c85149034..3278899c0ba2 100644 --- a/packages/InputDevices/res/values-tl/strings.xml +++ b/packages/InputDevices/res/values-tl/strings.xml @@ -52,8 +52,6 @@ <string name="keyboard_layout_georgian" msgid="4596185456863747454">"Georgian"</string> <string name="keyboard_layout_thai_kedmanee" msgid="6637147314580760938">"Thai (Kedmanee)"</string> <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"Thai (Pattachote)"</string> - <!-- no translation found for keyboard_layout_serbian_latin (3128791759390046571) --> - <skip /> - <!-- no translation found for keyboard_layout_montenegrin_latin (1467832503378949945) --> - <skip /> + <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"Serbian (Latin)"</string> + <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"Montenegrin (Latin)"</string> </resources> diff --git a/packages/InputDevices/res/values-tr/strings.xml b/packages/InputDevices/res/values-tr/strings.xml index 62360b528230..6582aaf24f00 100644 --- a/packages/InputDevices/res/values-tr/strings.xml +++ b/packages/InputDevices/res/values-tr/strings.xml @@ -52,8 +52,6 @@ <string name="keyboard_layout_georgian" msgid="4596185456863747454">"Gürcüce"</string> <string name="keyboard_layout_thai_kedmanee" msgid="6637147314580760938">"Tayca (Kedmanee)"</string> <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"Tayca (Pattachote)"</string> - <!-- no translation found for keyboard_layout_serbian_latin (3128791759390046571) --> - <skip /> - <!-- no translation found for keyboard_layout_montenegrin_latin (1467832503378949945) --> - <skip /> + <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"Sırpça (Latin)"</string> + <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"Karadağca (Latin)"</string> </resources> diff --git a/packages/InputDevices/res/values-uk/strings.xml b/packages/InputDevices/res/values-uk/strings.xml index 15b1a2563052..3a544e0c35a8 100644 --- a/packages/InputDevices/res/values-uk/strings.xml +++ b/packages/InputDevices/res/values-uk/strings.xml @@ -52,8 +52,6 @@ <string name="keyboard_layout_georgian" msgid="4596185456863747454">"Грузинська"</string> <string name="keyboard_layout_thai_kedmanee" msgid="6637147314580760938">"Тайська (кедмані)"</string> <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"Тайська (паттачоте)"</string> - <!-- no translation found for keyboard_layout_serbian_latin (3128791759390046571) --> - <skip /> - <!-- no translation found for keyboard_layout_montenegrin_latin (1467832503378949945) --> - <skip /> + <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"Сербська (латиниця)"</string> + <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"Чорногорська (латиниця)"</string> </resources> diff --git a/packages/InputDevices/res/values-ur/strings.xml b/packages/InputDevices/res/values-ur/strings.xml index d10c79883f1b..4b2932604fcc 100644 --- a/packages/InputDevices/res/values-ur/strings.xml +++ b/packages/InputDevices/res/values-ur/strings.xml @@ -52,8 +52,6 @@ <string name="keyboard_layout_georgian" msgid="4596185456863747454">"جارجیائی"</string> <string name="keyboard_layout_thai_kedmanee" msgid="6637147314580760938">"تھائی (کیڈمینی)"</string> <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"تھائی (پٹاچوٹے)"</string> - <!-- no translation found for keyboard_layout_serbian_latin (3128791759390046571) --> - <skip /> - <!-- no translation found for keyboard_layout_montenegrin_latin (1467832503378949945) --> - <skip /> + <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"سربیائی (لاطینی)"</string> + <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"مونٹے نیگریائی (لاطینی)"</string> </resources> diff --git a/packages/InputDevices/res/values-zh-rCN/strings.xml b/packages/InputDevices/res/values-zh-rCN/strings.xml index 5934e3b512bc..6a5dd74d34e8 100644 --- a/packages/InputDevices/res/values-zh-rCN/strings.xml +++ b/packages/InputDevices/res/values-zh-rCN/strings.xml @@ -52,8 +52,6 @@ <string name="keyboard_layout_georgian" msgid="4596185456863747454">"格鲁吉亚语"</string> <string name="keyboard_layout_thai_kedmanee" msgid="6637147314580760938">"泰语 (Kedmanee)"</string> <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"泰语 (Pattachote)"</string> - <!-- no translation found for keyboard_layout_serbian_latin (3128791759390046571) --> - <skip /> - <!-- no translation found for keyboard_layout_montenegrin_latin (1467832503378949945) --> - <skip /> + <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"塞尔维亚语(拉丁字母)"</string> + <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"黑山语(拉丁字母)"</string> </resources> diff --git a/packages/InputDevices/res/values-zh-rHK/strings.xml b/packages/InputDevices/res/values-zh-rHK/strings.xml index dbcfd1ce1392..a5a934aaac36 100644 --- a/packages/InputDevices/res/values-zh-rHK/strings.xml +++ b/packages/InputDevices/res/values-zh-rHK/strings.xml @@ -52,8 +52,6 @@ <string name="keyboard_layout_georgian" msgid="4596185456863747454">"格魯吉亞文"</string> <string name="keyboard_layout_thai_kedmanee" msgid="6637147314580760938">"泰文 (Kedmanee)"</string> <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"泰文 (Pattachote)"</string> - <!-- no translation found for keyboard_layout_serbian_latin (3128791759390046571) --> - <skip /> - <!-- no translation found for keyboard_layout_montenegrin_latin (1467832503378949945) --> - <skip /> + <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"塞爾維亞文 (拉丁)"</string> + <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"蒙特內哥羅文 (拉丁)"</string> </resources> diff --git a/packages/InputDevices/res/values-zh-rTW/strings.xml b/packages/InputDevices/res/values-zh-rTW/strings.xml index c87f2ac398d3..54c8b233cb5b 100644 --- a/packages/InputDevices/res/values-zh-rTW/strings.xml +++ b/packages/InputDevices/res/values-zh-rTW/strings.xml @@ -52,8 +52,6 @@ <string name="keyboard_layout_georgian" msgid="4596185456863747454">"喬治亞文"</string> <string name="keyboard_layout_thai_kedmanee" msgid="6637147314580760938">"泰文 (Kedmanee)"</string> <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"泰文 (Pattachote)"</string> - <!-- no translation found for keyboard_layout_serbian_latin (3128791759390046571) --> - <skip /> - <!-- no translation found for keyboard_layout_montenegrin_latin (1467832503378949945) --> - <skip /> + <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"塞爾維亞文 (拉丁字母)"</string> + <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"蒙特內哥羅文 (拉丁字母)"</string> </resources> diff --git a/packages/InputDevices/res/values-zu/strings.xml b/packages/InputDevices/res/values-zu/strings.xml index f62afba2018f..78dfa3b5cfa5 100644 --- a/packages/InputDevices/res/values-zu/strings.xml +++ b/packages/InputDevices/res/values-zu/strings.xml @@ -52,8 +52,6 @@ <string name="keyboard_layout_georgian" msgid="4596185456863747454">"Georgian"</string> <string name="keyboard_layout_thai_kedmanee" msgid="6637147314580760938">"Isi-Thai (Kedmanee)"</string> <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"Isi-Thai (Pattachote)"</string> - <!-- no translation found for keyboard_layout_serbian_latin (3128791759390046571) --> - <skip /> - <!-- no translation found for keyboard_layout_montenegrin_latin (1467832503378949945) --> - <skip /> + <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"IsiSerbian (Latin)"</string> + <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"IsiMontenegrin (Latin)"</string> </resources> diff --git a/packages/InputDevices/res/values/strings.xml b/packages/InputDevices/res/values/strings.xml index be7d2c11a8f9..5a911256d9be 100644 --- a/packages/InputDevices/res/values/strings.xml +++ b/packages/InputDevices/res/values/strings.xml @@ -158,4 +158,10 @@ <!-- Montenegrin (Latin) keyboard layout label. [CHAR LIMIT=35] --> <string name="keyboard_layout_montenegrin_latin">Montenegrin (Latin)</string> + + <!-- Serbian (Cyrillic) keyboard layout label. [CHAR LIMIT=35] --> + <string name="keyboard_layout_serbian_cyrillic">Serbian (Cyrillic)</string> + + <!-- Montenegrin (Cyrillic) keyboard layout label. [CHAR LIMIT=35] --> + <string name="keyboard_layout_montenegrin_cyrillic">Montenegrin (Cyrillic)</string> </resources> diff --git a/packages/InputDevices/res/xml/keyboard_layouts.xml b/packages/InputDevices/res/xml/keyboard_layouts.xml index 84e4b9e14c55..93094890418d 100644 --- a/packages/InputDevices/res/xml/keyboard_layouts.xml +++ b/packages/InputDevices/res/xml/keyboard_layouts.xml @@ -336,14 +336,28 @@ <keyboard-layout android:name="keyboard_layout_serbian_latin" android:label="@string/keyboard_layout_serbian_latin" - android:keyboardLayout="@raw/keyboard_layout_serbian_latin" + android:keyboardLayout="@raw/keyboard_layout_serbian_and_montenegrin_latin" android:keyboardLocale="sr-Latn-RS" android:keyboardLayoutType="qwertz" /> <keyboard-layout android:name="keyboard_layout_montenegrin_latin" android:label="@string/keyboard_layout_montenegrin_latin" - android:keyboardLayout="@raw/keyboard_layout_serbian_latin" + android:keyboardLayout="@raw/keyboard_layout_serbian_and_montenegrin_latin" android:keyboardLocale="cnr-Latn-ME" android:keyboardLayoutType="qwertz" /> + + <keyboard-layout + android:name="keyboard_layout_serbian_cyrillic" + android:label="@string/keyboard_layout_serbian_cyrillic" + android:keyboardLayout="@raw/keyboard_layout_serbian_and_montenegrin_cyrillic" + android:keyboardLocale="sr-Cyrl-RS" + android:keyboardLayoutType="extended" /> + + <keyboard-layout + android:name="keyboard_layout_montenegrin_cyrillic" + android:label="@string/keyboard_layout_montenegrin_cyrillic" + android:keyboardLayout="@raw/keyboard_layout_serbian_and_montenegrin_cyrillic" + android:keyboardLocale="cnr-Cyrl-ME" + android:keyboardLayoutType="extended" /> </keyboard-layouts> diff --git a/packages/PackageInstaller/res/values-pa/strings.xml b/packages/PackageInstaller/res/values-pa/strings.xml index 1db92a00683e..80dc8897e3c3 100644 --- a/packages/PackageInstaller/res/values-pa/strings.xml +++ b/packages/PackageInstaller/res/values-pa/strings.xml @@ -63,7 +63,7 @@ <string name="archive_application_text_all_users" msgid="3151229641681672580">"ਕੀ ਇਸ ਐਪ ਨੂੰ ਸਾਰੇ ਵਰਤੋਂਕਾਰਾਂ ਲਈ ਪੁਰਾਲੇਖਬੱਧ ਕਰਨਾ ਹੈ? ਤੁਹਾਡਾ ਨਿੱਜੀ ਡਾਟਾ ਰੱਖਿਅਤ ਕੀਤਾ ਜਾਵੇਗਾ"</string> <string name="archive_application_text_current_user_work_profile" msgid="1450487362134779752">"ਕੀ ਇਸ ਐਪ ਨੂੰ ਤੁਹਾਡੇ ਕਾਰਜ ਪ੍ਰੋਫਾਈਲ \'ਤੇ ਪੁਰਾਲੇਖਬੱਧ ਕਰਨਾ ਹੈ? ਤੁਹਾਡਾ ਨਿੱਜੀ ਡਾਟਾ ਰੱਖਿਅਤ ਕੀਤਾ ਜਾਵੇਗਾ"</string> <string name="archive_application_text_user" msgid="2586558895535581451">"ਕੀ ਇਸ ਐਪ ਨੂੰ <xliff:g id="USERNAME">%1$s</xliff:g> ਲਈ ਪੁਰਾਲੇਖਬੱਧ ਕਰਨਾ ਹੈ? ਤੁਹਾਡਾ ਨਿੱਜੀ ਡਾਟਾ ਰੱਖਿਅਤ ਕੀਤਾ ਜਾਵੇਗਾ"</string> - <string name="archive_application_text_current_user_private_profile" msgid="1958423158655599132">"ਕੀ ਤੁਸੀਂ ਇਸ ਐਪ ਨੂੰ ਆਪਣੀ ਨਿੱਜੀ ਸਪੇਸ ਤੋਂ ਪੁਰਾਲੇਖਬੱਧ ਕਰਨਾ ਚਾਹੁੰਦੇ ਹੋ? ਤੁਹਾਡਾ ਨਿੱਜੀ ਡਾਟਾ ਰੱਖਿਅਤ ਕੀਤਾ ਜਾਵੇਗਾ"</string> + <string name="archive_application_text_current_user_private_profile" msgid="1958423158655599132">"ਕੀ ਤੁਸੀਂ ਇਸ ਐਪ ਨੂੰ ਆਪਣੀ ਪ੍ਰਾਈਵੇਟ ਸਪੇਸ ਤੋਂ ਪੁਰਾਲੇਖਬੱਧ ਕਰਨਾ ਚਾਹੁੰਦੇ ਹੋ? ਤੁਹਾਡਾ ਨਿੱਜੀ ਡਾਟਾ ਰੱਖਿਅਤ ਕੀਤਾ ਜਾਵੇਗਾ"</string> <string name="uninstall_application_text_all_users" msgid="575491774380227119">"ਕੀ ਤੁਸੀਂ ਇਸ ਐਪ ਨੂੰ "<b>"ਸਾਰੇ"</b>" ਵਰਤੋਂਕਾਰਾਂ ਲਈ ਅਣਸਥਾਪਤ ਕਰਨਾ ਚਾਹੁੰਦੇ ਹੋ? ਐਪਲੀਕੇਸ਼ਨ ਅਤੇ ਇਸਦਾ ਡਾਟਾ ਡੀਵਾਈਸ \'ਤੇ "<b>"ਸਾਰੇ"</b>" ਵਰਤੋਂਕਾਰਾਂ ਵੱਲੋਂ ਹਟਾ ਦਿੱਤਾ ਜਾਵੇਗਾ।"</string> <string name="uninstall_application_text_user" msgid="498072714173920526">"ਕੀ ਤੁਸੀਂ ਵਰਤੋਂਕਾਰ <xliff:g id="USERNAME">%1$s</xliff:g> ਲਈ ਇਸ ਐਪ ਨੂੰ ਅਣਸਥਾਪਤ ਕਰਨਾ ਚਾਹੁੰਦੇ ਹੋ?"</string> <string name="uninstall_application_text_current_user_work_profile" msgid="8788387739022366193">"ਕੀ ਤੁਸੀਂ ਇਸ ਐਪ ਨੂੰ ਆਪਣੇ ਕਾਰਜ ਪ੍ਰੋਫਾਈਲ ਤੋਂ ਅਣਸਥਾਪਤ ਕਰਨਾ ਚਾਹੁੰਦੇ ਹੋ?"</string> diff --git a/packages/PackageInstaller/res/values-tr/strings.xml b/packages/PackageInstaller/res/values-tr/strings.xml index 006ad52297de..e10eb0e52fcd 100644 --- a/packages/PackageInstaller/res/values-tr/strings.xml +++ b/packages/PackageInstaller/res/values-tr/strings.xml @@ -55,7 +55,7 @@ <string name="user_is_not_allowed_dlg_text" msgid="3468447791330611681">"Geçerli kullanıcının bu yüklemeyi kaldırma izni yok."</string> <string name="generic_error_dlg_title" msgid="5863195085927067752">"Hata"</string> <string name="generic_error_dlg_text" msgid="5287861443265795232">"Uygulamanın yüklemesi kaldırılamadı."</string> - <string name="uninstall_application_title" msgid="4045420072401428123">"Uygulamanın yüklemesini kaldır"</string> + <string name="uninstall_application_title" msgid="4045420072401428123">"Uygulamayı kaldır"</string> <string name="uninstall_update_title" msgid="824411791011583031">"Güncelleme kaldırılsın mı?"</string> <string name="uninstall_activity_text" msgid="1928194674397770771">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g>, şu uygulamanın bir parçasıdır:"</string> <string name="uninstall_application_text" msgid="3816830743706143980">"Bu uygulamanın yüklemesini kaldırmak istiyor musunuz?"</string> diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/InstallFailed.java b/packages/PackageInstaller/src/com/android/packageinstaller/InstallFailed.java index c96644ca8920..03d52d0868d1 100644 --- a/packages/PackageInstaller/src/com/android/packageinstaller/InstallFailed.java +++ b/packages/PackageInstaller/src/com/android/packageinstaller/InstallFailed.java @@ -28,6 +28,7 @@ import android.content.pm.PackageManager; import android.os.Bundle; import android.util.Log; import android.view.View; + import androidx.annotation.Nullable; /** @@ -84,22 +85,28 @@ public class InstallFailed extends Activity { int statusCode = intent.getIntExtra(PackageInstaller.EXTRA_STATUS, PackageInstaller.STATUS_FAILURE); boolean returnResult = intent.getBooleanExtra(Intent.EXTRA_RETURN_RESULT, false); - - if (returnResult) { - int legacyStatus = intent.getIntExtra(PackageInstaller.EXTRA_LEGACY_STATUS, + int legacyStatus = intent.getIntExtra(PackageInstaller.EXTRA_LEGACY_STATUS, PackageManager.INSTALL_FAILED_INTERNAL_ERROR); + // TODO (b/346655018): Use INSTALL_FAILED_ABORTED legacyCode in the condition + // statusCode can be STATUS_FAILURE_ABORTED if: + // 1. GPP blocks an install. + // 2. User denies ownership update explicitly. + // InstallFailed dialog must not be shown only when the user denies ownership update. We + // must show this dialog for all other install failures. + boolean userDenied = statusCode == PackageInstaller.STATUS_FAILURE_ABORTED + && legacyStatus != PackageManager.INSTALL_FAILED_VERIFICATION_TIMEOUT + && legacyStatus != PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE; + + if (returnResult) { // Return result if requested Intent result = new Intent(); result.putExtra(Intent.EXTRA_INSTALL_RESULT, legacyStatus); setResult(Activity.RESULT_FIRST_USER, result); finish(); - } else if (statusCode != PackageInstaller.STATUS_FAILURE_ABORTED) { - // statusCode will be STATUS_FAILURE_ABORTED if the update-owner confirmation dialog was - // dismissed by the user. We don't want to show a InstallFailed dialog in this case. - // If the user denies install permission for normal installs, this dialog will never be - // triggered as the status code is returned from PackageInstallerActivity.java - + } else if (userDenied) { + finish(); + } else { // Set header icon and title PackageUtil.AppSnippet as = intent.getParcelableExtra( PackageInstallerActivity.EXTRA_APP_SNIPPET, PackageUtil.AppSnippet.class); @@ -127,8 +134,6 @@ public class InstallFailed extends Activity { // Get status messages setExplanationFromErrorCode(statusCode); - } else { - finish(); } } diff --git a/packages/PrintRecommendationService/src/com/android/printservice/recommendation/plugin/xerox/MDnsUtils.java b/packages/PrintRecommendationService/src/com/android/printservice/recommendation/plugin/xerox/MDnsUtils.java index b0da08bf3003..b0da08bf3003 100755..100644 --- a/packages/PrintRecommendationService/src/com/android/printservice/recommendation/plugin/xerox/MDnsUtils.java +++ b/packages/PrintRecommendationService/src/com/android/printservice/recommendation/plugin/xerox/MDnsUtils.java diff --git a/packages/PrintRecommendationService/src/com/android/printservice/recommendation/plugin/xerox/ServiceResolver.java b/packages/PrintRecommendationService/src/com/android/printservice/recommendation/plugin/xerox/ServiceResolver.java index 9ada969c84c9..9ada969c84c9 100755..100644 --- a/packages/PrintRecommendationService/src/com/android/printservice/recommendation/plugin/xerox/ServiceResolver.java +++ b/packages/PrintRecommendationService/src/com/android/printservice/recommendation/plugin/xerox/ServiceResolver.java diff --git a/packages/PrintRecommendationService/src/com/android/printservice/recommendation/plugin/xerox/VendorInfo.java b/packages/PrintRecommendationService/src/com/android/printservice/recommendation/plugin/xerox/VendorInfo.java index 20967a742896..20967a742896 100755..100644 --- a/packages/PrintRecommendationService/src/com/android/printservice/recommendation/plugin/xerox/VendorInfo.java +++ b/packages/PrintRecommendationService/src/com/android/printservice/recommendation/plugin/xerox/VendorInfo.java diff --git a/packages/PrintRecommendationService/src/com/android/printservice/recommendation/plugin/xerox/XeroxPrintServiceRecommendationPlugin.java b/packages/PrintRecommendationService/src/com/android/printservice/recommendation/plugin/xerox/XeroxPrintServiceRecommendationPlugin.java index e6bca434cba5..e6bca434cba5 100755..100644 --- a/packages/PrintRecommendationService/src/com/android/printservice/recommendation/plugin/xerox/XeroxPrintServiceRecommendationPlugin.java +++ b/packages/PrintRecommendationService/src/com/android/printservice/recommendation/plugin/xerox/XeroxPrintServiceRecommendationPlugin.java diff --git a/packages/PrintSpooler/res/values-as/strings.xml b/packages/PrintSpooler/res/values-as/strings.xml index 020eac770134..950c5c8f585c 100644 --- a/packages/PrintSpooler/res/values-as/strings.xml +++ b/packages/PrintSpooler/res/values-as/strings.xml @@ -100,7 +100,7 @@ </string-array> <string-array name="orientation_labels"> <item msgid="4061931020926489228">"প\'ৰ্ট্ৰেইট"</item> - <item msgid="3199660090246166812">"লেণ্ডস্কেইপ"</item> + <item msgid="3199660090246166812">"লেণ্ডস্কে’প"</item> </string-array> <string name="print_write_error_message" msgid="5787642615179572543">"ফাইলত লিখিব পৰা নহ\'ল"</string> <string name="print_error_default_message" msgid="8602678405502922346">"দুঃখিত, প্ৰিণ্টিঙৰ কাম নহ\'ল। পুনৰ চেষ্টা কৰক।"</string> diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/res/layout-v31/collapsing_toolbar_content_layout.xml b/packages/SettingsLib/CollapsingToolbarBaseActivity/res/layout-v31/collapsing_toolbar_content_layout.xml index 72b569f22d6c..9848749e2d1c 100644 --- a/packages/SettingsLib/CollapsingToolbarBaseActivity/res/layout-v31/collapsing_toolbar_content_layout.xml +++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/res/layout-v31/collapsing_toolbar_content_layout.xml @@ -32,21 +32,9 @@ android:id="@+id/collapsing_toolbar" android:layout_width="match_parent" android:layout_height="@dimen/settingslib_toolbar_layout_height" - android:clipToPadding="false" - app:forceApplySystemWindowInsetTop="true" - app:extraMultilineHeightEnabled="true" - app:contentScrim="@color/settingslib_colorSurfaceHeader" - app:maxLines="3" app:layout_scrollFlags="scroll|exitUntilCollapsed|snap" - app:scrimAnimationDuration="50" - app:scrimVisibleHeightTrigger="@dimen/settingslib_scrim_visible_height_trigger" - app:statusBarScrim="@null" - app:titleCollapseMode="fade" - app:collapsedTitleTextAppearance="@style/CollapsingToolbarTitle.Collapsed" - app:expandedTitleTextAppearance="@style/CollapsingToolbarTitle.Expanded" - app:expandedTitleMarginStart="@dimen/expanded_title_margin_start" - app:expandedTitleMarginEnd="@dimen/expanded_title_margin_end" - app:toolbarId="@id/action_bar"> + app:toolbarId="@id/action_bar" + style="@style/CollapsingToolbarLayoutStyle.SettingsLib"> <Toolbar android:id="@+id/action_bar" diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values-v31/dimens.xml b/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values-v31/dimens.xml index 15c1abbf97ba..40b9fcd79d49 100644 --- a/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values-v31/dimens.xml +++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values-v31/dimens.xml @@ -20,4 +20,5 @@ <dimen name="settingslib_scrim_visible_height_trigger">137dp</dimen> <dimen name="expanded_title_margin_start">24dp</dimen> <dimen name="expanded_title_margin_end">24dp</dimen> + <dimen name="expanded_title_margin_bottom">32dp</dimen> </resources>
\ No newline at end of file diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values-v31/styles.xml b/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values-v31/styles.xml index d0b6c4d54bb1..afd0d76ad468 100644 --- a/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values-v31/styles.xml +++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values-v31/styles.xml @@ -18,11 +18,27 @@ <style name="CollapsingToolbarTitle.Collapsed" parent="@android:style/TextAppearance.DeviceDefault.Widget.ActionBar.Title"> <item name="android:fontFamily">@string/settingslib_config_headlineFontFamily</item> <item name="android:textSize">20dp</item> - <item name="android:textColor">@color/settingslib_text_color_primary_device_default</item> </style> <style name="CollapsingToolbarTitle.Expanded" parent="CollapsingToolbarTitle.Collapsed"> <item name="android:textSize">36dp</item> - <item name="android:textColor">@color/settingslib_text_color_primary_device_default</item> + </style> + + <style name="Base.CollapsingToolbarLayoutStyle.SettingsLib" parent="@style/Widget.Material3.CollapsingToolbar"> + <item name="expandedTitleTextAppearance">@style/CollapsingToolbarTitle.Expanded</item> + <item name="collapsedTitleTextAppearance">@style/CollapsingToolbarTitle.Collapsed</item> + <item name="expandedTitleMarginStart">@dimen/expanded_title_margin_start</item> + <item name="expandedTitleMarginEnd">@dimen/expanded_title_margin_end</item> + <item name="expandedTitleMarginBottom">@dimen/expanded_title_margin_bottom</item> + <item name="maxLines">3</item> + <item name="scrimVisibleHeightTrigger">@dimen/settingslib_scrim_visible_height_trigger</item> + <item name="contentScrim">@color/settingslib_colorSurfaceHeader</item> + <item name="statusBarScrim">@null</item> + <item name="scrimAnimationDuration">50</item> + </style> + + <style name="CollapsingToolbarLayoutStyle.SettingsLib" parent="@style/Base.CollapsingToolbarLayoutStyle.SettingsLib"> + <item name="collapsedTitleTextColor">@color/settingslib_text_color_primary_device_default</item> + <item name="expandedTitleTextColor">@color/settingslib_text_color_primary_device_default</item> </style> </resources>
\ No newline at end of file diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values-v35/styles.xml b/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values-v35/styles.xml index 0c2028744bf7..0f71a788e8c6 100644 --- a/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values-v35/styles.xml +++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values-v35/styles.xml @@ -15,14 +15,8 @@ limitations under the License. --> <resources> - <style name="CollapsingToolbarTitle.Collapsed" parent="@android:style/TextAppearance.DeviceDefault.Widget.ActionBar.Title"> - <item name="android:fontFamily">@string/settingslib_config_headlineFontFamily</item> - <item name="android:textSize">20dp</item> - <item name="android:textColor">@color/settingslib_materialColorOnSurface</item> - </style> - - <style name="CollapsingToolbarTitle.Expanded" parent="CollapsingToolbarTitle.Collapsed"> - <item name="android:textSize">36dp</item> - <item name="android:textColor">@color/settingslib_materialColorOnSurface</item> + <style name="CollapsingToolbarLayoutStyle.SettingsLib" parent="@style/Base.CollapsingToolbarLayoutStyle.SettingsLib"> + <item name="collapsedTitleTextColor">@color/settingslib_materialColorOnSurface</item> + <item name="expandedTitleTextColor">@color/settingslib_materialColorOnSurface</item> </style> </resources>
\ No newline at end of file diff --git a/packages/SettingsLib/Color/res/values/colors.xml b/packages/SettingsLib/Color/res/values/colors.xml index 709752362153..7b08a5b60932 100644 --- a/packages/SettingsLib/Color/res/values/colors.xml +++ b/packages/SettingsLib/Color/res/values/colors.xml @@ -17,6 +17,7 @@ <resources> <!-- Dynamic colors--> + <color name="settingslib_color_blue700">#0B57D0</color> <color name="settingslib_color_blue600">#1a73e8</color> <color name="settingslib_color_blue400">#669df6</color> <color name="settingslib_color_blue300">#8ab4f8</color> diff --git a/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/LottieColorUtils.java b/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/LottieColorUtils.java index cf645f73beb3..0ab33b71178f 100644 --- a/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/LottieColorUtils.java +++ b/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/LottieColorUtils.java @@ -59,6 +59,9 @@ public class LottieColorUtils { ".black", android.R.color.white); map.put( + ".blue200", + R.color.settingslib_color_blue700); + map.put( ".blue400", R.color.settingslib_color_blue600); map.put( diff --git a/packages/SettingsLib/ProfileSelector/res/values-pt-rBR/strings.xml b/packages/SettingsLib/ProfileSelector/res/values-pt-rBR/strings.xml index eb33d573c495..75e1a186d028 100644 --- a/packages/SettingsLib/ProfileSelector/res/values-pt-rBR/strings.xml +++ b/packages/SettingsLib/ProfileSelector/res/values-pt-rBR/strings.xml @@ -19,5 +19,5 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="settingslib_category_personal" msgid="1142302328104700620">"Pessoal"</string> <string name="settingslib_category_work" msgid="4867750733682444676">"Trabalho"</string> - <string name="settingslib_category_private" msgid="5039276873477591386">"Particular"</string> + <string name="settingslib_category_private" msgid="5039276873477591386">"Privado"</string> </resources> diff --git a/packages/SettingsLib/ProfileSelector/res/values-pt/strings.xml b/packages/SettingsLib/ProfileSelector/res/values-pt/strings.xml index eb33d573c495..75e1a186d028 100644 --- a/packages/SettingsLib/ProfileSelector/res/values-pt/strings.xml +++ b/packages/SettingsLib/ProfileSelector/res/values-pt/strings.xml @@ -19,5 +19,5 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="settingslib_category_personal" msgid="1142302328104700620">"Pessoal"</string> <string name="settingslib_category_work" msgid="4867750733682444676">"Trabalho"</string> - <string name="settingslib_category_private" msgid="5039276873477591386">"Particular"</string> + <string name="settingslib_category_private" msgid="5039276873477591386">"Privado"</string> </resources> diff --git a/packages/SettingsLib/Spa/build.gradle.kts b/packages/SettingsLib/Spa/build.gradle.kts index 45667f55882d..232fa9204517 100644 --- a/packages/SettingsLib/Spa/build.gradle.kts +++ b/packages/SettingsLib/Spa/build.gradle.kts @@ -29,7 +29,7 @@ val androidTop: String = File(rootDir, "../../../../..").canonicalPath allprojects { extra["androidTop"] = androidTop - extra["jetpackComposeVersion"] = "1.7.0-alpha08" + extra["jetpackComposeVersion"] = "1.7.0-SNAPSHOT" } subprojects { diff --git a/packages/SettingsLib/Spa/settings.gradle.kts b/packages/SettingsLib/Spa/settings.gradle.kts index e003c81d7a0c..31f462e5e918 100644 --- a/packages/SettingsLib/Spa/settings.gradle.kts +++ b/packages/SettingsLib/Spa/settings.gradle.kts @@ -36,6 +36,9 @@ dependencyResolutionManagement { } mavenCentral() maven { + url = uri("https://androidx.dev/snapshots/builds/11846308/artifacts/repository") + } + maven { url = uri("https://jitpack.io") content { includeGroup("com.github.PhilJay") diff --git a/packages/SettingsLib/Spa/spa/build.gradle.kts b/packages/SettingsLib/Spa/spa/build.gradle.kts index 4aa57b3876fc..f98695eb2e1d 100644 --- a/packages/SettingsLib/Spa/spa/build.gradle.kts +++ b/packages/SettingsLib/Spa/spa/build.gradle.kts @@ -57,13 +57,13 @@ dependencies { api("androidx.slice:slice-builders:1.1.0-alpha02") api("androidx.slice:slice-core:1.1.0-alpha02") api("androidx.slice:slice-view:1.1.0-alpha02") - api("androidx.compose.material3:material3:1.3.0-alpha06") + api("androidx.compose.material3:material3:1.3.0-SNAPSHOT") api("androidx.compose.material:material-icons-extended:$jetpackComposeVersion") api("androidx.compose.runtime:runtime-livedata:$jetpackComposeVersion") api("androidx.compose.ui:ui-tooling-preview:$jetpackComposeVersion") api("androidx.lifecycle:lifecycle-livedata-ktx") api("androidx.lifecycle:lifecycle-runtime-compose") - api("androidx.navigation:navigation-compose:2.8.0-alpha08") + api("androidx.navigation:navigation-compose:2.8.0-beta01") api("com.github.PhilJay:MPAndroidChart:v3.1.0-alpha") api("com.google.android.material:material:1.11.0") debugApi("androidx.compose.ui:ui-tooling:$jetpackComposeVersion") diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/util/AnnotatedStringResource.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/util/AnnotatedStringResource.kt index 1a10bf021f47..2cf0d3b19e60 100644 --- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/util/AnnotatedStringResource.kt +++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/util/AnnotatedStringResource.kt @@ -29,6 +29,7 @@ import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.text.AnnotatedString import androidx.compose.ui.text.LinkAnnotation import androidx.compose.ui.text.SpanStyle +import androidx.compose.ui.text.TextLinkStyles import androidx.compose.ui.text.buildAnnotatedString import androidx.compose.ui.text.font.FontStyle import androidx.compose.ui.text.font.FontWeight @@ -98,7 +99,9 @@ private fun AnnotatedString.Builder.addUrlSpan( ) { val url = LinkAnnotation.Url( url = urlSpan.url, - style = SpanStyle(color = urlSpanColor, textDecoration = TextDecoration.Underline), + styles = TextLinkStyles( + style = SpanStyle(color = urlSpanColor, textDecoration = TextDecoration.Underline), + ), ) addLink(url, start, end) } diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/util/Flows.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/util/Flows.kt index 83cb549bb3a2..61b8b7f73754 100644 --- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/util/Flows.kt +++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/util/Flows.kt @@ -20,6 +20,7 @@ import androidx.lifecycle.Lifecycle import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.lifecycleScope import androidx.lifecycle.repeatOnLifecycle +import kotlinx.coroutines.Job import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.combine @@ -61,10 +62,8 @@ fun <T> Flow<T>.collectLatestWithLifecycle( lifecycleOwner: LifecycleOwner, minActiveState: Lifecycle.State = Lifecycle.State.STARTED, action: suspend (value: T) -> Unit, -) { - lifecycleOwner.lifecycleScope.launch { - lifecycleOwner.repeatOnLifecycle(minActiveState) { - collectLatest(action) - } +): Job = lifecycleOwner.lifecycleScope.launch { + lifecycleOwner.repeatOnLifecycle(minActiveState) { + collectLatest(action) } } diff --git a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/framework/util/AnnotatedStringResourceTest.kt b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/framework/util/AnnotatedStringResourceTest.kt index 612f9e525f20..4c1efd75a0b4 100644 --- a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/framework/util/AnnotatedStringResourceTest.kt +++ b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/framework/util/AnnotatedStringResourceTest.kt @@ -21,6 +21,7 @@ import androidx.compose.ui.test.junit4.createComposeRule import androidx.compose.ui.text.AnnotatedString import androidx.compose.ui.text.LinkAnnotation import androidx.compose.ui.text.SpanStyle +import androidx.compose.ui.text.TextLinkStyles import androidx.compose.ui.text.font.FontStyle import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextDecoration @@ -47,9 +48,11 @@ class AnnotatedStringResourceTest { AnnotatedString.Range( item = LinkAnnotation.Url( url = "https://www.android.com/", - style = SpanStyle( - color = MaterialTheme.colorScheme.primary, - textDecoration = TextDecoration.Underline, + styles = TextLinkStyles( + style = SpanStyle( + color = MaterialTheme.colorScheme.primary, + textDecoration = TextDecoration.Underline, + ), ), ), start = 31, diff --git a/packages/SettingsLib/SpaPrivileged/res/values-pt-rPT/strings.xml b/packages/SettingsLib/SpaPrivileged/res/values-pt-rPT/strings.xml index e7030df189cd..59b19baba540 100644 --- a/packages/SettingsLib/SpaPrivileged/res/values-pt-rPT/strings.xml +++ b/packages/SettingsLib/SpaPrivileged/res/values-pt-rPT/strings.xml @@ -20,8 +20,8 @@ <string name="no_applications" msgid="5800789569715871963">"Sem apps."</string> <string name="menu_show_system" msgid="906304605807554788">"Mostrar sistema"</string> <string name="menu_hide_system" msgid="374571689914923020">"Ocultar sistema"</string> - <string name="app_permission_summary_allowed" msgid="6115213465364138103">"Permitida"</string> - <string name="app_permission_summary_not_allowed" msgid="58396132188553920">"Não permitida"</string> + <string name="app_permission_summary_allowed" msgid="6115213465364138103">"Permitido"</string> + <string name="app_permission_summary_not_allowed" msgid="58396132188553920">"Não permitido"</string> <string name="version_text" msgid="4001669804596458577">"versão <xliff:g id="VERSION_NUM">%1$s</xliff:g>"</string> <string name="cloned_app_info_label" msgid="1765651167024478391">"Clone de <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string> </resources> diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppOpPermissionAppList.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppOpPermissionAppList.kt index 120b75ecb666..050527e52f0f 100644 --- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppOpPermissionAppList.kt +++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppOpPermissionAppList.kt @@ -68,8 +68,7 @@ abstract class AppOpPermissionListModel( open val permissionHasAppOpFlag: Boolean = true /** These not changeable packages will also be hidden from app list. */ - private val notChangeablePackages = - setOf("android", "com.android.systemui", context.packageName) + private val notChangeablePackages = setOf("com.android.systemui") private fun createAppOpsPermissionController(app: ApplicationInfo) = AppOpsPermissionController(context, app, appOps, permission) diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppInfoPage.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppInfoPage.kt index 27e00c05c33f..ea6a2720c512 100644 --- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppInfoPage.kt +++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppInfoPage.kt @@ -110,7 +110,7 @@ internal fun <T : AppRecord> TogglePermissionAppListModel<T>.TogglePermissionApp app: ApplicationInfo, ) { val record = remember { transformItem(app) } - if (!remember { isChangeable(record) }) return + if (!remember { isChangeableWithSystemUidCheck(record) }) return val context = LocalContext.current val internalListModel = remember { TogglePermissionInternalAppListModel( @@ -178,6 +178,6 @@ private fun <T : AppRecord> TogglePermissionAppListModel<T>.rememberRecord(app: private fun <T : AppRecord> TogglePermissionAppListModel<T>.rememberIsChangeable(record: T) = remember(record) { flow { - emit(isChangeable(record)) + emit(isChangeableWithSystemUidCheck(record)) }.flowOn(Dispatchers.Default) }.collectAsStateWithLifecycle(initialValue = false) diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppList.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppList.kt index 3f7a8526839f..2a04424d70bf 100644 --- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppList.kt +++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppList.kt @@ -18,6 +18,7 @@ package com.android.settingslib.spaprivileged.template.app import android.content.Context import android.content.pm.ApplicationInfo +import android.os.Process import androidx.compose.runtime.Composable import com.android.settingslib.spa.framework.common.SettingsEntryBuilder import com.android.settingslib.spa.framework.common.SettingsPageProvider @@ -79,9 +80,26 @@ interface TogglePermissionAppListModel<T : AppRecord> { fun setAllowed(record: T, newAllowed: Boolean) @Composable - fun InfoPageAdditionalContent(record: T, isAllowed: () -> Boolean?){} + fun InfoPageAdditionalContent(record: T, isAllowed: () -> Boolean?) {} } +/** + * And if the given app has system or root UID. + * + * If true, the app gets all permissions, so the permission toggle always not changeable. + */ +fun AppRecord.isSystemOrRootUid(): Boolean = app.uid in listOf(Process.SYSTEM_UID, Process.ROOT_UID) + +/** + * Gets whether the permission on / off is changeable for the given app. + * + * And if the given app has system or root UID, it gets all permissions, so always not changeable. + */ +fun <T : AppRecord> TogglePermissionAppListModel<T>.isChangeableWithSystemUidCheck( + record: T, +): Boolean = !record.isSystemOrRootUid() && isChangeable(record) + + interface TogglePermissionAppListProvider { val permissionType: String diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppListPage.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppListPage.kt index 2e8b76a03722..57102ba9ea46 100644 --- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppListPage.kt +++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppListPage.kt @@ -33,6 +33,7 @@ import com.android.settingslib.spa.framework.common.SettingsPage import com.android.settingslib.spa.framework.common.SettingsPageProvider import com.android.settingslib.spa.framework.compose.navigator import com.android.settingslib.spa.framework.compose.rememberContext +import com.android.settingslib.spa.framework.util.filterItem import com.android.settingslib.spa.framework.util.getStringArg import com.android.settingslib.spa.widget.preference.Preference import com.android.settingslib.spa.widget.preference.PreferenceModel @@ -143,7 +144,7 @@ internal class TogglePermissionInternalAppListModel<T : AppRecord>( listModel.transform(userIdFlow, appListFlow) override fun filter(userIdFlow: Flow<Int>, option: Int, recordListFlow: Flow<List<T>>) = - listModel.filter(userIdFlow, recordListFlow) + listModel.filter(userIdFlow, recordListFlow.filterItem { !it.isSystemOrRootUid() }) @Composable override fun getSummary(option: Int, record: T) = getSummary(record) diff --git a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/AppOpPermissionAppListTest.kt b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/AppOpPermissionAppListTest.kt index 9d12fc7611cb..60eccd987724 100644 --- a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/AppOpPermissionAppListTest.kt +++ b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/AppOpPermissionAppListTest.kt @@ -278,7 +278,7 @@ class AppOpPermissionAppListTest { const val PERMISSION = "PERMISSION" const val BROADER_PERMISSION = "BROADER_PERMISSION" val APP = ApplicationInfo().apply { packageName = PACKAGE_NAME } - val NOT_CHANGEABLE_APP = ApplicationInfo().apply { packageName = "android" } + val NOT_CHANGEABLE_APP = ApplicationInfo().apply { packageName = "com.android.systemui" } } } diff --git a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppInfoPageTest.kt b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppInfoPageTest.kt index 270b3faa7ec6..d7147b5f7fe7 100644 --- a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppInfoPageTest.kt +++ b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppInfoPageTest.kt @@ -228,6 +228,7 @@ class TogglePermissionAppInfoPageTest { const val PACKAGE_NAME = "package.name" val APP = ApplicationInfo().apply { packageName = PACKAGE_NAME + uid = 11000 } val PACKAGE_INFO = PackageInfo().apply { packageName = PACKAGE_NAME diff --git a/packages/SettingsLib/UsageProgressBarPreference/src/com/android/settingslib/widget/UsageProgressBarPreference.java b/packages/SettingsLib/UsageProgressBarPreference/src/com/android/settingslib/widget/UsageProgressBarPreference.java index ea3dbd925792..0e71a1b127e7 100644 --- a/packages/SettingsLib/UsageProgressBarPreference/src/com/android/settingslib/widget/UsageProgressBarPreference.java +++ b/packages/SettingsLib/UsageProgressBarPreference/src/com/android/settingslib/widget/UsageProgressBarPreference.java @@ -38,8 +38,9 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; /** - * Progres bar preference with a usage summary and a total summary. - * This preference shows number in usage summary with enlarged font size. + * Progress bar preference with a usage summary and a total summary. + * + * <p>This preference shows number in usage summary with enlarged font size. */ public class UsageProgressBarPreference extends Preference { @@ -48,18 +49,18 @@ public class UsageProgressBarPreference extends Preference { private CharSequence mUsageSummary; private CharSequence mTotalSummary; private CharSequence mBottomSummary; + private CharSequence mBottomSummaryContentDescription; private ImageView mCustomImageView; private int mPercent = -1; /** * Perform inflation from XML and apply a class-specific base style. * - * @param context The {@link Context} this is associated with, through which it can - * access the current theme, resources, {@link SharedPreferences}, etc. - * @param attrs The attributes of the XML tag that is inflating the preference + * @param context The {@link Context} this is associated with, through which it can access the + * current theme, resources, {@link SharedPreferences}, etc. + * @param attrs The attributes of the XML tag that is inflating the preference * @param defStyle An attribute in the current theme that contains a reference to a style - * resource that supplies default values for the view. Can be 0 to not - * look for defaults. + * resource that supplies default values for the view. Can be 0 to not look for defaults. */ public UsageProgressBarPreference(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); @@ -69,9 +70,9 @@ public class UsageProgressBarPreference extends Preference { /** * Perform inflation from XML and apply a class-specific base style. * - * @param context The {@link Context} this is associated with, through which it can - * access the current theme, resources, {@link SharedPreferences}, etc. - * @param attrs The attributes of the XML tag that is inflating the preference + * @param context The {@link Context} this is associated with, through which it can access the + * current theme, resources, {@link SharedPreferences}, etc. + * @param attrs The attributes of the XML tag that is inflating the preference */ public UsageProgressBarPreference(Context context, AttributeSet attrs) { super(context, attrs); @@ -114,9 +115,17 @@ public class UsageProgressBarPreference extends Preference { notifyChanged(); } + /** Set content description for the bottom summary. */ + public void setBottomSummaryContentDescription(CharSequence contentDescription) { + if (!TextUtils.equals(mBottomSummaryContentDescription, contentDescription)) { + mBottomSummaryContentDescription = contentDescription; + notifyChanged(); + } + } + /** Set percentage of the progress bar. */ public void setPercent(long usage, long total) { - if (usage > total) { + if (usage > total) { return; } if (total == 0L) { @@ -146,14 +155,13 @@ public class UsageProgressBarPreference extends Preference { /** * Binds the created View to the data for this preference. * - * <p>This is a good place to grab references to custom Views in the layout and set - * properties on them. + * <p>This is a good place to grab references to custom Views in the layout and set properties + * on them. * * <p>Make sure to call through to the superclass's implementation. * * @param holder The ViewHolder that provides references to the views to fill in. These views - * will be recycled, so you should not hold a reference to them after this method - * returns. + * will be recycled, so you should not hold a reference to them after this method returns. */ @Override public void onBindViewHolder(PreferenceViewHolder holder) { @@ -177,6 +185,9 @@ public class UsageProgressBarPreference extends Preference { bottomSummary.setVisibility(View.VISIBLE); bottomSummary.setMovementMethod(LinkMovementMethod.getInstance()); bottomSummary.setText(mBottomSummary); + if (!TextUtils.isEmpty(mBottomSummaryContentDescription)) { + bottomSummary.setContentDescription(mBottomSummaryContentDescription); + } } final ProgressBar progressBar = (ProgressBar) holder.findViewById(android.R.id.progress); @@ -205,9 +216,12 @@ public class UsageProgressBarPreference extends Preference { final Matcher matcher = mNumberPattern.matcher(summary); if (matcher.find()) { - final SpannableString spannableSummary = new SpannableString(summary); - spannableSummary.setSpan(new AbsoluteSizeSpan(64, true /* dip */), matcher.start(), - matcher.end(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + final SpannableString spannableSummary = new SpannableString(summary); + spannableSummary.setSpan( + new AbsoluteSizeSpan(64, true /* dip */), + matcher.start(), + matcher.end(), + Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); return spannableSummary; } return summary; diff --git a/packages/SettingsLib/res/values-be/strings.xml b/packages/SettingsLib/res/values-be/strings.xml index a72322350b4c..95586eaf9da5 100644 --- a/packages/SettingsLib/res/values-be/strings.xml +++ b/packages/SettingsLib/res/values-be/strings.xml @@ -84,7 +84,7 @@ <string name="preference_summary_default_combination" msgid="2644094566845577901">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="DESCRIPTION">%2$s</xliff:g>"</string> <string name="bluetooth_disconnected" msgid="7739366554710388701">"Адключана"</string> <string name="bluetooth_disconnecting" msgid="7638892134401574338">"Адключэнне..."</string> - <string name="bluetooth_connecting" msgid="5871702668260192755">"Злучэнне..."</string> + <string name="bluetooth_connecting" msgid="5871702668260192755">"Падключэнне..."</string> <string name="bluetooth_connected" msgid="8065345572198502293">"Падключана прылада <xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string> <string name="bluetooth_pairing" msgid="4269046942588193600">"Спалучэнне..."</string> <string name="bluetooth_connected_no_headset" msgid="2224101138659967604">"Падключана прылада <xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g> (без званкоў)"</string> diff --git a/packages/SettingsLib/res/values-ca/strings.xml b/packages/SettingsLib/res/values-ca/strings.xml index cc67420f5ee6..45f08da8b928 100644 --- a/packages/SettingsLib/res/values-ca/strings.xml +++ b/packages/SettingsLib/res/values-ca/strings.xml @@ -283,9 +283,9 @@ <string name="keep_screen_on_summary" msgid="1510731514101925829">"La pantalla no entra mai en mode de repòs si el dispositiu s\'està carregant"</string> <string name="bt_hci_snoop_log" msgid="7291287955649081448">"Activa el registre de Bluetooth HCI"</string> <string name="bt_hci_snoop_log_summary" msgid="6808538971394092284">"Captura els paquets de Bluetooth. Activa el Bluetooth un cop hagis canviat aquesta opció."</string> - <string name="oem_unlock_enable" msgid="5334869171871566731">"Desbloqueig d\'OEM"</string> + <string name="oem_unlock_enable" msgid="5334869171871566731">"Desbloqueig OEM"</string> <string name="oem_unlock_enable_summary" msgid="5857388174390953829">"Permet desbloquejar el bootloader"</string> - <string name="confirm_enable_oem_unlock_title" msgid="8249318129774367535">"Permetre el desbloqueig d\'OEM?"</string> + <string name="confirm_enable_oem_unlock_title" msgid="8249318129774367535">"Permetre el desbloqueig OEM?"</string> <string name="confirm_enable_oem_unlock_text" msgid="854131050791011970">"ADVERTIMENT: les funcions de protecció del dispositiu no funcionaran mentre aquesta opció estigui activada."</string> <string name="mock_location_app" msgid="6269380172542248304">"Selecciona una aplicació d\'ubicació simulada"</string> <string name="mock_location_app_not_set" msgid="6972032787262831155">"No s\'ha definit cap aplicació d\'ubicació simulada"</string> diff --git a/packages/SettingsLib/res/values-de/strings.xml b/packages/SettingsLib/res/values-de/strings.xml index f0fb4df441b6..caeee064bf99 100644 --- a/packages/SettingsLib/res/values-de/strings.xml +++ b/packages/SettingsLib/res/values-de/strings.xml @@ -710,7 +710,7 @@ <string name="keyboard_layout_default_label" msgid="1997292217218546957">"Standard"</string> <string name="turn_screen_on_title" msgid="2662312432042116026">"Steuerelement zum Aktivieren des Displays"</string> <string name="allow_turn_screen_on" msgid="6194845766392742639">"Aktivieren des Displays erlauben"</string> - <string name="allow_turn_screen_on_description" msgid="43834403291575164">"Einer App erlauben, das Display zu aktivieren. Wenn du diese Erlaubnis erteilst, kann die App jederzeit das Display aktivieren – auch ohne deine explizite Absicht."</string> + <string name="allow_turn_screen_on_description" msgid="43834403291575164">"Einer App erlauben, das Display zu aktivieren. Wenn du diese Erlaubnis erteilst, kann die App jederzeit das Display aktivieren – auch ohne dass du dies beabsichtigst."</string> <string name="bt_le_audio_broadcast_dialog_title" msgid="5392738488989777074">"<xliff:g id="APP_NAME">%1$s</xliff:g> nicht mehr streamen?"</string> <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="268234802198852753">"Wenn du <xliff:g id="SWITCHAPP">%1$s</xliff:g> streamst oder die Ausgabe änderst, wird dein aktueller Stream beendet"</string> <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="5749813313369517812">"<xliff:g id="SWITCHAPP">%1$s</xliff:g> streamen"</string> diff --git a/packages/SettingsLib/res/values-es/strings.xml b/packages/SettingsLib/res/values-es/strings.xml index 3c63119497df..0b96faa7f6b4 100644 --- a/packages/SettingsLib/res/values-es/strings.xml +++ b/packages/SettingsLib/res/values-es/strings.xml @@ -127,7 +127,7 @@ <string name="bluetooth_profile_opp" msgid="6692618568149493430">"Transferencia de archivos"</string> <string name="bluetooth_profile_hid" msgid="2969922922664315866">"Dispositivo de entrada"</string> <string name="bluetooth_profile_pan" msgid="1006235139308318188">"Acceso a Internet"</string> - <string name="bluetooth_profile_pbap" msgid="2103406516858653017">"Acceso a contactos e historial de llamadas"</string> + <string name="bluetooth_profile_pbap" msgid="2103406516858653017">"Permitir acceso a contactos e historial de llamadas"</string> <string name="bluetooth_profile_pbap_summary" msgid="402819589201138227">"La información se usará para avisos de llamada y más"</string> <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Compartir conexión a Internet"</string> <string name="bluetooth_profile_map" msgid="8907204701162107271">"Mensajes de texto"</string> diff --git a/packages/SettingsLib/res/values-eu/strings.xml b/packages/SettingsLib/res/values-eu/strings.xml index 25f002a29c49..6376ad580308 100644 --- a/packages/SettingsLib/res/values-eu/strings.xml +++ b/packages/SettingsLib/res/values-eu/strings.xml @@ -710,7 +710,7 @@ <string name="keyboard_layout_default_label" msgid="1997292217218546957">"Lehenetsia"</string> <string name="turn_screen_on_title" msgid="2662312432042116026">"Pantaila pizteko modua kontrolatzeko aukera"</string> <string name="allow_turn_screen_on" msgid="6194845766392742639">"Eman pantaila pizteko baimena"</string> - <string name="allow_turn_screen_on_description" msgid="43834403291575164">"Eman pantaila pizteko baimena aplikazioei. Baimena emanez gero, aplikazioek edonoiz piztu ahal izango dute pantaila, zuk halako asmorik izan ez arren."</string> + <string name="allow_turn_screen_on_description" msgid="43834403291575164">"Eman pantaila pizteko baimena aplikazioari. Baimena emanez gero, aplikazioak edonoiz piztu ahal izango du pantaila, zuk halako asmorik izan ez arren."</string> <string name="bt_le_audio_broadcast_dialog_title" msgid="5392738488989777074">"<xliff:g id="APP_NAME">%1$s</xliff:g> aplikazioaren audioa igortzeari utzi nahi diozu?"</string> <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="268234802198852753">"<xliff:g id="SWITCHAPP">%1$s</xliff:g> aplikazioaren audioa igortzen edo audio-irteera aldatzen baduzu, une hartako igorpena etengo da"</string> <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="5749813313369517812">"Igorri <xliff:g id="SWITCHAPP">%1$s</xliff:g> aplikazioaren audioa"</string> diff --git a/packages/SettingsLib/res/values-fa/strings.xml b/packages/SettingsLib/res/values-fa/strings.xml index 399e3968acf2..6eb8bd161bfe 100644 --- a/packages/SettingsLib/res/values-fa/strings.xml +++ b/packages/SettingsLib/res/values-fa/strings.xml @@ -710,7 +710,7 @@ <string name="keyboard_layout_default_label" msgid="1997292217218546957">"پیشفرض"</string> <string name="turn_screen_on_title" msgid="2662312432042116026">"کنترل روشن شدن صفحهنمایش"</string> <string name="allow_turn_screen_on" msgid="6194845766392742639">"اجازه روشن کردن صفحهنمایش"</string> - <string name="allow_turn_screen_on_description" msgid="43834403291575164">"به برنامه اجازه میدهد صفحهنمایش را روشن کند. اگر اجازه داده شود، ممکن است این برنامه در هر زمانی بدون هدف صریح شما صفحهنمایش را روشن کند."</string> + <string name="allow_turn_screen_on_description" msgid="43834403291575164">"به برنامه اجازه میدهد صفحهنمایش را روشن کند. اگر این اجازه داده شود، ممکن است این برنامه در هر زمانی بدون توجه به منظور صریح شما صفحهنمایش را روشن کند."</string> <string name="bt_le_audio_broadcast_dialog_title" msgid="5392738488989777074">"همهفرستی <xliff:g id="APP_NAME">%1$s</xliff:g> متوقف شود؟"</string> <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="268234802198852753">"اگر <xliff:g id="SWITCHAPP">%1$s</xliff:g> را همهفرستی کنید یا خروجی را تغییر دهید، همهفرستی کنونی متوقف خواهد شد"</string> <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="5749813313369517812">"همهفرستی <xliff:g id="SWITCHAPP">%1$s</xliff:g>"</string> diff --git a/packages/SettingsLib/res/values-fi/strings.xml b/packages/SettingsLib/res/values-fi/strings.xml index d2afaac3eed6..a0dc830dc58f 100644 --- a/packages/SettingsLib/res/values-fi/strings.xml +++ b/packages/SettingsLib/res/values-fi/strings.xml @@ -632,7 +632,7 @@ <string name="profile_info_settings_title" msgid="105699672534365099">"Profiilin tiedot"</string> <string name="user_need_lock_message" msgid="4311424336209509301">"Ennen kuin voit luoda rajoitetun profiilin, määritä näytön lukitus, joka suojelee sovelluksiasi ja henkilökohtaisia tietojasi."</string> <string name="user_set_lock_button" msgid="1427128184982594856">"Aseta lukitus"</string> - <string name="user_switch_to_user" msgid="6975428297154968543">"Vaihda tähän käyttäjään: <xliff:g id="USER_NAME">%s</xliff:g>"</string> + <string name="user_switch_to_user" msgid="6975428297154968543">"Vaihda käyttäjään: <xliff:g id="USER_NAME">%s</xliff:g>"</string> <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Luodaan uutta käyttäjää…"</string> <string name="creating_new_guest_dialog_message" msgid="1114905602181350690">"Luodaan uutta vierasta…"</string> <string name="add_user_failed" msgid="4809887794313944872">"Uuden käyttäjän luominen epäonnistui"</string> @@ -709,8 +709,8 @@ <string name="keyboard_layout_dialog_title" msgid="3927180147005616290">"Valitse näppäimistöasettelu"</string> <string name="keyboard_layout_default_label" msgid="1997292217218546957">"Oletus"</string> <string name="turn_screen_on_title" msgid="2662312432042116026">"Näytön päälle laittamisen asetukset"</string> - <string name="allow_turn_screen_on" msgid="6194845766392742639">"Salli näytön käynnistäminen"</string> - <string name="allow_turn_screen_on_description" msgid="43834403291575164">"Salli sovelluksen käynnistää näyttö. Jos sovellus saa luvan, se voi käynnistää näytön itsenäisesti milloin tahansa."</string> + <string name="allow_turn_screen_on" msgid="6194845766392742639">"Salli näytön laittaminen päälle"</string> + <string name="allow_turn_screen_on_description" msgid="43834403291575164">"Salli sovelluksen laittaa näyttö päälle. Jos sovellus saa luvan, se voi laittaa näytön päälle itsenäisesti milloin tahansa."</string> <string name="bt_le_audio_broadcast_dialog_title" msgid="5392738488989777074">"Lopetetaanko <xliff:g id="APP_NAME">%1$s</xliff:g>-sovelluksen lähettäminen?"</string> <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="268234802198852753">"Jos lähetät <xliff:g id="SWITCHAPP">%1$s</xliff:g>-sovellusta tai muutat ulostuloa, nykyinen lähetyksesi loppuu"</string> <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="5749813313369517812">"Lähetä <xliff:g id="SWITCHAPP">%1$s</xliff:g>-sovellusta"</string> diff --git a/packages/SettingsLib/res/values-fr-rCA/strings.xml b/packages/SettingsLib/res/values-fr-rCA/strings.xml index 1d9d5c8c7018..0e03d181cf8f 100644 --- a/packages/SettingsLib/res/values-fr-rCA/strings.xml +++ b/packages/SettingsLib/res/values-fr-rCA/strings.xml @@ -514,7 +514,7 @@ <string name="disabled" msgid="8017887509554714950">"Désactivée"</string> <string name="external_source_trusted" msgid="1146522036773132905">"Autorisée"</string> <string name="external_source_untrusted" msgid="5037891688911672227">"Non autorisée"</string> - <string name="install_other_apps" msgid="3232595082023199454">"Installer les applications inconnues"</string> + <string name="install_other_apps" msgid="3232595082023199454">"Installer les applis inconnues"</string> <string name="home" msgid="973834627243661438">"Accueil des paramètres"</string> <string-array name="battery_labels"> <item msgid="7878690469765357158">"0 %"</item> diff --git a/packages/SettingsLib/res/values-fr/strings.xml b/packages/SettingsLib/res/values-fr/strings.xml index 5f4480cd58e4..ed4bf4851165 100644 --- a/packages/SettingsLib/res/values-fr/strings.xml +++ b/packages/SettingsLib/res/values-fr/strings.xml @@ -710,7 +710,7 @@ <string name="keyboard_layout_default_label" msgid="1997292217218546957">"Par défaut"</string> <string name="turn_screen_on_title" msgid="2662312432042116026">"Commande d\'activation de l\'écran"</string> <string name="allow_turn_screen_on" msgid="6194845766392742639">"Autoriser l\'activation de l\'écran"</string> - <string name="allow_turn_screen_on_description" msgid="43834403291575164">"Autoriser une appli à activer l\'écran. Si elle y est autorisée, l\'appli pourra activer l\'écran à tout moment sans que vous le lui demandiez."</string> + <string name="allow_turn_screen_on_description" msgid="43834403291575164">"Vous permet d\'autoriser une appli à activer l\'écran. Si elle y est autorisée, l\'appli pourra activer l\'écran à tout moment sans que vous le lui demandiez."</string> <string name="bt_le_audio_broadcast_dialog_title" msgid="5392738488989777074">"Arrêter la diffusion de <xliff:g id="APP_NAME">%1$s</xliff:g> ?"</string> <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="268234802198852753">"Si vous diffusez <xliff:g id="SWITCHAPP">%1$s</xliff:g> ou que vous modifiez le résultat, votre annonce actuelle s\'arrêtera"</string> <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="5749813313369517812">"Diffuser <xliff:g id="SWITCHAPP">%1$s</xliff:g>"</string> diff --git a/packages/SettingsLib/res/values-hi/strings.xml b/packages/SettingsLib/res/values-hi/strings.xml index f66499fba432..7db157e40514 100644 --- a/packages/SettingsLib/res/values-hi/strings.xml +++ b/packages/SettingsLib/res/values-hi/strings.xml @@ -708,9 +708,9 @@ <string name="physical_keyboard_title" msgid="4811935435315835220">"फ़िज़िकल कीबोर्ड"</string> <string name="keyboard_layout_dialog_title" msgid="3927180147005616290">"कीबोर्ड का लेआउट चुनें"</string> <string name="keyboard_layout_default_label" msgid="1997292217218546957">"डिफ़ॉल्ट"</string> - <string name="turn_screen_on_title" msgid="2662312432042116026">"इस ऐप के पास स्क्रीन को चालू करने का कंट्रोल है"</string> + <string name="turn_screen_on_title" msgid="2662312432042116026">"स्क्रीन को चालू करने से जुड़ा कंट्रोल"</string> <string name="allow_turn_screen_on" msgid="6194845766392742639">"स्क्रीन चालू करने की अनुमति दें"</string> - <string name="allow_turn_screen_on_description" msgid="43834403291575164">"ऐप्लिकेशन को स्क्रीन चालू करने की अनुमति दें. ऐसा करने पर, ऐप्लिकेशन आपकी अनुमति लिए बिना भी, जब चाहे स्क्रीन चालू कर सकता है."</string> + <string name="allow_turn_screen_on_description" msgid="43834403291575164">"ऐप्लिकेशन को स्क्रीन चालू करने की अनुमति दें. इससे आपके न चाहते हुए भी, ऐप्लिकेशन जब चाहे स्क्रीन चालू कर सकता है."</string> <string name="bt_le_audio_broadcast_dialog_title" msgid="5392738488989777074">"<xliff:g id="APP_NAME">%1$s</xliff:g> पर ब्रॉडकास्ट करना रोकें?"</string> <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="268234802198852753">"<xliff:g id="SWITCHAPP">%1$s</xliff:g> पर ब्रॉडकास्ट शुरू करने पर या आउटपुट बदलने पर, आपका मौजूदा ब्रॉडकास्ट बंद हो जाएगा"</string> <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="5749813313369517812">"<xliff:g id="SWITCHAPP">%1$s</xliff:g> पर ब्रॉडकास्ट करें"</string> diff --git a/packages/SettingsLib/res/values-hr/strings.xml b/packages/SettingsLib/res/values-hr/strings.xml index 8ce90ec1e4b0..ef0d589ddbc6 100644 --- a/packages/SettingsLib/res/values-hr/strings.xml +++ b/packages/SettingsLib/res/values-hr/strings.xml @@ -710,7 +710,7 @@ <string name="keyboard_layout_default_label" msgid="1997292217218546957">"Zadano"</string> <string name="turn_screen_on_title" msgid="2662312432042116026">"Kontrola za uključivanje zaslona"</string> <string name="allow_turn_screen_on" msgid="6194845766392742639">"Dopusti uključivanje zaslona"</string> - <string name="allow_turn_screen_on_description" msgid="43834403291575164">"Dopustite aplikaciji da uključuje zaslon. Ako date to dopuštenje, aplikacija može uključiti zaslon u bilo kojem trenutku bez vaše izričite namjere."</string> + <string name="allow_turn_screen_on_description" msgid="43834403291575164">"Dopušta aplikaciji da uključuje zaslon. Ako date to dopuštenje, aplikacija može uključiti zaslon u bilo kojem trenutku bez vaše izričite namjere."</string> <string name="bt_le_audio_broadcast_dialog_title" msgid="5392738488989777074">"Zaustaviti emitiranje aplikacije <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="268234802198852753">"Ako emitirate aplikaciju <xliff:g id="SWITCHAPP">%1$s</xliff:g> ili promijenite izlaz, vaše će se trenutačno emitiranje zaustaviti"</string> <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="5749813313369517812">"Emitiranje aplikacije <xliff:g id="SWITCHAPP">%1$s</xliff:g>"</string> diff --git a/packages/SettingsLib/res/values-ja/strings.xml b/packages/SettingsLib/res/values-ja/strings.xml index acdad9c9d3a8..17e902ef9604 100644 --- a/packages/SettingsLib/res/values-ja/strings.xml +++ b/packages/SettingsLib/res/values-ja/strings.xml @@ -708,7 +708,7 @@ <string name="physical_keyboard_title" msgid="4811935435315835220">"物理キーボード"</string> <string name="keyboard_layout_dialog_title" msgid="3927180147005616290">"キーボード レイアウトの選択"</string> <string name="keyboard_layout_default_label" msgid="1997292217218546957">"デフォルト"</string> - <string name="turn_screen_on_title" msgid="2662312432042116026">"画面をオンにする設定"</string> + <string name="turn_screen_on_title" msgid="2662312432042116026">"画面をオンにする"</string> <string name="allow_turn_screen_on" msgid="6194845766392742639">"画面をオンにすることを許可する"</string> <string name="allow_turn_screen_on_description" msgid="43834403291575164">"画面をオンにすることをアプリに許可します。許可すると、ユーザーからの明示的インテントを必要とせずに、アプリがいつでも画面をオンにできるようになります。"</string> <string name="bt_le_audio_broadcast_dialog_title" msgid="5392738488989777074">"<xliff:g id="APP_NAME">%1$s</xliff:g> のブロードキャストを停止しますか?"</string> diff --git a/packages/SettingsLib/res/values-kn/strings.xml b/packages/SettingsLib/res/values-kn/strings.xml index 13819033d166..a25e179ee452 100644 --- a/packages/SettingsLib/res/values-kn/strings.xml +++ b/packages/SettingsLib/res/values-kn/strings.xml @@ -632,7 +632,7 @@ <string name="profile_info_settings_title" msgid="105699672534365099">"ಪ್ರೊಫೈಲ್ ಮಾಹಿತಿ"</string> <string name="user_need_lock_message" msgid="4311424336209509301">"ನೀವು ನಿರ್ಬಂಧಿತ ಪ್ರೊಫೈಲ್ ಅನ್ನು ರಚಿಸಬಹುದಾದರ ಮೊದಲು, ನಿಮ್ಮ ಅಪ್ಲಿಕೇಶನ್ಗಳು ಮತ್ತು ವೈಯಕ್ತಿಕ ಡೇಟಾವನ್ನು ರಕ್ಷಿಸಲು ನೀವು ಪರದೆಯ ಲಾಕ್ ಹೊಂದಿಸುವ ಅಗತ್ಯವಿದೆ."</string> <string name="user_set_lock_button" msgid="1427128184982594856">"ಲಾಕ್ ಹೊಂದಿಸಿ"</string> - <string name="user_switch_to_user" msgid="6975428297154968543">"<xliff:g id="USER_NAME">%s</xliff:g> ಗೆ ಬದಲಿಸಿ"</string> + <string name="user_switch_to_user" msgid="6975428297154968543">"<xliff:g id="USER_NAME">%s</xliff:g>ಗೆ ಬದಲಿಸಿ"</string> <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"ಹೊಸ ಬಳಕೆದಾರರನ್ನು ರಚಿಸಲಾಗುತ್ತಿದೆ…"</string> <string name="creating_new_guest_dialog_message" msgid="1114905602181350690">"ಹೊಸ ಅತಿಥಿಯನ್ನು ರಚಿಸಲಾಗುತ್ತಿದೆ…"</string> <string name="add_user_failed" msgid="4809887794313944872">"ಹೊಸ ಬಳಕೆದಾರರನ್ನು ರಚಿಸಲು ವಿಫಲವಾಗಿದೆ"</string> diff --git a/packages/SettingsLib/res/values-ne/strings.xml b/packages/SettingsLib/res/values-ne/strings.xml index 5032e358f213..1e33f88879d1 100644 --- a/packages/SettingsLib/res/values-ne/strings.xml +++ b/packages/SettingsLib/res/values-ne/strings.xml @@ -641,7 +641,7 @@ <string name="edit_user_info_message" msgid="6677556031419002895">"यो डिभाइस प्रयोग गर्ने सबै जना तपाईंले छनौट गर्ने नाम र फोटो देख्न सक्ने छन्।"</string> <string name="user_add_user" msgid="7876449291500212468">"प्रयोगकर्ता कनेक्ट गर्नुहोस्"</string> <string name="guest_new_guest" msgid="3482026122932643557">"अतिथि कनेक्ट गर्नुहोस्"</string> - <string name="guest_exit_guest" msgid="5908239569510734136">"गेस्ट मोडबाट बाहिर निस्कियोस्"</string> + <string name="guest_exit_guest" msgid="5908239569510734136">"अथिति हटाउनुहोस्"</string> <string name="guest_reset_guest" msgid="6110013010356013758">"अतिथि सत्र रिसेट गर्नुहोस्"</string> <string name="guest_reset_guest_dialog_title" msgid="8047270010895437534">"अतिथिका रूपमा ब्राउज गर्ने सेसन रिसेट गर्ने हो?"</string> <string name="guest_remove_guest_dialog_title" msgid="4548511006624088072">"यी अतिथि हटाउने हो?"</string> diff --git a/packages/SettingsLib/res/values-pa/strings.xml b/packages/SettingsLib/res/values-pa/strings.xml index 72e77609f9b1..f80ec8d1cb00 100644 --- a/packages/SettingsLib/res/values-pa/strings.xml +++ b/packages/SettingsLib/res/values-pa/strings.xml @@ -239,7 +239,7 @@ <string name="category_work" msgid="4014193632325996115">"ਕੰਮ ਸੰਬੰਧੀ"</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_title" msgid="140296922921597393">"ਵਿਕਾਸਕਾਰ ਵਿਕਲਪ"</string> <string name="development_settings_enable" msgid="4285094651288242183">"ਵਿਕਾਸਕਾਰ ਵਿਕਲਪਾਂ ਨੂੰ ਚਾਲੂ ਕਰੋ"</string> <string name="development_settings_summary" msgid="8718917813868735095">"ਐਪ ਵਿਕਾਸ ਲਈ ਚੋਣਾਂ ਸੈੱਟ ਕਰੋ"</string> <string name="development_settings_not_available" msgid="355070198089140951">"ਇਸ ਵਰਤੋਂਕਾਰ ਲਈ ਵਿਕਾਸਕਾਰ ਵਿਕਲਪ ਉਪਲਬਧ ਨਹੀਂ ਹਨ"</string> @@ -422,7 +422,7 @@ <string name="show_notification_channel_warnings_summary" msgid="68031143745094339">"ਐਪ ਵੱਲੋਂ ਵੈਧ ਚੈਨਲ ਤੋਂ ਬਿਨਾਂ ਸੂਚਨਾ ਪੋਸਟ ਕਰਨ \'ਤੇ ਸਕ੍ਰੀਨ \'ਤੇ ਚਿਤਾਵਨੀ ਦਿਖਾਉਂਦੀ ਹੈ"</string> <string name="force_allow_on_external" msgid="9187902444231637880">"ਐਪਾਂ ਨੂੰ ਜ਼ਬਰਦਸਤੀ ਬਾਹਰੀ ਸਟੋਰੇਜ \'ਤੇ ਆਗਿਆ ਦਿਓ"</string> <string name="force_allow_on_external_summary" msgid="8525425782530728238">"ਮੈਨੀਫੈਸਟ ਮੁੱਲਾਂ ਦੀ ਪਰਵਾਹ ਕੀਤੇ ਬਿਨਾਂ, ਕਿਸੇ ਵੀ ਐਪ ਨੂੰ ਬਾਹਰੀ ਸਟੋਰੇਜ \'ਤੇ ਲਿਖਣ ਦੇ ਯੋਗ ਬਣਾਉਂਦੀ ਹੈ"</string> - <string name="force_resizable_activities" msgid="7143612144399959606">"ਵਿੰਡੋ ਮੁਤਾਬਕ ਸਰਗਰਮੀਆਂ ਦਾ ਆਕਾਰ ਬਦਲਣ ਦਿਓ"</string> + <string name="force_resizable_activities" msgid="7143612144399959606">"ਸਰਗਰਮੀਆਂ ਨੂੰ ਜ਼ਬਰਦਸਤੀ ਆਕਾਰ ਬਦਲਣਯੋਗ ਬਣਾਓ"</string> <string name="force_resizable_activities_summary" msgid="2490382056981583062">"ਮੈਨੀਫ਼ੈਸਟ ਮੁੱਲਾਂ ਦੀ ਪਰਵਾਹ ਕੀਤੇ ਬਿਨਾਂ, ਮਲਟੀ-ਵਿੰਡੋ ਲਈ ਸਾਰੀਆਂ ਸਰਗਰਮੀਆਂ ਨੂੰ ਆਕਾਰ ਬਦਲਣਯੋਗ ਬਣਾਓ।"</string> <string name="enable_freeform_support" msgid="7599125687603914253">"ਫ੍ਰੀਫਾਰਮ ਵਿੰਡੋਜ਼ ਨੂੰ ਚਾਲੂ ਕਰੋ"</string> <string name="enable_freeform_support_summary" msgid="1822862728719276331">"ਪ੍ਰਯੋਗਮਈ ਫ੍ਰੀਫਾਰਮ ਵਿੰਡੋਜ਼ ਲਈ ਸਮਰਥਨ ਨੂੰ ਚਾਲੂ ਕਰੋ।"</string> diff --git a/packages/SettingsLib/res/values-pl/strings.xml b/packages/SettingsLib/res/values-pl/strings.xml index 3b6d758f8c77..8babb8be0a39 100644 --- a/packages/SettingsLib/res/values-pl/strings.xml +++ b/packages/SettingsLib/res/values-pl/strings.xml @@ -288,7 +288,7 @@ <string name="confirm_enable_oem_unlock_title" msgid="8249318129774367535">"Zezwolić na zdjęcie blokady OEM?"</string> <string name="confirm_enable_oem_unlock_text" msgid="854131050791011970">"UWAGA: gdy to ustawienie jest włączone, na urządzeniu nie będą działać funkcje ochrony."</string> <string name="mock_location_app" msgid="6269380172542248304">"Aplikacja do pozorowania lokalizacji"</string> - <string name="mock_location_app_not_set" msgid="6972032787262831155">"Nie ustawiono aplikacji do pozorowania lokalizacji"</string> + <string name="mock_location_app_not_set" msgid="6972032787262831155">"Nie wybrano aplikacji"</string> <string name="mock_location_app_set" msgid="4706722469342913843">"Aplikacja do pozorowania lokalizacji: <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> <string name="debug_networking_category" msgid="6829757985772659599">"Sieci"</string> <string name="wifi_display_certification" msgid="1805579519992520381">"Certyfikacja wyświetlacza bezprzewodowego"</string> diff --git a/packages/SettingsLib/res/values-pt-rBR/strings.xml b/packages/SettingsLib/res/values-pt-rBR/strings.xml index d47a1f4d3b5a..337b011cc359 100644 --- a/packages/SettingsLib/res/values-pt-rBR/strings.xml +++ b/packages/SettingsLib/res/values-pt-rBR/strings.xml @@ -237,7 +237,7 @@ <string name="choose_profile" msgid="343803890897657450">"Escolher perfil"</string> <string name="category_personal" msgid="6236798763159385225">"Pessoal"</string> <string name="category_work" msgid="4014193632325996115">"Trabalho"</string> - <string name="category_private" msgid="4244892185452788977">"Particular"</string> + <string name="category_private" msgid="4244892185452788977">"Privado"</string> <string name="category_clone" msgid="1554511758987195974">"Clone"</string> <string name="development_settings_title" msgid="140296922921597393">"Opções do desenvolvedor"</string> <string name="development_settings_enable" msgid="4285094651288242183">"Ativar opções do desenvolvedor"</string> diff --git a/packages/SettingsLib/res/values-pt/strings.xml b/packages/SettingsLib/res/values-pt/strings.xml index d47a1f4d3b5a..337b011cc359 100644 --- a/packages/SettingsLib/res/values-pt/strings.xml +++ b/packages/SettingsLib/res/values-pt/strings.xml @@ -237,7 +237,7 @@ <string name="choose_profile" msgid="343803890897657450">"Escolher perfil"</string> <string name="category_personal" msgid="6236798763159385225">"Pessoal"</string> <string name="category_work" msgid="4014193632325996115">"Trabalho"</string> - <string name="category_private" msgid="4244892185452788977">"Particular"</string> + <string name="category_private" msgid="4244892185452788977">"Privado"</string> <string name="category_clone" msgid="1554511758987195974">"Clone"</string> <string name="development_settings_title" msgid="140296922921597393">"Opções do desenvolvedor"</string> <string name="development_settings_enable" msgid="4285094651288242183">"Ativar opções do desenvolvedor"</string> diff --git a/packages/SettingsLib/res/values-ro/strings.xml b/packages/SettingsLib/res/values-ro/strings.xml index 1c76fdd9191a..4bddeb9a1cd0 100644 --- a/packages/SettingsLib/res/values-ro/strings.xml +++ b/packages/SettingsLib/res/values-ro/strings.xml @@ -94,7 +94,7 @@ <string name="bluetooth_connected_no_headset_battery_level" msgid="2661863370509206428">"Conectat (fără telefon), baterie <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">"Conectat (fără conținut media), baterie <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">"Conectat (fără telefon sau conținut media), baterie <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">"Activ. Nivelul bateriei <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>."</string> + <string name="bluetooth_active_battery_level" msgid="2685517576209066008">"Activ. Nivelul bateriei: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>."</string> <string name="bluetooth_active_battery_level_untethered" msgid="4961338936672922617">"Activ. Nivelul bateriei din stânga: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, dreapta: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g>."</string> <string name="bluetooth_active_battery_level_untethered_left" msgid="2895644748625343977">"Activ. Nivelul bateriei din stânga: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>."</string> <string name="bluetooth_active_battery_level_untethered_right" msgid="7407517998880370179">"Activ. Nivelul bateriei din dreapta: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>."</string> diff --git a/packages/SettingsLib/res/values-ru/strings.xml b/packages/SettingsLib/res/values-ru/strings.xml index 2972e611d565..f7d1ce2d8ee3 100644 --- a/packages/SettingsLib/res/values-ru/strings.xml +++ b/packages/SettingsLib/res/values-ru/strings.xml @@ -281,7 +281,7 @@ <string name="bugreport_in_power_summary" msgid="1885529649381831775">"Показывать в меню кнопки питания пункт для отправки отчета об ошибке"</string> <string name="keep_screen_on" msgid="1187161672348797558">"Не выключать экран"</string> <string name="keep_screen_on_summary" msgid="1510731514101925829">"Во время зарядки экран будет всегда включен"</string> - <string name="bt_hci_snoop_log" msgid="7291287955649081448">"Включить snoop-логи HCI Bluetooth"</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_summary" msgid="5857388174390953829">"Разрешить разблокировку загрузчика ОС"</string> diff --git a/packages/SettingsLib/res/values-sv/strings.xml b/packages/SettingsLib/res/values-sv/strings.xml index c4677beb6e11..636d969b30a5 100644 --- a/packages/SettingsLib/res/values-sv/strings.xml +++ b/packages/SettingsLib/res/values-sv/strings.xml @@ -341,7 +341,7 @@ <string name="allow_mock_location_summary" msgid="179780881081354579">"Tillåt skenplatser"</string> <string name="debug_view_attributes" msgid="3539609843984208216">"Aktivera inspektion av visningsattribut"</string> <string name="mobile_data_always_on_summary" msgid="1112156365594371019">"Håll alltid mobildata aktiverad, även när wifi är aktiverat (så att du snabbt kan byta mellan nätverk)."</string> - <string name="tethering_hardware_offload_summary" msgid="7801345335142803029">"Använd maskinvaruacceleration för internetdelning om tillgängligt"</string> + <string name="tethering_hardware_offload_summary" msgid="7801345335142803029">"Använd hårdvaruacceleration för internetdelning om tillgängligt"</string> <string name="adb_warning_title" msgid="7708653449506485728">"Ska USB-felsökning tillåtas?"</string> <string name="adb_warning_message" msgid="8145270656419669221">"USB-felsökning ska endast användas i utvecklingssyfte. Använd den för att kopiera data mellan datorn och enheten, installera appar på enheten utan meddelanden och läsa loggdata."</string> <string name="adbwifi_warning_title" msgid="727104571653031865">"Vill du tillåta trådlös felsökning?"</string> @@ -369,7 +369,7 @@ <string name="wait_for_debugger_summary" msgid="6846330006113363286">"Felsökaren måste ansluta till appen först"</string> <string name="debug_input_category" msgid="7349460906970849771">"Indata"</string> <string name="debug_drawing_category" msgid="5066171112313666619">"Ritning"</string> - <string name="debug_hw_drawing_category" msgid="5830815169336975162">"Maskinvaruaccelererad rendering"</string> + <string name="debug_hw_drawing_category" msgid="5830815169336975162">"Hårdvaruaccelererad rendering"</string> <string name="media_category" msgid="8122076702526144053">"Media"</string> <string name="debug_monitoring_category" msgid="1597387133765424994">"Övervakning"</string> <string name="strict_mode" msgid="889864762140862437">"Strikt läge aktiverat"</string> @@ -384,8 +384,8 @@ <string name="show_screen_updates_summary" msgid="2126932969682087406">"Hela fönstret blinkar vid uppdatering"</string> <string name="show_hw_screen_updates" msgid="2021286231267747506">"Visa visningsuppdatering"</string> <string name="show_hw_screen_updates_summary" msgid="3539770072741435691">"Visningar blinkar i fönster vid ritningar"</string> - <string name="show_hw_layers_updates" msgid="5268370750002509767">"Visa maskinvaruskiktuppdatering"</string> - <string name="show_hw_layers_updates_summary" msgid="5850955890493054618">"Låt maskinvaruskikt blinka grönt vid uppdateringar"</string> + <string name="show_hw_layers_updates" msgid="5268370750002509767">"Visa hårdvaruskiktuppdatering"</string> + <string name="show_hw_layers_updates_summary" msgid="5850955890493054618">"Låt hårdvaruskikt blinka grönt vid uppdateringar"</string> <string name="debug_hw_overdraw" msgid="8944851091008756796">"Felsök GPU-överritning"</string> <string name="disable_overlays" msgid="4206590799671557143">"Inaktivera HW-överlagringar"</string> <string name="disable_overlays_summary" msgid="1954852414363338166">"Använd alltid GPU för skärmsammansättning"</string> diff --git a/packages/SettingsLib/res/values-ta/strings.xml b/packages/SettingsLib/res/values-ta/strings.xml index ff62ba0d29a4..e8acd6ec391d 100644 --- a/packages/SettingsLib/res/values-ta/strings.xml +++ b/packages/SettingsLib/res/values-ta/strings.xml @@ -161,7 +161,7 @@ <string name="bluetooth_pairing_decline" msgid="6483118841204885890">"ரத்துசெய்"</string> <string name="bluetooth_pairing_will_share_phonebook" msgid="3064334458659165176">"இணைத்தலானது உங்கள் தொடர்புகள், அழைப்பு வரலாறுக்கான அணுகலை வழங்குகிறது."</string> <string name="bluetooth_pairing_error_message" msgid="6626399020672335565">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> உடன் இணைய முடியவில்லை."</string> - <string name="bluetooth_pairing_pin_error_message" msgid="264422127613704940">"தவறான பின் அல்லது கடவுச்சொல் காரணமாக <xliff:g id="DEVICE_NAME">%1$s</xliff:g> உடன் இணைக்க முடியவில்லை."</string> + <string name="bluetooth_pairing_pin_error_message" msgid="264422127613704940">"தவறான பின் அல்லது கடவுச்சாவி காரணமாக <xliff:g id="DEVICE_NAME">%1$s</xliff:g> உடன் இணைக்க முடியவில்லை."</string> <string name="bluetooth_pairing_device_down_error_message" msgid="2554424863101358857">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> உடன் இணைக்க முடியவில்லை."</string> <string name="bluetooth_pairing_rejected_error_message" msgid="5943444352777314442">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> இணைப்பதை நிராகரித்தது."</string> <string name="bluetooth_talkback_computer" msgid="3736623135703893773">"கணினி"</string> diff --git a/packages/SettingsLib/res/values-te/strings.xml b/packages/SettingsLib/res/values-te/strings.xml index b7446c2e934a..16d40c246d58 100644 --- a/packages/SettingsLib/res/values-te/strings.xml +++ b/packages/SettingsLib/res/values-te/strings.xml @@ -281,7 +281,7 @@ <string name="bugreport_in_power_summary" msgid="1885529649381831775">"బగ్ రిపోర్ట్ను తీసుకోవడానికి పవర్ మెనూలో బటన్ను చూపు"</string> <string name="keep_screen_on" msgid="1187161672348797558">"యాక్టివ్గా ఉంచు"</string> <string name="keep_screen_on_summary" msgid="1510731514101925829">"ఛార్జ్ చేస్తున్నప్పుడు స్క్రీన్ ఎప్పటికీ నిద్రావస్థలోకి వెళ్లదు"</string> - <string name="bt_hci_snoop_log" msgid="7291287955649081448">"బ్లూటూత్ HCI స్నూప్ లాగ్ను ఎనేబుల్ చేయి"</string> + <string name="bt_hci_snoop_log" msgid="7291287955649081448">"బ్లూటూత్ HCI స్నూప్ లాగ్ను ఎనేబుల్ చేయండి"</string> <string name="bt_hci_snoop_log_summary" msgid="6808538971394092284">"బ్లూటూత్ ప్యాకెట్లను క్యాప్చర్ చేయి. (ఈ సెట్టింగ్ని మార్చిన తర్వాత బ్లూటూత్ని టోగుల్ చేయండి)"</string> <string name="oem_unlock_enable" msgid="5334869171871566731">"OEM అన్లాకింగ్"</string> <string name="oem_unlock_enable_summary" msgid="5857388174390953829">"బూట్లోడర్ అన్లాక్ కావడానికి అనుమతించండి"</string> diff --git a/packages/SettingsLib/res/values-tr/strings.xml b/packages/SettingsLib/res/values-tr/strings.xml index 6aea659292c3..e8bac312b4dc 100644 --- a/packages/SettingsLib/res/values-tr/strings.xml +++ b/packages/SettingsLib/res/values-tr/strings.xml @@ -492,9 +492,9 @@ <string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - Tamamen şarj olmasına <xliff:g id="TIME">%2$s</xliff:g> kaldı"</string> <string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - Şarj işlemi optimize edildi"</string> <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> ‑ Şarj ediliyor"</string> - <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATUS">%2$s</xliff:g> - <xliff:g id="TIME">%3$s</xliff:g> içinde tamamen dolacak"</string> - <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> içinde tamamen şarj olacak"</string> - <string name="power_remaining_charging_duration_only_v2" msgid="5358176435722950193">"<xliff:g id="TIME">%1$s</xliff:g> içinde tamamen şarj olacak"</string> + <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATUS">%2$s</xliff:g> - Tamamen dolacağı zaman: <xliff:g id="TIME">%3$s</xliff:g>"</string> + <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> - Tamamen şarj olacağı zaman: <xliff:g id="TIME">%2$s</xliff:g>"</string> + <string name="power_remaining_charging_duration_only_v2" msgid="5358176435722950193">"Tamamen şarj olacağı zaman: <xliff:g id="TIME">%1$s</xliff:g>"</string> <string name="power_remaining_fast_charging_duration_only_v2" msgid="6270950195810579563">"<xliff:g id="TIME">%1$s</xliff:g> itibarıyla tamamen dolacak"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"Bilinmiyor"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"Şarj oluyor"</string> diff --git a/packages/SettingsLib/res/values-uk/strings.xml b/packages/SettingsLib/res/values-uk/strings.xml index 0400e0e321bb..deb0a4045280 100644 --- a/packages/SettingsLib/res/values-uk/strings.xml +++ b/packages/SettingsLib/res/values-uk/strings.xml @@ -710,7 +710,7 @@ <string name="keyboard_layout_default_label" msgid="1997292217218546957">"За умовчанням"</string> <string name="turn_screen_on_title" msgid="2662312432042116026">"Керування ввімкненням екрана"</string> <string name="allow_turn_screen_on" msgid="6194845766392742639">"Дозволити ввімкнення екрана"</string> - <string name="allow_turn_screen_on_description" msgid="43834403291575164">"Дозвольте додатку вмикати екран. Якщо ви надасте цей дозвіл, додаток зможе будь-коли вмикати екран пристрою навіть без вашого явного наміру."</string> + <string name="allow_turn_screen_on_description" msgid="43834403291575164">"Додатку буде дозволено вмикати екран. З таким дозволом додаток зможе будь-коли вмикати екран пристрою навіть без вашого явного наміру."</string> <string name="bt_le_audio_broadcast_dialog_title" msgid="5392738488989777074">"Зупинити трансляцію з додатка <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="268234802198852753">"Якщо ви зміните додаток (<xliff:g id="SWITCHAPP">%1$s</xliff:g>) або аудіовихід, поточну трансляцію буде припинено"</string> <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="5749813313369517812">"Змінити додаток для трансляції на <xliff:g id="SWITCHAPP">%1$s</xliff:g>"</string> diff --git a/packages/SettingsLib/res/values-zh-rCN/strings.xml b/packages/SettingsLib/res/values-zh-rCN/strings.xml index 7a355b9bdc54..e16e46aefe3a 100644 --- a/packages/SettingsLib/res/values-zh-rCN/strings.xml +++ b/packages/SettingsLib/res/values-zh-rCN/strings.xml @@ -708,8 +708,8 @@ <string name="physical_keyboard_title" msgid="4811935435315835220">"实体键盘"</string> <string name="keyboard_layout_dialog_title" msgid="3927180147005616290">"选择键盘布局"</string> <string name="keyboard_layout_default_label" msgid="1997292217218546957">"默认"</string> - <string name="turn_screen_on_title" msgid="2662312432042116026">"屏幕开启控件"</string> - <string name="allow_turn_screen_on" msgid="6194845766392742639">"允许开启屏幕"</string> + <string name="turn_screen_on_title" msgid="2662312432042116026">"屏幕唤醒设置"</string> + <string name="allow_turn_screen_on" msgid="6194845766392742639">"允许唤醒屏幕"</string> <string name="allow_turn_screen_on_description" msgid="43834403291575164">"允许应用唤醒屏幕。如获授权,该应用便可在您未明确表达意愿的情况下随时唤醒屏幕。"</string> <string name="bt_le_audio_broadcast_dialog_title" msgid="5392738488989777074">"要停止广播“<xliff:g id="APP_NAME">%1$s</xliff:g>”的内容吗?"</string> <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="268234802198852753">"如果广播“<xliff:g id="SWITCHAPP">%1$s</xliff:g>”的内容或更改输出来源,当前的广播就会停止"</string> diff --git a/packages/SettingsLib/res/values-zh-rTW/strings.xml b/packages/SettingsLib/res/values-zh-rTW/strings.xml index 3f35957b4a60..fdb4cf3ab209 100644 --- a/packages/SettingsLib/res/values-zh-rTW/strings.xml +++ b/packages/SettingsLib/res/values-zh-rTW/strings.xml @@ -708,7 +708,7 @@ <string name="physical_keyboard_title" msgid="4811935435315835220">"實體鍵盤"</string> <string name="keyboard_layout_dialog_title" msgid="3927180147005616290">"選擇鍵盤配置"</string> <string name="keyboard_layout_default_label" msgid="1997292217218546957">"預設"</string> - <string name="turn_screen_on_title" msgid="2662312432042116026">"螢幕開啟控制選項"</string> + <string name="turn_screen_on_title" msgid="2662312432042116026">"螢幕開啟設定"</string> <string name="allow_turn_screen_on" msgid="6194845766392742639">"允許開啟螢幕"</string> <string name="allow_turn_screen_on_description" msgid="43834403291575164">"允許應用程式開啟螢幕。如果授予這項權限,即使你未明確指示,應用程式也隨時可能會開啟螢幕。"</string> <string name="bt_le_audio_broadcast_dialog_title" msgid="5392738488989777074">"要停止播送「<xliff:g id="APP_NAME">%1$s</xliff:g>」的內容嗎?"</string> diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidDeviceManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidDeviceManager.java index ed964a9d0f40..b3e48b26782e 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidDeviceManager.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidDeviceManager.java @@ -209,44 +209,34 @@ public class HearingAidDeviceManager { CachedBluetoothDevice mainDevice = findMainDevice(cachedDevice); if (mainDevice != null) { if (mainDevice.isConnected()) { - // When main device exists and in connected state, receiving sub device - // connection. To refresh main device UI + // Sub/member device is connected and main device is connected + // To refresh main device UI mainDevice.refresh(); } else { - // When both Hearing Aid devices are disconnected, receiving sub device - // connection. To switch content and dispatch to notify UI change - mBtManager.getEventManager().dispatchDeviceRemoved(mainDevice); - mainDevice.switchSubDeviceContent(); - mainDevice.refresh(); - // It is necessary to do remove and add for updating the mapping on - // preference and device - mBtManager.getEventManager().dispatchDeviceAdded(mainDevice); + // Sub/member device is connected and main device is disconnected + // To switch content and dispatch to notify UI change + switchDeviceContent(mainDevice, cachedDevice); } return true; } break; case BluetoothProfile.STATE_DISCONNECTED: - mainDevice = findMainDevice(cachedDevice); if (cachedDevice.getUnpairing()) { return true; } + mainDevice = findMainDevice(cachedDevice); if (mainDevice != null) { - // When main device exists, receiving sub device disconnection + // Sub/member device is disconnected and main device exists // To update main device UI mainDevice.refresh(); return true; } - CachedBluetoothDevice subDevice = cachedDevice.getSubDevice(); - if (subDevice != null && subDevice.isConnected()) { - // Main device is disconnected and sub device is connected - // To copy data from sub device to main device - mBtManager.getEventManager().dispatchDeviceRemoved(cachedDevice); - cachedDevice.switchSubDeviceContent(); - cachedDevice.refresh(); - // It is necessary to do remove and add for updating the mapping on - // preference and device - mBtManager.getEventManager().dispatchDeviceAdded(cachedDevice); - + CachedBluetoothDevice connectedSecondaryDevice = getConnectedSecondaryDevice( + cachedDevice); + if (connectedSecondaryDevice != null) { + // Main device is disconnected and sub/member device is connected + // To switch content and dispatch to notify UI change + switchDeviceContent(cachedDevice, connectedSecondaryDevice); return true; } break; @@ -254,6 +244,29 @@ public class HearingAidDeviceManager { return false; } + private void switchDeviceContent(CachedBluetoothDevice mainDevice, + CachedBluetoothDevice secondaryDevice) { + mBtManager.getEventManager().dispatchDeviceRemoved(mainDevice); + if (mainDevice.getSubDevice() != null + && mainDevice.getSubDevice().equals(secondaryDevice)) { + mainDevice.switchSubDeviceContent(); + } else { + mainDevice.switchMemberDeviceContent(secondaryDevice); + } + mainDevice.refresh(); + // It is necessary to do remove and add for updating the mapping on + // preference and device + mBtManager.getEventManager().dispatchDeviceAdded(mainDevice); + } + + private CachedBluetoothDevice getConnectedSecondaryDevice(CachedBluetoothDevice cachedDevice) { + if (cachedDevice.getSubDevice() != null && cachedDevice.getSubDevice().isConnected()) { + return cachedDevice.getSubDevice(); + } + return cachedDevice.getMemberDevice().stream().filter( + CachedBluetoothDevice::isConnected).findAny().orElse(null); + } + void onActiveDeviceChanged(CachedBluetoothDevice device) { if (FeatureFlagUtils.isEnabled(mContext, FeatureFlagUtils.SETTINGS_AUDIO_ROUTING)) { if (device.isActiveDevice(BluetoothProfile.HEARING_AID) || device.isActiveDevice( diff --git a/packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaDevice.java index cdb87404b016..063807abeb0a 100644 --- a/packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaDevice.java +++ b/packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaDevice.java @@ -17,6 +17,8 @@ package com.android.settingslib.media; import static com.android.settingslib.media.MediaDevice.SelectionBehavior.SELECTION_BEHAVIOR_TRANSFER; +import android.annotation.NonNull; +import android.annotation.Nullable; import android.bluetooth.BluetoothClass; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothHearingAid; @@ -37,21 +39,14 @@ public class BluetoothMediaDevice extends MediaDevice { private static final String TAG = "BluetoothMediaDevice"; - private CachedBluetoothDevice mCachedDevice; + private final CachedBluetoothDevice mCachedDevice; private final AudioManager mAudioManager; BluetoothMediaDevice( - Context context, - CachedBluetoothDevice device, - MediaRoute2Info info) { - this(context, device, info, null); - } - - BluetoothMediaDevice( - Context context, - CachedBluetoothDevice device, - MediaRoute2Info info, - RouteListingPreference.Item item) { + @NonNull Context context, + @NonNull CachedBluetoothDevice device, + @Nullable MediaRoute2Info info, + @Nullable RouteListingPreference.Item item) { super(context, info, item); mCachedDevice = device; mAudioManager = context.getSystemService(AudioManager.class); diff --git a/packages/SettingsLib/src/com/android/settingslib/media/ComplexMediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/ComplexMediaDevice.java index 338fb872650c..a87daf90a84f 100644 --- a/packages/SettingsLib/src/com/android/settingslib/media/ComplexMediaDevice.java +++ b/packages/SettingsLib/src/com/android/settingslib/media/ComplexMediaDevice.java @@ -16,6 +16,8 @@ package com.android.settingslib.media; +import android.annotation.NonNull; +import android.annotation.Nullable; import android.content.Context; import android.graphics.drawable.Drawable; import android.media.MediaRoute2Info; @@ -32,9 +34,9 @@ public class ComplexMediaDevice extends MediaDevice { private final String mSummary = ""; ComplexMediaDevice( - Context context, - MediaRoute2Info info, - RouteListingPreference.Item item) { + @NonNull Context context, + @NonNull MediaRoute2Info info, + @Nullable RouteListingPreference.Item item) { super(context, info, item); } diff --git a/packages/SettingsLib/src/com/android/settingslib/media/DeviceIconUtil.java b/packages/SettingsLib/src/com/android/settingslib/media/DeviceIconUtil.java index 3de49336f427..717a8ee32082 100644 --- a/packages/SettingsLib/src/com/android/settingslib/media/DeviceIconUtil.java +++ b/packages/SettingsLib/src/com/android/settingslib/media/DeviceIconUtil.java @@ -16,135 +16,136 @@ package com.android.settingslib.media; +import static android.media.AudioDeviceInfo.AudioDeviceType; +import static android.media.AudioDeviceInfo.TYPE_BUILTIN_SPEAKER; +import static android.media.AudioDeviceInfo.TYPE_DOCK; +import static android.media.AudioDeviceInfo.TYPE_HDMI; +import static android.media.AudioDeviceInfo.TYPE_HDMI_ARC; +import static android.media.AudioDeviceInfo.TYPE_HDMI_EARC; +import static android.media.AudioDeviceInfo.TYPE_USB_ACCESSORY; +import static android.media.AudioDeviceInfo.TYPE_USB_DEVICE; +import static android.media.AudioDeviceInfo.TYPE_USB_HEADSET; +import static android.media.AudioDeviceInfo.TYPE_WIRED_HEADPHONES; +import static android.media.AudioDeviceInfo.TYPE_WIRED_HEADSET; + import android.annotation.DrawableRes; +import android.annotation.SuppressLint; import android.content.Context; import android.content.pm.PackageManager; import android.graphics.drawable.Drawable; -import android.media.AudioDeviceInfo; import android.media.MediaRoute2Info; +import android.os.SystemProperties; +import android.util.SparseIntArray; + +import androidx.annotation.NonNull; +import com.android.internal.annotations.VisibleForTesting; import com.android.settingslib.R; import com.android.settingslib.media.flags.Flags; import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import java.util.Objects; /** A util class to get the appropriate icon for different device types. */ public class DeviceIconUtil { - // A default icon to use if the type is not present in the map. - @DrawableRes private static final int DEFAULT_ICON = R.drawable.ic_smartphone; - @DrawableRes private static final int DEFAULT_ICON_TV = R.drawable.ic_media_speaker_device; - - // A map from a @AudioDeviceInfo.AudioDeviceType to full device information. - private final Map<Integer, Device> mAudioDeviceTypeToIconMap = new HashMap<>(); - // A map from a @MediaRoute2Info.Type to full device information. - private final Map<Integer, Device> mMediaRouteTypeToIconMap = new HashMap<>(); + private static final SparseIntArray AUDIO_DEVICE_TO_MEDIA_ROUTE_TYPE = new SparseIntArray(); private final boolean mIsTv; - - public DeviceIconUtil(Context context) { - this(context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_LEANBACK)); - } - - public DeviceIconUtil(boolean isTv) { - mIsTv = isTv && Flags.enableTvMediaOutputDialog(); - List<Device> deviceList = Arrays.asList( - new Device( - AudioDeviceInfo.TYPE_USB_DEVICE, - MediaRoute2Info.TYPE_USB_DEVICE, - R.drawable.ic_headphone), - new Device( - AudioDeviceInfo.TYPE_USB_HEADSET, - MediaRoute2Info.TYPE_USB_HEADSET, - R.drawable.ic_headphone), - new Device( - AudioDeviceInfo.TYPE_USB_ACCESSORY, - MediaRoute2Info.TYPE_USB_ACCESSORY, - mIsTv ? R.drawable.ic_usb : R.drawable.ic_headphone), - new Device( - AudioDeviceInfo.TYPE_DOCK, - MediaRoute2Info.TYPE_DOCK, - R.drawable.ic_dock_device), - new Device( - AudioDeviceInfo.TYPE_HDMI, - MediaRoute2Info.TYPE_HDMI, - mIsTv ? R.drawable.ic_tv : R.drawable.ic_external_display), - new Device( - AudioDeviceInfo.TYPE_HDMI_ARC, - MediaRoute2Info.TYPE_HDMI_ARC, - mIsTv ? R.drawable.ic_hdmi : R.drawable.ic_external_display), - new Device( - AudioDeviceInfo.TYPE_HDMI_EARC, - MediaRoute2Info.TYPE_HDMI_EARC, - mIsTv ? R.drawable.ic_hdmi : R.drawable.ic_external_display), - new Device( - AudioDeviceInfo.TYPE_WIRED_HEADSET, - MediaRoute2Info.TYPE_WIRED_HEADSET, - mIsTv ? R.drawable.ic_wired_device : R.drawable.ic_headphone), - new Device( - AudioDeviceInfo.TYPE_WIRED_HEADPHONES, - MediaRoute2Info.TYPE_WIRED_HEADPHONES, - mIsTv ? R.drawable.ic_wired_device : R.drawable.ic_headphone), - new Device( - AudioDeviceInfo.TYPE_BUILTIN_SPEAKER, - MediaRoute2Info.TYPE_BUILTIN_SPEAKER, - mIsTv ? R.drawable.ic_tv : R.drawable.ic_smartphone)); - for (int i = 0; i < deviceList.size(); i++) { - Device device = deviceList.get(i); - mAudioDeviceTypeToIconMap.put(device.mAudioDeviceType, device); - mMediaRouteTypeToIconMap.put(device.mMediaRouteType, device); - } + private final boolean mIsTablet; + private final Context mContext; + public DeviceIconUtil(@NonNull Context context) { + mContext = Objects.requireNonNull(context); + mIsTv = + mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_LEANBACK) + && Flags.enableTvMediaOutputDialog(); + mIsTablet = + Arrays.asList(SystemProperties.get("ro.build.characteristics").split(",")) + .contains("tablet"); } - private int getDefaultIcon() { - return mIsTv ? DEFAULT_ICON_TV : DEFAULT_ICON; + @VisibleForTesting + /* package */ DeviceIconUtil(boolean isTv) { + mContext = null; + mIsTv = isTv; + mIsTablet = false; } /** Returns a drawable for an icon representing the given audioDeviceType. */ - public Drawable getIconFromAudioDeviceType( - @AudioDeviceInfo.AudioDeviceType int audioDeviceType, Context context) { - return context.getDrawable(getIconResIdFromAudioDeviceType(audioDeviceType)); + public Drawable getIconFromAudioDeviceType(@AudioDeviceType int audioDeviceType) { + return mContext.getDrawable(getIconResIdFromAudioDeviceType(audioDeviceType)); } /** Returns a drawable res ID for an icon representing the given audioDeviceType. */ @DrawableRes - public int getIconResIdFromAudioDeviceType( - @AudioDeviceInfo.AudioDeviceType int audioDeviceType) { - if (mAudioDeviceTypeToIconMap.containsKey(audioDeviceType)) { - return mAudioDeviceTypeToIconMap.get(audioDeviceType).mIconDrawableRes; - } - return getDefaultIcon(); + public int getIconResIdFromAudioDeviceType(@AudioDeviceType int audioDeviceType) { + int mediaRouteType = + AUDIO_DEVICE_TO_MEDIA_ROUTE_TYPE.get(audioDeviceType, /* defaultValue */ -1); + return getIconResIdFromMediaRouteType(mediaRouteType); } /** Returns a drawable res ID for an icon representing the given mediaRouteType. */ @DrawableRes - public int getIconResIdFromMediaRouteType( - @MediaRoute2Info.Type int mediaRouteType) { - if (mMediaRouteTypeToIconMap.containsKey(mediaRouteType)) { - return mMediaRouteTypeToIconMap.get(mediaRouteType).mIconDrawableRes; - } - return getDefaultIcon(); + public int getIconResIdFromMediaRouteType(@MediaRoute2Info.Type int type) { + return mIsTv + ? getIconResourceIdForTv(type) + : getIconResourceIdForPhoneOrTablet(type, mIsTablet); } - private static class Device { - @AudioDeviceInfo.AudioDeviceType - private final int mAudioDeviceType; - - @MediaRoute2Info.Type - private final int mMediaRouteType; + @SuppressLint("SwitchIntDef") + @DrawableRes + private static int getIconResourceIdForPhoneOrTablet( + @MediaRoute2Info.Type int type, boolean isTablet) { + int defaultResId = isTablet ? R.drawable.ic_media_tablet : R.drawable.ic_smartphone; + + return switch (type) { + case MediaRoute2Info.TYPE_USB_DEVICE, + MediaRoute2Info.TYPE_USB_HEADSET, + MediaRoute2Info.TYPE_USB_ACCESSORY, + MediaRoute2Info.TYPE_WIRED_HEADSET, + MediaRoute2Info.TYPE_WIRED_HEADPHONES -> + R.drawable.ic_headphone; + case MediaRoute2Info.TYPE_DOCK -> R.drawable.ic_dock_device; + case MediaRoute2Info.TYPE_HDMI, + MediaRoute2Info.TYPE_HDMI_ARC, + MediaRoute2Info.TYPE_HDMI_EARC -> + R.drawable.ic_external_display; + default -> defaultResId; // Includes TYPE_BUILTIN_SPEAKER. + }; + } - @DrawableRes - private final int mIconDrawableRes; + @SuppressLint("SwitchIntDef") + @DrawableRes + private static int getIconResourceIdForTv(@MediaRoute2Info.Type int type) { + return switch (type) { + case MediaRoute2Info.TYPE_USB_DEVICE, MediaRoute2Info.TYPE_USB_HEADSET -> + R.drawable.ic_headphone; + case MediaRoute2Info.TYPE_USB_ACCESSORY -> R.drawable.ic_usb; + case MediaRoute2Info.TYPE_DOCK -> R.drawable.ic_dock_device; + case MediaRoute2Info.TYPE_HDMI, MediaRoute2Info.TYPE_BUILTIN_SPEAKER -> + R.drawable.ic_tv; + case MediaRoute2Info.TYPE_HDMI_ARC, MediaRoute2Info.TYPE_HDMI_EARC -> + R.drawable.ic_hdmi; + case MediaRoute2Info.TYPE_WIRED_HEADSET, MediaRoute2Info.TYPE_WIRED_HEADPHONES -> + R.drawable.ic_wired_device; + default -> R.drawable.ic_media_speaker_device; + }; + } - Device(@AudioDeviceInfo.AudioDeviceType int audioDeviceType, - @MediaRoute2Info.Type int mediaRouteType, - @DrawableRes int iconDrawableRes) { - mAudioDeviceType = audioDeviceType; - mMediaRouteType = mediaRouteType; - mIconDrawableRes = iconDrawableRes; - } + static { + AUDIO_DEVICE_TO_MEDIA_ROUTE_TYPE.put(TYPE_USB_DEVICE, MediaRoute2Info.TYPE_USB_DEVICE); + AUDIO_DEVICE_TO_MEDIA_ROUTE_TYPE.put(TYPE_USB_HEADSET, MediaRoute2Info.TYPE_USB_HEADSET); + AUDIO_DEVICE_TO_MEDIA_ROUTE_TYPE.put( + TYPE_USB_ACCESSORY, MediaRoute2Info.TYPE_USB_ACCESSORY); + AUDIO_DEVICE_TO_MEDIA_ROUTE_TYPE.put(TYPE_DOCK, MediaRoute2Info.TYPE_DOCK); + AUDIO_DEVICE_TO_MEDIA_ROUTE_TYPE.put(TYPE_HDMI, MediaRoute2Info.TYPE_HDMI); + AUDIO_DEVICE_TO_MEDIA_ROUTE_TYPE.put(TYPE_HDMI_ARC, MediaRoute2Info.TYPE_HDMI_ARC); + AUDIO_DEVICE_TO_MEDIA_ROUTE_TYPE.put(TYPE_HDMI_EARC, MediaRoute2Info.TYPE_HDMI_EARC); + AUDIO_DEVICE_TO_MEDIA_ROUTE_TYPE.put( + TYPE_WIRED_HEADSET, MediaRoute2Info.TYPE_WIRED_HEADSET); + AUDIO_DEVICE_TO_MEDIA_ROUTE_TYPE.put( + TYPE_WIRED_HEADPHONES, MediaRoute2Info.TYPE_WIRED_HEADPHONES); + AUDIO_DEVICE_TO_MEDIA_ROUTE_TYPE.put( + TYPE_BUILTIN_SPEAKER, MediaRoute2Info.TYPE_BUILTIN_SPEAKER); } } diff --git a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaDevice.java index 1347dd131f69..21873ef3aeab 100644 --- a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaDevice.java +++ b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaDevice.java @@ -26,6 +26,8 @@ import static android.media.MediaRoute2Info.TYPE_REMOTE_TABLET; import static android.media.MediaRoute2Info.TYPE_REMOTE_TABLET_DOCKED; import static android.media.MediaRoute2Info.TYPE_REMOTE_TV; +import android.annotation.NonNull; +import android.annotation.Nullable; import android.content.Context; import android.graphics.drawable.Drawable; import android.media.MediaRoute2Info; @@ -43,17 +45,13 @@ public class InfoMediaDevice extends MediaDevice { private static final String TAG = "InfoMediaDevice"; InfoMediaDevice( - Context context, - MediaRoute2Info info, - RouteListingPreference.Item item) { + @NonNull Context context, + @NonNull MediaRoute2Info info, + @Nullable RouteListingPreference.Item item) { super(context, info, item); initDeviceRecord(); } - InfoMediaDevice(Context context, MediaRoute2Info info) { - this(context, info, null); - } - @Override public String getName() { return mRouteInfo.getName().toString(); diff --git a/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java index cfa825bbb1c4..72a60fbc9fea 100644 --- a/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java +++ b/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java @@ -570,7 +570,7 @@ public class LocalMediaManager implements BluetoothCallback { final CachedBluetoothDevice cachedDevice = cachedDeviceManager.findDevice(device); if (isBondedMediaDevice(cachedDevice) && isMutingExpectedDevice(cachedDevice)) { - return new BluetoothMediaDevice(mContext, cachedDevice, null); + return new BluetoothMediaDevice(mContext, cachedDevice, null, /* item */ null); } } return null; @@ -617,7 +617,7 @@ public class LocalMediaManager implements BluetoothCallback { mDisconnectedMediaDevices.clear(); for (CachedBluetoothDevice cachedDevice : cachedBluetoothDeviceList) { final MediaDevice mediaDevice = - new BluetoothMediaDevice(mContext, cachedDevice, null); + new BluetoothMediaDevice(mContext, cachedDevice, null, /* item */ null); if (!mMediaDevices.contains(mediaDevice)) { cachedDevice.registerCallback(mDeviceAttributeChangeCallback); mDisconnectedMediaDevices.add(mediaDevice); diff --git a/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java index 0c4cf769ca90..ce1f29766bed 100644 --- a/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java +++ b/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java @@ -49,6 +49,8 @@ import static android.media.RouteListingPreference.Item.SUBTEXT_UNAUTHORIZED; import static com.android.settingslib.media.LocalMediaManager.MediaDeviceState.STATE_SELECTED; import static com.android.settingslib.media.MediaDevice.SelectionBehavior.SELECTION_BEHAVIOR_TRANSFER; +import android.annotation.NonNull; +import android.annotation.Nullable; import android.annotation.SuppressLint; import android.content.Context; import android.graphics.drawable.Drawable; @@ -123,9 +125,9 @@ public abstract class MediaDevice implements Comparable<MediaDevice> { protected final RouteListingPreference.Item mItem; MediaDevice( - Context context, - MediaRoute2Info info, - RouteListingPreference.Item item) { + @NonNull Context context, + @Nullable MediaRoute2Info info, + @Nullable RouteListingPreference.Item item) { mContext = context; mRouteInfo = info; mItem = item; diff --git a/packages/SettingsLib/src/com/android/settingslib/media/PhoneMediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/PhoneMediaDevice.java index ba9180db0887..9eaf8d3838d8 100644 --- a/packages/SettingsLib/src/com/android/settingslib/media/PhoneMediaDevice.java +++ b/packages/SettingsLib/src/com/android/settingslib/media/PhoneMediaDevice.java @@ -29,6 +29,8 @@ import static android.media.MediaRoute2Info.TYPE_WIRED_HEADSET; import static com.android.settingslib.media.MediaDevice.SelectionBehavior.SELECTION_BEHAVIOR_TRANSFER; import android.Manifest; +import android.annotation.NonNull; +import android.annotation.Nullable; import android.content.Context; import android.content.pm.PackageManager; import android.graphics.drawable.Drawable; @@ -40,7 +42,6 @@ import android.media.RouteListingPreference; import android.os.SystemProperties; import android.util.Log; -import androidx.annotation.NonNull; import androidx.annotation.VisibleForTesting; import com.android.settingslib.R; @@ -100,17 +101,6 @@ public class PhoneMediaDevice extends MediaDevice { R.string.media_transfer_external_device_name); break; case TYPE_HDMI_ARC: - if (isTv) { - String deviceName = getHdmiOutDeviceName(context); - if (deviceName != null) { - name = deviceName; - } else { - name = context.getString(R.string.tv_media_transfer_arc_fallback_title); - } - } else { - name = context.getString(R.string.media_transfer_external_device_name); - } - break; case TYPE_HDMI_EARC: if (isTv) { String deviceName = getHdmiOutDeviceName(context); @@ -130,14 +120,10 @@ public class PhoneMediaDevice extends MediaDevice { return name.toString(); } - PhoneMediaDevice(Context context, MediaRoute2Info info) { - this(context, info, null); - } - PhoneMediaDevice( - Context context, - MediaRoute2Info info, - RouteListingPreference.Item item) { + @NonNull Context context, + @NonNull MediaRoute2Info info, + @Nullable RouteListingPreference.Item item) { super(context, info, item); mDeviceIconUtil = new DeviceIconUtil(mContext); initDeviceRecord(); diff --git a/packages/SettingsLib/src/com/android/settingslib/volume/data/repository/AudioRepository.kt b/packages/SettingsLib/src/com/android/settingslib/volume/data/repository/AudioRepository.kt index 8204569ce2f8..20b949f4a30f 100644 --- a/packages/SettingsLib/src/com/android/settingslib/volume/data/repository/AudioRepository.kt +++ b/packages/SettingsLib/src/com/android/settingslib/volume/data/repository/AudioRepository.kt @@ -37,6 +37,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.conflate import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.filterIsInstance @@ -161,6 +162,7 @@ class AudioRepositoryImpl( }, volumeSettingChanges(audioStream), ) + .conflate() .map { getCurrentAudioStream(audioStream) } .onStart { emit(getCurrentAudioStream(audioStream)) } .flowOn(backgroundCoroutineContext) @@ -184,10 +186,11 @@ class AudioRepositoryImpl( } } - override suspend fun setVolume(audioStream: AudioStream, volume: Int) = + override suspend fun setVolume(audioStream: AudioStream, volume: Int) { withContext(backgroundCoroutineContext) { audioManager.setStreamVolume(audioStream.value, volume, 0) } + } override suspend fun setMuted(audioStream: AudioStream, isMuted: Boolean): Boolean { return withContext(backgroundCoroutineContext) { diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HearingAidDeviceManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HearingAidDeviceManagerTest.java index 4188d2ec7aaa..bf927a1eb4cc 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HearingAidDeviceManagerTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HearingAidDeviceManagerTest.java @@ -681,6 +681,53 @@ public class HearingAidDeviceManagerTest { verify(mCachedDevice1).refresh(); } + + /** + * Test onProfileConnectionStateChangedIfProcessed. + * When main device is disconnected, to verify switch() result for member device connected + * event + */ + @Test + public void onProfileConnectionStateChanged_connect_member_mainDisconnected_switch() { + when(mCachedDevice1.isConnected()).thenReturn(false); + when(mCachedDevice1.getGroupId()).thenReturn(GROUP_ID_1); + when(mCachedDevice2.getGroupId()).thenReturn(GROUP_ID_1); + mCachedDeviceManager.mCachedDevices.add(mCachedDevice1); + mCachedDevice1.addMemberDevice(mCachedDevice2); + + assertThat(mCachedDevice1.mDevice).isEqualTo(mDevice1); + assertThat(mCachedDevice2.mDevice).isEqualTo(mDevice2); + assertThat(mHearingAidDeviceManager.onProfileConnectionStateChangedIfProcessed( + mCachedDevice2, BluetoothProfile.STATE_CONNECTED)).isTrue(); + + assertThat(mCachedDevice1.mDevice).isEqualTo(mDevice2); + assertThat(mCachedDevice2.mDevice).isEqualTo(mDevice1); + verify(mCachedDevice1).refresh(); + } + + /** + * Test onProfileConnectionStateChangedIfProcessed. + * When member device is connected, to verify switch() result for main device disconnected + * event + */ + @Test + public void onProfileConnectionStateChanged_disconnect_main_subDeviceConnected_switch() { + when(mCachedDevice2.isConnected()).thenReturn(true); + when(mCachedDevice1.getGroupId()).thenReturn(GROUP_ID_1); + when(mCachedDevice2.getGroupId()).thenReturn(GROUP_ID_1); + mCachedDeviceManager.mCachedDevices.add(mCachedDevice1); + mCachedDevice1.addMemberDevice(mCachedDevice2); + + assertThat(mCachedDevice1.mDevice).isEqualTo(mDevice1); + assertThat(mCachedDevice2.mDevice).isEqualTo(mDevice2); + assertThat(mHearingAidDeviceManager.onProfileConnectionStateChangedIfProcessed( + mCachedDevice1, BluetoothProfile.STATE_DISCONNECTED)).isTrue(); + + assertThat(mCachedDevice1.mDevice).isEqualTo(mDevice2); + assertThat(mCachedDevice2.mDevice).isEqualTo(mDevice1); + verify(mCachedDevice1).refresh(); + } + @Test public void onActiveDeviceChanged_connected_callSetStrategies() { when(mHelper.getMatchedHearingDeviceAttributes(mCachedDevice1)).thenReturn( diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/DeviceIconUtilTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/DeviceIconUtilTest.java index 8edda1a1f3a2..883640db5e27 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/DeviceIconUtilTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/DeviceIconUtilTest.java @@ -18,6 +18,7 @@ package com.android.settingslib.media; import static com.google.common.truth.Truth.assertThat; +import android.content.Context; import android.media.AudioDeviceInfo; import android.media.MediaRoute2Info; import android.platform.test.flag.junit.SetFlagsRule; @@ -30,6 +31,8 @@ import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.robolectric.RobolectricTestRunner; +import org.robolectric.RuntimeEnvironment; +import org.robolectric.shadows.ShadowSystemProperties; @RunWith(RobolectricTestRunner.class) public class DeviceIconUtilTest { @@ -37,9 +40,12 @@ public class DeviceIconUtilTest { @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); + private Context mContext; + @Before public void setup() { mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_TV_MEDIA_OUTPUT_DIALOG); + mContext = RuntimeEnvironment.getApplication(); } @Test @@ -171,6 +177,14 @@ public class DeviceIconUtilTest { } @Test + public void getIconResIdFromMediaRouteType_onTablet_builtinSpeaker_isTablet() { + ShadowSystemProperties.override("ro.build.characteristics", "tablet"); + assertThat(new DeviceIconUtil(mContext) + .getIconResIdFromMediaRouteType(MediaRoute2Info.TYPE_BUILTIN_SPEAKER)) + .isEqualTo(R.drawable.ic_media_tablet); + } + + @Test public void getIconResIdFromMediaRouteType_unsupportedType_isSmartphone() { assertThat(new DeviceIconUtil(/* isTv */ false) .getIconResIdFromMediaRouteType(MediaRoute2Info.TYPE_UNKNOWN)) @@ -178,6 +192,14 @@ public class DeviceIconUtilTest { } @Test + public void getIconResIdFromMediaRouteType_onTablet_unsupportedType_isTablet() { + ShadowSystemProperties.override("ro.build.characteristics", "tablet"); + assertThat(new DeviceIconUtil(mContext) + .getIconResIdFromMediaRouteType(MediaRoute2Info.TYPE_UNKNOWN)) + .isEqualTo(R.drawable.ic_media_tablet); + } + + @Test public void getIconResIdFromMediaRouteType_tv_unsupportedType_isSpeaker() { assertThat(new DeviceIconUtil(/* isTv */ true) .getIconResIdFromMediaRouteType(MediaRoute2Info.TYPE_UNKNOWN)) diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaDeviceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaDeviceTest.java index 0665308fdbfb..6647a278a6bd 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaDeviceTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaDeviceTest.java @@ -65,7 +65,7 @@ public class InfoMediaDeviceTest { MockitoAnnotations.initMocks(this); mContext = RuntimeEnvironment.application; - mInfoMediaDevice = new InfoMediaDevice(mContext, mRouteInfo); + mInfoMediaDevice = new InfoMediaDevice(mContext, mRouteInfo, /* item */ null); } @Test diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java index ce07fe9fdf0a..c9b35a0ae833 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java @@ -559,7 +559,7 @@ public class InfoMediaManagerTest { routingSessionInfos.add(info); final MediaRoute2Info route2Info = mock(MediaRoute2Info.class); - final MediaDevice device = new InfoMediaDevice(mContext, route2Info); + final MediaDevice device = new InfoMediaDevice(mContext, route2Info, /* item */ null); final List<String> list = new ArrayList<>(); list.add(TEST_ID); @@ -580,7 +580,7 @@ public class InfoMediaManagerTest { routingSessionInfos.add(info); final MediaRoute2Info route2Info = mock(MediaRoute2Info.class); - final MediaDevice device = new InfoMediaDevice(mContext, route2Info); + final MediaDevice device = new InfoMediaDevice(mContext, route2Info, /* item */ null); final List<String> list = new ArrayList<>(); list.add("fake_id"); @@ -602,7 +602,7 @@ public class InfoMediaManagerTest { routingSessionInfos.add(info); final MediaRoute2Info route2Info = mock(MediaRoute2Info.class); - final MediaDevice device = new InfoMediaDevice(mContext, route2Info); + final MediaDevice device = new InfoMediaDevice(mContext, route2Info, /* item */ null); final List<String> list = new ArrayList<>(); list.add(TEST_ID); @@ -623,7 +623,7 @@ public class InfoMediaManagerTest { routingSessionInfos.add(info); final MediaRoute2Info route2Info = mock(MediaRoute2Info.class); - final MediaDevice device = new InfoMediaDevice(mContext, route2Info); + final MediaDevice device = new InfoMediaDevice(mContext, route2Info, /* item */ null); final List<String> list = new ArrayList<>(); list.add("fake_id"); diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java index 12541bb51cc8..a30d6a787971 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java @@ -135,8 +135,8 @@ public class LocalMediaManagerTest { .when(mInfoMediaManager) .getRoutingSessionsForPackage(); - mInfoMediaDevice1 = spy(new InfoMediaDevice(mContext, mRouteInfo1)); - mInfoMediaDevice2 = new InfoMediaDevice(mContext, mRouteInfo2); + mInfoMediaDevice1 = spy(new InfoMediaDevice(mContext, mRouteInfo1, /* item */ null)); + mInfoMediaDevice2 = new InfoMediaDevice(mContext, mRouteInfo2, /* item */ null); mLocalMediaManager = new LocalMediaManager( mContext, mLocalBluetoothManager, mInfoMediaManager, TEST_PACKAGE_NAME); diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/MediaDeviceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/MediaDeviceTest.java index 098ab162c225..3d16d6f1cd56 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/MediaDeviceTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/MediaDeviceTest.java @@ -171,17 +171,17 @@ public class MediaDeviceTest { mBluetoothMediaDevice1 = new BluetoothMediaDevice( - mContext, mCachedDevice1, mBluetoothRouteInfo1); + mContext, mCachedDevice1, mBluetoothRouteInfo1, /* item */ null); mBluetoothMediaDevice2 = new BluetoothMediaDevice( - mContext, mCachedDevice2, mBluetoothRouteInfo2); + mContext, mCachedDevice2, mBluetoothRouteInfo2, /* item */ null); mBluetoothMediaDevice3 = new BluetoothMediaDevice( - mContext, mCachedDevice3, mBluetoothRouteInfo3); - mInfoMediaDevice1 = new InfoMediaDevice(mContext, mRouteInfo1); - mInfoMediaDevice2 = new InfoMediaDevice(mContext, mRouteInfo2); - mInfoMediaDevice3 = new InfoMediaDevice(mContext, mRouteInfo3); - mPhoneMediaDevice = new PhoneMediaDevice(mContext, mPhoneRouteInfo); + mContext, mCachedDevice3, mBluetoothRouteInfo3, /* item */ null); + mInfoMediaDevice1 = new InfoMediaDevice(mContext, mRouteInfo1, /* item */ null); + mInfoMediaDevice2 = new InfoMediaDevice(mContext, mRouteInfo2, /* item */ null); + mInfoMediaDevice3 = new InfoMediaDevice(mContext, mRouteInfo3, /* item */ null); + mPhoneMediaDevice = new PhoneMediaDevice(mContext, mPhoneRouteInfo, /* item */ null); } @Test @@ -316,7 +316,7 @@ public class MediaDeviceTest { when(phoneRouteInfo.getType()).thenReturn(TYPE_WIRED_HEADPHONES); final PhoneMediaDevice phoneMediaDevice = - new PhoneMediaDevice(mContext, phoneRouteInfo); + new PhoneMediaDevice(mContext, phoneRouteInfo, /* item */ null); mMediaDevices.add(mBluetoothMediaDevice1); mMediaDevices.add(phoneMediaDevice); @@ -332,7 +332,7 @@ public class MediaDeviceTest { when(phoneRouteInfo.getType()).thenReturn(TYPE_WIRED_HEADPHONES); final PhoneMediaDevice phoneMediaDevice = - new PhoneMediaDevice(mContext, phoneRouteInfo); + new PhoneMediaDevice(mContext, phoneRouteInfo, /* item */ null); mMediaDevices.add(mInfoMediaDevice1); mMediaDevices.add(phoneMediaDevice); @@ -483,7 +483,7 @@ public class MediaDeviceTest { public void getFeatures_noRouteInfo_returnEmptyList() { mBluetoothMediaDevice1 = new BluetoothMediaDevice( - mContext, mCachedDevice1, /* MediaRoute2Info */ null); + mContext, mCachedDevice1, /* MediaRoute2Info */ null, /* item */ null); assertThat(mBluetoothMediaDevice1.getFeatures().size()).isEqualTo(0); } diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/GlobalSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/GlobalSettings.java index 1706a6f6b1d4..4125a81f9bbc 100644 --- a/packages/SettingsProvider/src/android/provider/settings/backup/GlobalSettings.java +++ b/packages/SettingsProvider/src/android/provider/settings/backup/GlobalSettings.java @@ -101,7 +101,6 @@ public class GlobalSettings { Settings.Global.Wearable.AMBIENT_TILT_TO_WAKE, Settings.Global.Wearable.AMBIENT_TOUCH_TO_WAKE, Settings.Global.Wearable.GESTURE_TOUCH_AND_HOLD_WATCH_FACE_ENABLED, - Settings.Global.Wearable.BATTERY_SAVER_MODE, Settings.Global.Wearable.WEAR_ACTIVITY_AUTO_RESUME_TIMEOUT_MS, Settings.Global.Wearable.WEAR_ACTIVITY_AUTO_RESUME_TIMEOUT_SET_BY_USER, Settings.Global.Wearable.DYNAMIC_COLOR_THEME_ENABLED, diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java index 11fa8f43290d..00fb7a1feab1 100644 --- a/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java +++ b/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java @@ -79,6 +79,8 @@ public class SystemSettings { Settings.System.SIP_CALL_OPTIONS, Settings.System.SIP_RECEIVE_CALLS, Settings.System.POINTER_SPEED, + Settings.System.POINTER_FILL_STYLE, + Settings.System.POINTER_SCALE, Settings.System.VIBRATE_ON, Settings.System.VIBRATE_WHEN_RINGING, Settings.System.RINGTONE, diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java index 011b42f451bc..4235bc4157bf 100644 --- a/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java +++ b/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java @@ -26,6 +26,10 @@ import static android.provider.settings.validators.SettingsValidators.NON_NEGATI import static android.provider.settings.validators.SettingsValidators.NON_NEGATIVE_INTEGER_VALIDATOR; import static android.provider.settings.validators.SettingsValidators.URI_VALIDATOR; import static android.provider.settings.validators.SettingsValidators.VIBRATION_INTENSITY_VALIDATOR; +import static android.view.PointerIcon.DEFAULT_POINTER_SCALE; +import static android.view.PointerIcon.LARGE_POINTER_SCALE; +import static android.view.PointerIcon.POINTER_ICON_VECTOR_STYLE_FILL_BEGIN; +import static android.view.PointerIcon.POINTER_ICON_VECTOR_STYLE_FILL_END; import android.annotation.Nullable; import android.compat.annotation.UnsupportedAppUsage; @@ -206,6 +210,11 @@ public class SystemSettingsValidators { VALIDATORS.put(System.SIP_ADDRESS_ONLY, BOOLEAN_VALIDATOR); VALIDATORS.put(System.SIP_ASK_ME_EACH_TIME, BOOLEAN_VALIDATOR); VALIDATORS.put(System.POINTER_SPEED, new InclusiveFloatRangeValidator(-7, 7)); + VALIDATORS.put(System.POINTER_FILL_STYLE, + new InclusiveIntegerRangeValidator(POINTER_ICON_VECTOR_STYLE_FILL_BEGIN, + POINTER_ICON_VECTOR_STYLE_FILL_END)); + VALIDATORS.put(System.POINTER_SCALE, + new InclusiveFloatRangeValidator(DEFAULT_POINTER_SCALE, LARGE_POINTER_SCALE)); VALIDATORS.put(System.TOUCHPAD_POINTER_SPEED, new InclusiveIntegerRangeValidator(-7, 7)); VALIDATORS.put(System.TOUCHPAD_NATURAL_SCROLLING, BOOLEAN_VALIDATOR); VALIDATORS.put(System.TOUCHPAD_TAP_TO_CLICK, BOOLEAN_VALIDATOR); diff --git a/packages/SettingsProvider/src/com/android/providers/settings/NonWritableNamespacesForBackgroundUserPrefixes.java b/packages/SettingsProvider/src/com/android/providers/settings/NonWritableNamespacesForBackgroundUserPrefixes.java new file mode 100644 index 000000000000..5c5ca46111ab --- /dev/null +++ b/packages/SettingsProvider/src/com/android/providers/settings/NonWritableNamespacesForBackgroundUserPrefixes.java @@ -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.providers.settings; + +import android.util.ArraySet; + +import java.util.Arrays; +import java.util.Set; + +/** + * Contains the list of prefixes for namespaces in which nothing can be written by background + * user. + * + * <p> + * The list in enforced is Auto devices only. To add to + * the list, create a change and tag the OWNER. In the change description, include a + * description of the flag's functionality, and a justification for why it needs to be + * denylisted. + */ +final class NonWritableNamespacesForBackgroundUserPrefixes { + public static final Set<String> DENYLIST = + new ArraySet<String>(Arrays.asList( + "game_overlay" + )); +} diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java index 70ce202c289a..384cb7ee9c49 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java @@ -2911,6 +2911,14 @@ class SettingsProtoDumpUtil { // Settings.System.NOTIFICATIONS_USE_RING_VOLUME intentionally excluded since it's deprecated. p.end(notificationToken); + final long pointerToken = p.start(SystemSettingsProto.POINTER); + dumpSetting(s, p, + Settings.System.POINTER_FILL_STYLE, + SystemSettingsProto.Pointer.POINTER_FILL_STYLE); + dumpSetting(s, p, + Settings.System.POINTER_SCALE, + SystemSettingsProto.Pointer.POINTER_SCALE); + p.end(pointerToken); dumpSetting(s, p, Settings.System.POINTER_SPEED, SystemSettingsProto.POINTER_SPEED); diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java index 15f8a7b92878..d54236e60dd7 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java @@ -2392,8 +2392,12 @@ public class SettingsProvider extends ContentProvider { == PackageManager.PERMISSION_GRANTED; boolean isRoot = Binder.getCallingUid() == Process.ROOT_UID; - if (isRoot || hasWritePermission) { + if (isRoot) { return; + } + + if (hasWritePermission) { + assertCallingUserDenyList(flags); } else if (hasAllowlistPermission) { for (String flag : flags) { boolean namespaceAllowed = false; @@ -2410,12 +2414,49 @@ public class SettingsProvider extends ContentProvider { + "'; allowlist permission granted, but must add flag to the allowlist."); } } + assertCallingUserDenyList(flags); } else { throw new SecurityException("Permission denial to mutate flag, must have root, " + "WRITE_DEVICE_CONFIG, or WRITE_ALLOWLISTED_DEVICE_CONFIG"); } } + // The check is added mainly for auto devices. On auto devices, it is possible that + // multiple users are visible simultaneously using visible background users. + // In such cases, it is desired that Non-current user (ex. visible background users) can + // only change settings for certain namespaces. + private void assertCallingUserDenyList(@NonNull Set<String> flags) { + if (!UserManager.isVisibleBackgroundUsersEnabled()) { + // enforce the deny list only on devices supporting visible background user. + return; + } + + int callingUser = UserHandle.getCallingUserId(); + final long identity = Binder.clearCallingIdentity(); + try { + int currentUser = ActivityManager.getCurrentUser(); + if (callingUser == currentUser) { + // enforce the deny list only if the caller is not current user. Currently only auto + // uses background visible user, and auto doesn't support profiles so profiles of + // current users is not checked here. + return; + } + } finally { + Binder.restoreCallingIdentity(identity); + } + + for (String flag : flags) { + for (String denylistedPrefix : + NonWritableNamespacesForBackgroundUserPrefixes.DENYLIST) { + if (flag.startsWith(denylistedPrefix)) { + throw new SecurityException("Permission denial for flag '" + flag + + "' for background user " + callingUser + ". Namespace is added to " + + "denylist."); + } + } + } + } + private static void warnOrThrowForUndesiredSecureSettingsMutationForTargetSdk( int targetSdkVersion, String name) { // If the app targets Lollipop MR1 or older SDK we warn, otherwise crash. diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java index 4ec170dda6f3..c6ae96ed193d 100644 --- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java +++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java @@ -595,6 +595,7 @@ public class SettingsBackupTest { Settings.Global.Wearable.MOBILE_SIGNAL_DETECTOR, Settings.Global.Wearable.AMBIENT_LOW_BIT_ENABLED_DEV, Settings.Global.Wearable.AMBIENT_TILT_TO_BRIGHT, + Settings.Global.Wearable.BATTERY_SAVER_MODE, Settings.Global.Wearable.DECOMPOSABLE_WATCHFACE, Settings.Global.Wearable.AMBIENT_FORCE_WHEN_DOCKED, Settings.Global.Wearable.AMBIENT_LOW_BIT_ENABLED, diff --git a/packages/SoundPicker/res/values-vi/strings.xml b/packages/SoundPicker/res/values-vi/strings.xml index bed0e9687271..b6f8793cf5d0 100644 --- a/packages/SoundPicker/res/values-vi/strings.xml +++ b/packages/SoundPicker/res/values-vi/strings.xml @@ -20,7 +20,7 @@ <string name="notification_sound_default" msgid="8133121186242636840">"Âm thanh thông báo mặc định"</string> <string name="alarm_sound_default" msgid="4787646764557462649">"Âm thanh chuông báo mặc định"</string> <string name="add_ringtone_text" msgid="6642389991738337529">"Thêm nhạc chuông"</string> - <string name="add_alarm_text" msgid="3545497316166999225">"Thêm báo thức"</string> + <string name="add_alarm_text" msgid="3545497316166999225">"Thêm chuông báo"</string> <string name="add_notification_text" msgid="4431129543300614788">"Thêm thông báo"</string> <string name="delete_ringtone_text" msgid="201443984070732499">"Xóa"</string> <string name="unable_to_add_ringtone" msgid="4583511263449467326">"Không thể thêm nhạc chuông tùy chỉnh"</string> diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp index 07a00fb7e8e3..58c39b477a82 100644 --- a/packages/SystemUI/Android.bp +++ b/packages/SystemUI/Android.bp @@ -78,10 +78,50 @@ filegroup { visibility: ["//visibility:private"], } +// Tests where robolectric conversion caused errors in SystemUITests at runtime +filegroup { + name: "SystemUI-tests-broken-robofiles-sysui-run", + srcs: [ + "tests/src/**/systemui/broadcast/BroadcastDispatcherTest.kt", + "tests/src/**/systemui/broadcast/ActionReceiverTest.kt", + "tests/src/**/systemui/doze/DozeMachineTest.java", + "tests/src/**/systemui/globalactions/GlobalActionsDialogLiteTest.java", + "tests/src/**/systemui/globalactions/GlobalActionsImeTest.java", + "tests/src/**/systemui/keyguard/data/repository/KeyguardTransitionRepositoryTest.kt", + "tests/src/**/systemui/media/controls/domain/pipeline/LegacyMediaDataManagerImplTest.kt", + "tests/src/**/systemui/media/controls/domain/pipeline/MediaDataProcessorTest.kt", + "tests/src/**/systemui/media/dialog/MediaOutputAdapterTest.java", + "tests/src/**/systemui/media/dialog/MediaOutputBaseDialogTest.java", + "tests/src/**/systemui/media/dialog/MediaOutputBroadcastDialogTest.java", + "tests/src/**/systemui/media/dialog/MediaOutputDialogTest.java", + "tests/src/**/systemui/mediaprojection/permission/MediaProjectionPermissionDialogDelegateTest.kt", + ], +} + // Tests where robolectric failed at runtime. (go/multivalent-tests) filegroup { name: "SystemUI-tests-broken-robofiles-run", srcs: [ + "tests/src/**/systemui/accessibility/AccessibilityButtonModeObserverTest.java", + "tests/src/**/systemui/accessibility/AccessibilityButtonTargetsObserverTest.java", + "tests/src/**/systemui/accessibility/FullscreenMagnificationControllerTest.java", + "tests/src/**/systemui/accessibility/WindowMagnificationAnimationControllerTest.java", + "tests/src/**/systemui/animation/FontInterpolatorTest.kt", + "tests/src/**/systemui/animation/TextAnimatorTest.kt", + "tests/src/**/systemui/animation/TextInterpolatorTest.kt", + "tests/src/**/systemui/animation/ActivityTransitionAnimatorTest.kt", + "tests/src/**/systemui/animation/AnimatorTestRuleOrderTest.kt", + "tests/src/**/systemui/animation/DialogTransitionAnimatorTest.kt", + "tests/src/**/systemui/broadcast/ActionReceiverTest.kt", + "tests/src/**/systemui/broadcast/BroadcastDispatcherTest.kt", + "tests/src/**/systemui/compose/ComposeInitializerTest.kt", + "tests/src/**/systemui/controls/ui/ControlsActivityTest.kt", + "tests/src/**/systemui/controls/management/ControlsEditingActivityTest.kt", + "tests/src/**/systemui/controls/management/ControlsRequestDialogTest.kt", + "tests/src/**/systemui/controls/ui/DetailDialogTest.kt", + "tests/src/**/systemui/doze/DozeMachineTest.kt", + "tests/src/**/systemui/fontscaling/FontScalingDialogDelegateTest.kt", + "tests/src/**/systemui/keyguard/CustomizationProviderTest.kt", "tests/src/**/systemui/globalactions/GlobalActionsColumnLayoutTest.java", "tests/src/**/systemui/globalactions/GlobalActionsDialogLiteTest.java", "tests/src/**/systemui/globalactions/GlobalActionsImeTest.java", @@ -176,9 +216,7 @@ filegroup { ], } -// We are running robolectric tests in the tests directory as well as -// multivalent tests. If you add a test, and it doesn't run in robolectric, -// it should be added to this exclusion list. go/multivalent-tests +// Tests where robolectric failed at compile time. (go/multivalent-tests) filegroup { name: "SystemUI-tests-broken-robofiles-compile", srcs: [ @@ -330,6 +368,7 @@ filegroup { "tests/src/**/systemui/shared/system/RemoteTransitionTest.java", "tests/src/**/systemui/navigationbar/NavigationBarControllerImplTest.java", "tests/src/**/systemui/bluetooth/qsdialog/AudioSharingInteractorTest.kt", + "tests/src/**/systemui/bluetooth/qsdialog/DeviceItemActionInteractorTest.kt", "tests/src/**/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfigTest.kt", "tests/src/**/systemui/notetask/LaunchNotesRoleSettingsTrampolineActivityTest.kt", "tests/src/**/systemui/notetask/shortcut/LaunchNoteTaskActivityTest.kt", @@ -483,6 +522,7 @@ android_library { "androidx.compose.material_material-icons-extended", "androidx.activity_activity-compose", "androidx.compose.animation_animation-graphics", + "androidx.lifecycle_lifecycle-viewmodel-compose", "device_policy_aconfig_flags_lib", ], libs: [ @@ -644,6 +684,7 @@ android_library { "androidx.compose.material_material-icons-extended", "androidx.activity_activity-compose", "androidx.compose.animation_animation-graphics", + "androidx.lifecycle_lifecycle-viewmodel-compose", "TraceurCommon", ], } @@ -744,7 +785,6 @@ android_app { kotlincflags: ["-Xjvm-default=all"], optimize: { shrink_resources: false, - optimized_shrink_resources: false, proguard_flags_files: ["proguard.flags"], }, @@ -809,6 +849,7 @@ android_robolectric_test { exclude_srcs: [ ":SystemUI-tests-broken-robofiles-compile", ":SystemUI-tests-broken-robofiles-run", + ":SystemUI-tests-broken-robofiles-sysui-run", ], static_libs: [ "RoboTestLibraries", @@ -880,7 +921,6 @@ systemui_optimized_java_defaults { optimize: true, shrink: true, shrink_resources: true, - optimized_shrink_resources: true, ignore_warnings: false, proguard_compatibility: false, }, diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml index 9c58371a387d..8a99263637af 100644 --- a/packages/SystemUI/AndroidManifest.xml +++ b/packages/SystemUI/AndroidManifest.xml @@ -387,7 +387,7 @@ android:killAfterRestore="false" android:hardwareAccelerated="true" android:label="@string/app_label" - android:icon="@drawable/android14_patch_adaptive" + android:icon="@drawable/android15_patch_adaptive" android:process="com.android.systemui" android:supportsRtl="true" android:theme="@style/Theme.SystemUI" @@ -475,6 +475,15 @@ android:exported="false" android:noHistory="true" /> + <activity android:name=".touchpad.tutorial.ui.view.TouchpadTutorialActivity" + android:exported="true" + android:theme="@style/Theme.AppCompat.NoActionBar"> + <intent-filter> + <action android:name="com.android.systemui.action.TOUCHPAD_TUTORIAL"/> + <category android:name="android.intent.category.DEFAULT"/> + </intent-filter> + </activity> + <service android:name=".screenshot.appclips.AppClipsScreenshotHelperService" android:exported="false" android:singleUser="true" @@ -641,6 +650,7 @@ <!-- started from MediaProjectionManager --> <activity android:name=".mediaprojection.permission.MediaProjectionPermissionActivity" + android:showForAllUsers="true" android:exported="true" android:theme="@style/Theme.SystemUI.MediaProjectionAlertDialog" android:finishOnCloseSystemDialogs="true" @@ -651,6 +661,7 @@ <activity android:name=".mediaprojection.appselector.MediaProjectionAppSelectorActivity" android:theme="@style/Theme.SystemUI.MediaProjectionAppSelector" + android:showForAllUsers="true" android:finishOnCloseSystemDialogs="true" android:excludeFromRecents="true" android:documentLaunchMode="never" diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/values-pt-rBR/strings.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/values-pt-rBR/strings.xml index 160d310f5f7d..f12278a15a8b 100644 --- a/packages/SystemUI/accessibility/accessibilitymenu/res/values-pt-rBR/strings.xml +++ b/packages/SystemUI/accessibility/accessibilitymenu/res/values-pt-rBR/strings.xml @@ -2,7 +2,7 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="accessibility_menu_service_name" msgid="730136711554740131">"Menu de acessibilidade"</string> - <string name="accessibility_menu_intro" msgid="3164193281544042394">"\"Acessibilidade\" é um menu grande mostrado na tela para controlar seu dispositivo. Você pode bloquear o dispositivo, controlar o volume e o brilho, fazer capturas de tela e muito mais."</string> + <string name="accessibility_menu_intro" msgid="3164193281544042394">"Com este menu de tamanho grande na tela, você consegue bloquear o dispositivo, ajustar o volume e o brilho, fazer capturas de tela, entre outras funções de controle do aparelho."</string> <string name="assistant_label" msgid="6796392082252272356">"Assistente"</string> <string name="assistant_utterance" msgid="65509599221141377">"Google Assistente"</string> <string name="a11y_settings_label" msgid="3977714687248445050">"Configurações de acessibilidade"</string> diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/values-pt/strings.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/values-pt/strings.xml index 160d310f5f7d..f12278a15a8b 100644 --- a/packages/SystemUI/accessibility/accessibilitymenu/res/values-pt/strings.xml +++ b/packages/SystemUI/accessibility/accessibilitymenu/res/values-pt/strings.xml @@ -2,7 +2,7 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="accessibility_menu_service_name" msgid="730136711554740131">"Menu de acessibilidade"</string> - <string name="accessibility_menu_intro" msgid="3164193281544042394">"\"Acessibilidade\" é um menu grande mostrado na tela para controlar seu dispositivo. Você pode bloquear o dispositivo, controlar o volume e o brilho, fazer capturas de tela e muito mais."</string> + <string name="accessibility_menu_intro" msgid="3164193281544042394">"Com este menu de tamanho grande na tela, você consegue bloquear o dispositivo, ajustar o volume e o brilho, fazer capturas de tela, entre outras funções de controle do aparelho."</string> <string name="assistant_label" msgid="6796392082252272356">"Assistente"</string> <string name="assistant_utterance" msgid="65509599221141377">"Google Assistente"</string> <string name="a11y_settings_label" msgid="3977714687248445050">"Configurações de acessibilidade"</string> diff --git a/packages/SystemUI/aconfig/communal.aconfig b/packages/SystemUI/aconfig/communal.aconfig index 2e9af7e3a763..afcd8a9624c8 100644 --- a/packages/SystemUI/aconfig/communal.aconfig +++ b/packages/SystemUI/aconfig/communal.aconfig @@ -7,3 +7,13 @@ flag { description: "Enables the communal hub experience" bug: "304584416" } + +flag { + name: "enable_widget_picker_size_filter" + namespace: "communal" + description: "Enables passing a size filter to the widget picker" + bug: "345482907" + metadata { + purpose: PURPOSE_BUGFIX + } +} diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig index 2cb297ad1359..1cbf67ee0d0e 100644 --- a/packages/SystemUI/aconfig/systemui.aconfig +++ b/packages/SystemUI/aconfig/systemui.aconfig @@ -804,6 +804,13 @@ flag { } flag { + name: "brightness_slider_focus_state" + namespace: "systemui" + description: "enables new focus outline for the brightness slider when focused on with physical keyboard" + bug: "329244723" +} + +flag { name: "edgeback_gesture_handler_get_running_tasks_background" namespace: "systemui" description: "Decide whether to get the running tasks from activity manager in EdgebackGestureHandler" @@ -974,6 +981,16 @@ flag { } flag { + name: "media_controls_lockscreen_shade_bug_fix" + namespace: "systemui" + description: "Use ShadeInteractor for media location changes" + bug: "319244625" + metadata { + purpose: PURPOSE_BUGFIX + } +} + +flag { namespace: "systemui" name: "enable_view_capture_tracing" description: "Enables view capture tracing in System UI." @@ -984,6 +1001,16 @@ flag { } flag { + namespace: "systemui" + name: "privacy_dot_unfold_wrong_corner_fix" + description: "Fixes an issue where the privacy dot is at the wrong corner after unfolding/folding." + bug: "339335643" + metadata { + purpose: PURPOSE_BUGFIX + } +} + +flag { name: "validate_keyboard_shortcut_helper_icon_uri" namespace: "systemui" description: "Adds a check that the caller can access the content URI of an icon in the shortcut helper." @@ -1021,6 +1048,7 @@ flag { bug: "343505271" } + flag { name: "new_touchpad_gestures_tutorial" namespace: "systemui" @@ -1039,6 +1067,16 @@ flag { } flag { + name: "enable_efficient_display_repository" + namespace: "systemui" + description: "Decide whether to use the new implementation of DisplayRepository that minimizes binder calls and background lock contention." + bug: "345472038" + metadata { + purpose: PURPOSE_BUGFIX + } +} + +flag { name: "notification_media_manager_background_execution" namespace: "systemui" description: "Decide whether to execute binder calls in background thread" @@ -1047,3 +1085,23 @@ flag { purpose: PURPOSE_BUGFIX } } + +flag { + name: "dozeui_scheduling_alarms_background_execution" + namespace: "systemui" + description: "Decide whether to execute binder calls to schedule alarms in background thread" + bug: "330492575" + metadata { + purpose: PURPOSE_BUGFIX + } +} + +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 + } +} diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PinBouncer.kt b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PinBouncer.kt index 64ace2f18372..54f396989161 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PinBouncer.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PinBouncer.kt @@ -24,6 +24,7 @@ import androidx.compose.animation.core.AnimationVector1D import androidx.compose.animation.core.animateDpAsState import androidx.compose.animation.core.animateFloatAsState import androidx.compose.animation.core.tween +import androidx.compose.foundation.focusable import androidx.compose.foundation.gestures.detectTapGestures import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.aspectRatio @@ -41,6 +42,8 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.drawBehind +import androidx.compose.ui.focus.FocusRequester +import androidx.compose.ui.focus.focusRequester import androidx.compose.ui.geometry.CornerRadius import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.graphicsLayer @@ -284,6 +287,8 @@ private fun PinPadButton( contentAlignment = Alignment.Center, modifier = modifier + .focusRequester(FocusRequester.Default) + .focusable() .sizeIn(maxWidth = pinButtonMaxSize, maxHeight = pinButtonMaxSize) .aspectRatio(1f) .drawBehind { 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 c32938497147..c4659cfc146b 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 @@ -1,6 +1,6 @@ package com.android.systemui.communal.ui.compose -import androidx.compose.animation.core.LinearEasing +import androidx.compose.animation.core.CubicBezierEasing import androidx.compose.animation.core.RepeatMode import androidx.compose.animation.core.animateFloat import androidx.compose.animation.core.infiniteRepeatable @@ -20,20 +20,18 @@ import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.runtime.Composable import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope -import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.composed import androidx.compose.ui.draw.alpha +import androidx.compose.ui.draw.drawBehind import androidx.compose.ui.geometry.Offset +import androidx.compose.ui.graphics.BlendMode import androidx.compose.ui.graphics.Brush import androidx.compose.ui.graphics.Color -import androidx.compose.ui.layout.onGloballyPositioned +import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.res.dimensionResource -import androidx.compose.ui.unit.IntSize import androidx.compose.ui.unit.dp import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.android.compose.animation.scene.CommunalSwipeDetector @@ -57,6 +55,7 @@ import com.android.systemui.Flags.glanceableHubFullscreenSwipe import com.android.systemui.communal.shared.model.CommunalBackgroundType import com.android.systemui.communal.shared.model.CommunalScenes import com.android.systemui.communal.shared.model.CommunalTransitionKeys +import com.android.systemui.communal.ui.compose.Dimensions.SlideOffsetY import com.android.systemui.communal.ui.compose.extensions.allowGestures import com.android.systemui.communal.ui.viewmodel.CommunalViewModel import com.android.systemui.communal.util.CommunalColors @@ -70,6 +69,7 @@ object Communal { val Grid = ElementKey("CommunalContent") val LockIcon = ElementKey("CommunalLockIcon") val IndicationArea = ElementKey("CommunalIndicationArea") + val StatusBar = ElementKey("StatusBar") } } @@ -77,6 +77,15 @@ object AllElements : ElementMatcher { override fun matches(key: ElementKey, scene: SceneKey) = true } +private object TransitionDuration { + const val BETWEEN_HUB_AND_EDIT_MODE_MS = 1000 + const val EDIT_MODE_TO_HUB_CONTENT_MS = 167 + const val EDIT_MODE_TO_HUB_GRID_DELAY_MS = 167 + const val EDIT_MODE_TO_HUB_GRID_END_MS = + EDIT_MODE_TO_HUB_GRID_DELAY_MS + EDIT_MODE_TO_HUB_CONTENT_MS + const val HUB_TO_EDIT_MODE_CONTENT_MS = 250 +} + val sceneTransitions = transitions { to(CommunalScenes.Communal, key = CommunalTransitionKeys.SimpleFade) { spec = tween(durationMillis = 250) @@ -94,9 +103,34 @@ val sceneTransitions = transitions { fade(Communal.Elements.Grid) fade(Communal.Elements.IndicationArea) fade(Communal.Elements.LockIcon) + fade(Communal.Elements.StatusBar) } timestampRange(startMillis = 167, endMillis = 334) { fade(Communal.Elements.Scrim) } } + to(CommunalScenes.Blank, key = CommunalTransitionKeys.ToEditMode) { + spec = tween(durationMillis = TransitionDuration.BETWEEN_HUB_AND_EDIT_MODE_MS) + timestampRange(endMillis = TransitionDuration.HUB_TO_EDIT_MODE_CONTENT_MS) { + fade(Communal.Elements.Grid) + fade(Communal.Elements.IndicationArea) + fade(Communal.Elements.LockIcon) + } + fade(Communal.Elements.Scrim) + } + to(CommunalScenes.Communal, key = CommunalTransitionKeys.FromEditMode) { + spec = tween(durationMillis = TransitionDuration.BETWEEN_HUB_AND_EDIT_MODE_MS) + translate(Communal.Elements.Grid, y = SlideOffsetY) + timestampRange(endMillis = TransitionDuration.EDIT_MODE_TO_HUB_CONTENT_MS) { + fade(Communal.Elements.IndicationArea) + fade(Communal.Elements.LockIcon) + fade(Communal.Elements.Scrim) + } + timestampRange( + startMillis = TransitionDuration.EDIT_MODE_TO_HUB_GRID_DELAY_MS, + endMillis = TransitionDuration.EDIT_MODE_TO_HUB_GRID_END_MS + ) { + fade(Communal.Elements.Grid) + } + } } /** @@ -217,6 +251,7 @@ private fun SceneScope.CommunalScene( CommunalBackgroundType.DEFAULT -> DefaultBackground(colors = colors) CommunalBackgroundType.STATIC_GRADIENT -> StaticLinearGradient() CommunalBackgroundType.ANIMATED -> AnimatedLinearGradient() + CommunalBackgroundType.NONE -> BackgroundTopScrim() } } with(content) { Content(modifier = modifier) } @@ -252,7 +287,11 @@ private fun BoxScope.AnimatedLinearGradient() { val colors = LocalAndroidColorScheme.current Box( Modifier.matchParentSize() - .animatedGradientBackground(colors = listOf(colors.primary, colors.primaryContainer)) + .background(colors.primary) + .animatedRadialGradientBackground( + toColor = colors.primary, + fromColor = colors.primaryContainer.copy(alpha = 0.6f) + ) ) BackgroundTopScrim() } @@ -265,29 +304,76 @@ private fun BoxScope.BackgroundTopScrim() { Box(Modifier.matchParentSize().alpha(0.34f).background(scrimOnTopColor)) } -/** Modifier which sets the background of a composable to an animated gradient */ +/** The duration to use for the gradient background animation. */ +private const val ANIMATION_DURATION_MS = 10_000 + +/** The offset to use in order to place the center of each gradient offscreen. */ +private val ANIMATION_OFFSCREEN_OFFSET = 128.dp + +/** Modifier which creates two radial gradients that animate up and down. */ @Composable -private fun Modifier.animatedGradientBackground(colors: List<Color>): Modifier = composed { - var size by remember { mutableStateOf(IntSize.Zero) } - val transition = rememberInfiniteTransition(label = "scrim background") - val startOffsetX by - transition.animateFloat( - initialValue = -size.width.toFloat(), - targetValue = size.width.toFloat(), +fun Modifier.animatedRadialGradientBackground(toColor: Color, fromColor: Color): Modifier { + val density = LocalDensity.current + val infiniteTransition = rememberInfiniteTransition(label = "radial gradient transition") + val centerFraction by + infiniteTransition.animateFloat( + initialValue = 0f, + targetValue = 1f, animationSpec = infiniteRepeatable( - animation = tween(durationMillis = 5_000, easing = LinearEasing), - repeatMode = RepeatMode.Reverse, + animation = + tween( + durationMillis = ANIMATION_DURATION_MS, + easing = CubicBezierEasing(0.33f, 0f, 0.67f, 1f), + ), + repeatMode = RepeatMode.Reverse ), - label = "scrim start offset" + label = "radial gradient center fraction" ) - background( + + // Offset to place the center of the gradients offscreen. This is applied to both the + // x and y coordinates. + val offsetPx = remember(density) { with(density) { ANIMATION_OFFSCREEN_OFFSET.toPx() } } + + return drawBehind { + val gradientRadius = (size.width / 2) + offsetPx + val totalHeight = size.height + 2 * offsetPx + + val leftCenter = + Offset( + x = -offsetPx, + y = totalHeight * centerFraction - offsetPx, + ) + val rightCenter = + Offset( + x = offsetPx + size.width, + y = totalHeight * (1f - centerFraction) - offsetPx, + ) + + // Right gradient + drawCircle( brush = - Brush.linearGradient( - colors = colors, - start = Offset(startOffsetX, 0f), - end = Offset(startOffsetX + size.width.toFloat(), size.height.toFloat()), - ) + Brush.radialGradient( + colors = listOf(fromColor, toColor), + center = rightCenter, + radius = gradientRadius + ), + center = rightCenter, + radius = gradientRadius, + blendMode = BlendMode.SrcAtop, ) - .onGloballyPositioned { size = it.size } + + // Left gradient + drawCircle( + brush = + Brush.radialGradient( + colors = listOf(fromColor, toColor), + center = leftCenter, + radius = gradientRadius + ), + center = leftCenter, + radius = gradientRadius, + blendMode = BlendMode.SrcAtop, + ) + } } 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 776651558e48..18085ab29135 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 @@ -16,13 +16,17 @@ package com.android.systemui.communal.ui.compose +import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.layout.Layout import androidx.compose.ui.unit.IntRect import com.android.compose.animation.scene.SceneScope import com.android.compose.theme.LocalAndroidColorScheme +import com.android.systemui.communal.smartspace.SmartspaceInteractionHandler +import com.android.systemui.communal.ui.compose.section.AmbientStatusBarSection import com.android.systemui.communal.ui.viewmodel.CommunalViewModel import com.android.systemui.keyguard.ui.composable.blueprint.BlueprintAlignmentLines import com.android.systemui.keyguard.ui.composable.section.LockSection @@ -34,20 +38,27 @@ class CommunalContent @Inject constructor( private val viewModel: CommunalViewModel, + private val interactionHandler: SmartspaceInteractionHandler, private val dialogFactory: SystemUIDialogFactory, private val lockSection: LockSection, + private val ambientStatusBarSection: AmbientStatusBarSection, ) { - @Composable fun SceneScope.Content(modifier: Modifier = Modifier) { Layout( modifier = modifier.fillMaxSize(), content = { - CommunalHub( - viewModel = viewModel, - dialogFactory = dialogFactory, - modifier = Modifier.element(Communal.Elements.Grid) - ) + Box(modifier = Modifier.fillMaxSize()) { + with(ambientStatusBarSection) { + AmbientStatusBar(modifier = Modifier.fillMaxWidth()) + } + CommunalHub( + viewModel = viewModel, + interactionHandler = interactionHandler, + dialogFactory = dialogFactory, + modifier = Modifier.element(Communal.Elements.Grid) + ) + } with(lockSection) { LockIcon( overrideColor = LocalAndroidColorScheme.current.onPrimaryContainer, 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 1f7f07bb072d..927890e9d6af 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 @@ -16,17 +16,16 @@ package com.android.systemui.communal.ui.compose -import android.appwidget.AppWidgetHostView import android.graphics.drawable.Icon import android.os.Bundle import android.util.SizeF import android.view.View.IMPORTANT_FOR_ACCESSIBILITY_AUTO import android.view.View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS import android.widget.FrameLayout +import android.widget.RemoteViews import androidx.annotation.VisibleForTesting import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.AnimatedVisibilityScope -import androidx.compose.animation.ExperimentalAnimationApi import androidx.compose.animation.core.LinearEasing import androidx.compose.animation.core.Spring import androidx.compose.animation.core.animateFloatAsState @@ -34,6 +33,8 @@ import androidx.compose.animation.core.spring import androidx.compose.animation.core.tween import androidx.compose.animation.fadeIn import androidx.compose.animation.fadeOut +import androidx.compose.animation.slideInVertically +import androidx.compose.animation.slideOutVertically import androidx.compose.foundation.BorderStroke import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.Image @@ -46,6 +47,7 @@ import androidx.compose.foundation.layout.BoxScope import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.RowScope import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth @@ -128,6 +130,7 @@ import androidx.compose.ui.viewinterop.AndroidView import androidx.compose.ui.window.Popup import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.window.layout.WindowMetricsCalculator +import com.android.compose.animation.Easings.Emphasized import com.android.compose.modifiers.thenIf import com.android.compose.theme.LocalAndroidColorScheme import com.android.compose.ui.graphics.painter.rememberDrawablePainter @@ -144,6 +147,7 @@ import com.android.systemui.communal.ui.viewmodel.BaseCommunalViewModel import com.android.systemui.communal.ui.viewmodel.CommunalEditModeViewModel import com.android.systemui.communal.ui.viewmodel.CommunalViewModel import com.android.systemui.communal.ui.viewmodel.PopupType +import com.android.systemui.communal.widgets.SmartspaceAppWidgetHostView import com.android.systemui.communal.widgets.WidgetConfigurator import com.android.systemui.res.R import com.android.systemui.statusbar.phone.SystemUIDialogFactory @@ -154,6 +158,7 @@ import kotlinx.coroutines.launch fun CommunalHub( modifier: Modifier = Modifier, viewModel: BaseCommunalViewModel, + interactionHandler: RemoteViews.InteractionHandler? = null, dialogFactory: SystemUIDialogFactory? = null, widgetConfigurator: WidgetConfigurator? = null, onOpenWidgetPicker: (() -> Unit)? = null, @@ -173,6 +178,10 @@ fun CommunalHub( derivedStateOf { selectedKey.value != null || reorderingWidgets } } val isEmptyState by viewModel.isEmptyState.collectAsStateWithLifecycle(initialValue = false) + val isCommunalContentVisible by + viewModel.isCommunalContentVisible.collectAsStateWithLifecycle( + initialValue = !viewModel.isEditMode + ) val contentPadding = gridContentPadding(viewModel.isEditMode, toolbarSize) val contentOffset = beforeContentPadding(contentPadding).toOffset() @@ -245,46 +254,88 @@ fun CommunalHub( viewModel = viewModel, ) } else { - CommunalHubLazyGrid( - communalContent = communalContent, - viewModel = viewModel, - contentPadding = contentPadding, - contentOffset = contentOffset, - setGridCoordinates = { gridCoordinates = it }, - updateDragPositionForRemove = { offset -> - isPointerWithinEnabledRemoveButton( - removeEnabled = removeButtonEnabled, - offset = gridCoordinates?.let { it.positionInWindow() + offset }, - containerToCheck = removeButtonCoordinates + val slideOffsetInPx = + with(LocalDensity.current) { Dimensions.SlideOffsetY.toPx().toInt() } + AnimatedVisibility( + visible = isCommunalContentVisible, + enter = + fadeIn( + animationSpec = + tween(durationMillis = 83, delayMillis = 83, easing = LinearEasing) + ) + + slideInVertically( + animationSpec = tween(durationMillis = 1000, easing = Emphasized), + initialOffsetY = { -slideOffsetInPx } + ), + exit = + fadeOut( + animationSpec = tween(durationMillis = 167, easing = LinearEasing) + ) + + slideOutVertically( + animationSpec = tween(durationMillis = 1000, easing = Emphasized), + targetOffsetY = { -slideOffsetInPx } + ), + modifier = Modifier.fillMaxSize(), + ) { + Box { + CommunalHubLazyGrid( + communalContent = communalContent, + viewModel = viewModel, + contentPadding = contentPadding, + contentOffset = contentOffset, + setGridCoordinates = { gridCoordinates = it }, + updateDragPositionForRemove = { offset -> + isPointerWithinEnabledRemoveButton( + removeEnabled = removeButtonEnabled, + offset = + gridCoordinates?.let { it.positionInWindow() + offset }, + containerToCheck = removeButtonCoordinates + ) + }, + gridState = gridState, + contentListState = contentListState, + selectedKey = selectedKey, + widgetConfigurator = widgetConfigurator, + interactionHandler = interactionHandler, ) - }, - gridState = gridState, - contentListState = contentListState, - selectedKey = selectedKey, - widgetConfigurator = widgetConfigurator, - ) + } + } } } - if (viewModel.isEditMode && onOpenWidgetPicker != null && onEditDone != null) { - Toolbar( - setToolbarSize = { toolbarSize = it }, - setRemoveButtonCoordinates = { removeButtonCoordinates = it }, - onEditDone = onEditDone, - onOpenWidgetPicker = onOpenWidgetPicker, - onRemoveClicked = { - val index = - selectedKey.value?.let { key -> - contentListState.list.indexOfFirst { it.key == key } + if (onOpenWidgetPicker != null && onEditDone != null) { + AnimatedVisibility( + visible = viewModel.isEditMode && isCommunalContentVisible, + enter = + fadeIn(animationSpec = tween(durationMillis = 250, easing = LinearEasing)) + + slideInVertically( + animationSpec = tween(durationMillis = 1000, easing = Emphasized), + ), + exit = + fadeOut(animationSpec = tween(durationMillis = 167, easing = LinearEasing)) + + slideOutVertically( + animationSpec = tween(durationMillis = 1000, easing = Emphasized) + ), + ) { + Toolbar( + setToolbarSize = { toolbarSize = it }, + setRemoveButtonCoordinates = { removeButtonCoordinates = it }, + onEditDone = onEditDone, + onOpenWidgetPicker = onOpenWidgetPicker, + onRemoveClicked = { + val index = + selectedKey.value?.let { key -> + contentListState.list.indexOfFirst { it.key == key } + } + index?.let { + contentListState.onRemove(it) + contentListState.onSaveList() + viewModel.setSelectedKey(null) } - index?.let { - contentListState.onRemove(it) - contentListState.onSaveList() - viewModel.setSelectedKey(null) - } - }, - removeEnabled = removeButtonEnabled - ) + }, + removeEnabled = removeButtonEnabled + ) + } } if (currentPopup == PopupType.CtaTile) { PopupOnDismissCtaTile(viewModel::onHidePopup) @@ -391,6 +442,7 @@ private fun BoxScope.CommunalHubLazyGrid( setGridCoordinates: (coordinates: LayoutCoordinates) -> Unit, updateDragPositionForRemove: (offset: Offset) -> Boolean, widgetConfigurator: WidgetConfigurator?, + interactionHandler: RemoteViews.InteractionHandler?, ) { var gridModifier = Modifier.align(Alignment.TopStart).onGloballyPositioned { setGridCoordinates(it) } @@ -468,7 +520,8 @@ private fun BoxScope.CommunalHubLazyGrid( selected = selected && !isDragging, widgetConfigurator = widgetConfigurator, index = index, - contentListState = contentListState + contentListState = contentListState, + interactionHandler = interactionHandler, ) } } else { @@ -479,7 +532,8 @@ private fun BoxScope.CommunalHubLazyGrid( size = size, selected = false, index = index, - contentListState = contentListState + contentListState = contentListState, + interactionHandler = interactionHandler, ) } } @@ -578,24 +632,23 @@ private fun Toolbar( ) .onSizeChanged { setToolbarSize(it) }, ) { - val spacerModifier = Modifier.width(ButtonDefaults.IconSpacing) - - if (!removeEnabled) { - Button( - modifier = Modifier.align(Alignment.CenterStart), - onClick = onOpenWidgetPicker, - colors = filledButtonColors(), - contentPadding = Dimensions.ButtonPadding - ) { - Icon(Icons.Default.Add, stringResource(R.string.hub_mode_add_widget_button_text)) - Spacer(spacerModifier) - Text( - text = stringResource(R.string.hub_mode_add_widget_button_text), - ) - } + ToolbarButton( + isPrimary = !removeEnabled, + modifier = Modifier.align(Alignment.CenterStart), + onClick = onOpenWidgetPicker, + ) { + Icon(Icons.Default.Add, stringResource(R.string.hub_mode_add_widget_button_text)) + Text( + text = stringResource(R.string.hub_mode_add_widget_button_text), + ) } - if (removeEnabled) { + AnimatedVisibility( + modifier = Modifier.align(Alignment.Center), + visible = removeEnabled, + enter = fadeIn(), + exit = fadeOut() + ) { Button( onClick = onRemoveClicked, colors = filledButtonColors(), @@ -603,33 +656,97 @@ private fun Toolbar( modifier = Modifier.graphicsLayer { alpha = removeButtonAlpha } .onGloballyPositioned { setRemoveButtonCoordinates(it) } - .align(Alignment.Center) ) { - RemoveButtonContent(spacerModifier) + Row( + horizontalArrangement = + Arrangement.spacedBy( + ButtonDefaults.IconSpacing, + Alignment.CenterHorizontally + ), + verticalAlignment = Alignment.CenterVertically + ) { + Icon(Icons.Default.Close, stringResource(R.string.button_to_remove_widget)) + Text( + text = stringResource(R.string.button_to_remove_widget), + ) + } } } - if (!removeEnabled) { - Button( - modifier = Modifier.align(Alignment.CenterEnd), - onClick = onEditDone, - colors = filledButtonColors(), - contentPadding = Dimensions.ButtonPadding + ToolbarButton( + isPrimary = !removeEnabled, + modifier = Modifier.align(Alignment.CenterEnd), + onClick = onEditDone, + ) { + Icon( + Icons.Default.Check, + stringResource(id = R.string.hub_mode_editing_exit_button_text) + ) + Text( + text = stringResource(R.string.hub_mode_editing_exit_button_text), + ) + } + } +} + +/** + * Toolbar button that displays as a filled button if primary, and an outline button if secondary. + */ +@Composable +private fun ToolbarButton( + isPrimary: Boolean = true, + onClick: () -> Unit, + modifier: Modifier = Modifier, + content: @Composable RowScope.() -> Unit +) { + val colors = LocalAndroidColorScheme.current + AnimatedVisibility( + visible = isPrimary, + modifier = modifier, + enter = fadeIn(), + exit = fadeOut() + ) { + Button( + onClick = onClick, + colors = filledButtonColors(), + contentPadding = Dimensions.ButtonPadding, + ) { + Row( + horizontalArrangement = + Arrangement.spacedBy(ButtonDefaults.IconSpacing, Alignment.CenterHorizontally), + verticalAlignment = Alignment.CenterVertically ) { - Icon( - Icons.Default.Check, - stringResource(id = R.string.hub_mode_editing_exit_button_text) - ) - Spacer(spacerModifier) - Text( - text = stringResource(R.string.hub_mode_editing_exit_button_text), - ) + content() + } + } + } + + AnimatedVisibility( + visible = !isPrimary, + modifier = modifier, + enter = fadeIn(), + exit = fadeOut() + ) { + OutlinedButton( + onClick = onClick, + colors = + ButtonDefaults.outlinedButtonColors( + contentColor = colors.primary, + ), + border = BorderStroke(width = 2.0.dp, color = colors.primary), + contentPadding = Dimensions.ButtonPadding, + ) { + Row( + horizontalArrangement = + Arrangement.spacedBy(ButtonDefaults.IconSpacing, Alignment.CenterHorizontally), + verticalAlignment = Alignment.CenterVertically + ) { + content() } } } } -@OptIn(ExperimentalAnimationApi::class) @Composable private fun AnimatedVisibilityScope.ButtonToEditWidgets( onClick: () -> Unit, @@ -732,15 +849,6 @@ private fun PopupOnDismissCtaTile(onHidePopup: () -> Unit) { } @Composable -private fun RemoveButtonContent(spacerModifier: Modifier) { - Icon(Icons.Default.Close, stringResource(R.string.button_to_remove_widget)) - Spacer(spacerModifier) - Text( - text = stringResource(R.string.button_to_remove_widget), - ) -} - -@Composable private fun filledButtonColors(): ButtonColors { val colors = LocalAndroidColorScheme.current return ButtonDefaults.buttonColors( @@ -759,6 +867,7 @@ private fun CommunalContent( widgetConfigurator: WidgetConfigurator? = null, index: Int, contentListState: ContentListState, + interactionHandler: RemoteViews.InteractionHandler?, ) { when (model) { is CommunalContentModel.WidgetContent.Widget -> @@ -778,7 +887,7 @@ private fun CommunalContent( is CommunalContentModel.WidgetContent.PendingWidget -> PendingWidgetPlaceholder(model, modifier) is CommunalContentModel.CtaTileInViewMode -> CtaTileInViewModeContent(viewModel, modifier) - is CommunalContentModel.Smartspace -> SmartspaceContent(model, modifier) + is CommunalContentModel.Smartspace -> SmartspaceContent(interactionHandler, model, modifier) is CommunalContentModel.Tutorial -> TutorialContent(modifier) is CommunalContentModel.Umo -> Umo(viewModel, modifier) } @@ -1091,13 +1200,17 @@ fun PendingWidgetPlaceholder( @Composable private fun SmartspaceContent( + interactionHandler: RemoteViews.InteractionHandler?, model: CommunalContentModel.Smartspace, modifier: Modifier = Modifier, ) { AndroidView( modifier = modifier, factory = { context -> - AppWidgetHostView(context).apply { updateAppWidget(model.remoteViews) } + SmartspaceAppWidgetHostView(context).apply { + interactionHandler?.let { setInteractionHandler(it) } + updateAppWidget(model.remoteViews) + } }, // For reusing composition in lazy lists. onReset = {}, @@ -1263,6 +1376,7 @@ object Dimensions { horizontal = ToolbarButtonPaddingHorizontal, ) val IconSize = 40.dp + val SlideOffsetY = 30.dp } private object Colors { diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalScene.kt index 9e905ac11b1e..94018bbdbd22 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalScene.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalScene.kt @@ -24,6 +24,7 @@ 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.communal.ui.viewmodel.CommunalViewModel +import com.android.systemui.communal.widgets.WidgetInteractionHandler import com.android.systemui.dagger.SysUISingleton import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.scene.ui.composable.ComposableScene @@ -40,6 +41,7 @@ class CommunalScene constructor( private val viewModel: CommunalViewModel, private val dialogFactory: SystemUIDialogFactory, + private val interactionHandler: WidgetInteractionHandler, ) : ComposableScene { override val key = Scenes.Communal @@ -53,6 +55,6 @@ constructor( @Composable override fun SceneScope.Content(modifier: Modifier) { - CommunalHub(modifier, viewModel, dialogFactory) + CommunalHub(modifier, viewModel, interactionHandler, dialogFactory) } } diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/section/AmbientStatusBarSection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/section/AmbientStatusBarSection.kt new file mode 100644 index 000000000000..3b335fa3141e --- /dev/null +++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/section/AmbientStatusBarSection.kt @@ -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 com.android.systemui.communal.ui.compose.section + +import android.view.LayoutInflater +import android.view.View +import android.widget.FrameLayout +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.viewinterop.AndroidView +import com.android.compose.animation.scene.SceneScope +import com.android.systemui.ambient.statusbar.dagger.AmbientStatusBarComponent +import com.android.systemui.ambient.statusbar.ui.AmbientStatusBarView +import com.android.systemui.communal.ui.compose.Communal +import com.android.systemui.res.R +import javax.inject.Inject + +class AmbientStatusBarSection +@Inject +constructor( + private val factory: AmbientStatusBarComponent.Factory, +) { + @Composable + fun SceneScope.AmbientStatusBar(modifier: Modifier = Modifier) { + AndroidView( + factory = { context -> + (LayoutInflater.from(context) + .inflate( + /* resource = */ R.layout.ambient_status_bar_view, + /* root = */ FrameLayout(context), + /* attachToRoot = */ false, + ) as AmbientStatusBarView) + .apply { + visibility = View.VISIBLE + factory.create(this).getController().apply { init() } + } + }, + modifier = modifier.element(Communal.Elements.StatusBar) + ) + } +} diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationsShadeScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationsShadeScene.kt index edb17273dba1..f62a28c94763 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationsShadeScene.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationsShadeScene.kt @@ -16,11 +16,11 @@ package com.android.systemui.notifications.ui.composable -import androidx.compose.foundation.layout.Arrangement 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.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp import com.android.compose.animation.scene.SceneScope @@ -75,7 +75,7 @@ constructor( OverlayShade( modifier = modifier, viewModel = overlayShadeViewModel, - horizontalArrangement = Arrangement.End, + panelAlignment = Alignment.TopEnd, lockscreenContent = lockscreenContent, ) { Column { diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QSMediaMeasurePolicy.kt b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QSMediaMeasurePolicy.kt new file mode 100644 index 000000000000..8fe870393dd1 --- /dev/null +++ b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QSMediaMeasurePolicy.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.ui.composable + +import androidx.compose.ui.layout.Measurable +import androidx.compose.ui.layout.MeasurePolicy +import androidx.compose.ui.layout.MeasureResult +import androidx.compose.ui.layout.MeasureScope +import androidx.compose.ui.layout.layoutId +import androidx.compose.ui.unit.Constraints +import androidx.compose.ui.unit.Density +import androidx.compose.ui.util.fastFirst +import kotlin.math.max + +/* + This layout puts QS taking all horizontal space and media taking the right half of the space. + However, QS (in QSPanel) puts an empty view taking half the horizontal space so that it can be + covered by media. +*/ +class QSMediaMeasurePolicy( + val qsHeight: () -> Int, + val mediaVerticalOffset: Density.() -> Int = { 0 }, +) : MeasurePolicy { + override fun MeasureScope.measure( + measurables: List<Measurable>, + constraints: Constraints + ): MeasureResult { + val qsMeasurable = measurables.fastFirst { it.layoutId == LayoutId.QS } + val mediaMeasurable = measurables.fastFirst { it.layoutId == LayoutId.Media } + + val qsPlaceable = qsMeasurable.measure(constraints) + val mediaPlaceable = + mediaMeasurable.measure(constraints.copy(maxWidth = constraints.maxWidth / 2)) + + val width = qsPlaceable.width + val height = max(qsHeight(), mediaPlaceable.height) + return layout(width, height) { + qsPlaceable.placeRelative(0, 0) + mediaPlaceable.placeRelative(width / 2, mediaVerticalOffset()) + } + } + + enum class LayoutId { + QS, + Media, + } +} diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettings.kt b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettings.kt index 8195df3b01e8..8058dcde8cf8 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettings.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettings.kt @@ -29,6 +29,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.draw.drawWithContent import androidx.compose.ui.layout.layout import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.unit.dp import androidx.compose.ui.viewinterop.AndroidView import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.android.compose.animation.scene.ElementKey @@ -62,11 +63,21 @@ object QuickSettings { object SharedValues { val TilesSquishiness = ValueKey("QuickSettingsTileSquishiness") + object SquishinessValues { val Default = 1f val LockscreenSceneStarting = 0f val GoneSceneStarting = 0.3f } + + val MediaLandscapeTopOffset = ValueKey("MediaLandscapeTopOffset") + + object MediaOffset { + val InQQS = 0.dp + // Brightness + padding + val InQS = 92.dp + val Default = 0.dp + } } } @@ -77,8 +88,8 @@ private fun SceneScope.stateForQuickSettingsContent( return when (val transitionState = layoutState.transitionState) { is TransitionState.Idle -> { when (transitionState.currentScene) { - Scenes.Shade -> QSSceneAdapter.State.QQS.takeUnless { isSplitShade } - ?: QSSceneAdapter.State.QS + Scenes.Shade -> + QSSceneAdapter.State.QQS.takeUnless { isSplitShade } ?: QSSceneAdapter.State.QS Scenes.QuickSettings -> QSSceneAdapter.State.QS else -> QSSceneAdapter.State.CLOSED } diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt index 0ee485c496be..1b49b6702275 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt @@ -46,6 +46,7 @@ import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.wrapContentHeight import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll +import androidx.compose.material3.windowsizeclass.WindowHeightSizeClass import androidx.compose.material3.windowsizeclass.WindowWidthSizeClass import androidx.compose.runtime.Composable import androidx.compose.runtime.DisposableEffect @@ -56,6 +57,8 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.CompositingStrategy import androidx.compose.ui.graphics.graphicsLayer +import androidx.compose.ui.layout.Layout +import androidx.compose.ui.layout.layoutId import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.platform.LocalLifecycleOwner import androidx.compose.ui.res.colorResource @@ -63,6 +66,7 @@ import androidx.compose.ui.unit.dp import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.android.compose.animation.scene.SceneScope import com.android.compose.animation.scene.TransitionState +import com.android.compose.animation.scene.animateSceneDpAsState import com.android.compose.animation.scene.animateSceneFloatAsState import com.android.compose.modifiers.thenIf import com.android.compose.windowsizeclass.LocalWindowSizeClass @@ -79,6 +83,8 @@ import com.android.systemui.media.controls.ui.view.MediaHost import com.android.systemui.media.dagger.MediaModule import com.android.systemui.notifications.ui.composable.HeadsUpNotificationSpace import com.android.systemui.qs.footer.ui.compose.FooterActionsWithAnimatedVisibility +import com.android.systemui.qs.ui.composable.QuickSettings.SharedValues.MediaLandscapeTopOffset +import com.android.systemui.qs.ui.composable.QuickSettings.SharedValues.MediaOffset.InQS import com.android.systemui.qs.ui.viewmodel.QuickSettingsSceneViewModel import com.android.systemui.res.R import com.android.systemui.scene.session.ui.composable.SaveableSession @@ -258,6 +264,14 @@ private fun SceneScope.QuickSettingsScene( } } + // ############# Media ############### + val isMediaVisible by viewModel.isMediaVisible.collectAsStateWithLifecycle() + val mediaInRow = + isMediaVisible && + LocalWindowSizeClass.current.heightSizeClass == WindowHeightSizeClass.Compact + val mediaOffset by + animateSceneDpAsState(value = InQS, key = MediaLandscapeTopOffset, canOverflow = false) + // This is the background for the whole scene, as the elements don't necessarily provide // a background that extends to the edges. Spacer( @@ -337,21 +351,37 @@ private fun SceneScope.QuickSettingsScene( } Spacer(modifier = Modifier.height(16.dp)) // This view has its own horizontal padding - QuickSettings( - viewModel.qsSceneAdapter, - { viewModel.qsSceneAdapter.qsHeight }, - isSplitShade = false, - modifier = Modifier - ) + val content: @Composable () -> Unit = { + QuickSettings( + viewModel.qsSceneAdapter, + { viewModel.qsSceneAdapter.qsHeight }, + isSplitShade = false, + modifier = Modifier.layoutId(QSMediaMeasurePolicy.LayoutId.QS) + ) - val isMediaVisible by viewModel.isMediaVisible.collectAsStateWithLifecycle() - - MediaCarousel( - isVisible = isMediaVisible, - mediaHost = mediaHost, - modifier = Modifier.fillMaxWidth(), - carouselController = mediaCarouselController, - ) + MediaCarousel( + isVisible = isMediaVisible, + mediaHost = mediaHost, + modifier = + Modifier.fillMaxWidth() + .layoutId(QSMediaMeasurePolicy.LayoutId.Media), + carouselController = mediaCarouselController, + ) + } + val landscapeQsMediaMeasurePolicy = remember { + QSMediaMeasurePolicy( + { viewModel.qsSceneAdapter.qsHeight }, + { mediaOffset.roundToPx() }, + ) + } + if (mediaInRow) { + Layout( + content = content, + measurePolicy = landscapeQsMediaMeasurePolicy, + ) + } else { + content() + } } } diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsShadeScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsShadeScene.kt index 4bf90ec69f46..a0d6be9c68dc 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsShadeScene.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsShadeScene.kt @@ -82,7 +82,7 @@ constructor( ) { OverlayShade( viewModel = viewModel.overlayShadeViewModel, - horizontalArrangement = Arrangement.End, + panelAlignment = Alignment.TopEnd, lockscreenContent = lockscreenContent, modifier = modifier, ) { diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/GoneScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/GoneScene.kt index efda4cd3638e..4e334c274657 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/GoneScene.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/GoneScene.kt @@ -28,11 +28,14 @@ import androidx.compose.ui.unit.IntOffset import com.android.compose.animation.scene.SceneScope import com.android.compose.animation.scene.UserAction import com.android.compose.animation.scene.UserActionResult +import com.android.compose.animation.scene.animateSceneDpAsState import com.android.compose.animation.scene.animateSceneFloatAsState import com.android.internal.policy.SystemBarUtils import com.android.systemui.dagger.SysUISingleton import com.android.systemui.notifications.ui.composable.HeadsUpNotificationSpace import com.android.systemui.qs.ui.composable.QuickSettings +import com.android.systemui.qs.ui.composable.QuickSettings.SharedValues.MediaLandscapeTopOffset +import com.android.systemui.qs.ui.composable.QuickSettings.SharedValues.MediaOffset.Default import com.android.systemui.res.R import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.scene.ui.viewmodel.GoneSceneViewModel @@ -67,6 +70,7 @@ constructor( value = QuickSettings.SharedValues.SquishinessValues.GoneSceneStarting, key = QuickSettings.SharedValues.TilesSquishiness, ) + animateSceneDpAsState(value = Default, key = MediaLandscapeTopOffset, canOverflow = false) Spacer(modifier.fillMaxSize()) HeadsUpNotificationStack( stackScrollView = notificationStackScrolLView.get(), 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 10c403055c8e..68395b463b6a 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 @@ -59,6 +59,13 @@ val SceneContainerTransitions = transitions { goneToShadeTransition(durationScale = 0.9) } from(Scenes.Gone, to = Scenes.QuickSettings) { goneToQuickSettingsTransition() } + from( + Scenes.Gone, + to = Scenes.QuickSettings, + key = SlightlyFasterShadeCollapse, + ) { + goneToQuickSettingsTransition(durationScale = 0.9) + } from(Scenes.Gone, to = Scenes.QuickSettingsShade) { goneToQuickSettingsShadeTransition() } from(Scenes.Lockscreen, to = Scenes.Bouncer) { lockscreenToBouncerTransition() } from(Scenes.Lockscreen, to = Scenes.Communal) { lockscreenToCommunalTransition() } diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/OverlayShade.kt b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/OverlayShade.kt index 6924d432453c..c189d73e2db5 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/OverlayShade.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/OverlayShade.kt @@ -20,11 +20,9 @@ package com.android.systemui.shade.ui.composable import androidx.compose.foundation.background import androidx.compose.foundation.clickable -import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.ExperimentalLayoutApi import androidx.compose.foundation.layout.PaddingValues -import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.WindowInsets import androidx.compose.foundation.layout.asPaddingValues @@ -43,6 +41,7 @@ import androidx.compose.material3.windowsizeclass.WindowWidthSizeClass import androidx.compose.runtime.Composable import androidx.compose.runtime.ReadOnlyComposable import androidx.compose.runtime.getValue +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color @@ -64,7 +63,7 @@ import java.util.Optional @Composable fun SceneScope.OverlayShade( viewModel: OverlayShadeViewModel, - horizontalArrangement: Arrangement.Horizontal, + panelAlignment: Alignment, lockscreenContent: Lazy<Optional<LockscreenContent>>, modifier: Modifier = Modifier, content: @Composable () -> Unit, @@ -81,9 +80,9 @@ fun SceneScope.OverlayShade( Scrim(onClicked = viewModel::onScrimClicked) - Row( + Box( modifier = Modifier.fillMaxSize().panelPadding(), - horizontalArrangement = horizontalArrangement, + contentAlignment = panelAlignment, ) { Panel( modifier = Modifier.element(OverlayShade.Elements.Panel).panelSize(), diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeHeader.kt b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeHeader.kt index ac3e015e52a9..b5a10ca1e478 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeHeader.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeHeader.kt @@ -19,7 +19,10 @@ package com.android.systemui.shade.ui.composable import android.view.ContextThemeWrapper import android.view.ViewGroup +import androidx.compose.foundation.background import androidx.compose.foundation.clickable +import androidx.compose.foundation.interaction.MutableInteractionSource +import androidx.compose.foundation.interaction.collectIsHoveredAsState import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column @@ -32,7 +35,9 @@ import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.width import androidx.compose.foundation.layout.widthIn +import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.ColorScheme +import androidx.compose.material3.MaterialTheme import androidx.compose.material3.windowsizeclass.WindowWidthSizeClass import androidx.compose.runtime.Composable import androidx.compose.runtime.derivedStateOf @@ -40,6 +45,7 @@ import androidx.compose.runtime.getValue import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.TransformOrigin import androidx.compose.ui.graphics.graphicsLayer @@ -58,6 +64,7 @@ import com.android.compose.animation.scene.SceneScope import com.android.compose.animation.scene.TransitionState import com.android.compose.animation.scene.ValueKey import com.android.compose.animation.scene.animateElementFloatAsState +import com.android.compose.modifiers.thenIf import com.android.compose.windowsizeclass.LocalWindowSizeClass import com.android.settingslib.Utils import com.android.systemui.battery.BatteryMeterView @@ -69,6 +76,7 @@ import com.android.systemui.compose.modifiers.sysuiResTag import com.android.systemui.privacy.OngoingPrivacyChip import com.android.systemui.res.R import com.android.systemui.scene.shared.model.Scenes +import com.android.systemui.shade.ui.composable.ShadeHeader.Colors.onScrimDim import com.android.systemui.shade.ui.composable.ShadeHeader.Dimensions.CollapsedHeight import com.android.systemui.shade.ui.composable.ShadeHeader.Values.ClockScale import com.android.systemui.shade.ui.viewmodel.ShadeHeaderViewModel @@ -79,7 +87,6 @@ import com.android.systemui.statusbar.phone.ui.TintedIconManager import com.android.systemui.statusbar.pipeline.mobile.ui.view.ModernShadeCarrierGroupMobileView import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.ShadeCarrierGroupMobileIconViewModel import com.android.systemui.statusbar.policy.Clock -import kotlin.math.max object ShadeHeader { object Elements { @@ -103,6 +110,8 @@ object ShadeHeader { object Colors { val ColorScheme.shadeHeaderText: Color get() = Color.White + val ColorScheme.onScrimDim: Color + get() = Color.DarkGray } object TestTags { @@ -130,7 +139,7 @@ fun SceneScope.CollapsedShadeHeader( val horizontalPadding = max(LocalScreenCornerRadius.current / 2f, Shade.Dimensions.HorizontalPadding) - val useExpandedFormat by + val useExpandedTextFormat by remember(cutoutLocation) { derivedStateOf { cutoutLocation != CutoutLocation.CENTER || @@ -138,6 +147,10 @@ fun SceneScope.CollapsedShadeHeader( } } + val isLargeScreenLayout = + LocalWindowSizeClass.current.widthSizeClass == WindowWidthSizeClass.Medium || + LocalWindowSizeClass.current.widthSizeClass == WindowWidthSizeClass.Expanded + val isPrivacyChipVisible by viewModel.isPrivacyChipVisible.collectAsStateWithLifecycle() // This layout assumes it is globally positioned at (0, 0) and is the @@ -182,22 +195,22 @@ fun SceneScope.CollapsedShadeHeader( Modifier.element(ShadeHeader.Elements.CollapsedContentEnd) .padding(horizontal = horizontalPadding) ) { + if (isLargeScreenLayout) { + ShadeCarrierGroup( + viewModel = viewModel, + modifier = Modifier.align(Alignment.CenterVertically), + ) + } SystemIconContainer( + viewModel = viewModel, + isClickable = isLargeScreenLayout, modifier = Modifier.align(Alignment.CenterVertically) ) { - when (LocalWindowSizeClass.current.widthSizeClass) { - WindowWidthSizeClass.Medium, - WindowWidthSizeClass.Expanded -> - ShadeCarrierGroup( - viewModel = viewModel, - modifier = Modifier.align(Alignment.CenterVertically), - ) - } StatusIcons( viewModel = viewModel, createTintedIconManager = createTintedIconManager, statusBarIconController = statusBarIconController, - useExpandedFormat = useExpandedFormat, + useExpandedFormat = useExpandedTextFormat, modifier = Modifier.align(Alignment.CenterVertically) .padding(end = 6.dp) @@ -206,7 +219,7 @@ fun SceneScope.CollapsedShadeHeader( BatteryIcon( createBatteryMeterViewController = createBatteryMeterViewController, - useExpandedFormat = useExpandedFormat, + useExpandedFormat = useExpandedTextFormat, modifier = Modifier.align(Alignment.CenterVertically), ) } @@ -322,7 +335,7 @@ fun SceneScope.ExpandedShadeHeader( modifier = Modifier.widthIn(max = 90.dp).align(Alignment.CenterVertically), ) Spacer(modifier = Modifier.weight(1f)) - SystemIconContainer { + SystemIconContainer(viewModel = viewModel, isClickable = false) { StatusIcons( viewModel = viewModel, createTintedIconManager = createTintedIconManager, @@ -531,12 +544,30 @@ private fun SceneScope.StatusIcons( @Composable private fun SystemIconContainer( + viewModel: ShadeHeaderViewModel, + isClickable: Boolean, modifier: Modifier = Modifier, content: @Composable RowScope.() -> Unit ) { - // TODO(b/298524053): add hover state for this container + val interactionSource = remember { MutableInteractionSource() } + val isHovered by interactionSource.collectIsHoveredAsState() + + val hoverModifier = Modifier + .clip(RoundedCornerShape(CollapsedHeight / 4)) + .background(MaterialTheme.colorScheme.onScrimDim) + Row( - modifier = modifier.height(CollapsedHeight), + modifier = modifier + .height(CollapsedHeight) + .padding(vertical = CollapsedHeight / 4) + .thenIf(isClickable) { + Modifier.clickable( + interactionSource = interactionSource, + indication = null, + onClick = { viewModel.onSystemIconContainerClicked() }, + ) + } + .thenIf(isHovered) { hoverModifier }, content = content, ) } diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt index 33a630c8086a..d51cdd3036f4 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt @@ -25,18 +25,15 @@ import androidx.compose.foundation.clickable import androidx.compose.foundation.clipScrollableContainer import androidx.compose.foundation.gestures.Orientation import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Arrangement.Absolute.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.asPaddingValues import androidx.compose.foundation.layout.displayCutoutPadding import androidx.compose.foundation.layout.fillMaxHeight 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.offset import androidx.compose.foundation.layout.padding @@ -55,6 +52,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.CompositingStrategy import androidx.compose.ui.graphics.graphicsLayer import androidx.compose.ui.layout.Layout +import androidx.compose.ui.layout.layoutId import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.platform.LocalLifecycleOwner import androidx.compose.ui.res.colorResource @@ -66,6 +64,7 @@ import com.android.compose.animation.scene.SceneScope import com.android.compose.animation.scene.TransitionState import com.android.compose.animation.scene.UserAction import com.android.compose.animation.scene.UserActionResult +import com.android.compose.animation.scene.animateSceneDpAsState import com.android.compose.animation.scene.animateSceneFloatAsState import com.android.compose.modifiers.padding import com.android.compose.modifiers.thenIf @@ -85,7 +84,10 @@ import com.android.systemui.media.dagger.MediaModule.QUICK_QS_PANEL import com.android.systemui.notifications.ui.composable.NotificationScrollingStack import com.android.systemui.qs.footer.ui.compose.FooterActionsWithAnimatedVisibility import com.android.systemui.qs.ui.composable.BrightnessMirror +import com.android.systemui.qs.ui.composable.QSMediaMeasurePolicy import com.android.systemui.qs.ui.composable.QuickSettings +import com.android.systemui.qs.ui.composable.QuickSettings.SharedValues.MediaLandscapeTopOffset +import com.android.systemui.qs.ui.composable.QuickSettings.SharedValues.MediaOffset.InQQS import com.android.systemui.res.R import com.android.systemui.scene.session.ui.composable.SaveableSession import com.android.systemui.scene.shared.model.Scenes @@ -242,6 +244,8 @@ private fun SceneScope.SingleShade( val mediaInRow = isMediaVisible && LocalWindowSizeClass.current.heightSizeClass == WindowHeightSizeClass.Compact + val mediaOffset by + animateSceneDpAsState(value = InQQS, key = MediaLandscapeTopOffset, canOverflow = false) Box( modifier = @@ -282,10 +286,10 @@ private fun SceneScope.SingleShade( statusBarIconController = statusBarIconController, ) - val content: @Composable (Modifier) -> Unit = { modifier -> + val content: @Composable () -> Unit = { Box( Modifier.element(QuickSettings.Elements.QuickQuickSettings) - .then(modifier) + .layoutId(QSMediaMeasurePolicy.LayoutId.QS) ) { QuickSettings( viewModel.qsSceneAdapter, @@ -298,23 +302,26 @@ private fun SceneScope.SingleShade( MediaCarousel( isVisible = isMediaVisible, mediaHost = mediaHost, - modifier = Modifier.fillMaxWidth().then(modifier), + modifier = + Modifier.fillMaxWidth() + .layoutId(QSMediaMeasurePolicy.LayoutId.Media), carouselController = mediaCarouselController, ) } - - if (!mediaInRow) { - content(Modifier) + val landscapeQsMediaMeasurePolicy = remember { + QSMediaMeasurePolicy( + { viewModel.qsSceneAdapter.qqsHeight }, + { mediaOffset.roundToPx() }, + ) + } + if (mediaInRow) { + Layout( + content = content, + measurePolicy = landscapeQsMediaMeasurePolicy, + ) } else { - Row( - modifier = Modifier.fillMaxWidth(), - horizontalArrangement = spacedBy(16.dp), - verticalAlignment = Alignment.CenterVertically, - ) { - content(Modifier.weight(1f)) - } + content() } - Spacer(modifier = Modifier.height(16.dp)) } }, { diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/button/ui/composable/ButtonComponent.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/button/ui/composable/ButtonComponent.kt index e1ae80f13312..6d03118645f3 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/button/ui/composable/ButtonComponent.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/button/ui/composable/ButtonComponent.kt @@ -88,7 +88,7 @@ class ButtonComponent( } else { MaterialTheme.colorScheme.surface }, - shape = RoundedCornerShape(28.dp), + shape = RoundedCornerShape(20.dp), contentColor = if (viewModel.isActive) { MaterialTheme.colorScheme.onTertiaryContainer diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/button/ui/composable/ToggleButtonComponent.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/button/ui/composable/ToggleButtonComponent.kt index 1b821d36dceb..bb2daecd3a25 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/button/ui/composable/ToggleButtonComponent.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/button/ui/composable/ToggleButtonComponent.kt @@ -91,7 +91,7 @@ class ToggleButtonComponent( contentDescription = label }, onClick = { onCheckedChange(!viewModel.isActive) }, - shape = RoundedCornerShape(28.dp), + shape = RoundedCornerShape(20.dp), colors = colors, contentPadding = PaddingValues(0.dp) ) { diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/spatialaudio/ui/composable/SpatialAudioPopup.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/spatialaudio/ui/composable/SpatialAudioPopup.kt index 9891b5b5eba2..3295dde55238 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/spatialaudio/ui/composable/SpatialAudioPopup.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/spatialaudio/ui/composable/SpatialAudioPopup.kt @@ -17,6 +17,7 @@ package com.android.systemui.volume.panel.component.spatialaudio.ui.composable import android.view.Gravity +import androidx.annotation.VisibleForTesting import androidx.compose.foundation.basicMarquee import androidx.compose.material3.LocalContentColor import androidx.compose.material3.MaterialTheme @@ -71,7 +72,8 @@ constructor( } @Composable - private fun Content(dialog: SystemUIDialog) { + @VisibleForTesting + fun Content(dialog: SystemUIDialog) { val isAvailable by viewModel.isAvailable.collectAsStateWithLifecycle() if (!isAvailable) { diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateSharedAsState.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateSharedAsState.kt index 7fd3a176acaa..114dcf4fbc7e 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateSharedAsState.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateSharedAsState.kt @@ -381,18 +381,17 @@ private class AnimatedStateImpl<T, Delta>( // relayout/redraw for nothing. fromValue } else { - // In the case of bouncing, if the value remains constant during the overscroll, we - // should use the value of the scene we are bouncing around. - if (!canOverflow && transition is TransitionState.HasOverscrollProperties) { - val bouncingScene = transition.bouncingScene - if (bouncingScene != null) { - return sharedValue[bouncingScene] + val overscrollSpec = transition.currentOverscrollSpec + val progress = + when { + overscrollSpec == null -> { + if (canOverflow) transition.progress + else transition.progress.fastCoerceIn(0f, 1f) + } + overscrollSpec.scene == transition.toScene -> 1f + else -> 0f } - } - val progress = - if (canOverflow) transition.progress - else transition.progress.fastCoerceIn(0f, 1f) sharedValue.type.lerp(fromValue, toValue, progress) } } else fromValue ?: toValue diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateToScene.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateToScene.kt index 48a348b9d1c5..c2dd80375d5a 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateToScene.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateToScene.kt @@ -109,8 +109,7 @@ internal fun CoroutineScope.animateToScene( layoutState.transitions.interruptionHandler.onInterruption( transitionState, target, - ) - ?: DefaultInterruptionHandler.onInterruption(transitionState, target) + ) ?: DefaultInterruptionHandler.onInterruption(transitionState, target) val animateFrom = interruptionResult.animateFrom if ( @@ -159,6 +158,7 @@ private fun CoroutineScope.animate( val transition = if (reversed) { OneOffTransition( + key = transitionKey, fromScene = targetScene, toScene = fromScene, currentScene = targetScene, @@ -167,6 +167,7 @@ private fun CoroutineScope.animate( ) } else { OneOffTransition( + key = transitionKey, fromScene = fromScene, toScene = targetScene, currentScene = targetScene, @@ -178,7 +179,7 @@ private fun CoroutineScope.animate( // Change the current layout state to start this new transition. This will compute the // TransformationSpec associated to this transition, which we need to initialize the Animatable // that will actually animate it. - layoutState.startTransition(transition, transitionKey, chain) + layoutState.startTransition(transition, chain) // The transition now contains the transformation spec that we should use to instantiate the // Animatable. @@ -207,6 +208,7 @@ private fun CoroutineScope.animate( } private class OneOffTransition( + override val key: TransitionKey?, fromScene: SceneKey, toScene: SceneKey, override val currentScene: SceneKey, 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 e9633c2f6603..60d78feec8c2 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 @@ -257,7 +257,7 @@ private class DragControllerImpl( fun updateTransition(newTransition: SwipeTransition, force: Boolean = false) { if (isDrivingTransition || force) { - layoutState.startTransition(newTransition, newTransition.key) + layoutState.startTransition(newTransition) } swipeTransition = newTransition @@ -555,7 +555,7 @@ private class SwipeTransition( val layoutImpl: SceneTransitionLayoutImpl, val layoutState: BaseSceneTransitionLayoutState, val coroutineScope: CoroutineScope, - val key: TransitionKey?, + override val key: TransitionKey?, val _fromScene: Scene, val _toScene: Scene, val userActionDistanceScope: UserActionDistanceScope, 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 f0fb9f62fdad..5611c6ec1dee 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 @@ -33,12 +33,14 @@ import androidx.compose.ui.graphics.drawscope.scale import androidx.compose.ui.layout.ApproachLayoutModifierNode import androidx.compose.ui.layout.ApproachMeasureScope import androidx.compose.ui.layout.LayoutCoordinates -import androidx.compose.ui.layout.LookaheadScope import androidx.compose.ui.layout.Measurable import androidx.compose.ui.layout.MeasureResult +import androidx.compose.ui.layout.MeasureScope import androidx.compose.ui.layout.Placeable import androidx.compose.ui.node.DrawModifierNode import androidx.compose.ui.node.ModifierNodeElement +import androidx.compose.ui.node.TraversableNode +import androidx.compose.ui.node.traverseDescendants import androidx.compose.ui.platform.testTag import androidx.compose.ui.unit.Constraints import androidx.compose.ui.unit.IntSize @@ -66,6 +68,9 @@ internal class Element(val key: ElementKey) { */ var lastTransition: TransitionState.Transition? = null + /** Whether this element was ever drawn in a scene. */ + var wasDrawnInAnyScene = false + override fun toString(): String { return "Element(key=$key)" } @@ -162,7 +167,7 @@ internal class ElementNode( private var currentTransitions: List<TransitionState.Transition>, private var scene: Scene, private var key: ElementKey, -) : Modifier.Node(), DrawModifierNode, ApproachLayoutModifierNode { +) : Modifier.Node(), DrawModifierNode, ApproachLayoutModifierNode, TraversableNode { private var _element: Element? = null private val element: Element get() = _element!! @@ -171,6 +176,8 @@ internal class ElementNode( private val sceneState: Element.SceneState get() = _sceneState!! + override val traverseKey: Any = ElementTraverseKey + override fun onAttach() { super.onAttach() updateElementAndSceneValues() @@ -248,15 +255,36 @@ internal class ElementNode( } @ExperimentalComposeUiApi + override fun MeasureScope.measure( + measurable: Measurable, + constraints: Constraints + ): MeasureResult { + check(isLookingAhead) + + return measurable.measure(constraints).run { + // Update the size this element has in this scene when idle. + sceneState.targetSize = size() + + layout(width, height) { + // Update the offset (relative to the SceneTransitionLayout) this element has in + // this scene when idle. + coordinates?.let { coords -> + with(layoutImpl.lookaheadScope) { + sceneState.targetOffset = + lookaheadScopeCoordinates.localLookaheadPositionOf(coords) + } + } + place(0, 0) + } + } + } + override fun ApproachMeasureScope.approachMeasure( measurable: Measurable, constraints: Constraints, ): MeasureResult { - // Update the size this element has in this scene when idle. - sceneState.targetSize = lookaheadSize - val transitions = currentTransitions - val transition = elementTransition(element, transitions) + val transition = elementTransition(layoutImpl, element, transitions) // If this element is not supposed to be laid out now, either because it is not part of any // ongoing transition or the other scene of its transition is overscrolling, then lay out @@ -265,43 +293,136 @@ internal class ElementNode( val isOtherSceneOverscrolling = overscrollScene != null && overscrollScene != scene.key val isNotPartOfAnyOngoingTransitions = transitions.isNotEmpty() && transition == null if (isNotPartOfAnyOngoingTransitions || isOtherSceneOverscrolling) { - sceneState.lastOffset = Offset.Unspecified - sceneState.lastScale = Scale.Unspecified - sceneState.lastAlpha = Element.AlphaUnspecified + recursivelyClearPlacementValues() + sceneState.lastSize = Element.SizeUnspecified val placeable = measurable.measure(constraints) - sceneState.lastSize = placeable.size() + return layout(placeable.width, placeable.height) { /* Do not place */ } + } - this as LookaheadScope - return layout(placeable.width, placeable.height) { - // Update the offset (relative to the SceneTransitionLayout) this element has in - // this scene when idle. - coordinates?.let { coords -> - sceneState.targetOffset = - lookaheadScopeCoordinates.localLookaheadPositionOf(coords) + val placeable = + measure(layoutImpl, element, transition, sceneState, measurable, constraints) + sceneState.lastSize = placeable.size() + return layout(placeable.width, placeable.height) { place(transition, placeable) } + } + + @OptIn(ExperimentalComposeUiApi::class) + private fun Placeable.PlacementScope.place( + transition: TransitionState.Transition?, + placeable: Placeable, + ) { + with(layoutImpl.lookaheadScope) { + // Update the offset (relative to the SceneTransitionLayout) this element has in this + // scene when idle. + val coords = + coordinates ?: error("Element ${element.key} does not have any coordinates") + + // No need to place the element in this scene if we don't want to draw it anyways. + if (!shouldPlaceElement(layoutImpl, scene.key, element, transition)) { + recursivelyClearPlacementValues() + return + } + + val currentOffset = lookaheadScopeCoordinates.localPositionOf(coords, Offset.Zero) + val targetOffset = + computeValue( + layoutImpl, + sceneState, + element, + transition, + sceneValue = { it.targetOffset }, + transformation = { it.offset }, + currentValue = { currentOffset }, + isSpecified = { it != Offset.Unspecified }, + ::lerp, + ) + + val interruptedOffset = + computeInterruptedValue( + layoutImpl, + transition, + value = targetOffset, + unspecifiedValue = Offset.Unspecified, + zeroValue = Offset.Zero, + getValueBeforeInterruption = { sceneState.offsetBeforeInterruption }, + setValueBeforeInterruption = { sceneState.offsetBeforeInterruption = it }, + getInterruptionDelta = { sceneState.offsetInterruptionDelta }, + setInterruptionDelta = { delta -> + setPlacementInterruptionDelta( + element = element, + sceneState = sceneState, + transition = transition, + delta = delta, + setter = { sceneState, delta -> + sceneState.offsetInterruptionDelta = delta + }, + ) + }, + diff = { a, b -> a - b }, + add = { a, b, bProgress -> a + b * bProgress }, + ) + + sceneState.lastOffset = interruptedOffset + + val offset = (interruptedOffset - currentOffset).round() + if ( + isElementOpaque(scene, element, transition) && + interruptedAlpha(layoutImpl, element, transition, sceneState, alpha = 1f) == 1f + ) { + sceneState.lastAlpha = 1f + + // TODO(b/291071158): Call placeWithLayer() if offset != IntOffset.Zero and size is + // not animated once b/305195729 is fixed. Test that drawing is not invalidated in + // that case. + placeable.place(offset) + } else { + placeable.placeWithLayer(offset) { + // This layer might still run on its own (outside of the placement phase) even + // if this element is not placed or composed anymore, so we need to double check + // again here before calling [elementAlpha] (which will update + // [SceneState.lastAlpha]). We also need to recompute the current transition to + // make sure that we are using the current transition and not a reference to an + // old one. See b/343138966 for details. + if (_element == null) { + return@placeWithLayer + } + + val transition = elementTransition(layoutImpl, element, currentTransitions) + if (!shouldPlaceElement(layoutImpl, scene.key, element, transition)) { + return@placeWithLayer + } + + alpha = elementAlpha(layoutImpl, element, transition, sceneState) + compositingStrategy = CompositingStrategy.ModulateAlpha } } } + } - val placeable = - measure(layoutImpl, scene, element, transition, sceneState, measurable, constraints) - sceneState.lastSize = placeable.size() - return layout(placeable.width, placeable.height) { - place( - layoutImpl, - scene, - element, - transition, - sceneState, - placeable, - placementScope = this, - ) + /** + * Recursively clear the last placement values on this node and all descendants ElementNodes. + * This should be called when this node is not placed anymore, so that we correctly clear values + * for the descendants for which approachMeasure() won't be called. + */ + private fun recursivelyClearPlacementValues() { + fun Element.SceneState.clearLastPlacementValues() { + lastOffset = Offset.Unspecified + lastScale = Scale.Unspecified + lastAlpha = Element.AlphaUnspecified + } + + sceneState.clearLastPlacementValues() + traverseDescendants(ElementTraverseKey) { node -> + (node as ElementNode).sceneState.clearLastPlacementValues() + TraversableNode.Companion.TraverseDescendantsAction.ContinueTraversal } } override fun ContentDrawScope.draw() { - val transition = elementTransition(element, currentTransitions) - val drawScale = getDrawScale(layoutImpl, scene, element, transition, sceneState) + element.wasDrawnInAnyScene = true + + val transition = elementTransition(layoutImpl, element, currentTransitions) + val drawScale = getDrawScale(layoutImpl, element, transition, sceneState) if (drawScale == Scale.Default) { drawContent() } else { @@ -316,6 +437,8 @@ internal class ElementNode( } companion object { + private val ElementTraverseKey = Any() + private fun maybePruneMaps( layoutImpl: SceneTransitionLayoutImpl, element: Element, @@ -341,6 +464,7 @@ internal class ElementNode( * its scenes contains the element. */ private fun elementTransition( + layoutImpl: SceneTransitionLayoutImpl, element: Element, transitions: List<TransitionState.Transition>, ): TransitionState.Transition? { @@ -354,7 +478,7 @@ private fun elementTransition( if (transition != previousTransition && transition != null && previousTransition != null) { // The previous transition was interrupted by another transition. - prepareInterruption(element, transition, previousTransition) + prepareInterruption(layoutImpl, element, transition, previousTransition) } else if (transition == null && previousTransition != null) { // The transition was just finished. element.sceneStates.values.forEach { @@ -367,18 +491,44 @@ private fun elementTransition( } private fun prepareInterruption( + layoutImpl: SceneTransitionLayoutImpl, element: Element, transition: TransitionState.Transition, previousTransition: TransitionState.Transition, ) { val sceneStates = element.sceneStates - sceneStates[previousTransition.fromScene]?.selfUpdateValuesBeforeInterruption() - sceneStates[previousTransition.toScene]?.selfUpdateValuesBeforeInterruption() - sceneStates[transition.fromScene]?.selfUpdateValuesBeforeInterruption() - sceneStates[transition.toScene]?.selfUpdateValuesBeforeInterruption() + fun updatedSceneState(key: SceneKey): Element.SceneState? { + return sceneStates[key]?.also { it.selfUpdateValuesBeforeInterruption() } + } + + val previousFromState = updatedSceneState(previousTransition.fromScene) + val previousToState = updatedSceneState(previousTransition.toScene) + val fromState = updatedSceneState(transition.fromScene) + val toState = updatedSceneState(transition.toScene) reconcileStates(element, previousTransition) reconcileStates(element, transition) + + // Remove the interruption values to all scenes but the scene(s) where the element will be + // placed, to make sure that interruption deltas are computed only right after this interruption + // is prepared. + fun cleanInterruptionValues(sceneState: Element.SceneState) { + sceneState.sizeInterruptionDelta = IntSize.Zero + sceneState.offsetInterruptionDelta = Offset.Zero + sceneState.alphaInterruptionDelta = 0f + sceneState.scaleInterruptionDelta = Scale.Zero + + if (!shouldPlaceElement(layoutImpl, sceneState.scene, element, transition)) { + sceneState.offsetBeforeInterruption = Offset.Unspecified + sceneState.alphaBeforeInterruption = Element.AlphaUnspecified + sceneState.scaleBeforeInterruption = Scale.Unspecified + } + } + + previousFromState?.let { cleanInterruptionValues(it) } + previousToState?.let { cleanInterruptionValues(it) } + fromState?.let { cleanInterruptionValues(it) } + toState?.let { cleanInterruptionValues(it) } } /** @@ -485,9 +635,38 @@ private inline fun <T> computeInterruptedValue( } } +/** + * Set the interruption delta of a *placement/drawing*-related value (offset, alpha, scale). This + * ensures that the delta is also set on the other scene in the transition for shared elements, so + * that there is no jump cut if the scene where the element is placed has changed. + */ +private inline fun <T> setPlacementInterruptionDelta( + element: Element, + sceneState: Element.SceneState, + transition: TransitionState.Transition?, + delta: T, + setter: (Element.SceneState, T) -> Unit, +) { + // Set the interruption delta on the current scene. + setter(sceneState, delta) + + if (transition == null) { + return + } + + // If the element is shared, also set the delta on the other scene so that it is used by that + // scene if we start overscrolling it and change the scene where the element is placed. + val otherScene = + if (sceneState.scene == transition.fromScene) transition.toScene else transition.fromScene + val otherSceneState = element.sceneStates[otherScene] ?: return + if (isSharedElementEnabled(element.key, transition)) { + setter(otherSceneState, delta) + } +} + private fun shouldPlaceElement( layoutImpl: SceneTransitionLayoutImpl, - scene: Scene, + scene: SceneKey, element: Element, transition: TransitionState.Transition?, ): Boolean { @@ -498,16 +677,13 @@ private fun shouldPlaceElement( // Don't place the element in this scene if this scene is not part of the current element // transition. - if (scene.key != transition.fromScene && scene.key != transition.toScene) { + if (scene != transition.fromScene && scene != transition.toScene) { return false } - // Place the element if it is not shared or if the current scene is the one that is currently - // overscrolling with [OverscrollSpec]. + // Place the element if it is not shared. if ( - transition.fromScene !in element.sceneStates || - transition.toScene !in element.sceneStates || - transition.currentOverscrollSpec?.scene == scene.key + transition.fromScene !in element.sceneStates || transition.toScene !in element.sceneStates ) { return true } @@ -517,20 +693,26 @@ private fun shouldPlaceElement( return true } - return shouldDrawOrComposeSharedElement( + return shouldPlaceOrComposeSharedElement( layoutImpl, - scene.key, + scene, element.key, transition, ) } -internal fun shouldDrawOrComposeSharedElement( +internal fun shouldPlaceOrComposeSharedElement( layoutImpl: SceneTransitionLayoutImpl, scene: SceneKey, element: ElementKey, transition: TransitionState.Transition, ): Boolean { + // If we are overscrolling, only place/compose the element in the overscrolling scene. + val overscrollScene = transition.currentOverscrollSpec?.scene + if (overscrollScene != null) { + return scene == overscrollScene + } + val scenePicker = element.scenePicker val fromScene = transition.fromScene val toScene = transition.toScene @@ -541,10 +723,9 @@ internal fun shouldDrawOrComposeSharedElement( transition = transition, fromSceneZIndex = layoutImpl.scenes.getValue(fromScene).zIndex, toSceneZIndex = layoutImpl.scenes.getValue(toScene).zIndex, - ) - ?: return false + ) ?: return false - return pickedScene == scene || transition.currentOverscrollSpec?.scene == scene + return pickedScene == scene } private fun isSharedElementEnabled( @@ -618,7 +799,6 @@ private fun isElementOpaque( */ private fun elementAlpha( layoutImpl: SceneTransitionLayoutImpl, - scene: Scene, element: Element, transition: TransitionState.Transition?, sceneState: Element.SceneState, @@ -626,25 +806,31 @@ private fun elementAlpha( val alpha = computeValue( layoutImpl, - scene, + sceneState, element, transition, sceneValue = { 1f }, transformation = { it.alpha }, - idleValue = 1f, currentValue = { 1f }, isSpecified = { true }, ::lerp, ) .fastCoerceIn(0f, 1f) - val interruptedAlpha = interruptedAlpha(layoutImpl, transition, sceneState, alpha) + // If the element is fading during this transition and that it is drawn for the first time, make + // sure that it doesn't instantly appear on screen. + if (!element.wasDrawnInAnyScene && alpha > 0f) { + element.sceneStates.forEach { it.value.alphaBeforeInterruption = 0f } + } + + val interruptedAlpha = interruptedAlpha(layoutImpl, element, transition, sceneState, alpha) sceneState.lastAlpha = interruptedAlpha return interruptedAlpha } private fun interruptedAlpha( layoutImpl: SceneTransitionLayoutImpl, + element: Element, transition: TransitionState.Transition?, sceneState: Element.SceneState, alpha: Float, @@ -658,15 +844,22 @@ private fun interruptedAlpha( getValueBeforeInterruption = { sceneState.alphaBeforeInterruption }, setValueBeforeInterruption = { sceneState.alphaBeforeInterruption = it }, getInterruptionDelta = { sceneState.alphaInterruptionDelta }, - setInterruptionDelta = { sceneState.alphaInterruptionDelta = it }, + setInterruptionDelta = { delta -> + setPlacementInterruptionDelta( + element = element, + sceneState = sceneState, + transition = transition, + delta = delta, + setter = { sceneState, delta -> sceneState.alphaInterruptionDelta = delta }, + ) + }, diff = { a, b -> a - b }, add = { a, b, bProgress -> a + b * bProgress }, ) } -private fun ApproachMeasureScope.measure( +private fun measure( layoutImpl: SceneTransitionLayoutImpl, - scene: Scene, element: Element, transition: TransitionState.Transition?, sceneState: Element.SceneState, @@ -681,12 +874,11 @@ private fun ApproachMeasureScope.measure( val targetSize = computeValue( layoutImpl, - scene, + sceneState, element, transition, sceneValue = { it.targetSize }, transformation = { it.size }, - idleValue = lookaheadSize, currentValue = { measurable.measure(constraints).also { maybePlaceable = it }.size() }, isSpecified = { it != Element.SizeUnspecified }, ::lerp, @@ -732,7 +924,6 @@ private fun Placeable.size(): IntSize = IntSize(width, height) private fun ContentDrawScope.getDrawScale( layoutImpl: SceneTransitionLayoutImpl, - scene: Scene, element: Element, transition: TransitionState.Transition?, sceneState: Element.SceneState, @@ -740,12 +931,11 @@ private fun ContentDrawScope.getDrawScale( val scale = computeValue( layoutImpl, - scene, + sceneState, element, transition, sceneValue = { Scale.Default }, transformation = { it.drawScale }, - idleValue = Scale.Default, currentValue = { Scale.Default }, isSpecified = { true }, ::lerp, @@ -765,7 +955,15 @@ private fun ContentDrawScope.getDrawScale( getValueBeforeInterruption = { sceneState.scaleBeforeInterruption }, setValueBeforeInterruption = { sceneState.scaleBeforeInterruption = it }, getInterruptionDelta = { sceneState.scaleInterruptionDelta }, - setInterruptionDelta = { sceneState.scaleInterruptionDelta = it }, + setInterruptionDelta = { delta -> + setPlacementInterruptionDelta( + element = element, + sceneState = sceneState, + transition = transition, + delta = delta, + setter = { sceneState, delta -> sceneState.scaleInterruptionDelta = delta }, + ) + }, diff = { a, b -> Scale( scaleX = a.scaleX - b.scaleX, @@ -796,88 +994,6 @@ private fun ContentDrawScope.getDrawScale( return interruptedScale } -@OptIn(ExperimentalComposeUiApi::class) -private fun ApproachMeasureScope.place( - layoutImpl: SceneTransitionLayoutImpl, - scene: Scene, - element: Element, - transition: TransitionState.Transition?, - sceneState: Element.SceneState, - placeable: Placeable, - placementScope: Placeable.PlacementScope, -) { - this as LookaheadScope - - with(placementScope) { - // Update the offset (relative to the SceneTransitionLayout) this element has in this scene - // when idle. - val coords = coordinates ?: error("Element ${element.key} does not have any coordinates") - val targetOffsetInScene = lookaheadScopeCoordinates.localLookaheadPositionOf(coords) - sceneState.targetOffset = targetOffsetInScene - - // No need to place the element in this scene if we don't want to draw it anyways. - if (!shouldPlaceElement(layoutImpl, scene, element, transition)) { - sceneState.lastOffset = Offset.Unspecified - sceneState.lastScale = Scale.Unspecified - sceneState.lastAlpha = Element.AlphaUnspecified - - sceneState.clearValuesBeforeInterruption() - sceneState.clearInterruptionDeltas() - return - } - - val currentOffset = lookaheadScopeCoordinates.localPositionOf(coords, Offset.Zero) - val targetOffset = - computeValue( - layoutImpl, - scene, - element, - transition, - sceneValue = { it.targetOffset }, - transformation = { it.offset }, - idleValue = targetOffsetInScene, - currentValue = { currentOffset }, - isSpecified = { it != Offset.Unspecified }, - ::lerp, - ) - - val interruptedOffset = - computeInterruptedValue( - layoutImpl, - transition, - value = targetOffset, - unspecifiedValue = Offset.Unspecified, - zeroValue = Offset.Zero, - getValueBeforeInterruption = { sceneState.offsetBeforeInterruption }, - setValueBeforeInterruption = { sceneState.offsetBeforeInterruption = it }, - getInterruptionDelta = { sceneState.offsetInterruptionDelta }, - setInterruptionDelta = { sceneState.offsetInterruptionDelta = it }, - diff = { a, b -> a - b }, - add = { a, b, bProgress -> a + b * bProgress }, - ) - - sceneState.lastOffset = interruptedOffset - - val offset = (interruptedOffset - currentOffset).round() - if ( - isElementOpaque(scene, element, transition) && - interruptedAlpha(layoutImpl, transition, sceneState, alpha = 1f) == 1f - ) { - sceneState.lastAlpha = 1f - - // TODO(b/291071158): Call placeWithLayer() if offset != IntOffset.Zero and size is not - // animated once b/305195729 is fixed. Test that drawing is not invalidated in that - // case. - placeable.place(offset) - } else { - placeable.placeWithLayer(offset) { - alpha = elementAlpha(layoutImpl, scene, element, transition, sceneState) - compositingStrategy = CompositingStrategy.ModulateAlpha - } - } - } -} - /** * Return the value that should be used depending on the current layout state and transition. * @@ -886,11 +1002,12 @@ private fun ApproachMeasureScope.place( * Measurable. * * @param layoutImpl the [SceneTransitionLayoutImpl] associated to [element]. - * @param scene the scene containing [element]. + * @param currentSceneState the scene state of the scene for which we are computing the value. Note + * that during interruptions, this could be the state of a scene that is neither + * [transition.toScene] nor [transition.fromScene]. * @param element the element being animated. * @param sceneValue the value being animated. * @param transformation the transformation associated to the value being animated. - * @param idleValue the value when idle, i.e. when there is no transition happening. * @param currentValue the value that would be used if it is not transformed. Note that this is * different than [idleValue] even if the value is not transformed directly because it could be * impacted by the transformations on other elements, like a parent that is being translated or @@ -900,12 +1017,11 @@ private fun ApproachMeasureScope.place( */ private inline fun <T> computeValue( layoutImpl: SceneTransitionLayoutImpl, - scene: Scene, + currentSceneState: Element.SceneState, element: Element, transition: TransitionState.Transition?, sceneValue: (Element.SceneState) -> T, transformation: (ElementTransformations) -> PropertyTransformation<T>?, - idleValue: T, currentValue: () -> T, isSpecified: (T) -> Boolean, lerp: (T, T, Float) -> T, @@ -927,19 +1043,22 @@ private inline fun <T> computeValue( if (fromState == null && toState == null) { // TODO(b/311600838): Throw an exception instead once layers of disposed elements are not // run anymore. - return idleValue + return sceneValue(currentSceneState) } + val currentScene = currentSceneState.scene if (transition is TransitionState.HasOverscrollProperties) { val overscroll = transition.currentOverscrollSpec - if (overscroll?.scene == scene.key) { - val elementSpec = overscroll.transformationSpec.transformations(element.key, scene.key) + if (overscroll?.scene == currentScene) { + val elementSpec = + overscroll.transformationSpec.transformations(element.key, currentScene) val propertySpec = transformation(elementSpec) ?: return currentValue() - val overscrollState = checkNotNull(if (scene.key == toScene) toState else fromState) + val overscrollState = checkNotNull(if (currentScene == toScene) toState else fromState) + val idleValue = sceneValue(overscrollState) val targetValue = propertySpec.transform( layoutImpl, - scene, + currentScene, element, overscrollState, transition, @@ -983,24 +1102,30 @@ private inline fun <T> computeValue( return if (start == end) start else lerp(start, end, transition.progress) } - val transformation = - transformation(transition.transformationSpec.transformations(element.key, scene.key)) - // If there is no transformation explicitly associated to this element value, let's use - // the value given by the system (like the current position and size given by the layout - // pass). - ?: return currentValue() - // Get the transformed value, i.e. the target value at the beginning (for entering elements) or // end (for leaving elements) of the transition. val sceneState = checkNotNull( when { - isSharedElement && scene.key == fromScene -> fromState + isSharedElement && currentScene == fromScene -> fromState isSharedElement -> toState else -> fromState ?: toState } ) + // The scene for which we compute the transformation. Note that this is not necessarily + // [currentScene] because [currentScene] could be a different scene than the transition + // fromScene or toScene during interruptions. + val scene = sceneState.scene + + val transformation = + transformation(transition.transformationSpec.transformations(element.key, scene)) + // If there is no transformation explicitly associated to this element value, let's use + // the value given by the system (like the current position and size given by the layout + // pass). + ?: return currentValue() + + val idleValue = sceneValue(sceneState) val targetValue = transformation.transform( layoutImpl, @@ -1022,7 +1147,7 @@ private inline fun <T> computeValue( val rangeProgress = transformation.range?.progress(progress) ?: progress // Interpolate between the value at rest and the value before entering/after leaving. - val isEntering = scene.key == toScene + val isEntering = scene == toScene return if (isEntering) { lerp(targetValue, idleValue, rangeProgress) } else { diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MovableElement.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MovableElement.kt index be005eac1b75..32eadde7bf30 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MovableElement.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MovableElement.kt @@ -187,7 +187,7 @@ private fun shouldComposeMovableElement( } ?: return false // Always compose movable elements in the scene picked by their scene picker. - return shouldDrawOrComposeSharedElement( + return shouldPlaceOrComposeSharedElement( layoutImpl, scene, element, 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 3cc8431cd87e..6001f1fd6db0 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 @@ -19,8 +19,6 @@ package com.android.compose.animation.scene import androidx.compose.foundation.gestures.Orientation import androidx.compose.foundation.gestures.awaitHorizontalTouchSlopOrCancellation import androidx.compose.foundation.gestures.awaitVerticalTouchSlopOrCancellation -import androidx.compose.foundation.gestures.horizontalDrag -import androidx.compose.foundation.gestures.verticalDrag import androidx.compose.runtime.Stable import androidx.compose.ui.Modifier import androidx.compose.ui.geometry.Offset @@ -32,7 +30,9 @@ import androidx.compose.ui.input.pointer.PointerInputChange import androidx.compose.ui.input.pointer.PointerInputScope import androidx.compose.ui.input.pointer.SuspendingPointerInputModifierNode import androidx.compose.ui.input.pointer.changedToDownIgnoreConsumed +import androidx.compose.ui.input.pointer.changedToUpIgnoreConsumed import androidx.compose.ui.input.pointer.positionChange +import androidx.compose.ui.input.pointer.positionChangeIgnoreConsumed import androidx.compose.ui.input.pointer.util.VelocityTracker import androidx.compose.ui.input.pointer.util.addPointerInputChange import androidx.compose.ui.node.CompositionLocalConsumerModifierNode @@ -46,6 +46,8 @@ import androidx.compose.ui.platform.LocalViewConfiguration import androidx.compose.ui.unit.IntSize import androidx.compose.ui.unit.Velocity import androidx.compose.ui.util.fastAll +import androidx.compose.ui.util.fastAny +import androidx.compose.ui.util.fastFirstOrNull import androidx.compose.ui.util.fastForEach import kotlin.coroutines.cancellation.CancellationException import kotlin.math.sign @@ -236,8 +238,23 @@ internal class MultiPointerDraggableNode( onDragCancel: (controller: DragController) -> Unit, swipeDetector: SwipeDetector, ) { - // Wait for a consumable event in [PointerEventPass.Main] pass - val consumablePointer = awaitConsumableEvent().changes.first() + val consumablePointer = + awaitConsumableEvent { + // We are searching for an event that can be used as the starting point for the + // drag gesture. Our options are: + // - Initial: These events should never be consumed by the MultiPointerDraggable + // since our ancestors can consume the gesture, but we would eliminate this + // possibility for our descendants. + // - Main: These events are consumed during the drag gesture, and they are a + // good place to start if the previous event has not been consumed. + // - Final: If the previous event has been consumed, we can wait for the Main + // pass to finish. If none of our ancestors were interested in the event, we + // can wait for an unconsumed event in the Final pass. + val previousConsumed = currentEvent.changes.fastAny { it.isConsumed } + if (previousConsumed) PointerEventPass.Final else PointerEventPass.Main + } + .changes + .first() var overSlop = 0f val drag = @@ -297,18 +314,22 @@ internal class MultiPointerDraggableNode( onDrag(controller, drag, overSlop) successful = - when (orientation) { - Orientation.Horizontal -> - horizontalDrag(drag.id) { - onDrag(controller, it, it.positionChange().toFloat()) - it.consume() - } - Orientation.Vertical -> - verticalDrag(drag.id) { - onDrag(controller, it, it.positionChange().toFloat()) - it.consume() - } - } + drag( + initialPointerId = drag.id, + hasDragged = { it.positionChangeIgnoreConsumed().toFloat() != 0f }, + onDrag = { + onDrag(controller, it, 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 @@ -322,7 +343,9 @@ internal class MultiPointerDraggableNode( } } - private suspend fun AwaitPointerEventScope.awaitConsumableEvent(): PointerEvent { + private suspend fun AwaitPointerEventScope.awaitConsumableEvent( + pass: () -> PointerEventPass, + ): PointerEvent { fun canBeConsumed(changes: List<PointerInputChange>): Boolean { // All pointers must be: return changes.fastAll { @@ -337,9 +360,7 @@ internal class MultiPointerDraggableNode( var event: PointerEvent do { - // To allow the descendants with the opportunity to consume the event, we wait for it in - // the Main pass. - event = awaitPointerEvent() + event = awaitPointerEvent(pass = pass()) } while (!canBeConsumed(event.changes)) // We found a consumable event in the Main pass @@ -352,4 +373,82 @@ internal class MultiPointerDraggableNode( Orientation.Horizontal -> x } } + + /** + * Continues to read drag events until all pointers are up or the drag event is canceled. The + * initial pointer to use for driving the drag is [initialPointerId]. [hasDragged] passes the + * result whether a change was detected from the drag function or not. + * + * Whenever the pointer moves, if [hasDragged] returns true, [onDrag] is called; otherwise, + * [onIgnoredEvent] is called. + * + * @return true when gesture ended with all pointers up and false when the gesture was canceled. + * + * Note: Inspired by DragGestureDetector.kt + */ + private suspend inline fun AwaitPointerEventScope.drag( + initialPointerId: PointerId, + hasDragged: (PointerInputChange) -> Boolean, + onDrag: (PointerInputChange) -> Unit, + onIgnoredEvent: (PointerInputChange) -> Unit, + ): Boolean { + val pointer = currentEvent.changes.fastFirstOrNull { it.id == initialPointerId } + val isPointerUp = pointer?.pressed != true + if (isPointerUp) { + return false // The pointer has already been lifted, so the gesture is canceled + } + var pointerId = initialPointerId + while (true) { + val change = awaitDragOrUp(pointerId, hasDragged, onIgnoredEvent) ?: return false + + if (change.isConsumed) { + return false + } + + if (change.changedToUpIgnoreConsumed()) { + return true + } + + onDrag(change) + pointerId = change.id + } + } + + /** + * Waits for a single drag in one axis, final pointer up, or all pointers are up. When + * [initialPointerId] has lifted, another pointer that is down is chosen to be the finger + * governing the drag. When the final pointer is lifted, that [PointerInputChange] is returned. + * When a drag is detected, that [PointerInputChange] is returned. A drag is only detected when + * [hasDragged] returns `true`. Events that should not be captured are passed to + * [onIgnoredEvent]. + * + * `null` is returned if there was an error in the pointer input stream and the pointer that was + * down was dropped before the 'up' was received. + * + * Note: Inspired by DragGestureDetector.kt + */ + private suspend inline fun AwaitPointerEventScope.awaitDragOrUp( + initialPointerId: PointerId, + hasDragged: (PointerInputChange) -> Boolean, + onIgnoredEvent: (PointerInputChange) -> Unit, + ): PointerInputChange? { + var pointerId = initialPointerId + while (true) { + val event = awaitPointerEvent() + val dragEvent = event.changes.fastFirstOrNull { it.id == pointerId } ?: return null + if (dragEvent.changedToUpIgnoreConsumed()) { + val otherDown = event.changes.fastFirstOrNull { it.pressed } + if (otherDown == null) { + // This is the last "up" + return dragEvent + } else { + pointerId = otherDown.id + } + } else if (hasDragged(dragEvent)) { + return dragEvent + } else { + onIgnoredEvent(dragEvent) + } + } + } } 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 2946b04af51a..b9251307f1d2 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 @@ -56,7 +56,7 @@ fun SceneTransitionLayout( modifier: Modifier = Modifier, swipeSourceDetector: SwipeSourceDetector = DefaultEdgeDetector, swipeDetector: SwipeDetector = DefaultSwipeDetector, - @FloatRange(from = 0.0, to = 0.5) transitionInterceptionThreshold: Float = 0f, + @FloatRange(from = 0.0, to = 0.5) transitionInterceptionThreshold: Float = 0.05f, scenes: SceneTransitionLayoutScope.() -> Unit, ) { SceneTransitionLayoutForTesting( diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutImpl.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutImpl.kt index 5fa7c874c879..7ea8cbdd7a97 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutImpl.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutImpl.kt @@ -107,6 +107,13 @@ internal class SceneTransitionLayoutImpl( _userActionDistanceScope = it } + /** + * The [LookaheadScope] of this layout, that can be used to compute offsets relative to the + * layout. + */ + internal lateinit var lookaheadScope: LookaheadScope + private set + init { updateScenes(builder) @@ -195,6 +202,8 @@ internal class SceneTransitionLayoutImpl( .then(LayoutElement(layoutImpl = this)) ) { LookaheadScope { + lookaheadScope = this + BackHandler() scenesToCompose().fastForEach { scene -> key(scene.key) { scene.Content() } } @@ -284,7 +293,15 @@ private class LayoutNode(var layoutImpl: SceneTransitionLayoutImpl) : width = fromSize.width height = fromSize.height } else { - val size = lerp(fromSize, toSize, transition.progress) + val overscrollSpec = transition.currentOverscrollSpec + val progress = + when { + overscrollSpec == null -> transition.progress + overscrollSpec.scene == transition.toScene -> 1f + else -> 0f + } + + val size = lerp(fromSize, toSize, progress) width = size.width.coerceAtLeast(0) height = size.height.coerceAtLeast(0) } 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 44affd968513..a8df6f4ae8b1 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 @@ -226,6 +226,12 @@ sealed interface TransitionState { val toScene: SceneKey, ) : TransitionState { /** + * The key of this transition. This should usually be null, but it can be specified to use a + * specific set of transformations associated to this transition. + */ + open val key: TransitionKey? = null + + /** * The progress of the transition. This is usually in the `[0; 1]` range, but it can also be * less than `0` or greater than `1` when using transitions with a spring AnimationSpec or * when flinging quickly during a swipe gesture. @@ -455,11 +461,7 @@ internal abstract class BaseSceneTransitionLayoutState( * * Important: you *must* call [finishTransition] once the transition is finished. */ - internal fun startTransition( - transition: TransitionState.Transition, - transitionKey: TransitionKey? = null, - chain: Boolean = true, - ) { + internal fun startTransition(transition: TransitionState.Transition, chain: Boolean = true) { checkThread() // Compute the [TransformationSpec] when the transition starts. @@ -469,7 +471,9 @@ internal abstract class BaseSceneTransitionLayoutState( // Update the transition specs. transition.transformationSpec = - transitions.transitionSpec(fromScene, toScene, key = transitionKey).transformationSpec() + transitions + .transitionSpec(fromScene, toScene, key = transition.key) + .transformationSpec() if (orientation != null) { transition.updateOverscrollSpecs( fromSpec = transitions.overscrollSpec(fromScene, orientation), @@ -568,9 +572,10 @@ internal abstract class BaseSceneTransitionLayoutState( originalTransition = transitionState, fromScene = targetCurrentScene, toScene = matchingLink.targetTo, + key = matchingLink.targetTransitionKey, ) - stateLink.target.startTransition(linkedTransition, matchingLink.targetTransitionKey) + stateLink.target.startTransition(linkedTransition) activeTransitionLinks[stateLink] = linkedTransition } } @@ -763,7 +768,7 @@ internal class HoistedSceneTransitionLayoutState( /** A [MutableSceneTransitionLayoutState] that holds the value for the current scene. */ internal class MutableSceneTransitionLayoutStateImpl( initialScene: SceneKey, - override var transitions: SceneTransitions, + override var transitions: SceneTransitions = transitions {}, private val canChangeScene: (SceneKey) -> Boolean = { true }, stateLinks: List<StateLink> = emptyList(), enableInterruptions: Boolean = DEFAULT_INTERRUPTIONS_ENABLED, 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 124ec290f42a..73ee4512c31f 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 @@ -20,7 +20,6 @@ import androidx.compose.ui.unit.IntSize import com.android.compose.animation.scene.Element import com.android.compose.animation.scene.ElementKey import com.android.compose.animation.scene.ElementMatcher -import com.android.compose.animation.scene.Scene import com.android.compose.animation.scene.SceneKey import com.android.compose.animation.scene.SceneTransitionLayoutImpl import com.android.compose.animation.scene.TransitionState @@ -34,28 +33,33 @@ internal class AnchoredSize( ) : PropertyTransformation<IntSize> { override fun transform( layoutImpl: SceneTransitionLayoutImpl, - scene: Scene, + scene: SceneKey, element: Element, sceneState: Element.SceneState, transition: TransitionState.Transition, value: IntSize, ): IntSize { fun anchorSizeIn(scene: SceneKey): IntSize { - val size = layoutImpl.elements[anchor]?.sceneStates?.get(scene)?.targetSize - return if (size != null && size != Element.SizeUnspecified) { - IntSize( - width = if (anchorWidth) size.width else value.width, - height = if (anchorHeight) size.height else value.height, - ) - } else { - value - } + val size = + layoutImpl.elements[anchor]?.sceneStates?.get(scene)?.targetSize?.takeIf { + it != Element.SizeUnspecified + } + ?: throwMissingAnchorException( + transformation = "AnchoredSize", + anchor = anchor, + scene = scene, + ) + + return IntSize( + width = if (anchorWidth) size.width else value.width, + height = if (anchorHeight) size.height else value.height, + ) } // This simple implementation assumes that the size of [element] is the same as the size of // the [anchor] in [scene], so simply transform to the size of the anchor in the other // scene. - return if (scene.key == transition.fromScene) { + return if (scene == transition.fromScene) { anchorSizeIn(transition.toScene) } else { anchorSizeIn(transition.fromScene) 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 7aa702b0bbd2..70dca4c065d3 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 @@ -21,7 +21,6 @@ import androidx.compose.ui.geometry.isSpecified import com.android.compose.animation.scene.Element import com.android.compose.animation.scene.ElementKey import com.android.compose.animation.scene.ElementMatcher -import com.android.compose.animation.scene.Scene import com.android.compose.animation.scene.SceneKey import com.android.compose.animation.scene.SceneTransitionLayoutImpl import com.android.compose.animation.scene.TransitionState @@ -33,13 +32,21 @@ internal class AnchoredTranslate( ) : PropertyTransformation<Offset> { override fun transform( layoutImpl: SceneTransitionLayoutImpl, - scene: Scene, + scene: SceneKey, element: Element, sceneState: Element.SceneState, transition: TransitionState.Transition, value: Offset, ): Offset { - val anchor = layoutImpl.elements[anchor] ?: return value + fun throwException(scene: SceneKey?): Nothing { + throwMissingAnchorException( + transformation = "AnchoredTranslate", + anchor = anchor, + scene = scene, + ) + } + + val anchor = layoutImpl.elements[anchor] ?: throwException(scene = null) fun anchorOffsetIn(scene: SceneKey): Offset? { return anchor.sceneStates[scene]?.targetOffset?.takeIf { it.isSpecified } } @@ -47,11 +54,13 @@ internal class AnchoredTranslate( // [element] will move the same amount as [anchor] does. // TODO(b/290184746): Also support anchors that are not shared but translated because of // other transformations, like an edge translation. - val anchorFromOffset = anchorOffsetIn(transition.fromScene) ?: return value - val anchorToOffset = anchorOffsetIn(transition.toScene) ?: return value + val anchorFromOffset = + anchorOffsetIn(transition.fromScene) ?: throwException(transition.fromScene) + val anchorToOffset = + anchorOffsetIn(transition.toScene) ?: throwException(transition.toScene) val offset = anchorToOffset - anchorFromOffset - return if (scene.key == transition.toScene) { + return if (scene == transition.toScene) { Offset( value.x - offset.x, value.y - offset.y, @@ -64,3 +73,20 @@ internal class AnchoredTranslate( } } } + +internal fun throwMissingAnchorException( + transformation: String, + anchor: ElementKey, + scene: SceneKey?, +): Nothing { + error( + """ + Anchor ${anchor.debugName} does not have a target state in scene ${scene?.debugName}. + This either means that it was not composed at all during the transition or that it was + composed too late, for instance during layout/subcomposition. To avoid flickers in + $transformation, you should make sure that the composition and layout of anchor is *not* + deferred, for instance by moving it out of lazy layouts. + """ + .trimIndent() + ) +} 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 6704a3bbeff2..98c2dd3dc1cc 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 @@ -20,7 +20,7 @@ import androidx.compose.ui.geometry.Offset import com.android.compose.animation.scene.Element import com.android.compose.animation.scene.ElementMatcher import com.android.compose.animation.scene.Scale -import com.android.compose.animation.scene.Scene +import com.android.compose.animation.scene.SceneKey import com.android.compose.animation.scene.SceneTransitionLayoutImpl import com.android.compose.animation.scene.TransitionState @@ -37,7 +37,7 @@ internal class DrawScale( override fun transform( layoutImpl: SceneTransitionLayoutImpl, - scene: Scene, + scene: SceneKey, element: Element, sceneState: Element.SceneState, transition: TransitionState.Transition, 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 191a8fbcd009..aa8dc38fdd8f 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 @@ -20,7 +20,7 @@ import androidx.compose.ui.geometry.Offset import com.android.compose.animation.scene.Edge import com.android.compose.animation.scene.Element import com.android.compose.animation.scene.ElementMatcher -import com.android.compose.animation.scene.Scene +import com.android.compose.animation.scene.SceneKey import com.android.compose.animation.scene.SceneTransitionLayoutImpl import com.android.compose.animation.scene.TransitionState @@ -32,13 +32,13 @@ internal class EdgeTranslate( ) : PropertyTransformation<Offset> { override fun transform( layoutImpl: SceneTransitionLayoutImpl, - scene: Scene, + scene: SceneKey, element: Element, sceneState: Element.SceneState, transition: TransitionState.Transition, value: Offset ): Offset { - val sceneSize = scene.targetSize + val sceneSize = layoutImpl.scene(scene).targetSize val elementSize = sceneState.targetSize if (elementSize == Element.SizeUnspecified) { return value 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 41f626e24e79..ada814e04ab2 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 @@ -18,7 +18,7 @@ package com.android.compose.animation.scene.transformation import com.android.compose.animation.scene.Element import com.android.compose.animation.scene.ElementMatcher -import com.android.compose.animation.scene.Scene +import com.android.compose.animation.scene.SceneKey import com.android.compose.animation.scene.SceneTransitionLayoutImpl import com.android.compose.animation.scene.TransitionState @@ -28,7 +28,7 @@ internal class Fade( ) : PropertyTransformation<Float> { override fun transform( layoutImpl: SceneTransitionLayoutImpl, - scene: Scene, + scene: SceneKey, element: Element, sceneState: Element.SceneState, transition: TransitionState.Transition, 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 f5207dc4d345..dca8f8521f1a 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 @@ -19,7 +19,7 @@ package com.android.compose.animation.scene.transformation import androidx.compose.ui.unit.IntSize import com.android.compose.animation.scene.Element import com.android.compose.animation.scene.ElementMatcher -import com.android.compose.animation.scene.Scene +import com.android.compose.animation.scene.SceneKey import com.android.compose.animation.scene.SceneTransitionLayoutImpl import com.android.compose.animation.scene.TransitionState import kotlin.math.roundToInt @@ -35,7 +35,7 @@ internal class ScaleSize( ) : PropertyTransformation<IntSize> { override fun transform( layoutImpl: SceneTransitionLayoutImpl, - scene: Scene, + scene: SceneKey, element: Element, sceneState: Element.SceneState, transition: TransitionState.Transition, 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 603f7ba947c4..7be9ce1e39fc 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 @@ -21,7 +21,7 @@ import androidx.compose.ui.util.fastCoerceAtMost import androidx.compose.ui.util.fastCoerceIn import com.android.compose.animation.scene.Element import com.android.compose.animation.scene.ElementMatcher -import com.android.compose.animation.scene.Scene +import com.android.compose.animation.scene.SceneKey import com.android.compose.animation.scene.SceneTransitionLayoutImpl import com.android.compose.animation.scene.TransitionState @@ -61,7 +61,7 @@ internal sealed interface PropertyTransformation<T> : Transformation { // to these internal classes. fun transform( layoutImpl: SceneTransitionLayoutImpl, - scene: Scene, + scene: SceneKey, element: Element, sceneState: Element.SceneState, transition: TransitionState.Transition, 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 849c9d71ec2f..f066511f68ab 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 @@ -22,7 +22,7 @@ import androidx.compose.ui.unit.dp import com.android.compose.animation.scene.Element import com.android.compose.animation.scene.ElementMatcher import com.android.compose.animation.scene.OverscrollScope -import com.android.compose.animation.scene.Scene +import com.android.compose.animation.scene.SceneKey import com.android.compose.animation.scene.SceneTransitionLayoutImpl import com.android.compose.animation.scene.TransitionState @@ -33,7 +33,7 @@ internal class Translate( ) : PropertyTransformation<Offset> { override fun transform( layoutImpl: SceneTransitionLayoutImpl, - scene: Scene, + scene: SceneKey, element: Element, sceneState: Element.SceneState, transition: TransitionState.Transition, @@ -55,7 +55,7 @@ internal class OverscrollTranslate( ) : PropertyTransformation<Offset> { override fun transform( layoutImpl: SceneTransitionLayoutImpl, - scene: Scene, + scene: SceneKey, element: Element, sceneState: Element.SceneState, transition: TransitionState.Transition, 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 index 79f126d24561..ed9888560f05 100644 --- 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 @@ -17,6 +17,7 @@ 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.TransitionState import kotlinx.coroutines.Job @@ -25,6 +26,7 @@ internal class LinkedTransition( private val originalTransition: TransitionState.Transition, fromScene: SceneKey, toScene: SceneKey, + override val key: TransitionKey? = null, ) : TransitionState.Transition(fromScene, toScene) { override val currentScene: SceneKey diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/AnimatedSharedAsStateTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/AnimatedSharedAsStateTest.kt index 6e8b208ea9e8..a7889e2fac58 100644 --- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/AnimatedSharedAsStateTest.kt +++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/AnimatedSharedAsStateTest.kt @@ -18,10 +18,13 @@ package com.android.compose.animation.scene import androidx.compose.animation.core.LinearEasing import androidx.compose.animation.core.tween +import androidx.compose.foundation.gestures.Orientation import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.SideEffect import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.setValue import androidx.compose.runtime.snapshotFlow import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color @@ -443,4 +446,56 @@ class AnimatedSharedAsStateTest { assertThat(lastValues[bar]?.get(SceneC)).isWithin(0.001f).of(7f) assertThat(lastValues[bar]?.get(SceneD)).isWithin(0.001f).of(7f) } + + @Test + fun animatedValueDoesNotOverscrollWhenOverscrollIsSpecified() { + val state = + rule.runOnUiThread { + MutableSceneTransitionLayoutStateImpl( + SceneA, + transitions { overscroll(SceneB, Orientation.Horizontal) } + ) + } + + val key = ValueKey("foo") + val lastValues = mutableMapOf<SceneKey, Float>() + + @Composable + fun SceneScope.animateFloat(value: Float, key: ValueKey) { + val animatedValue = animateSceneFloatAsState(value, key) + LaunchedEffect(animatedValue) { + snapshotFlow { animatedValue.value }.collect { lastValues[sceneKey] = it } + } + } + + rule.setContent { + SceneTransitionLayout(state) { + scene(SceneA) { animateFloat(0f, key) } + scene(SceneB) { animateFloat(100f, key) } + } + } + + // Overscroll on A at -100%: value should be interpolated given that there is no overscroll + // defined for scene A. + var progress by mutableStateOf(-1f) + rule.runOnIdle { + state.startTransition(transition(from = SceneA, to = SceneB, progress = { progress })) + } + rule.waitForIdle() + assertThat(lastValues[SceneA]).isWithin(0.001f).of(-100f) + assertThat(lastValues[SceneB]).isWithin(0.001f).of(-100f) + + // Middle of the transition. + progress = 0.5f + rule.waitForIdle() + assertThat(lastValues[SceneA]).isWithin(0.001f).of(50f) + assertThat(lastValues[SceneB]).isWithin(0.001f).of(50f) + + // Overscroll on B at 200%: value should not be interpolated given that there is an + // overscroll defined for scene B. + progress = 2f + rule.waitForIdle() + assertThat(lastValues[SceneA]).isWithin(0.001f).of(100f) + assertThat(lastValues[SceneB]).isWithin(0.001f).of(100f) + } } 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 beb74bc9bb36..a18da7364983 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 @@ -32,6 +32,7 @@ import androidx.compose.foundation.layout.offset import androidx.compose.foundation.layout.size import androidx.compose.foundation.pager.HorizontalPager import androidx.compose.foundation.pager.PagerState +import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.SideEffect @@ -46,9 +47,13 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.geometry.Offset import androidx.compose.ui.layout.approachLayout import androidx.compose.ui.platform.LocalViewConfiguration +import androidx.compose.ui.platform.testTag +import androidx.compose.ui.test.assertIsDisplayed import androidx.compose.ui.test.assertIsNotDisplayed import androidx.compose.ui.test.assertPositionInRootIsEqualTo import androidx.compose.ui.test.assertTopPositionInRootIsEqualTo +import androidx.compose.ui.test.hasTestTag +import androidx.compose.ui.test.hasText import androidx.compose.ui.test.junit4.createComposeRule import androidx.compose.ui.test.onNodeWithTag import androidx.compose.ui.test.onRoot @@ -635,10 +640,7 @@ class ElementTest { // Change the current transition. rule.runOnUiThread { - state.startTransition( - transition(from = SceneA, to = SceneB, progress = { 0.5f }), - transitionKey = null, - ) + state.startTransition(transition(from = SceneA, to = SceneB, progress = { 0.5f })) } // The size of Foo should still be 20dp given that the new state was not composed yet. @@ -1167,7 +1169,7 @@ class ElementTest { val offsetInAToB = lerp(offsetInA, offsetInB, aToBProgress) val sizeInAToB = lerp(sizeInA, sizeInB, aToBProgress) val valueInAToB = lerp(valueInA, valueInB, aToBProgress) - rule.runOnUiThread { state.startTransition(aToB, transitionKey = null) } + rule.runOnUiThread { state.startTransition(aToB) } rule .onNode(isElement(TestElements.Foo, SceneB)) .assertSizeIsEqualTo(sizeInAToB) @@ -1187,7 +1189,7 @@ class ElementTest { progress = { bToCProgress }, interruptionProgress = { interruptionProgress }, ) - rule.runOnUiThread { state.startTransition(bToC, transitionKey = null) } + rule.runOnUiThread { state.startTransition(bToC) } // The interruption deltas, which will be multiplied by the interruption progress then added // to the current transition offset and size. @@ -1329,9 +1331,9 @@ class ElementTest { interruptionProgress = { bToCInterruptionProgress }, onFinish = neverFinish(), ) - rule.runOnUiThread { state.startTransition(aToB, transitionKey = null) } + rule.runOnUiThread { state.startTransition(aToB) } rule.waitForIdle() - rule.runOnUiThread { state.startTransition(bToC, transitionKey = null) } + rule.runOnUiThread { state.startTransition(bToC) } // Foo is placed in both B and C given that the shared transition is disabled. In B, its // offset is impacted by the interruption but in C it is not. @@ -1367,7 +1369,7 @@ class ElementTest { progress = { 0.7f }, interruptionProgress = { 1f }, ) - rule.runOnUiThread { state.startTransition(bToA, transitionKey = null) } + rule.runOnUiThread { state.startTransition(bToA) } // Foo should have the position it had in B right before the interruption. rule @@ -1391,8 +1393,7 @@ class ElementTest { to = SceneB, progress = { -1f }, orientation = Orientation.Horizontal - ), - transitionKey = null, + ) ) } } @@ -1418,4 +1419,522 @@ class ElementTest { assertThat(bState.targetSize).isNotEqualTo(Element.SizeUnspecified) assertThat(bState.targetOffset).isNotEqualTo(Offset.Unspecified) } + + @Test + fun lastAlphaIsNotSetByOutdatedLayer() = runTest { + val state = + rule.runOnUiThread { + MutableSceneTransitionLayoutStateImpl( + SceneA, + transitions { from(SceneA, to = SceneB) { fade(TestElements.Foo) } } + ) + } + + lateinit var layoutImpl: SceneTransitionLayoutImpl + rule.setContent { + SceneTransitionLayoutForTesting(state, onLayoutImpl = { layoutImpl = it }) { + scene(SceneA) {} + scene(SceneB) { Box(Modifier.element(TestElements.Foo)) } + scene(SceneC) { Box(Modifier.element(TestElements.Foo)) } + } + } + + // Start A => B at 0.5f. + var aToBProgress by mutableStateOf(0.5f) + rule.runOnUiThread { + state.startTransition( + transition( + from = SceneA, + to = SceneB, + progress = { aToBProgress }, + onFinish = neverFinish(), + ) + ) + } + rule.waitForIdle() + + val foo = checkNotNull(layoutImpl.elements[TestElements.Foo]) + assertThat(foo.sceneStates[SceneA]).isNull() + + val fooInB = foo.sceneStates[SceneB] + assertThat(fooInB).isNotNull() + assertThat(fooInB!!.lastAlpha).isEqualTo(0.5f) + + // Move the progress of A => B to 0.7f. + aToBProgress = 0.7f + rule.waitForIdle() + assertThat(fooInB.lastAlpha).isEqualTo(0.7f) + + // Start B => C at 0.3f. + rule.runOnUiThread { + state.startTransition(transition(from = SceneB, to = SceneC, progress = { 0.3f })) + } + rule.waitForIdle() + val fooInC = foo.sceneStates[SceneC] + assertThat(fooInC).isNotNull() + assertThat(fooInC!!.lastAlpha).isEqualTo(1f) + assertThat(fooInB.lastAlpha).isEqualTo(Element.AlphaUnspecified) + + // Move the progress of A => B to 0.9f. This shouldn't change anything given that B => C is + // now the transition applied to Foo. + aToBProgress = 0.9f + rule.waitForIdle() + assertThat(fooInC.lastAlpha).isEqualTo(1f) + assertThat(fooInB.lastAlpha).isEqualTo(Element.AlphaUnspecified) + } + + @Test + fun fadingElementsDontAppearInstantly() { + val state = + rule.runOnUiThread { + MutableSceneTransitionLayoutStateImpl( + SceneA, + transitions { from(SceneA, to = SceneB) { fade(TestElements.Foo) } } + ) + } + + lateinit var layoutImpl: SceneTransitionLayoutImpl + rule.setContent { + SceneTransitionLayoutForTesting(state, onLayoutImpl = { layoutImpl = it }) { + scene(SceneA) {} + scene(SceneB) { Box(Modifier.element(TestElements.Foo)) } + } + } + + // Start A => B at 60%. + var interruptionProgress by mutableStateOf(1f) + rule.runOnUiThread { + state.startTransition( + transition( + from = SceneA, + to = SceneB, + progress = { 0.6f }, + interruptionProgress = { interruptionProgress }, + ) + ) + } + rule.waitForIdle() + + // Alpha of Foo should be 0f at interruption progress 100%. + val fooInB = layoutImpl.elements.getValue(TestElements.Foo).sceneStates.getValue(SceneB) + assertThat(fooInB.lastAlpha).isEqualTo(0f) + + // Alpha of Foo should be 0.6f at interruption progress 0%. + interruptionProgress = 0f + rule.waitForIdle() + assertThat(fooInB.lastAlpha).isEqualTo(0.6f) + + // Alpha of Foo should be 0.3f at interruption progress 50%. + interruptionProgress = 0.5f + rule.waitForIdle() + assertThat(fooInB.lastAlpha).isEqualTo(0.3f) + } + + @Test + fun sharedElementIsOnlyPlacedInOverscrollingScene() { + val state = + rule.runOnUiThread { + MutableSceneTransitionLayoutStateImpl( + SceneA, + transitions { + overscroll(SceneA, Orientation.Horizontal) + overscroll(SceneB, Orientation.Horizontal) + } + ) + } + + @Composable + fun SceneScope.Foo() { + Box(Modifier.element(TestElements.Foo).size(10.dp)) + } + + rule.setContent { + SceneTransitionLayout(state) { + scene(SceneA) { Foo() } + scene(SceneB) { Foo() } + } + } + + rule.onNode(isElement(TestElements.Foo, SceneA)).assertIsDisplayed() + rule.onNode(isElement(TestElements.Foo, SceneB)).assertDoesNotExist() + + // A => B while overscrolling at scene B. + var progress by mutableStateOf(2f) + rule.runOnUiThread { + state.startTransition(transition(from = SceneA, to = SceneB, progress = { progress })) + } + rule.waitForIdle() + + // Foo should only be placed in scene B. + rule.onNode(isElement(TestElements.Foo, SceneA)).assertExists().assertIsNotDisplayed() + rule.onNode(isElement(TestElements.Foo, SceneB)).assertIsDisplayed() + + // Overscroll at scene A. + progress = -1f + rule.waitForIdle() + + // Foo should only be placed in scene A. + rule.onNode(isElement(TestElements.Foo, SceneA)).assertIsDisplayed() + rule.onNode(isElement(TestElements.Foo, SceneB)).assertExists().assertIsNotDisplayed() + } + + @Test + fun sharedMovableElementIsOnlyComposedInOverscrollingScene() { + val state = + rule.runOnUiThread { + MutableSceneTransitionLayoutStateImpl( + SceneA, + transitions { + overscroll(SceneA, Orientation.Horizontal) + overscroll(SceneB, Orientation.Horizontal) + } + ) + } + + val fooInA = "fooInA" + val fooInB = "fooInB" + + @Composable + fun SceneScope.MovableFoo(text: String, modifier: Modifier = Modifier) { + MovableElement(TestElements.Foo, modifier) { content { Text(text) } } + } + + rule.setContent { + SceneTransitionLayout(state) { + scene(SceneA) { MovableFoo(text = fooInA) } + scene(SceneB) { MovableFoo(text = fooInB) } + } + } + + rule.onNode(hasText(fooInA)).assertIsDisplayed() + rule.onNode(hasText(fooInB)).assertDoesNotExist() + + // A => B while overscrolling at scene B. + var progress by mutableStateOf(2f) + rule.runOnUiThread { + state.startTransition(transition(from = SceneA, to = SceneB, progress = { progress })) + } + rule.waitForIdle() + + // Foo content should only be composed in scene B. + rule.onNode(hasText(fooInA)).assertDoesNotExist() + rule.onNode(hasText(fooInB)).assertIsDisplayed() + + // Overscroll at scene A. + progress = -1f + rule.waitForIdle() + + // Foo content should only be composed in scene A. + rule.onNode(hasText(fooInA)).assertIsDisplayed() + rule.onNode(hasText(fooInB)).assertDoesNotExist() + } + + @Test + fun interruptionThenOverscroll() = runTest { + val state = + rule.runOnUiThread { + MutableSceneTransitionLayoutStateImpl( + SceneA, + transitions { + overscroll(SceneB, Orientation.Vertical) { + translate(TestElements.Foo, y = 15.dp) + } + } + ) + } + + @Composable + fun SceneScope.SceneWithFoo(offset: DpOffset, modifier: Modifier = Modifier) { + Box(modifier.fillMaxSize()) { + Box(Modifier.offset(offset.x, offset.y).element(TestElements.Foo).size(100.dp)) + } + } + + rule.setContent { + SceneTransitionLayout(state, Modifier.size(200.dp)) { + scene(SceneA) { SceneWithFoo(offset = DpOffset.Zero) } + scene(SceneB) { SceneWithFoo(offset = DpOffset(x = 40.dp, y = 0.dp)) } + scene(SceneC) { SceneWithFoo(offset = DpOffset(x = 40.dp, y = 40.dp)) } + } + } + + // Start A => B at 75%. + rule.runOnUiThread { + state.startTransition( + transition( + from = SceneA, + to = SceneB, + progress = { 0.75f }, + onFinish = neverFinish(), + ) + ) + } + + // Foo should be at offset (30dp, 0dp) and placed in scene B. + rule.onNode(isElement(TestElements.Foo, SceneA)).assertIsNotDisplayed() + rule.onNode(isElement(TestElements.Foo, SceneB)).assertPositionInRootIsEqualTo(30.dp, 0.dp) + rule.onNode(isElement(TestElements.Foo, SceneC)).assertIsNotDisplayed() + + // Interrupt A => B with B => C at 0%. + var progress by mutableStateOf(0f) + var interruptionProgress by mutableStateOf(1f) + rule.runOnUiThread { + state.startTransition( + transition( + from = SceneB, + to = SceneC, + progress = { progress }, + interruptionProgress = { interruptionProgress }, + orientation = Orientation.Vertical, + onFinish = neverFinish(), + ) + ) + } + + // Because interruption progress is at 100M, Foo should still be at offset (30dp, 0dp) but + // placed in scene C. + rule.onNode(isElement(TestElements.Foo, SceneA)).assertIsNotDisplayed() + rule.onNode(isElement(TestElements.Foo, SceneB)).assertIsNotDisplayed() + rule.onNode(isElement(TestElements.Foo, SceneC)).assertPositionInRootIsEqualTo(30.dp, 0.dp) + + // Overscroll B => C on scene B at -100%. Because overscrolling on B => C translates Foo + // vertically by -15dp and that interruptionProgress is still 100%, we should now be at + // (30dp, -15dp) + progress = -1f + rule.onNode(isElement(TestElements.Foo, SceneA)).assertIsNotDisplayed() + rule + .onNode(isElement(TestElements.Foo, SceneB)) + .assertPositionInRootIsEqualTo(30.dp, -15.dp) + rule.onNode(isElement(TestElements.Foo, SceneC)).assertIsNotDisplayed() + + // Finish the interruption, we should now be at (40dp, -15dp), still on scene B. + interruptionProgress = 0f + rule.onNode(isElement(TestElements.Foo, SceneA)).assertIsNotDisplayed() + rule + .onNode(isElement(TestElements.Foo, SceneB)) + .assertPositionInRootIsEqualTo(40.dp, -15.dp) + rule.onNode(isElement(TestElements.Foo, SceneC)).assertIsNotDisplayed() + + // Finish the transition, we should be at the final position (40dp, 40dp) on scene C. + progress = 1f + rule.onNode(isElement(TestElements.Foo, SceneA)).assertIsNotDisplayed() + rule.onNode(isElement(TestElements.Foo, SceneB)).assertIsNotDisplayed() + rule.onNode(isElement(TestElements.Foo, SceneC)).assertPositionInRootIsEqualTo(40.dp, 40.dp) + } + + @Test + fun lastPlacementValuesAreClearedOnNestedElements() { + val state = rule.runOnIdle { MutableSceneTransitionLayoutStateImpl(SceneA) } + + @Composable + fun SceneScope.NestedFooBar() { + Box(Modifier.element(TestElements.Foo)) { + Box(Modifier.element(TestElements.Bar).size(10.dp)) + } + } + + lateinit var layoutImpl: SceneTransitionLayoutImpl + rule.setContent { + SceneTransitionLayoutForTesting(state, onLayoutImpl = { layoutImpl = it }) { + scene(SceneA) { NestedFooBar() } + scene(SceneB) { NestedFooBar() } + } + } + + // Idle on A: composed and placed only in B. + rule.onNode(isElement(TestElements.Foo, SceneA)).assertIsDisplayed() + rule.onNode(isElement(TestElements.Bar, SceneA)).assertIsDisplayed() + rule.onNode(isElement(TestElements.Foo, SceneB)).assertDoesNotExist() + rule.onNode(isElement(TestElements.Bar, SceneB)).assertDoesNotExist() + + assertThat(layoutImpl.elements).containsKey(TestElements.Foo) + assertThat(layoutImpl.elements).containsKey(TestElements.Bar) + val foo = layoutImpl.elements.getValue(TestElements.Foo) + val bar = layoutImpl.elements.getValue(TestElements.Bar) + + assertThat(foo.sceneStates).containsKey(SceneA) + assertThat(bar.sceneStates).containsKey(SceneA) + assertThat(foo.sceneStates).doesNotContainKey(SceneB) + assertThat(bar.sceneStates).doesNotContainKey(SceneB) + + val fooInA = foo.sceneStates.getValue(SceneA) + val barInA = bar.sceneStates.getValue(SceneA) + assertThat(fooInA.lastOffset).isNotEqualTo(Offset.Unspecified) + assertThat(fooInA.lastAlpha).isNotEqualTo(Element.AlphaUnspecified) + assertThat(fooInA.lastScale).isNotEqualTo(Scale.Unspecified) + + assertThat(barInA.lastOffset).isNotEqualTo(Offset.Unspecified) + assertThat(barInA.lastAlpha).isNotEqualTo(Element.AlphaUnspecified) + assertThat(barInA.lastScale).isNotEqualTo(Scale.Unspecified) + + // A => B: composed in both and placed only in B. + rule.runOnUiThread { state.startTransition(transition(from = SceneA, to = SceneB)) } + rule.onNode(isElement(TestElements.Foo, SceneA)).assertExists().assertIsNotDisplayed() + rule.onNode(isElement(TestElements.Bar, SceneA)).assertExists().assertIsNotDisplayed() + rule.onNode(isElement(TestElements.Foo, SceneB)).assertIsDisplayed() + rule.onNode(isElement(TestElements.Bar, SceneB)).assertIsDisplayed() + + assertThat(foo.sceneStates).containsKey(SceneB) + assertThat(bar.sceneStates).containsKey(SceneB) + + val fooInB = foo.sceneStates.getValue(SceneB) + val barInB = bar.sceneStates.getValue(SceneB) + assertThat(fooInA.lastOffset).isEqualTo(Offset.Unspecified) + assertThat(fooInA.lastAlpha).isEqualTo(Element.AlphaUnspecified) + assertThat(fooInA.lastScale).isEqualTo(Scale.Unspecified) + assertThat(fooInB.lastOffset).isNotEqualTo(Offset.Unspecified) + assertThat(fooInB.lastAlpha).isNotEqualTo(Element.AlphaUnspecified) + assertThat(fooInB.lastScale).isNotEqualTo(Scale.Unspecified) + + assertThat(barInA.lastOffset).isEqualTo(Offset.Unspecified) + assertThat(barInA.lastAlpha).isEqualTo(Element.AlphaUnspecified) + assertThat(barInA.lastScale).isEqualTo(Scale.Unspecified) + assertThat(barInB.lastOffset).isNotEqualTo(Offset.Unspecified) + assertThat(barInB.lastAlpha).isNotEqualTo(Element.AlphaUnspecified) + assertThat(barInB.lastScale).isNotEqualTo(Scale.Unspecified) + } + + @Test + fun currentTransitionSceneIsUsedToComputeElementValues() = runTest { + val state = + rule.runOnIdle { + MutableSceneTransitionLayoutStateImpl( + SceneA, + transitions { + from(SceneB, to = SceneC) { + scaleSize(TestElements.Foo, width = 2f, height = 3f) + } + } + ) + } + + @Composable + fun SceneScope.Foo() { + Box(Modifier.testTag("fooParentIn${sceneKey.debugName}")) { + Box(Modifier.element(TestElements.Foo).size(20.dp)) + } + } + + rule.setContent { + SceneTransitionLayout(state, Modifier.size(200.dp)) { + scene(SceneA) { Foo() } + scene(SceneB) {} + scene(SceneC) { Foo() } + } + } + + // We have 2 transitions: + // - A => B at 100% + // - B => C at 0% + // So Foo should have a size of (40dp, 60dp) in both A and C given that it is scaling its + // size in B => C. + rule.runOnUiThread { + state.startTransition( + transition(from = SceneA, to = SceneB, progress = { 1f }, onFinish = neverFinish()) + ) + state.startTransition(transition(from = SceneB, to = SceneC, progress = { 0f })) + } + + rule.onNode(hasTestTag("fooParentInSceneA")).assertSizeIsEqualTo(40.dp, 60.dp) + rule.onNode(hasTestTag("fooParentInSceneC")).assertSizeIsEqualTo(40.dp, 60.dp) + } + + @Test + fun interruptionDeltasAreProperlyCleaned() = runTest { + val state = rule.runOnIdle { MutableSceneTransitionLayoutStateImpl(SceneA) } + + @Composable + fun SceneScope.Foo(offset: Dp) { + Box(Modifier.fillMaxSize()) { + Box(Modifier.offset(offset, offset).element(TestElements.Foo).size(20.dp)) + } + } + + rule.setContent { + SceneTransitionLayout(state, Modifier.size(200.dp)) { + scene(SceneA) { Foo(offset = 0.dp) } + scene(SceneB) { Foo(offset = 20.dp) } + scene(SceneC) { Foo(offset = 40.dp) } + } + } + + // Start A => B at 50%. + val aToB = + transition(from = SceneA, to = SceneB, progress = { 0.5f }, onFinish = neverFinish()) + rule.runOnUiThread { state.startTransition(aToB) } + rule.onNode(isElement(TestElements.Foo, SceneB)).assertPositionInRootIsEqualTo(10.dp, 10.dp) + + // Start B => C at 0%. This will compute an interruption delta of (-10dp, -10dp) so that the + // position of Foo is unchanged and converges to (20dp, 20dp). + var interruptionProgress by mutableStateOf(1f) + val bToC = + transition( + from = SceneB, + to = SceneC, + progress = { 0f }, + interruptionProgress = { interruptionProgress }, + onFinish = neverFinish(), + ) + rule.runOnUiThread { state.startTransition(bToC) } + rule.onNode(isElement(TestElements.Foo, SceneC)).assertPositionInRootIsEqualTo(10.dp, 10.dp) + + // Finish the interruption and leave the transition progress at 0f. We should be at the same + // state as in B. + interruptionProgress = 0f + rule.onNode(isElement(TestElements.Foo, SceneC)).assertPositionInRootIsEqualTo(20.dp, 20.dp) + + // Finish both transitions but directly start a new one B => A with interruption progress + // 100%. We should be at (20dp, 20dp), unless the interruption deltas have not been + // correctly cleaned. + rule.runOnUiThread { + state.finishTransition(aToB, idleScene = SceneB) + state.finishTransition(bToC, idleScene = SceneB) + state.startTransition( + transition( + from = SceneB, + to = SceneA, + progress = { 0f }, + interruptionProgress = { 1f }, + ) + ) + } + rule.onNode(isElement(TestElements.Foo, SceneB)).assertPositionInRootIsEqualTo(20.dp, 20.dp) + } + + @Test + fun lastSizeIsUnspecifiedWhenOverscrollingOtherScene() = runTest { + val state = + rule.runOnIdle { + MutableSceneTransitionLayoutStateImpl( + SceneA, + transitions { overscroll(SceneA, Orientation.Horizontal) } + ) + } + + @Composable + fun SceneScope.Foo() { + Box(Modifier.element(TestElements.Foo).size(10.dp)) + } + + lateinit var layoutImpl: SceneTransitionLayoutImpl + rule.setContent { + SceneTransitionLayoutForTesting(state, onLayoutImpl = { layoutImpl = it }) { + scene(SceneA) { Foo() } + scene(SceneB) { Foo() } + } + } + + // Overscroll A => B on A. + rule.runOnUiThread { + state.startTransition( + transition(from = SceneA, to = SceneB, progress = { -1f }, onFinish = neverFinish()) + ) + } + rule.waitForIdle() + + assertThat( + layoutImpl.elements.getValue(TestElements.Foo).sceneStates.getValue(SceneB).lastSize + ) + .isEqualTo(Element.SizeUnspecified) + } } diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/InterruptionHandlerTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/InterruptionHandlerTest.kt index 85d4165b4bf6..09d1a827d0c7 100644 --- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/InterruptionHandlerTest.kt +++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/InterruptionHandlerTest.kt @@ -40,7 +40,7 @@ class InterruptionHandlerTest { val state = MutableSceneTransitionLayoutState( SceneA, - transitions { /* default interruption handler */}, + transitions { /* default interruption handler */ }, ) state.setTargetScene(SceneB, coroutineScope = this) @@ -160,7 +160,7 @@ class InterruptionHandlerTest { progressVelocity = { progressVelocity }, onFinish = { launch {} }, ) - state.startTransition(aToB, transitionKey = null) + state.startTransition(aToB) // Animate back to A. The previous transition is reversed, i.e. it has the same (from, to) // pair, and its velocity is used when animating the progress back to 0. @@ -186,7 +186,7 @@ class InterruptionHandlerTest { progressVelocity = { progressVelocity }, onFinish = { launch {} }, ) - state.startTransition(aToB, transitionKey = null) + state.startTransition(aToB) // Animate to B. The previous transition is reversed, i.e. it has the same (from, to) pair, // and its velocity is used when animating the progress to 1. 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 4bb643f8b89e..1a0740b54892 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 @@ -349,6 +349,121 @@ class MultiPointerDraggableTest { } @Test + fun multiPointerDuringAnotherGestureWaitAConsumableEventAfterMainPass() { + val size = 200f + val middle = Offset(size / 2f, size / 2f) + + var verticalStarted = false + var verticalDragged = false + var verticalStopped = false + var horizontalStarted = false + var horizontalDragged = false + var horizontalStopped = false + + var touchSlop = 0f + rule.setContent { + touchSlop = LocalViewConfiguration.current.touchSlop + Box( + Modifier.size(with(LocalDensity.current) { Size(size, size).toDpSize() }) + .multiPointerDraggable( + orientation = Orientation.Vertical, + enabled = { true }, + startDragImmediately = { false }, + onDragStarted = { _, _, _ -> + verticalStarted = true + object : DragController { + override fun onDrag(delta: Float) { + verticalDragged = true + } + + override fun onStop(velocity: Float, canChangeScene: Boolean) { + verticalStopped = true + } + } + }, + ) + .multiPointerDraggable( + orientation = Orientation.Horizontal, + enabled = { true }, + startDragImmediately = { false }, + onDragStarted = { _, _, _ -> + horizontalStarted = true + object : DragController { + override fun onDrag(delta: Float) { + horizontalDragged = true + } + + override fun onStop(velocity: Float, canChangeScene: Boolean) { + horizontalStopped = true + } + } + }, + ) + ) + } + + fun startDraggingDown() { + rule.onRoot().performTouchInput { + down(middle) + moveBy(Offset(0f, touchSlop)) + } + } + + fun startDraggingRight() { + rule.onRoot().performTouchInput { + down(middle) + moveBy(Offset(touchSlop, 0f)) + } + } + + fun stopDragging() { + rule.onRoot().performTouchInput { up() } + } + + fun continueDown() { + rule.onRoot().performTouchInput { moveBy(Offset(0f, touchSlop)) } + } + + fun continueRight() { + rule.onRoot().performTouchInput { moveBy(Offset(touchSlop, 0f)) } + } + + startDraggingDown() + assertThat(verticalStarted).isTrue() + assertThat(verticalDragged).isTrue() + assertThat(verticalStopped).isFalse() + + // Ignore right swipe, do not interrupt the dragging gesture. + continueRight() + assertThat(horizontalStarted).isFalse() + assertThat(horizontalDragged).isFalse() + assertThat(horizontalStopped).isFalse() + assertThat(verticalStopped).isFalse() + + stopDragging() + assertThat(verticalStopped).isTrue() + + verticalStarted = false + verticalDragged = false + verticalStopped = false + + startDraggingRight() + assertThat(horizontalStarted).isTrue() + assertThat(horizontalDragged).isTrue() + assertThat(horizontalStopped).isFalse() + + // Ignore down swipe, do not interrupt the dragging gesture. + continueDown() + assertThat(verticalStarted).isFalse() + assertThat(verticalDragged).isFalse() + assertThat(verticalStopped).isFalse() + assertThat(horizontalStopped).isFalse() + + stopDragging() + assertThat(horizontalStopped).isTrue() + } + + @Test fun multiPointerSwipeDetectorInteraction() { val size = 200f val middle = Offset(size / 2f, size / 2f) diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ObservableTransitionStateTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ObservableTransitionStateTest.kt index 2a75e13066df..55431354b693 100644 --- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ObservableTransitionStateTest.kt +++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ObservableTransitionStateTest.kt @@ -135,7 +135,7 @@ class ObservableTransitionStateTest { var transitionCurrentScene by mutableStateOf(SceneA) val transition = transition(from = SceneA, to = SceneB, current = { transitionCurrentScene }) - state.startTransition(transition, transitionKey = null) + state.startTransition(transition) assertThat(currentScene.value).isEqualTo(SceneA) // Change the transition current scene. 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 d2c8bd6928ee..de6f1cc518f4 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 @@ -57,7 +57,7 @@ class SceneTransitionLayoutStateTest { @Test fun isTransitioningTo_transition() { val state = MutableSceneTransitionLayoutStateImpl(SceneA, SceneTransitions.Empty) - state.startTransition(transition(from = SceneA, to = SceneB), transitionKey = null) + state.startTransition(transition(from = SceneA, to = SceneB)) assertThat(state.isTransitioning()).isTrue() assertThat(state.isTransitioning(from = SceneA)).isTrue() @@ -175,7 +175,7 @@ class SceneTransitionLayoutStateTest { val childTransition = transition(SceneA, SceneB) - childState.startTransition(childTransition, null) + childState.startTransition(childTransition) assertThat(childState.isTransitioning(SceneA, SceneB)).isTrue() assertThat(parentState.isTransitioning(SceneC, SceneD)).isTrue() @@ -211,7 +211,7 @@ class SceneTransitionLayoutStateTest { val childTransition = transition(SceneA, SceneB) - childState.startTransition(childTransition, null) + childState.startTransition(childTransition) assertThat(childState.isTransitioning(SceneA, SceneB)).isTrue() assertThat(parentState.isTransitioning(SceneC, SceneD)).isTrue() assertThat(parentParentState.isTransitioning(SceneB, SceneC)).isTrue() @@ -229,7 +229,7 @@ class SceneTransitionLayoutStateTest { var progress = 0f val childTransition = transition(SceneA, SceneB, progress = { progress }) - childState.startTransition(childTransition, null) + childState.startTransition(childTransition) assertThat(parentState.currentTransition?.progress).isEqualTo(0f) progress = .5f @@ -242,7 +242,7 @@ class SceneTransitionLayoutStateTest { val childTransition = transition(SceneB, SceneA) - childState.startTransition(childTransition, null) + childState.startTransition(childTransition) assertThat(childState.isTransitioning(SceneB, SceneA)).isTrue() assertThat(parentState.transitionState).isEqualTo(TransitionState.Idle(SceneC)) @@ -256,7 +256,7 @@ class SceneTransitionLayoutStateTest { val (parentState, childState) = setupLinkedStates() val childTransition = transition(SceneA, SceneB) - childState.startTransition(childTransition, null) + childState.startTransition(childTransition) childState.finishTransition(childTransition, SceneA) assertThat(childState.transitionState).isEqualTo(TransitionState.Idle(SceneA)) @@ -268,7 +268,7 @@ class SceneTransitionLayoutStateTest { val (parentState, childState) = setupLinkedStates() val childTransition = transition(SceneA, SceneB) - childState.startTransition(childTransition, null) + childState.startTransition(childTransition) childState.finishTransition(childTransition, SceneD) assertThat(childState.transitionState).isEqualTo(TransitionState.Idle(SceneD)) @@ -283,16 +283,16 @@ class SceneTransitionLayoutStateTest { transition( SceneA, SceneB, - onFinish = { launch { /* Do nothing. */} }, + onFinish = { launch { /* Do nothing. */ } }, ) val parentTransition = transition( SceneC, SceneA, - onFinish = { launch { /* Do nothing. */} }, + onFinish = { launch { /* Do nothing. */ } }, ) - childState.startTransition(childTransition, null) - parentState.startTransition(parentTransition, null) + childState.startTransition(childTransition) + parentState.startTransition(parentTransition) childState.finishTransition(childTransition, SceneB) assertThat(childState.transitionState).isEqualTo(TransitionState.Idle(SceneB)) @@ -341,10 +341,7 @@ class SceneTransitionLayoutStateTest { @Test fun snapToIdleIfClose_snapToStart() = runMonotonicClockTest { val state = MutableSceneTransitionLayoutStateImpl(SceneA, SceneTransitions.Empty) - state.startTransition( - transition(from = SceneA, to = SceneB, progress = { 0.2f }), - transitionKey = null - ) + state.startTransition(transition(from = SceneA, to = SceneB, progress = { 0.2f })) assertThat(state.isTransitioning()).isTrue() // Ignore the request if the progress is not close to 0 or 1, using the threshold. @@ -360,10 +357,7 @@ class SceneTransitionLayoutStateTest { @Test fun snapToIdleIfClose_snapToEnd() = runMonotonicClockTest { val state = MutableSceneTransitionLayoutStateImpl(SceneA, SceneTransitions.Empty) - state.startTransition( - transition(from = SceneA, to = SceneB, progress = { 0.8f }), - transitionKey = null - ) + state.startTransition(transition(from = SceneA, to = SceneB, progress = { 0.8f })) assertThat(state.isTransitioning()).isTrue() // Ignore the request if the progress is not close to 0 or 1, using the threshold. @@ -385,13 +379,13 @@ class SceneTransitionLayoutStateTest { from = SceneA, to = SceneB, progress = { 0.5f }, - onFinish = { launch { /* do nothing */} }, + onFinish = { launch { /* do nothing */ } }, ) - state.startTransition(aToB, transitionKey = null) + state.startTransition(aToB) assertThat(state.currentTransitions).containsExactly(aToB).inOrder() val bToC = transition(from = SceneB, to = SceneC, progress = { 0.8f }) - state.startTransition(bToC, transitionKey = null) + state.startTransition(bToC) assertThat(state.currentTransitions).containsExactly(aToB, bToC).inOrder() // Ignore the request if the progress is not close to 0 or 1, using the threshold. @@ -409,7 +403,7 @@ class SceneTransitionLayoutStateTest { val (parentState, childState) = setupLinkedStates(SceneC, SceneA, null, null, null, SceneD) val childTransition = transition(SceneA, SceneB) - childState.startTransition(childTransition, null) + childState.startTransition(childTransition) assertThat(childState.isTransitioning(SceneA, SceneB)).isTrue() assertThat(parentState.isTransitioning(SceneC, SceneD)).isTrue() @@ -425,7 +419,7 @@ class SceneTransitionLayoutStateTest { val childTransition = transition(SceneA, SceneB) - childState.startTransition(childTransition, null) + childState.startTransition(childTransition) assertThat(childState.isTransitioning(SceneA, SceneB)).isTrue() assertThat(parentState.isTransitioning(SceneC, SceneD)).isTrue() @@ -440,7 +434,7 @@ class SceneTransitionLayoutStateTest { setupLinkedStates(SceneC, SceneA, SceneB, null, SceneC, SceneD) val childTransition = transition(SceneA, SceneB) - childState.startTransition(childTransition, null) + childState.startTransition(childTransition) assertThat(childState.isTransitioning(SceneA, SceneB)).isTrue() assertThat(parentState.isTransitioning(SceneC, SceneD)).isFalse() } @@ -460,8 +454,7 @@ class SceneTransitionLayoutStateTest { to = SceneB, progress = progress, orientation = Orientation.Vertical, - ), - transitionKey = null + ) ) assertThat(state.isTransitioning()).isTrue() return state @@ -583,19 +576,19 @@ class SceneTransitionLayoutStateTest { assertThat(state.currentTransitions).isEmpty() // A => B. - state.startTransition(aToB, transitionKey = null) + state.startTransition(aToB) assertThat(finishingTransitions).isEmpty() assertThat(state.finishedTransitions).isEmpty() assertThat(state.currentTransitions).containsExactly(aToB).inOrder() // B => C. This should automatically call finish() on aToB. - state.startTransition(bToC, transitionKey = null) + state.startTransition(bToC) assertThat(finishingTransitions).containsExactly(aToB) assertThat(state.finishedTransitions).isEmpty() assertThat(state.currentTransitions).containsExactly(aToB, bToC).inOrder() // C => A. This should automatically call finish() on bToC. - state.startTransition(cToA, transitionKey = null) + state.startTransition(cToA) assertThat(finishingTransitions).containsExactly(aToB, bToC) assertThat(state.finishedTransitions).isEmpty() assertThat(state.currentTransitions).containsExactly(aToB, bToC, cToA).inOrder() @@ -617,8 +610,8 @@ class SceneTransitionLayoutStateTest { val state = MutableSceneTransitionLayoutStateImpl(SceneA, EmptyTestTransitions) fun startTransition() { - val transition = transition(SceneA, SceneB, onFinish = { launch { /* do nothing */} }) - state.startTransition(transition, transitionKey = null) + val transition = transition(SceneA, SceneB, onFinish = { launch { /* do nothing */ } }) + state.startTransition(transition) } var hasLoggedWtf = false diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutTest.kt index 08532bd72e33..a8dd572c4d50 100644 --- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutTest.kt +++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutTest.kt @@ -21,6 +21,7 @@ import androidx.compose.animation.core.FastOutSlowInEasing import androidx.compose.animation.core.LinearEasing import androidx.compose.animation.core.tween import androidx.compose.foundation.background +import androidx.compose.foundation.gestures.Orientation import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.offset @@ -333,6 +334,42 @@ class SceneTransitionLayoutTest { } @Test + fun layoutSizeDoesNotOverscrollWhenOverscrollIsSpecified() { + val state = + rule.runOnUiThread { + MutableSceneTransitionLayoutStateImpl( + SceneA, + transitions { overscroll(SceneB, Orientation.Horizontal) } + ) + } + + val layoutTag = "layout" + rule.setContent { + SceneTransitionLayout(state, Modifier.testTag(layoutTag)) { + scene(SceneA) { Box(Modifier.size(50.dp)) } + scene(SceneB) { Box(Modifier.size(70.dp)) } + } + } + + // Overscroll on A at -100%: size should be interpolated given that there is no overscroll + // defined for scene A. + var progress by mutableStateOf(-1f) + rule.runOnIdle { + state.startTransition(transition(from = SceneA, to = SceneB, progress = { progress })) + } + rule.onNodeWithTag(layoutTag).assertSizeIsEqualTo(30.dp) + + // Middle of the transition. + progress = 0.5f + rule.onNodeWithTag(layoutTag).assertSizeIsEqualTo(60.dp) + + // Overscroll on B at 200%: size should not be interpolated given that there is an + // overscroll defined for scene B. + progress = 2f + rule.onNodeWithTag(layoutTag).assertSizeIsEqualTo(70.dp) + } + + @Test fun multipleTransitionsWillComposeMultipleScenes() { val duration = 10 * 16L diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/Transition.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/Transition.kt index e6fa69d87b34..322b0355c2bd 100644 --- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/Transition.kt +++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/Transition.kt @@ -30,7 +30,7 @@ fun transition( current: () -> SceneKey = { from }, progress: () -> Float = { 0f }, progressVelocity: () -> Float = { 0f }, - interruptionProgress: () -> Float = { 100f }, + interruptionProgress: () -> Float = { 0f }, isInitiatedByUserInput: Boolean = false, isUserInputOngoing: Boolean = false, isUpOrLeft: Boolean = false, diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/transformation/AnchoredTranslateTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/transformation/AnchoredTranslateTest.kt index d1205e727cf9..46075c3b3f9f 100644 --- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/transformation/AnchoredTranslateTest.kt +++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/transformation/AnchoredTranslateTest.kt @@ -27,6 +27,7 @@ import androidx.compose.ui.unit.dp import androidx.test.ext.junit.runners.AndroidJUnit4 import com.android.compose.animation.scene.TestElements import com.android.compose.animation.scene.testTransition +import com.android.compose.animation.scene.transition import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith @@ -83,4 +84,28 @@ class AnchoredTranslateTest { after { onElement(TestElements.Bar).assertPositionInRootIsEqualTo(20.dp, 40.dp) } } } + + @Test + fun anchorPlacedAfterAnchoredElement() { + rule.testTransition( + fromSceneContent = { Box(Modifier.offset(10.dp, 50.dp).element(TestElements.Foo)) }, + toSceneContent = { + Box(Modifier.offset(20.dp, 40.dp).element(TestElements.Bar)) + Box(Modifier.offset(30.dp, 10.dp).element(TestElements.Foo)) + }, + transition = { + spec = tween(16 * 4, easing = LinearEasing) + anchoredTranslate(TestElements.Bar, TestElements.Foo) + }, + ) { + // No exception is thrown even if Bar is placed before the anchor in toScene. + before { onElement(TestElements.Bar).assertDoesNotExist() } + at(0) { onElement(TestElements.Bar).assertPositionInRootIsEqualTo(0.dp, 80.dp) } + at(16) { onElement(TestElements.Bar).assertPositionInRootIsEqualTo(5.dp, 70.dp) } + at(32) { onElement(TestElements.Bar).assertPositionInRootIsEqualTo(10.dp, 60.dp) } + at(48) { onElement(TestElements.Bar).assertPositionInRootIsEqualTo(15.dp, 50.dp) } + at(64) { onElement(TestElements.Bar).assertPositionInRootIsEqualTo(20.dp, 40.dp) } + after { onElement(TestElements.Bar).assertPositionInRootIsEqualTo(20.dp, 40.dp) } + } + } } diff --git a/packages/SystemUI/compose/scene/tests/utils/src/com/android/compose/animation/scene/TestMatchers.kt b/packages/SystemUI/compose/scene/tests/utils/src/com/android/compose/animation/scene/TestMatchers.kt index e743c7885c14..6d063a0418d6 100644 --- a/packages/SystemUI/compose/scene/tests/utils/src/com/android/compose/animation/scene/TestMatchers.kt +++ b/packages/SystemUI/compose/scene/tests/utils/src/com/android/compose/animation/scene/TestMatchers.kt @@ -17,7 +17,7 @@ package com.android.compose.animation.scene import androidx.compose.ui.test.SemanticsMatcher -import androidx.compose.ui.test.hasParent +import androidx.compose.ui.test.hasAnyAncestor import androidx.compose.ui.test.hasTestTag /** A [SemanticsMatcher] that matches [element], optionally restricted to scene [scene]. */ @@ -25,6 +25,6 @@ fun isElement(element: ElementKey, scene: SceneKey? = null): SemanticsMatcher { return if (scene == null) { hasTestTag(element.testTag) } else { - hasTestTag(element.testTag) and hasParent(hasTestTag(scene.testTag)) + hasTestTag(element.testTag) and hasAnyAncestor(hasTestTag(scene.testTag)) } } diff --git a/packages/SystemUI/flag_check.py b/packages/SystemUI/flag_check.py index 95a25c58bc67..d78ef5a5f1bf 100755 --- a/packages/SystemUI/flag_check.py +++ b/packages/SystemUI/flag_check.py @@ -52,7 +52,7 @@ def main(): nargs='?', default='', help= - 'REPO_PATH in repo upload to determine whether the check should run for this project.') + 'REPO_PROJECT in repo upload to determine whether the check should run for this project.') # Parse the arguments args = parser.parse_args() @@ -112,16 +112,16 @@ def main(): sys.exit(0) -def should_run_path(path, files): +def should_run_path(project, files): """Returns a boolean if this check should run with these paths. If you want to check for a particular subdirectory under the path, add a check here, call should_run_files and check for a specific sub dir path in should_run_files. """ - if not path: + if not project: return False - if path == 'frameworks/base': + if project == 'platform/frameworks/base': return should_run_files(files) - # Default case, run for all other paths which calls this script. + # Default case, run for all other projects which calls this script. return True diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayStatusBarViewControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/ambient/statusbar/ui/AmbientStatusBarViewControllerTest.java index f561c531253b..201ed00acae3 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayStatusBarViewControllerTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/ambient/statusbar/ui/AmbientStatusBarViewControllerTest.java @@ -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,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.dreams; +package com.android.systemui.ambient.statusbar.ui; import static android.app.StatusBarManager.WINDOW_STATE_HIDDEN; import static android.app.StatusBarManager.WINDOW_STATE_SHOWING; @@ -44,6 +44,10 @@ import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; +import com.android.systemui.dreams.DreamOverlayNotificationCountProvider; +import com.android.systemui.dreams.DreamOverlayStateController; +import com.android.systemui.dreams.DreamOverlayStatusBarItemsProvider; +import com.android.systemui.kosmos.KosmosJavaAdapter; import com.android.systemui.log.LogBuffer; import com.android.systemui.log.core.FakeLogBuffer; import com.android.systemui.res.R; @@ -54,7 +58,6 @@ import com.android.systemui.statusbar.policy.NextAlarmController; import com.android.systemui.statusbar.policy.ZenModeController; import com.android.systemui.statusbar.window.StatusBarWindowStateController; import com.android.systemui.statusbar.window.StatusBarWindowStateListener; -import com.android.systemui.touch.TouchInsetManager; import com.android.systemui.util.time.DateFormatUtil; import org.junit.Before; @@ -72,14 +75,12 @@ import java.util.concurrent.Executor; @SmallTest @TestableLooper.RunWithLooper(setAsMainLooper = true) @RunWith(AndroidJUnit4.class) -public class DreamOverlayStatusBarViewControllerTest extends SysuiTestCase { +public class AmbientStatusBarViewControllerTest extends SysuiTestCase { private static final String NOTIFICATION_INDICATOR_FORMATTER_STRING = "{count, plural, =1 {# notification} other {# notifications}}"; @Mock - MockDreamOverlayStatusBarView mView; - @Mock - TouchInsetManager.TouchInsetSession mTouchSession; + MockAmbientStatusBarView mView; @Mock Resources mResources; @Mock @@ -114,9 +115,11 @@ public class DreamOverlayStatusBarViewControllerTest extends SysuiTestCase { private final Executor mMainExecutor = Runnable::run; - private final FakeWifiRepository mWifiRepository = new FakeWifiRepository(); + private final KosmosJavaAdapter mKosmos = new KosmosJavaAdapter(this); + + private final FakeWifiRepository mWifiRepository = mKosmos.getFakeWifiRepository(); - DreamOverlayStatusBarViewController mController; + AmbientStatusBarViewController mController; @Before public void setup() { @@ -128,11 +131,10 @@ public class DreamOverlayStatusBarViewControllerTest extends SysuiTestCase { doCallRealMethod().when(mView).getVisibility(); when(mUserTracker.getUserId()).thenReturn(ActivityManager.getCurrentUser()); - mController = new DreamOverlayStatusBarViewController( + mController = new AmbientStatusBarViewController( mView, mResources, mMainExecutor, - mTouchSession, mAlarmManager, mNextAlarmController, mDateFormatUtil, @@ -143,7 +145,8 @@ public class DreamOverlayStatusBarViewControllerTest extends SysuiTestCase { mDreamOverlayStatusBarItemsProvider, mDreamOverlayStateController, mUserTracker, - mWifiRepository, + mKosmos.getWifiInteractor(), + mKosmos.getCommunalSceneInteractor(), mLogBuffer); } @@ -164,7 +167,7 @@ public class DreamOverlayStatusBarViewControllerTest extends SysuiTestCase { mController.updateWifiUnavailableStatusIcon(false); verify(mView).showIcon( - DreamOverlayStatusBarView.STATUS_ICON_WIFI_UNAVAILABLE, true, null); + AmbientStatusBarView.STATUS_ICON_WIFI_UNAVAILABLE, true, null); } @Test @@ -173,7 +176,7 @@ public class DreamOverlayStatusBarViewControllerTest extends SysuiTestCase { mController.updateWifiUnavailableStatusIcon(true); verify(mView).showIcon( - DreamOverlayStatusBarView.STATUS_ICON_WIFI_UNAVAILABLE, false, null); + AmbientStatusBarView.STATUS_ICON_WIFI_UNAVAILABLE, false, null); } @Test @@ -183,7 +186,7 @@ public class DreamOverlayStatusBarViewControllerTest extends SysuiTestCase { when(mAlarmManager.getNextAlarmClock(anyInt())).thenReturn(alarmClockInfo); mController.onViewAttached(); verify(mView).showIcon( - eq(DreamOverlayStatusBarView.STATUS_ICON_ALARM_SET), eq(true), any()); + eq(AmbientStatusBarView.STATUS_ICON_ALARM_SET), eq(true), any()); } @Test @@ -191,7 +194,7 @@ public class DreamOverlayStatusBarViewControllerTest extends SysuiTestCase { when(mAlarmManager.getNextAlarmClock(anyInt())).thenReturn(null); mController.onViewAttached(); verify(mView).showIcon( - eq(DreamOverlayStatusBarView.STATUS_ICON_ALARM_SET), eq(false), isNull()); + eq(AmbientStatusBarView.STATUS_ICON_ALARM_SET), eq(false), isNull()); } @Test @@ -202,7 +205,7 @@ public class DreamOverlayStatusBarViewControllerTest extends SysuiTestCase { .thenReturn(false); mController.onViewAttached(); verify(mView).showIcon( - DreamOverlayStatusBarView.STATUS_ICON_MIC_DISABLED, true, null); + AmbientStatusBarView.STATUS_ICON_MIC_DISABLED, true, null); } @Test @@ -213,7 +216,7 @@ public class DreamOverlayStatusBarViewControllerTest extends SysuiTestCase { .thenReturn(true); mController.onViewAttached(); verify(mView).showIcon( - DreamOverlayStatusBarView.STATUS_ICON_CAMERA_DISABLED, true, null); + AmbientStatusBarView.STATUS_ICON_CAMERA_DISABLED, true, null); } @Test @@ -224,7 +227,7 @@ public class DreamOverlayStatusBarViewControllerTest extends SysuiTestCase { .thenReturn(true); mController.onViewAttached(); verify(mView).showIcon( - DreamOverlayStatusBarView.STATUS_ICON_MIC_CAMERA_DISABLED, true, null); + AmbientStatusBarView.STATUS_ICON_MIC_CAMERA_DISABLED, true, null); } @Test @@ -237,7 +240,7 @@ public class DreamOverlayStatusBarViewControllerTest extends SysuiTestCase { callbackCapture.getValue().onNotificationCountChanged(1); verify(mView).showIcon( - eq(DreamOverlayStatusBarView.STATUS_ICON_NOTIFICATIONS), eq(true), any()); + eq(AmbientStatusBarView.STATUS_ICON_NOTIFICATIONS), eq(true), any()); } @Test @@ -250,16 +253,15 @@ public class DreamOverlayStatusBarViewControllerTest extends SysuiTestCase { callbackCapture.getValue().onNotificationCountChanged(0); verify(mView).showIcon( - eq(DreamOverlayStatusBarView.STATUS_ICON_NOTIFICATIONS), eq(false), isNull()); + eq(AmbientStatusBarView.STATUS_ICON_NOTIFICATIONS), eq(false), isNull()); } @Test public void testNotificationsIconNotShownWhenCountProviderAbsent() { - DreamOverlayStatusBarViewController controller = new DreamOverlayStatusBarViewController( + AmbientStatusBarViewController controller = new AmbientStatusBarViewController( mView, mResources, mMainExecutor, - mTouchSession, mAlarmManager, mNextAlarmController, mDateFormatUtil, @@ -270,11 +272,12 @@ public class DreamOverlayStatusBarViewControllerTest extends SysuiTestCase { mDreamOverlayStatusBarItemsProvider, mDreamOverlayStateController, mUserTracker, - mWifiRepository, + mKosmos.getWifiInteractor(), + mKosmos.getCommunalSceneInteractor(), mLogBuffer); controller.onViewAttached(); verify(mView, never()).showIcon( - eq(DreamOverlayStatusBarView.STATUS_ICON_NOTIFICATIONS), eq(true), any()); + eq(AmbientStatusBarView.STATUS_ICON_NOTIFICATIONS), eq(true), any()); } @Test @@ -283,7 +286,7 @@ public class DreamOverlayStatusBarViewControllerTest extends SysuiTestCase { Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS); mController.onViewAttached(); verify(mView).showIcon( - DreamOverlayStatusBarView.STATUS_ICON_PRIORITY_MODE_ON, true, null); + AmbientStatusBarView.STATUS_ICON_PRIORITY_MODE_ON, true, null); } @Test @@ -292,7 +295,7 @@ public class DreamOverlayStatusBarViewControllerTest extends SysuiTestCase { Settings.Global.ZEN_MODE_OFF); mController.onViewAttached(); verify(mView).showIcon( - DreamOverlayStatusBarView.STATUS_ICON_PRIORITY_MODE_ON, false, null); + AmbientStatusBarView.STATUS_ICON_PRIORITY_MODE_ON, false, null); } @Test @@ -322,7 +325,7 @@ public class DreamOverlayStatusBarViewControllerTest extends SysuiTestCase { callbackCapture.getValue().onNotificationCountChanged(1); verify(mView).showIcon( - eq(DreamOverlayStatusBarView.STATUS_ICON_NOTIFICATIONS), eq(true), any()); + eq(AmbientStatusBarView.STATUS_ICON_NOTIFICATIONS), eq(true), any()); } @Test @@ -335,7 +338,7 @@ public class DreamOverlayStatusBarViewControllerTest extends SysuiTestCase { callbackCapture.getValue().onNotificationCountChanged(0); verify(mView).showIcon( - eq(DreamOverlayStatusBarView.STATUS_ICON_NOTIFICATIONS), eq(false), any()); + eq(AmbientStatusBarView.STATUS_ICON_NOTIFICATIONS), eq(false), any()); } @Test @@ -354,7 +357,7 @@ public class DreamOverlayStatusBarViewControllerTest extends SysuiTestCase { SensorPrivacyManager.Sensors.MICROPHONE, true); verify(mView).showIcon( - DreamOverlayStatusBarView.STATUS_ICON_MIC_CAMERA_DISABLED, true, null); + AmbientStatusBarView.STATUS_ICON_MIC_CAMERA_DISABLED, true, null); } @Test @@ -369,7 +372,7 @@ public class DreamOverlayStatusBarViewControllerTest extends SysuiTestCase { callbackCapture.getValue().onZenChanged(Settings.Global.ZEN_MODE_NO_INTERRUPTIONS); verify(mView).showIcon( - DreamOverlayStatusBarView.STATUS_ICON_PRIORITY_MODE_ON, true, null); + AmbientStatusBarView.STATUS_ICON_PRIORITY_MODE_ON, true, null); } @Test @@ -384,7 +387,7 @@ public class DreamOverlayStatusBarViewControllerTest extends SysuiTestCase { callbackCapture.getValue().onZenChanged(Settings.Global.ZEN_MODE_OFF); verify(mView).showIcon( - DreamOverlayStatusBarView.STATUS_ICON_PRIORITY_MODE_ON, false, null); + AmbientStatusBarView.STATUS_ICON_PRIORITY_MODE_ON, false, null); } @Test @@ -399,7 +402,7 @@ public class DreamOverlayStatusBarViewControllerTest extends SysuiTestCase { callbackCapture.getValue().onStateChanged(); verify(mView).showIcon( - DreamOverlayStatusBarView.STATUS_ICON_ASSISTANT_ATTENTION_ACTIVE, true, null); + AmbientStatusBarView.STATUS_ICON_ASSISTANT_ATTENTION_ACTIVE, true, null); } @Test @@ -460,7 +463,7 @@ public class DreamOverlayStatusBarViewControllerTest extends SysuiTestCase { final ArgumentCaptor<DreamOverlayStatusBarItemsProvider.Callback> callbackCapture = ArgumentCaptor.forClass( - DreamOverlayStatusBarItemsProvider.Callback.class); + DreamOverlayStatusBarItemsProvider.Callback.class); verify(mDreamOverlayStatusBarItemsProvider).addCallback(callbackCapture.capture()); callbackCapture.getValue().onStatusBarItemsChanged(List.of(mStatusBarItem)); @@ -532,10 +535,10 @@ public class DreamOverlayStatusBarViewControllerTest extends SysuiTestCase { callback.onStateChanged(); } - private static class MockDreamOverlayStatusBarView extends DreamOverlayStatusBarView { + private static class MockAmbientStatusBarView extends AmbientStatusBarView { private int mVisibility = View.VISIBLE; - private MockDreamOverlayStatusBarView(Context context) { + private MockAmbientStatusBarView(Context context) { super(context); } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/ambient/touch/ShadeTouchHandlerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/ambient/touch/ShadeTouchHandlerTest.java index 27bffd0818e7..07d889066438 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/ambient/touch/ShadeTouchHandlerTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/ambient/touch/ShadeTouchHandlerTest.java @@ -18,15 +18,22 @@ package com.android.systemui.ambient.touch; import static com.google.common.truth.Truth.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import android.app.DreamManager; +import android.platform.test.annotations.DisableFlags; +import android.platform.test.annotations.EnableFlags; import android.view.GestureDetector; import android.view.MotionEvent; 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.shade.ShadeViewController; import com.android.systemui.shared.system.InputChannelCompat; @@ -36,6 +43,7 @@ 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.Mockito; import org.mockito.MockitoAnnotations; @@ -52,63 +60,128 @@ public class ShadeTouchHandlerTest extends SysuiTestCase { ShadeViewController mShadeViewController; @Mock + DreamManager mDreamManager; + + @Mock TouchHandler.TouchSession mTouchSession; ShadeTouchHandler mTouchHandler; + @Captor + ArgumentCaptor<GestureDetector.OnGestureListener> mGestureListenerCaptor; + @Captor + ArgumentCaptor<InputChannelCompat.InputEventListener> mInputListenerCaptor; + private static final int TOUCH_HEIGHT = 20; @Before public void setup() { MockitoAnnotations.initMocks(this); + mTouchHandler = new ShadeTouchHandler(Optional.of(mCentralSurfaces), mShadeViewController, - TOUCH_HEIGHT); + mDreamManager, TOUCH_HEIGHT); } - /** - * Verify that touches aren't handled when the bouncer is showing. - */ + // Verifies that a swipe down in the gesture region is captured by the shade touch handler. @Test - public void testInactiveOnBouncer() { - when(mCentralSurfaces.isBouncerShowing()).thenReturn(true); - mTouchHandler.onSessionStart(mTouchSession); - verify(mTouchSession).pop(); + public void testSwipeDown_captured() { + final boolean captured = swipe(Direction.DOWN); + + assertThat(captured).isTrue(); } - /** - * Make sure {@link ShadeTouchHandler} - */ + // Verifies that a swipe in the upward direction is not captured. @Test - public void testTouchPilferingOnScroll() { - final MotionEvent motionEvent1 = Mockito.mock(MotionEvent.class); - final MotionEvent motionEvent2 = Mockito.mock(MotionEvent.class); + public void testSwipeUp_notCaptured() { + final boolean captured = swipe(Direction.UP); - final ArgumentCaptor<GestureDetector.OnGestureListener> gestureListenerArgumentCaptor = - ArgumentCaptor.forClass(GestureDetector.OnGestureListener.class); + // Motion events not captured as the swipe is going in the wrong direction. + assertThat(captured).isFalse(); + } - mTouchHandler.onSessionStart(mTouchSession); - verify(mTouchSession).registerGestureListener(gestureListenerArgumentCaptor.capture()); + // Verifies that a swipe down forwards captured touches to central surfaces for handling. + @Test + @EnableFlags(Flags.FLAG_COMMUNAL_HUB) + public void testSwipeDown_communalEnabled_sentToCentralSurfaces() { + swipe(Direction.DOWN); - assertThat(gestureListenerArgumentCaptor.getValue() - .onScroll(motionEvent1, motionEvent2, 1, 1)) - .isTrue(); + // Both motion events are sent for central surfaces to process. + verify(mCentralSurfaces, times(2)).handleExternalShadeWindowTouch(any()); } - /** - * Ensure touches are propagated to the {@link ShadeViewController}. - */ + // Verifies that a swipe down forwards captured touches to the shade view for handling. + @Test + @DisableFlags(Flags.FLAG_COMMUNAL_HUB) + public void testSwipeDown_communalDisabled_sentToShadeView() { + swipe(Direction.DOWN); + + // Both motion events are sent for the shade view to process. + verify(mShadeViewController, times(2)).handleExternalTouch(any()); + } + + // Verifies that a swipe down while dreaming forwards captured touches to the shade view for + // handling. + @Test + public void testSwipeDown_dreaming_sentToShadeView() { + when(mDreamManager.isDreaming()).thenReturn(true); + + swipe(Direction.DOWN); + + // Both motion events are sent for the shade view to process. + verify(mShadeViewController, times(2)).handleExternalTouch(any()); + } + + // Verifies that a swipe up is not forwarded to central surfaces. + @Test + @EnableFlags(Flags.FLAG_COMMUNAL_HUB) + public void testSwipeUp_communalEnabled_touchesNotSent() { + swipe(Direction.UP); + + // Motion events are not sent for central surfaces to process as the swipe is going in the + // wrong direction. + verify(mCentralSurfaces, never()).handleExternalShadeWindowTouch(any()); + } + + // Verifies that a swipe up is not forwarded to the shade view. @Test - public void testEventPropagation() { - final MotionEvent motionEvent = Mockito.mock(MotionEvent.class); + @DisableFlags(Flags.FLAG_COMMUNAL_HUB) + public void testSwipeUp_communalDisabled_touchesNotSent() { + swipe(Direction.UP); - final ArgumentCaptor<InputChannelCompat.InputEventListener> - inputEventListenerArgumentCaptor = - ArgumentCaptor.forClass(InputChannelCompat.InputEventListener.class); + // Motion events are not sent for the shade view to process as the swipe is going in the + // wrong direction. + verify(mShadeViewController, never()).handleExternalTouch(any()); + } + /** + * Simulates a swipe in the given direction and returns true if the touch was intercepted by the + * touch handler's gesture listener. + * <p> + * Swipe down starts from a Y coordinate of 0 and goes downward. Swipe up starts from the edge + * of the gesture region, {@link #TOUCH_HEIGHT}, and goes upward to 0. + */ + private boolean swipe(Direction direction) { + Mockito.clearInvocations(mTouchSession); mTouchHandler.onSessionStart(mTouchSession); - verify(mTouchSession).registerInputListener(inputEventListenerArgumentCaptor.capture()); - inputEventListenerArgumentCaptor.getValue().onInputEvent(motionEvent); - verify(mShadeViewController).handleExternalTouch(motionEvent); + + verify(mTouchSession).registerGestureListener(mGestureListenerCaptor.capture()); + verify(mTouchSession).registerInputListener(mInputListenerCaptor.capture()); + + final float startY = direction == Direction.UP ? TOUCH_HEIGHT : 0; + final float endY = direction == Direction.UP ? 0 : TOUCH_HEIGHT; + + // Send touches to the input and gesture listener. + final MotionEvent event1 = MotionEvent.obtain(0, 0, MotionEvent.ACTION_MOVE, 0, startY, 0); + final MotionEvent event2 = MotionEvent.obtain(0, 0, MotionEvent.ACTION_MOVE, 0, endY, 0); + mInputListenerCaptor.getValue().onInputEvent(event1); + mInputListenerCaptor.getValue().onInputEvent(event2); + final boolean captured = mGestureListenerCaptor.getValue().onScroll(event1, event2, 0, + startY - endY); + + return captured; } + private enum Direction { + DOWN, UP, + } } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/BiometricTestExtensions.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/BiometricTestExtensions.kt index 9c2791f5a257..75a77cf781d2 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/BiometricTestExtensions.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/BiometricTestExtensions.kt @@ -131,8 +131,9 @@ internal fun promptInfo( negativeButton: String = "neg", ): PromptInfo { val info = PromptInfo() - info.logoRes = logoRes - info.logoBitmap = logoBitmap + if (logoBitmap != null) { + info.setLogo(logoRes, logoBitmap) + } info.logoDescription = logoDescription info.title = title info.subtitle = subtitle diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerWithCoroutinesTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerWithCoroutinesTest.kt index ab551256cfc3..29a6e56891af 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerWithCoroutinesTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerWithCoroutinesTest.kt @@ -235,6 +235,7 @@ class UdfpsKeyguardViewLegacyControllerWithCoroutinesTest : job.cancel() } + @Test fun fadeFromDialogSuggestedAlpha() = testScope.runTest { @@ -511,9 +512,10 @@ class UdfpsKeyguardViewLegacyControllerWithCoroutinesTest : testScope.runTest { // GIVEN view is attached mController.onViewAttached() + val job = mController.listenForLockscreenAodTransitions(this) + runCurrent() Mockito.reset(mView) - val job = mController.listenForLockscreenAodTransitions(this) // WHEN aod to lockscreen transition is cancelled transitionRepository.sendTransitionStep( TransitionStep( @@ -537,7 +539,7 @@ class UdfpsKeyguardViewLegacyControllerWithCoroutinesTest : // THEN doze amount is updated to zero verify(mView) - .onDozeAmountChanged(eq(0f), eq(0f), eq(UdfpsKeyguardViewLegacy.ANIMATION_NONE)) + .onDozeAmountChanged(eq(0f), eq(0f), eq(ANIMATION_BETWEEN_AOD_AND_LOCKSCREEN)) job.cancel() } @@ -546,9 +548,10 @@ class UdfpsKeyguardViewLegacyControllerWithCoroutinesTest : testScope.runTest { // GIVEN view is attached mController.onViewAttached() + val job = mController.listenForLockscreenAodTransitions(this) + runCurrent() Mockito.reset(mView) - val job = mController.listenForLockscreenAodTransitions(this) // WHEN lockscreen to aod transition is cancelled transitionRepository.sendTransitionStep( TransitionStep( diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalDreamStartableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalDreamStartableTest.kt index bfed33c54019..fe683e07a93d 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalDreamStartableTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalDreamStartableTest.kt @@ -22,7 +22,6 @@ 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.communal.domain.interactor.communalInteractor import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository import com.android.systemui.keyguard.data.repository.keyguardRepository @@ -66,7 +65,6 @@ class CommunalDreamStartableTest : SysuiTestCase() { powerInteractor = kosmos.powerInteractor, keyguardInteractor = kosmos.keyguardInteractor, keyguardTransitionInteractor = kosmos.keyguardTransitionInteractor, - communalInteractor = kosmos.communalInteractor, dreamManager = dreamManager, bgScope = kosmos.applicationCoroutineScope, ) diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalSceneStartableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalSceneStartableTest.kt index e61b2d0f2e74..cf145471e55f 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalSceneStartableTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalSceneStartableTest.kt @@ -111,6 +111,25 @@ class CommunalSceneStartableTest : SysuiTestCase() { } } + @Test + fun keyguardGoesAway_whenInEditMode_doesNotChangeScene() = + with(kosmos) { + testScope.runTest { + val scene by collectLastValue(communalSceneInteractor.currentScene) + communalSceneInteractor.changeScene(CommunalScenes.Communal) + assertThat(scene).isEqualTo(CommunalScenes.Communal) + + communalInteractor.setEditModeOpen(true) + fakeKeyguardTransitionRepository.sendTransitionSteps( + from = KeyguardState.ALTERNATE_BOUNCER, + to = KeyguardState.GONE, + testScope = this + ) + // Scene change will be handled in EditWidgetsActivity not here + assertThat(scene).isEqualTo(CommunalScenes.Communal) + } + } + @Ignore("Ignored until custom animations are implemented in b/322787129") @Test fun deviceDocked_forceCommunalScene() = diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt index ffa63d83a97f..3d454a202220 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt @@ -52,6 +52,7 @@ import com.android.systemui.communal.domain.model.CommunalTransitionProgressMode import com.android.systemui.communal.shared.model.CommunalContentSize import com.android.systemui.communal.shared.model.CommunalScenes import com.android.systemui.communal.shared.model.CommunalWidgetContentModel +import com.android.systemui.communal.shared.model.EditModeState import com.android.systemui.communal.widgets.EditWidgetsActivityStarter import com.android.systemui.coroutines.collectLastValue import com.android.systemui.flags.EnableSceneContainer @@ -121,6 +122,7 @@ class CommunalInteractorTest : SysuiTestCase() { private lateinit var communalPrefsRepository: FakeCommunalPrefsRepository private lateinit var editWidgetsActivityStarter: EditWidgetsActivityStarter private lateinit var sceneInteractor: SceneInteractor + private lateinit var communalSceneInteractor: CommunalSceneInteractor private lateinit var userTracker: FakeUserTracker private lateinit var activityStarter: ActivityStarter private lateinit var userManager: UserManager @@ -141,6 +143,7 @@ class CommunalInteractorTest : SysuiTestCase() { editWidgetsActivityStarter = kosmos.editWidgetsActivityStarter communalPrefsRepository = kosmos.fakeCommunalPrefsRepository sceneInteractor = kosmos.sceneInteractor + communalSceneInteractor = kosmos.communalSceneInteractor userTracker = kosmos.fakeUserTracker activityStarter = kosmos.activityStarter userManager = kosmos.userManager @@ -295,7 +298,7 @@ class CommunalInteractorTest : SysuiTestCase() { val targets = listOf(target1, target2, target3) smartspaceRepository.setCommunalSmartspaceTargets(targets) - val smartspaceContent by collectLastValue(underTest.ongoingContent) + val smartspaceContent by collectLastValue(underTest.getOngoingContent(true)) assertThat(smartspaceContent?.size).isEqualTo(1) assertThat(smartspaceContent?.get(0)?.key) .isEqualTo(CommunalContentModel.KEY.smartspace("target3")) @@ -393,7 +396,7 @@ class CommunalInteractorTest : SysuiTestCase() { smartspaceRepository.setCommunalSmartspaceTargets(targets) - val smartspaceContent by collectLastValue(underTest.ongoingContent) + val smartspaceContent by collectLastValue(underTest.getOngoingContent(true)) assertThat(smartspaceContent?.size).isEqualTo(totalTargets) for (index in 0 until totalTargets) { assertThat(smartspaceContent?.get(index)?.size).isEqualTo(expectedSizes[index]) @@ -409,7 +412,7 @@ class CommunalInteractorTest : SysuiTestCase() { // Media is playing. mediaRepository.mediaActive() - val umoContent by collectLastValue(underTest.ongoingContent) + val umoContent by collectLastValue(underTest.getOngoingContent(true)) assertThat(umoContent?.size).isEqualTo(1) assertThat(umoContent?.get(0)).isInstanceOf(CommunalContentModel.Umo::class.java) @@ -417,6 +420,20 @@ class CommunalInteractorTest : SysuiTestCase() { } @Test + fun umo_mediaPlaying_doNotShowUmo() = + testScope.run { + // Tutorial completed. + tutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_COMPLETED) + + // Media is playing. + mediaRepository.mediaActive() + + val umoContent by collectLastValue(underTest.getOngoingContent(false)) + + assertThat(umoContent?.size).isEqualTo(0) + } + + @Test fun ongoing_shouldOrderAndSizeByTimestamp() = testScope.runTest { // Keyguard showing, and tutorial completed. @@ -439,7 +456,7 @@ class CommunalInteractorTest : SysuiTestCase() { val timer3 = smartspaceTimer("timer3", timestamp = 4L) smartspaceRepository.setCommunalSmartspaceTargets(listOf(timer1, timer2, timer3)) - val ongoingContent by collectLastValue(underTest.ongoingContent) + val ongoingContent by collectLastValue(underTest.getOngoingContent(true)) assertThat(ongoingContent?.size).isEqualTo(4) assertThat(ongoingContent?.get(0)?.key) .isEqualTo(CommunalContentModel.KEY.smartspace("timer3")) @@ -801,7 +818,11 @@ class CommunalInteractorTest : SysuiTestCase() { @Test fun testShowWidgetEditorStartsActivity() = testScope.runTest { + val editModeState by collectLastValue(communalSceneInteractor.editModeState) + underTest.showWidgetEditor() + + assertThat(editModeState).isEqualTo(EditModeState.STARTING) verify(editWidgetsActivityStarter).startActivity() } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalSceneInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalSceneInteractorTest.kt index aad2e6001f1c..6e48b999af47 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalSceneInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalSceneInteractorTest.kt @@ -20,15 +20,19 @@ import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.compose.animation.scene.ObservableTransitionState import com.android.systemui.SysuiTestCase +import com.android.systemui.animation.ActivityTransitionAnimator import com.android.systemui.communal.data.repository.communalSceneRepository import com.android.systemui.communal.domain.model.CommunalTransitionProgressModel import com.android.systemui.communal.shared.model.CommunalScenes +import com.android.systemui.communal.shared.model.EditModeState import com.android.systemui.coroutines.collectLastValue import com.android.systemui.kosmos.testScope import com.android.systemui.testKosmos import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.flowOf +import kotlinx.coroutines.test.advanceTimeBy import kotlinx.coroutines.test.runTest import org.junit.Test import org.junit.runner.RunWith @@ -63,6 +67,42 @@ class CommunalSceneInteractorTest : SysuiTestCase() { assertThat(currentScene).isEqualTo(CommunalScenes.Communal) } + @OptIn(ExperimentalCoroutinesApi::class) + @Test + fun snapToSceneWithDelay() = + testScope.runTest { + val currentScene by collectLastValue(underTest.currentScene) + assertThat(currentScene).isEqualTo(CommunalScenes.Blank) + underTest.snapToScene( + CommunalScenes.Communal, + ActivityTransitionAnimator.TIMINGS.totalDuration + ) + assertThat(currentScene).isEqualTo(CommunalScenes.Blank) + advanceTimeBy(ActivityTransitionAnimator.TIMINGS.totalDuration) + assertThat(currentScene).isEqualTo(CommunalScenes.Communal) + } + + @Test + fun snapToSceneForActivity() = + testScope.runTest { + val currentScene by collectLastValue(underTest.currentScene) + assertThat(currentScene).isEqualTo(CommunalScenes.Blank) + + underTest.snapToSceneForActivityStart(CommunalScenes.Communal) + assertThat(currentScene).isEqualTo(CommunalScenes.Communal) + } + + @Test + fun snapToSceneForActivity_willNotChangeScene_forEditModeActivity() = + testScope.runTest { + val currentScene by collectLastValue(underTest.currentScene) + assertThat(currentScene).isEqualTo(CommunalScenes.Blank) + + underTest.setEditModeState(EditModeState.STARTING) + underTest.snapToSceneForActivityStart(CommunalScenes.Communal) + assertThat(currentScene).isEqualTo(CommunalScenes.Blank) + } + @Test fun transitionProgress_fullProgress() = testScope.runTest { diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/smartspace/SmartspaceInteractionHandlerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/smartspace/SmartspaceInteractionHandlerTest.kt new file mode 100644 index 000000000000..9e24ef840a5b --- /dev/null +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/smartspace/SmartspaceInteractionHandlerTest.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.systemui.communal.smartspace + +import android.app.PendingIntent +import android.content.Intent +import android.view.View +import android.widget.FrameLayout +import android.widget.RemoteViews.RemoteResponse +import androidx.core.util.component1 +import androidx.core.util.component2 +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.communal.widgets.SmartspaceAppWidgetHostView +import com.android.systemui.plugins.ActivityStarter +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.kotlin.eq +import org.mockito.kotlin.isNull +import org.mockito.kotlin.mock +import org.mockito.kotlin.notNull +import org.mockito.kotlin.refEq +import org.mockito.kotlin.verify + +@SmallTest +@RunWith(AndroidJUnit4::class) +class SmartspaceInteractionHandlerTest : SysuiTestCase() { + private val activityStarter = mock<ActivityStarter>() + + private val testIntent = + PendingIntent.getActivity( + context, + /* requestCode = */ 0, + Intent("action"), + PendingIntent.FLAG_IMMUTABLE + ) + private val testResponse = RemoteResponse.fromPendingIntent(testIntent) + + private val underTest: SmartspaceInteractionHandler by lazy { + SmartspaceInteractionHandler(activityStarter) + } + + @Test + fun launchAnimatorIsUsedForSmartspaceView() { + val parent = FrameLayout(context) + val view = SmartspaceAppWidgetHostView(context) + parent.addView(view) + val (fillInIntent, activityOptions) = testResponse.getLaunchOptions(view) + + underTest.onInteraction(view, testIntent, testResponse) + + verify(activityStarter) + .startPendingIntentWithoutDismissing( + eq(testIntent), + eq(false), + isNull(), + notNull(), + refEq(fillInIntent), + refEq(activityOptions.toBundle()), + ) + } + + @Test + fun launchAnimatorIsNotUsedForRegularView() { + val parent = FrameLayout(context) + val view = View(context) + parent.addView(view) + val (fillInIntent, activityOptions) = testResponse.getLaunchOptions(view) + + underTest.onInteraction(view, testIntent, testResponse) + + verify(activityStarter) + .startPendingIntentWithoutDismissing( + eq(testIntent), + eq(false), + isNull(), + isNull(), + refEq(fillInIntent), + refEq(activityOptions.toBundle()), + ) + } +} diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/ui/viewmodel/CommunalTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/ui/viewmodel/CommunalTransitionViewModelTest.kt index f9d5073799db..0250c9d99e3a 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/ui/viewmodel/CommunalTransitionViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/ui/viewmodel/CommunalTransitionViewModelTest.kt @@ -19,17 +19,23 @@ package com.android.systemui.communal.ui.viewmodel import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase +import com.android.systemui.communal.data.repository.fakeCommunalSceneRepository +import com.android.systemui.communal.shared.model.CommunalScenes import com.android.systemui.coroutines.collectLastValue import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.kosmos.testScope import com.android.systemui.testKosmos import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.TestScope +import kotlinx.coroutines.test.runCurrent 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 CommunalTransitionViewModelTest : SysuiTestCase() { @@ -49,13 +55,9 @@ class CommunalTransitionViewModelTest : SysuiTestCase() { fun testIsUmoOnCommunalDuringTransitionBetweenLockscreenAndGlanceableHub() = testScope.runTest { val isUmoOnCommunal by collectLastValue(underTest.isUmoOnCommunal) - assertThat(isUmoOnCommunal).isNull() + runCurrent() - keyguardTransitionRepository.sendTransitionSteps( - from = KeyguardState.LOCKSCREEN, - to = KeyguardState.GLANCEABLE_HUB, - testScope - ) + enterCommunal(from = KeyguardState.LOCKSCREEN) assertThat(isUmoOnCommunal).isTrue() keyguardTransitionRepository.sendTransitionSteps( @@ -70,13 +72,9 @@ class CommunalTransitionViewModelTest : SysuiTestCase() { fun testIsUmoOnCommunalDuringTransitionBetweenDreamingAndGlanceableHub() = testScope.runTest { val isUmoOnCommunal by collectLastValue(underTest.isUmoOnCommunal) - assertThat(isUmoOnCommunal).isNull() + runCurrent() - keyguardTransitionRepository.sendTransitionSteps( - from = KeyguardState.DREAMING, - to = KeyguardState.GLANCEABLE_HUB, - testScope - ) + enterCommunal(from = KeyguardState.DREAMING) assertThat(isUmoOnCommunal).isTrue() keyguardTransitionRepository.sendTransitionSteps( @@ -91,13 +89,9 @@ class CommunalTransitionViewModelTest : SysuiTestCase() { fun testIsUmoOnCommunalDuringTransitionBetweenOccludedAndGlanceableHub() = testScope.runTest { val isUmoOnCommunal by collectLastValue(underTest.isUmoOnCommunal) - assertThat(isUmoOnCommunal).isNull() + runCurrent() - keyguardTransitionRepository.sendTransitionSteps( - from = KeyguardState.OCCLUDED, - to = KeyguardState.GLANCEABLE_HUB, - testScope - ) + enterCommunal(from = KeyguardState.OCCLUDED) assertThat(isUmoOnCommunal).isTrue() keyguardTransitionRepository.sendTransitionSteps( @@ -105,7 +99,33 @@ class CommunalTransitionViewModelTest : SysuiTestCase() { to = KeyguardState.OCCLUDED, testScope ) + assertThat(isUmoOnCommunal).isFalse() + } + + @Test + fun isUmoOnCommunal_noLongerVisible_returnsFalse() = + testScope.runTest { + val isUmoOnCommunal by collectLastValue(underTest.isUmoOnCommunal) + runCurrent() + enterCommunal(from = KeyguardState.LOCKSCREEN) + assertThat(isUmoOnCommunal).isTrue() + + // Communal is no longer visible. + kosmos.fakeCommunalSceneRepository.changeScene(CommunalScenes.Blank) + runCurrent() + + // isUmoOnCommunal returns false, even without any keyguard transition. assertThat(isUmoOnCommunal).isFalse() } + + private suspend fun TestScope.enterCommunal(from: KeyguardState) { + keyguardTransitionRepository.sendTransitionSteps( + from = from, + to = KeyguardState.GLANCEABLE_HUB, + testScope + ) + kosmos.fakeCommunalSceneRepository.changeScene(CommunalScenes.Communal) + runCurrent() + } } 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 84dbfd488fe7..d5fe2a1b2e47 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 @@ -38,14 +38,17 @@ import com.android.systemui.communal.data.repository.FakeCommunalWidgetRepositor import com.android.systemui.communal.data.repository.fakeCommunalMediaRepository import com.android.systemui.communal.data.repository.fakeCommunalTutorialRepository import com.android.systemui.communal.data.repository.fakeCommunalWidgetRepository +import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor import com.android.systemui.communal.domain.interactor.communalInteractor import com.android.systemui.communal.domain.interactor.communalSceneInteractor import com.android.systemui.communal.domain.interactor.communalSettingsInteractor import com.android.systemui.communal.domain.model.CommunalContentModel import com.android.systemui.communal.shared.log.CommunalUiEvent import com.android.systemui.communal.shared.model.CommunalWidgetContentModel +import com.android.systemui.communal.shared.model.EditModeState import com.android.systemui.communal.ui.viewmodel.CommunalEditModeViewModel import com.android.systemui.coroutines.collectLastValue +import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor import com.android.systemui.kosmos.testDispatcher import com.android.systemui.kosmos.testScope import com.android.systemui.log.logcatLogBuffer @@ -86,6 +89,7 @@ class CommunalEditModeViewModelTest : SysuiTestCase() { private lateinit var widgetRepository: FakeCommunalWidgetRepository private lateinit var smartspaceRepository: FakeSmartspaceRepository private lateinit var mediaRepository: FakeCommunalMediaRepository + private lateinit var communalSceneInteractor: CommunalSceneInteractor private val testableResources = context.orCreateTestableResources @@ -99,6 +103,7 @@ class CommunalEditModeViewModelTest : SysuiTestCase() { widgetRepository = kosmos.fakeCommunalWidgetRepository smartspaceRepository = kosmos.fakeSmartspaceRepository mediaRepository = kosmos.fakeCommunalMediaRepository + communalSceneInteractor = kosmos.communalSceneInteractor kosmos.fakeUserTracker.set( userInfos = listOf(MAIN_USER_INFO), selectedUserIndex = 0, @@ -107,9 +112,10 @@ class CommunalEditModeViewModelTest : SysuiTestCase() { underTest = CommunalEditModeViewModel( - kosmos.communalSceneInteractor, + communalSceneInteractor, kosmos.communalInteractor, kosmos.communalSettingsInteractor, + kosmos.keyguardTransitionInteractor, mediaHost, uiEventLogger, logcatLogBuffer("CommunalEditModeViewModelTest"), @@ -172,6 +178,22 @@ class CommunalEditModeViewModelTest : SysuiTestCase() { } @Test + fun isCommunalContentVisible_isTrue_whenEditModeShowing() = + testScope.runTest { + val isCommunalContentVisible by collectLastValue(underTest.isCommunalContentVisible) + communalSceneInteractor.setEditModeState(EditModeState.SHOWING) + assertThat(isCommunalContentVisible).isEqualTo(true) + } + + @Test + fun isCommunalContentVisible_isFalse_whenEditModeNotShowing() = + testScope.runTest { + val isCommunalContentVisible by collectLastValue(underTest.isCommunalContentVisible) + communalSceneInteractor.setEditModeState(null) + assertThat(isCommunalContentVisible).isEqualTo(false) + } + + @Test fun deleteWidget() = testScope.runTest { tutorialRepository.setTutorialSettingState(Settings.Secure.HUB_MODE_TUTORIAL_COMPLETED) diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt index 9dcea82e782b..e7a7b152d936 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt @@ -46,6 +46,7 @@ import com.android.systemui.communal.ui.viewmodel.CommunalViewModel import com.android.systemui.communal.ui.viewmodel.CommunalViewModel.Companion.POPUP_AUTO_HIDE_TIMEOUT_MS import com.android.systemui.communal.ui.viewmodel.PopupType import com.android.systemui.coroutines.collectLastValue +import com.android.systemui.coroutines.collectValues import com.android.systemui.flags.Flags.COMMUNAL_SERVICE_ENABLED import com.android.systemui.flags.andSceneContainer import com.android.systemui.flags.fakeFeatureFlagsClassic @@ -61,6 +62,7 @@ import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.shared.model.StatusBarState import com.android.systemui.keyguard.shared.model.TransitionState import com.android.systemui.keyguard.shared.model.TransitionStep +import com.android.systemui.kosmos.testDispatcher import com.android.systemui.kosmos.testScope import com.android.systemui.log.logcatLogBuffer import com.android.systemui.media.controls.ui.controller.MediaHierarchyManager @@ -142,11 +144,13 @@ class CommunalViewModelTest(flags: FlagsParameterization) : SysuiTestCase() { selectedUserIndex = 0, ) whenever(providerInfo.profile).thenReturn(UserHandle(MAIN_USER_INFO.id)) + whenever(mediaHost.visible).thenReturn(true) kosmos.powerInteractor.setAwakeForTest() underTest = CommunalViewModel( + kosmos.testDispatcher, testScope, context.resources, kosmos.keyguardTransitionInteractor, @@ -235,6 +239,45 @@ class CommunalViewModelTest(flags: FlagsParameterization) : SysuiTestCase() { } @Test + fun communalContent_mediaHostVisible_umoIncluded() = + testScope.runTest { + // Media playing. + mediaRepository.mediaActive() + + val communalContent by collectLastValue(underTest.communalContent) + assertThat(communalContent?.size).isEqualTo(2) + assertThat(communalContent?.get(0)).isInstanceOf(CommunalContentModel.Umo::class.java) + } + + @Test + fun communalContent_mediaHostVisible_umoExcluded() = + testScope.runTest { + whenever(mediaHost.visible).thenReturn(false) + mediaHost.updateViewVisibility() + // Media playing. + mediaRepository.mediaActive() + + val communalContent by collectLastValue(underTest.communalContent) + assertThat(communalContent?.size).isEqualTo(1) + assertThat(communalContent?.get(0)) + .isInstanceOf(CommunalContentModel.CtaTileInViewMode::class.java) + } + + @Test + fun communalContent_mediaHostVisible_umoToggle() = + testScope.runTest { + mediaHost.updateViewVisibility() + mediaRepository.mediaActive() + + val communalContent by collectValues(underTest.communalContent) + + whenever(mediaHost.visible).thenReturn(false) + mediaHost.updateViewVisibility() + + assertThat(communalContent.size).isEqualTo(1) + } + + @Test fun isEmptyState_isTrue_noWidgetButActiveLiveContent() = testScope.runTest { tutorialRepository.setTutorialSettingState(Settings.Secure.HUB_MODE_TUTORIAL_COMPLETED) diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/WidgetInteractionHandlerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/WidgetInteractionHandlerTest.kt index 420b11c4bde3..bf6a2b084a9b 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/WidgetInteractionHandlerTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/WidgetInteractionHandlerTest.kt @@ -27,23 +27,19 @@ import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.plugins.ActivityStarter -import com.android.systemui.util.mockito.eq -import org.junit.Before import org.junit.Test import org.junit.runner.RunWith -import org.mockito.ArgumentMatchers.refEq -import org.mockito.Mock -import org.mockito.Mockito.isNull -import org.mockito.Mockito.notNull -import org.mockito.Mockito.verify -import org.mockito.MockitoAnnotations +import org.mockito.kotlin.eq +import org.mockito.kotlin.isNull +import org.mockito.kotlin.mock +import org.mockito.kotlin.notNull +import org.mockito.kotlin.refEq +import org.mockito.kotlin.verify @SmallTest @RunWith(AndroidJUnit4::class) class WidgetInteractionHandlerTest : SysuiTestCase() { - @Mock private lateinit var activityStarter: ActivityStarter - - private lateinit var underTest: WidgetInteractionHandler + private val activityStarter = mock<ActivityStarter>() private val testIntent = PendingIntent.getActivity( @@ -54,10 +50,8 @@ class WidgetInteractionHandlerTest : SysuiTestCase() { ) private val testResponse = RemoteResponse.fromPendingIntent(testIntent) - @Before - fun setUp() { - MockitoAnnotations.initMocks(this) - underTest = WidgetInteractionHandler(activityStarter) + private val underTest: WidgetInteractionHandler by lazy { + WidgetInteractionHandler(activityStarter) } @Test diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayAnimationsControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayAnimationsControllerTest.kt index 86fdaa5872e8..73ef77540398 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayAnimationsControllerTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayAnimationsControllerTest.kt @@ -7,6 +7,7 @@ import android.view.View import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase +import com.android.systemui.ambient.statusbar.ui.AmbientStatusBarViewController import com.android.systemui.complication.ComplicationHostViewController import com.android.systemui.dreams.ui.viewmodel.DreamViewModel import com.android.systemui.log.core.FakeLogBuffer @@ -43,7 +44,7 @@ class DreamOverlayAnimationsControllerTest : SysuiTestCase() { @Mock private lateinit var mockAnimator: AnimatorSet @Mock private lateinit var blurUtils: BlurUtils @Mock private lateinit var hostViewController: ComplicationHostViewController - @Mock private lateinit var statusBarViewController: DreamOverlayStatusBarViewController + @Mock private lateinit var statusBarViewController: AmbientStatusBarViewController @Mock private lateinit var stateController: DreamOverlayStateController @Mock private lateinit var transitionViewModel: DreamViewModel private val logBuffer = FakeLogBuffer.Factory.create() diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java index f5c86e092a26..c48ced1e9a21 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java @@ -17,6 +17,7 @@ package com.android.systemui.dreams; import static kotlinx.coroutines.flow.FlowKt.emptyFlow; +import static kotlinx.coroutines.flow.StateFlowKt.MutableStateFlow; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; @@ -47,6 +48,7 @@ import com.android.dream.lowlight.LowLightTransitionCoordinator; import com.android.keyguard.BouncerPanelExpansionCalculator; import com.android.systemui.Flags; import com.android.systemui.SysuiTestCase; +import com.android.systemui.ambient.statusbar.ui.AmbientStatusBarViewController; import com.android.systemui.ambient.touch.scrim.BouncerlessScrimController; import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerCallbackInteractor; import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerCallbackInteractor.PrimaryBouncerExpansionCallback; @@ -55,6 +57,7 @@ import com.android.systemui.complication.ComplicationHostViewController; import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor; import com.android.systemui.shade.domain.interactor.ShadeInteractor; import com.android.systemui.statusbar.BlurUtils; +import com.android.systemui.touch.TouchInsetManager; import kotlinx.coroutines.CoroutineDispatcher; @@ -80,7 +83,7 @@ public class DreamOverlayContainerViewControllerTest extends SysuiTestCase { ViewTreeObserver mViewTreeObserver; @Mock - DreamOverlayStatusBarViewController mDreamOverlayStatusBarViewController; + AmbientStatusBarViewController mAmbientStatusBarViewController; @Mock LowLightTransitionCoordinator mLowLightTransitionCoordinator; @@ -131,6 +134,8 @@ public class DreamOverlayContainerViewControllerTest extends SysuiTestCase { CommunalInteractor mCommunalInteractor; @Mock private DreamManager mDreamManager; + @Mock + private TouchInsetManager.TouchInsetSession mTouchInsetSession; DreamOverlayContainerViewController mController; @@ -144,14 +149,17 @@ public class DreamOverlayContainerViewControllerTest extends SysuiTestCase { when(mDreamOverlayContainerView.getRootSurfaceControl()) .thenReturn(mAttachedSurfaceControl); when(mKeyguardTransitionInteractor.isFinishedInStateWhere(any())).thenReturn(emptyFlow()); + when(mShadeInteractor.isAnyExpanded()).thenReturn(MutableStateFlow(false)); + when(mCommunalInteractor.isCommunalShowing()).thenReturn(MutableStateFlow(false)); mController = new DreamOverlayContainerViewController( mDreamOverlayContainerView, mComplicationHostViewController, mDreamOverlayContentView, mHubGestureIndicatorView, - mDreamOverlayStatusBarViewController, + mAmbientStatusBarViewController, mLowLightTransitionCoordinator, + mTouchInsetSession, mBlurUtils, mHandler, mDispatcher, @@ -190,7 +198,7 @@ public class DreamOverlayContainerViewControllerTest extends SysuiTestCase { @Test public void testDreamOverlayStatusBarViewControllerInitialized() { mController.init(); - verify(mDreamOverlayStatusBarViewController).init(); + verify(mAmbientStatusBarViewController).init(); } @Test @@ -325,4 +333,12 @@ public class DreamOverlayContainerViewControllerTest extends SysuiTestCase { mController.onViewDetached(); verify(mBouncerlessScrimController).removeCallback(any()); } + + @EnableFlags(android.service.dreams.Flags.FLAG_DREAM_HANDLES_BEING_OBSCURED) + @Test + public void testOnViewAttachedSucceedsWhenDreamHandlesBeingObscuredFlagEnabled() { + // This test will catch failures in presubmit when the dream_handles_being_obscured flag is + // enabled. + mController.onViewAttached(); + } } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/haptics/qs/QSLongPressEffectTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/haptics/qs/QSLongPressEffectTest.kt index 4849e66d37d5..3d3c7788dd56 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/haptics/qs/QSLongPressEffectTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/haptics/qs/QSLongPressEffectTest.kt @@ -21,28 +21,35 @@ import android.testing.TestableLooper.RunWithLooper 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.haptics.vibratorHelper -import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository -import com.android.systemui.keyguard.domain.interactor.keyguardInteractor import com.android.systemui.kosmos.testScope import com.android.systemui.qs.qsTileFactory +import com.android.systemui.statusbar.policy.keyguardStateController import com.android.systemui.testKosmos import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.runTest import org.junit.Before +import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith +import org.mockito.Mock +import org.mockito.junit.MockitoJUnit +import org.mockito.junit.MockitoRule +import org.mockito.kotlin.times +import org.mockito.kotlin.verify +import org.mockito.kotlin.whenever @SmallTest @RunWith(AndroidJUnit4::class) @RunWithLooper(setAsMainLooper = true) class QSLongPressEffectTest : SysuiTestCase() { + @Rule @JvmField val mMockitoRule: MockitoRule = MockitoJUnit.rule() private val kosmos = testKosmos() private val vibratorHelper = kosmos.vibratorHelper private val qsTile = kosmos.qsTileFactory.createTile("Test Tile") + @Mock private lateinit var callback: QSLongPressEffect.Callback private val effectDuration = 400 private val lowTickDuration = 12 @@ -56,13 +63,14 @@ class QSLongPressEffectTest : SysuiTestCase() { lowTickDuration vibratorHelper.primitiveDurations[VibrationEffect.Composition.PRIMITIVE_SPIN] = spinDuration - kosmos.fakeKeyguardRepository.setKeyguardDismissible(true) + whenever(kosmos.keyguardStateController.isUnlocked).thenReturn(true) longPressEffect = QSLongPressEffect( vibratorHelper, - kosmos.keyguardInteractor, + kosmos.keyguardStateController, ) + longPressEffect.callback = callback longPressEffect.qsTile = qsTile } @@ -94,10 +102,8 @@ class QSLongPressEffectTest : SysuiTestCase() { // GIVEN an action down event occurs longPressEffect.handleActionDown() - // THEN the effect moves to the TIMEOUT_WAIT state and starts the wait - val action by collectLastValue(longPressEffect.actionType) + // THEN the effect moves to the TIMEOUT_WAIT state assertThat(longPressEffect.state).isEqualTo(QSLongPressEffect.State.TIMEOUT_WAIT) - assertThat(action).isEqualTo(QSLongPressEffect.ActionType.WAIT_TAP_TIMEOUT) } @Test @@ -118,8 +124,7 @@ class QSLongPressEffectTest : SysuiTestCase() { longPressEffect.handleTimeoutComplete() // THEN the effect emits the action to start an animator - val action by collectLastValue(longPressEffect.actionType) - assertThat(action).isEqualTo(QSLongPressEffect.ActionType.START_ANIMATOR) + verify(callback, times(1)).onStartAnimator() } @Test @@ -170,26 +175,28 @@ class QSLongPressEffectTest : SysuiTestCase() { } @Test - fun onAnimationComplete_keyguardDismissible_effectEndsWithLongPress() = + fun onAnimationComplete_keyguardDismissible_effectEndsWithPrepare() = testWhileInState(QSLongPressEffect.State.RUNNING_FORWARD) { // GIVEN that the animation completes longPressEffect.handleAnimationComplete() - // THEN the long-press effect completes with a LONG_PRESS - assertEffectCompleted(QSLongPressEffect.ActionType.LONG_PRESS) + // THEN the long-press effect completes and the view is called to prepare + assertEffectCompleted() + verify(callback, times(1)).onPrepareForLaunch() } @Test - fun onAnimationComplete_keyguardNotDismissible_effectEndsWithResetAndLongPress() = + fun onAnimationComplete_keyguardNotDismissible_effectEndsWithReset() = testWhileInState(QSLongPressEffect.State.RUNNING_FORWARD) { // GIVEN that the keyguard is not dismissible - kosmos.fakeKeyguardRepository.setKeyguardDismissible(false) + whenever(kosmos.keyguardStateController.isUnlocked).thenReturn(false) // GIVEN that the animation completes longPressEffect.handleAnimationComplete() - // THEN the long-press effect completes with RESET_AND_LONG_PRESS - assertEffectCompleted(QSLongPressEffect.ActionType.RESET_AND_LONG_PRESS) + // THEN the long-press effect completes and the properties are called to reset + assertEffectCompleted() + verify(callback, times(1)).onResetProperties() } @Test @@ -202,8 +209,7 @@ class QSLongPressEffectTest : SysuiTestCase() { longPressEffect.handleActionDown() // THEN the effect posts an action to cancel the animator - val action by collectLastValue(longPressEffect.actionType) - assertThat(action).isEqualTo(QSLongPressEffect.ActionType.CANCEL_ANIMATOR) + verify(callback, times(1)).onCancelAnimator() } @Test @@ -212,10 +218,8 @@ class QSLongPressEffectTest : SysuiTestCase() { // GIVEN that the animator was cancelled longPressEffect.handleAnimationCancel() - // THEN the state goes to the timeout wait and the wait is posted - val action by collectLastValue(longPressEffect.actionType) + // THEN the state goes to the timeout wait assertThat(longPressEffect.state).isEqualTo(QSLongPressEffect.State.TIMEOUT_WAIT) - assertThat(action).isEqualTo(QSLongPressEffect.ActionType.WAIT_TAP_TIMEOUT) } @Test @@ -316,16 +320,13 @@ class QSLongPressEffectTest : SysuiTestCase() { * Asserts that the effect completes by checking that: * 1. The final snap haptics are played * 2. The internal state goes back to [QSLongPressEffect.State.IDLE] - * 3. The action to perform on the tile is the action given as a parameter */ - private fun TestScope.assertEffectCompleted(expectedAction: QSLongPressEffect.ActionType) { - val action by collectLastValue(longPressEffect.actionType) + private fun assertEffectCompleted() { val snapEffect = LongPressHapticBuilder.createSnapEffect() assertThat(snapEffect).isNotNull() assertThat(vibratorHelper.hasVibratedWithEffects(snapEffect!!)).isTrue() assertThat(longPressEffect.state).isEqualTo(QSLongPressEffect.State.IDLE) - assertThat(action).isEqualTo(expectedAction) } /** @@ -333,10 +334,8 @@ class QSLongPressEffectTest : SysuiTestCase() { * 1. The internal state is [QSLongPressEffect.State.RUNNING_BACKWARDS] * 2. An action to reverse the animator is emitted */ - private fun TestScope.assertEffectReverses() { - val action by collectLastValue(longPressEffect.actionType) - + private fun assertEffectReverses() { assertThat(longPressEffect.state).isEqualTo(QSLongPressEffect.State.RUNNING_BACKWARDS) - assertThat(action).isEqualTo(QSLongPressEffect.ActionType.REVERSE_ANIMATOR) + verify(callback, times(1)).onReverseAnimator() } } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt index f375ec7b8884..5dac37a0c0f0 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt @@ -333,27 +333,16 @@ class KeyguardRepositoryImplTest : SysuiTestCase() { } @Test - fun isDreamingFromKeyguardUpdateMonitor() = - TestScope(mainDispatcher).runTest { - whenever(keyguardUpdateMonitor.isDreaming()).thenReturn(false) - var latest: Boolean? = null - val job = underTest.isDreaming.onEach { latest = it }.launchIn(this) - - runCurrent() - assertThat(latest).isFalse() - - val captor = argumentCaptor<KeyguardUpdateMonitorCallback>() - verify(keyguardUpdateMonitor).registerCallback(captor.capture()) - - captor.value.onDreamingStateChanged(true) - runCurrent() - assertThat(latest).isTrue() + fun isDreaming() = + testScope.runTest { + val isDreaming by collectLastValue(underTest.isDreaming) + assertThat(isDreaming).isFalse() - captor.value.onDreamingStateChanged(false) - runCurrent() - assertThat(latest).isFalse() + underTest.setDreaming(true) + assertThat(isDreaming).isTrue() - job.cancel() + underTest.setDreaming(false) + assertThat(isDreaming).isFalse() } @Test diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractorTest.kt index cfc6b3304399..d20fec44d60f 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractorTest.kt @@ -32,10 +32,14 @@ package com.android.systemui.keyguard.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 import com.android.systemui.SysuiTestCase import com.android.systemui.bouncer.data.repository.fakeKeyguardBouncerRepository +import com.android.systemui.communal.domain.interactor.communalInteractor import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository @@ -76,6 +80,7 @@ class FromAlternateBouncerTransitionInteractorTest : SysuiTestCase() { } @Test + @DisableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR) fun transitionToGone_keyguardOccluded_biometricAuthenticated() = testScope.runTest { transitionRepository.sendTransitionSteps( @@ -96,6 +101,25 @@ class FromAlternateBouncerTransitionInteractorTest : SysuiTestCase() { } @Test + @EnableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR) + fun transitionToGone_keyguardOccludedThenAltBouncer_authed_wmStateRefactor() = + testScope.runTest { + transitionRepository.sendTransitionSteps( + from = KeyguardState.OCCLUDED, + to = KeyguardState.ALTERNATE_BOUNCER, + testScope + ) + reset(transitionRepository) + + // Authentication results in calling startDismissKeyguardTransition. + kosmos.keyguardTransitionInteractor.startDismissKeyguardTransition() + runCurrent() + + assertThat(transitionRepository) + .startedTransition(from = KeyguardState.ALTERNATE_BOUNCER, to = KeyguardState.GONE) + } + + @Test fun noTransition_keyguardNotOccluded_biometricAuthenticated() = testScope.runTest { transitionRepository.sendTransitionSteps( @@ -143,4 +167,37 @@ class FromAlternateBouncerTransitionInteractorTest : SysuiTestCase() { to = KeyguardState.OCCLUDED ) } + + @Test + fun transitionToGone_whenOpeningGlanceableHubEditMode() = + testScope.runTest { + kosmos.fakeKeyguardBouncerRepository.setAlternateVisible(true) + runCurrent() + + // On Glanceable hub and edit mode activity is started + transitionRepository.sendTransitionSteps( + from = KeyguardState.GLANCEABLE_HUB, + to = KeyguardState.ALTERNATE_BOUNCER, + testScope + ) + reset(transitionRepository) + + kosmos.communalInteractor.setEditModeOpen(true) + runCurrent() + + // Auth and alternate bouncer is hidden + kosmos.fakeKeyguardBouncerRepository.setAlternateVisible(false) + advanceTimeBy(200) // advance past delay + + // Then no transition should occur yet + assertThat(transitionRepository).noTransitionsStarted() + + // When keyguard is going away + kosmos.fakeKeyguardRepository.setKeyguardGoingAway(true) + runCurrent() + + // Then transition to GONE should occur + assertThat(transitionRepository) + .startedTransition(from = KeyguardState.ALTERNATE_BOUNCER, to = KeyguardState.GONE) + } } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractorTest.kt index 6c5001ab9415..6eb9862fb4f1 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractorTest.kt @@ -53,6 +53,7 @@ import com.android.systemui.power.domain.interactor.PowerInteractor 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.statusbar.domain.interactor.keyguardOcclusionInteractor import com.android.systemui.testKosmos import junit.framework.Assert.assertEquals import kotlinx.coroutines.ExperimentalCoroutinesApi @@ -299,6 +300,7 @@ class FromAodTransitionInteractorTest : SysuiTestCase() { fun testTransitionToOccluded_onWake() = testScope.runTest { kosmos.fakeKeyguardRepository.setKeyguardOccluded(true) + kosmos.keyguardOcclusionInteractor.setWmNotifiedShowWhenLockedActivityOnTop(true) powerInteractor.setAwakeForTest() advanceTimeBy(100) // account for debouncing @@ -312,6 +314,7 @@ class FromAodTransitionInteractorTest : SysuiTestCase() { testScope.runTest { kosmos.fakeKeyguardRepository.setKeyguardShowing(false) kosmos.fakeKeyguardRepository.setKeyguardDismissible(true) + kosmos.keyguardTransitionInteractor.startDismissKeyguardTransition() powerInteractor.setAwakeForTest() advanceTimeBy(100) // account for debouncing diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt index addbdb664c77..7906a8244c5d 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt @@ -27,6 +27,7 @@ import com.android.systemui.SysuiTestCase import com.android.systemui.bouncer.data.repository.keyguardBouncerRepository import com.android.systemui.common.ui.data.repository.fakeConfigurationRepository import com.android.systemui.coroutines.collectLastValue +import com.android.systemui.coroutines.collectValues import com.android.systemui.flags.EnableSceneContainer import com.android.systemui.keyguard.data.repository.fakeCommandQueue import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository @@ -191,6 +192,7 @@ class KeyguardInteractorTest : SysuiTestCase() { fun dismissAlpha() = testScope.runTest { val dismissAlpha by collectLastValue(underTest.dismissAlpha) + assertThat(dismissAlpha).isEqualTo(1f) keyguardTransitionRepository.sendTransitionSteps( from = KeyguardState.AOD, @@ -202,9 +204,9 @@ class KeyguardInteractorTest : SysuiTestCase() { // User begins to swipe up shadeRepository.setLegacyShadeExpansion(0.99f) - // When not dismissable, no alpha value (null) should emit + // When not dismissable, the last alpha value should still be present repository.setKeyguardDismissible(false) - assertThat(dismissAlpha).isNull() + assertThat(dismissAlpha).isEqualTo(1f) repository.setKeyguardDismissible(true) shadeRepository.setLegacyShadeExpansion(0.98f) @@ -212,9 +214,11 @@ class KeyguardInteractorTest : SysuiTestCase() { } @Test - fun dismissAlpha_whenShadeIsExpandedEmitsNull() = + fun dismissAlpha_whenShadeResetsEmitsOne() = testScope.runTest { - val dismissAlpha by collectLastValue(underTest.dismissAlpha) + val dismissAlpha by collectValues(underTest.dismissAlpha) + assertThat(dismissAlpha[0]).isEqualTo(1f) + assertThat(dismissAlpha.size).isEqualTo(1) keyguardTransitionRepository.sendTransitionSteps( from = KeyguardState.AOD, @@ -222,14 +226,50 @@ class KeyguardInteractorTest : SysuiTestCase() { testScope, ) - repository.setStatusBarState(StatusBarState.SHADE_LOCKED) - shadeRepository.setQsExpansion(1f) + // User begins to swipe up + repository.setStatusBarState(StatusBarState.KEYGUARD) + repository.setKeyguardDismissible(true) + shadeRepository.setLegacyShadeExpansion(0.98f) - repository.setKeyguardDismissible(false) - assertThat(dismissAlpha).isNull() + assertThat(dismissAlpha[1]).isGreaterThan(0.5f) + assertThat(dismissAlpha[1]).isLessThan(1f) + assertThat(dismissAlpha.size).isEqualTo(2) + + // Now reset the shade + shadeRepository.setLegacyShadeExpansion(1f) + assertThat(dismissAlpha[2]).isEqualTo(1f) + assertThat(dismissAlpha.size).isEqualTo(3) + } + + @Test + fun dismissAlpha_doesNotEmitWhileTransitioning() = + testScope.runTest { + val dismissAlpha by collectLastValue(underTest.dismissAlpha) + assertThat(dismissAlpha).isEqualTo(1f) + + keyguardTransitionRepository.sendTransitionSteps( + listOf( + TransitionStep( + from = KeyguardState.AOD, + to = KeyguardState.GONE, + value = 0f, + transitionState = TransitionState.STARTED, + ), + TransitionStep( + from = KeyguardState.AOD, + to = KeyguardState.GONE, + value = 0.1f, + transitionState = TransitionState.RUNNING, + ), + ), + testScope, + ) repository.setKeyguardDismissible(true) - assertThat(dismissAlpha).isNull() + shadeRepository.setLegacyShadeExpansion(0.98f) + + // Should still be one + assertThat(dismissAlpha).isEqualTo(1f) } @Test diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt index 5756bca49cb2..0f061de5226c 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt @@ -125,68 +125,6 @@ class KeyguardTransitionInteractorTest : SysuiTestCase() { } @Test - fun dozeAmountTransitionTest_AodToFromLockscreen() = - testScope.runTest { - val dozeAmountSteps by collectValues(underTest.dozeAmountTransition) - - val steps = mutableListOf<TransitionStep>() - - steps.add(TransitionStep(AOD, LOCKSCREEN, 0f, STARTED)) - steps.add(TransitionStep(AOD, LOCKSCREEN, 0.5f, RUNNING)) - steps.add(TransitionStep(AOD, LOCKSCREEN, 1f, FINISHED)) - steps.add(TransitionStep(LOCKSCREEN, AOD, 0f, STARTED)) - steps.add(TransitionStep(LOCKSCREEN, AOD, 0.8f, RUNNING)) - steps.add(TransitionStep(LOCKSCREEN, AOD, 0.9f, RUNNING)) - steps.add(TransitionStep(LOCKSCREEN, AOD, 1f, FINISHED)) - - steps.forEach { - repository.sendTransitionStep(it) - runCurrent() - } - - assertThat(dozeAmountSteps.subList(0, 3)) - .isEqualTo( - listOf( - steps[0].copy(value = 1f - steps[0].value), - steps[1].copy(value = 1f - steps[1].value), - steps[2].copy(value = 1f - steps[2].value), - ) - ) - assertThat(dozeAmountSteps.subList(3, 7)).isEqualTo(steps.subList(3, 7)) - } - - @Test - fun dozeAmountTransitionTest_AodToFromGone() = - testScope.runTest { - val dozeAmountSteps by collectValues(underTest.dozeAmountTransition) - - val steps = mutableListOf<TransitionStep>() - - steps.add(TransitionStep(AOD, GONE, 0f, STARTED)) - steps.add(TransitionStep(AOD, GONE, 0.3f, RUNNING)) - steps.add(TransitionStep(AOD, GONE, 1f, FINISHED)) - steps.add(TransitionStep(GONE, AOD, 0f, STARTED)) - steps.add(TransitionStep(GONE, AOD, 0.1f, RUNNING)) - steps.add(TransitionStep(GONE, AOD, 0.3f, RUNNING)) - steps.add(TransitionStep(GONE, AOD, 1f, FINISHED)) - - steps.forEach { - repository.sendTransitionStep(it) - runCurrent() - } - - assertThat(dozeAmountSteps.subList(0, 3)) - .isEqualTo( - listOf( - steps[0].copy(value = 1f - steps[0].value), - steps[1].copy(value = 1f - steps[1].value), - steps[2].copy(value = 1f - steps[2].value), - ) - ) - assertThat(dozeAmountSteps.subList(3, 7)).isEqualTo(steps.subList(3, 7)) - } - - @Test fun finishedKeyguardStateTests() = testScope.runTest { val finishedSteps by collectValues(underTest.finishedKeyguardState) 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 9dc930babc10..6e16705b0739 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 @@ -197,7 +197,7 @@ class WindowManagerLockscreenVisibilityInteractorTest : SysuiTestCase() { @Test @EnableSceneContainer - fun surfaceBehindVisibility_fromLockscreenToGone_trueThroughout() = + fun surfaceBehindVisibility_fromLockscreenToGone_noUserInput_trueThroughout() = testScope.runTest { val isSurfaceBehindVisible by collectLastValue(underTest.value.surfaceBehindVisibility) val currentScene by collectLastValue(kosmos.sceneInteractor.currentScene) @@ -249,6 +249,43 @@ 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) diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryForegroundViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryForegroundViewModelTest.kt index 460a1fc44e67..b0959e4eea0b 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryForegroundViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryForegroundViewModelTest.kt @@ -25,6 +25,7 @@ import com.android.systemui.coroutines.collectLastValue import com.android.systemui.keyguard.data.repository.fakeBiometricSettingsRepository import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository import com.android.systemui.keyguard.shared.model.KeyguardState +import com.android.systemui.keyguard.shared.model.TransitionState import com.android.systemui.kosmos.testScope import com.android.systemui.testKosmos import com.google.common.truth.Truth.assertThat @@ -58,6 +59,62 @@ class DeviceEntryForegroundViewModelTest : SysuiTestCase() { assertThat(viewModel?.tint).isEqualTo(Color.WHITE) } + @Test + fun startsDozing_doNotShowAodVariant() = + testScope.runTest { + val viewModel by collectLastValue(underTest.viewModel) + + givenUdfpsEnrolledAndEnabled() + kosmos.run { + fakeKeyguardTransitionRepository.sendTransitionSteps( + from = KeyguardState.LOCKSCREEN, + to = KeyguardState.DOZING, + testScope = testScope, + throughTransitionState = TransitionState.STARTED, + ) + } + + assertThat(viewModel?.useAodVariant).isEqualTo(false) + } + + @Test + fun finishedDozing_showAodVariant() = + testScope.runTest { + val viewModel by collectLastValue(underTest.viewModel) + + givenUdfpsEnrolledAndEnabled() + kosmos.fakeKeyguardTransitionRepository.sendTransitionSteps( + from = KeyguardState.LOCKSCREEN, + to = KeyguardState.AOD, + testScope = testScope, + throughTransitionState = TransitionState.FINISHED, + ) + + assertThat(viewModel?.useAodVariant).isEqualTo(true) + } + + @Test + fun startTransitionToLockscreenFromDozing_doNotShowAodVariant() = + testScope.runTest { + val viewModel by collectLastValue(underTest.viewModel) + + givenUdfpsEnrolledAndEnabled() + kosmos.fakeKeyguardTransitionRepository.sendTransitionSteps( + from = KeyguardState.LOCKSCREEN, + to = KeyguardState.DOZING, + testScope = testScope, + throughTransitionState = TransitionState.FINISHED, + ) + kosmos.fakeKeyguardTransitionRepository.sendTransitionSteps( + from = KeyguardState.DOZING, + to = KeyguardState.LOCKSCREEN, + testScope = testScope, + throughTransitionState = TransitionState.RUNNING, + ) + + assertThat(viewModel?.useAodVariant).isEqualTo(false) + } + private fun givenUdfpsEnrolledAndEnabled() { kosmos.fakeFingerprintPropertyRepository.supportsUdfps() kosmos.fakeBiometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(true) diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModelTest.kt index 68fbd1c44ad7..3f93401cefc4 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModelTest.kt @@ -227,11 +227,11 @@ class DeviceEntryIconViewModelTest : SysuiTestCase() { assertThat(accessibilityDelegateHint) .isEqualTo(DeviceEntryIconView.AccessibilityHintType.AUTHENTICATE) - // non-interactive lock icon + // interactive lock icon for non udfps as well so that user can navigate to bouncer fingerprintPropertyRepository.supportsRearFps() assertThat(accessibilityDelegateHint) - .isEqualTo(DeviceEntryIconView.AccessibilityHintType.NONE) + .isEqualTo(DeviceEntryIconView.AccessibilityHintType.AUTHENTICATE) } @Test diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDozingTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDozingTransitionViewModelTest.kt index 59a6ce75e912..80a95324a185 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDozingTransitionViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDozingTransitionViewModelTest.kt @@ -21,6 +21,7 @@ import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.biometrics.data.repository.FakeFingerprintPropertyRepository import com.android.systemui.biometrics.data.repository.fakeFingerprintPropertyRepository +import com.android.systemui.coroutines.collectLastValue import com.android.systemui.coroutines.collectValues import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsRepository import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository @@ -32,6 +33,7 @@ import com.android.systemui.keyguard.shared.model.TransitionState.RUNNING import com.android.systemui.keyguard.shared.model.TransitionStep import com.android.systemui.kosmos.testScope import com.android.systemui.testKosmos +import com.google.common.collect.Range import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runTest @@ -104,6 +106,24 @@ class GoneToDozingTransitionViewModelTest : SysuiTestCase() { values.forEach { assertThat(it).isNull() } } + @Test + fun notificationAlpha_fadesOut() = + testScope.runTest { + val alpha by collectLastValue(underTest.notificationAlpha) + + keyguardTransitionRepository.sendTransitionStep(step(0f, TransitionState.STARTED)) + assertThat(alpha).isEqualTo(1f) + + keyguardTransitionRepository.sendTransitionStep(step(0.25f)) + assertThat(alpha).isIn(Range.open(.25f, .75f)) + + keyguardTransitionRepository.sendTransitionStep(step(1f)) + assertThat(alpha).isEqualTo(0f) + + keyguardTransitionRepository.sendTransitionStep(step(1f, TransitionState.FINISHED)) + assertThat(alpha).isEqualTo(1f) + } + private fun step(value: Float, state: TransitionState = RUNNING): TransitionStep { return TransitionStep( from = KeyguardState.GONE, diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt index 49df345397d4..194f362d984c 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt @@ -39,7 +39,9 @@ import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.shared.model.TransitionState import com.android.systemui.keyguard.shared.model.TransitionStep import com.android.systemui.kosmos.testScope +import com.android.systemui.scene.data.repository.Idle import com.android.systemui.scene.data.repository.sceneContainerRepository +import com.android.systemui.scene.data.repository.setSceneTransition import com.android.systemui.scene.shared.flag.SceneContainerFlag import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.shade.shadeTestUtil @@ -290,6 +292,7 @@ class KeyguardRootViewModelTest(flags: FlagsParameterization) : SysuiTestCase() testScope, ) + kosmos.setSceneTransition(Idle(Scenes.Gone)) // Make sure the value hasn't changed since we're GONE keyguardRepository.topClippingBounds.value = 5 assertThat(topClippingBounds).isEqualTo(1000) @@ -518,11 +521,14 @@ class KeyguardRootViewModelTest(flags: FlagsParameterization) : SysuiTestCase() to = KeyguardState.GONE, testScope = testScope, ) + kosmos.setSceneTransition(Idle(Scenes.Gone)) assertThat(alpha).isEqualTo(0f) - // Try pulling down shade and ensure the value doesn't change - shadeTestUtil.setQsExpansion(0.5f) - assertThat(alpha).isEqualTo(0f) + if (!SceneContainerFlag.isEnabled) { + // Try pulling down shade and ensure the value doesn't change + shadeTestUtil.setQsExpansion(0.5f) + assertThat(alpha).isEqualTo(0f) + } } @Test diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt index f46ca002a103..61d8216c87c2 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt @@ -50,6 +50,7 @@ import com.android.systemui.util.mockito.mock import com.google.common.truth.Truth.assertThat import kotlin.math.pow import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.test.runTest import org.junit.BeforeClass import org.junit.Test @@ -205,8 +206,13 @@ class LockscreenSceneViewModelTest : SysuiTestCase() { pointerCount = if (downWithTwoPointers) 2 else 1, ) ) - - assertThat(downDestination?.toScene) + val downScene by + collectLastValue( + downDestination?.let { + kosmos.sceneInteractor.resolveSceneFamily(downDestination.toScene) + } ?: flowOf(null) + ) + assertThat(downScene) .isEqualTo( expectedDownDestination( downFromEdge = downFromEdge, @@ -223,7 +229,14 @@ class LockscreenSceneViewModelTest : SysuiTestCase() { ) ) - assertThat(destinationScenes?.get(Swipe(SwipeDirection.Up))?.toScene) + val upScene by + collectLastValue( + destinationScenes?.get(Swipe(SwipeDirection.Up))?.toScene?.let { scene -> + kosmos.sceneInteractor.resolveSceneFamily(scene) + } ?: flowOf(null) + ) + + assertThat(upScene) .isEqualTo( expectedUpDestination( canSwipeToEnter = canSwipeToEnter, @@ -231,7 +244,14 @@ class LockscreenSceneViewModelTest : SysuiTestCase() { ) ) - assertThat(destinationScenes?.get(Swipe(SwipeDirection.Left))?.toScene) + val leftScene by + collectLastValue( + destinationScenes?.get(Swipe(SwipeDirection.Left))?.toScene?.let { scene -> + kosmos.sceneInteractor.resolveSceneFamily(scene) + } ?: flowOf(null) + ) + + assertThat(leftScene) .isEqualTo( expectedLeftDestination( isCommunalAvailable = isCommunalAvailable, diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/data/repository/MediaFilterRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/data/repository/MediaFilterRepositoryTest.kt index 7a37a9e03b16..bc0512a1468d 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/data/repository/MediaFilterRepositoryTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/data/repository/MediaFilterRepositoryTest.kt @@ -56,11 +56,15 @@ class MediaFilterRepositoryTest : SysuiTestCase() { underTest.addSelectedUserMediaEntry(userMedia) assertThat(selectedUserEntries?.get(instanceId)).isEqualTo(userMedia) + assertThat(underTest.hasActiveMedia()).isTrue() + assertThat(underTest.hasAnyMedia()).isTrue() underTest.addSelectedUserMediaEntry(userMedia.copy(active = false)) assertThat(selectedUserEntries?.get(instanceId)).isNotEqualTo(userMedia) assertThat(selectedUserEntries?.get(instanceId)?.active).isFalse() + assertThat(underTest.hasActiveMedia()).isFalse() + assertThat(underTest.hasAnyMedia()).isTrue() } @Test @@ -74,8 +78,12 @@ class MediaFilterRepositoryTest : SysuiTestCase() { underTest.addSelectedUserMediaEntry(userMedia) assertThat(selectedUserEntries?.get(instanceId)).isEqualTo(userMedia) + assertThat(underTest.hasActiveMedia()).isTrue() + assertThat(underTest.hasAnyMedia()).isTrue() assertThat(underTest.removeSelectedUserMediaEntry(instanceId, userMedia)).isTrue() + assertThat(underTest.hasActiveMedia()).isFalse() + assertThat(underTest.hasAnyMedia()).isFalse() } @Test @@ -144,7 +152,7 @@ class MediaFilterRepositoryTest : SysuiTestCase() { underTest.setRecommendation(mediaRecommendation.copy(isActive = false)) assertThat(smartspaceMediaData).isNotEqualTo(mediaRecommendation) - assertThat(smartspaceMediaData?.isActive).isFalse() + assertThat(underTest.isRecommendationActive()).isFalse() } @Test @@ -349,6 +357,14 @@ class MediaFilterRepositoryTest : SysuiTestCase() { .inOrder() } + @Test + fun hasAnyMedia_noMediaSet_returnsFalse() = + testScope.runTest { assertThat(underTest.hasAnyMedia()).isFalse() } + + @Test + fun hasActiveMedia_noMediaSet_returnsFalse() = + testScope.runTest { assertThat(underTest.hasActiveMedia()).isFalse() } + private fun createMediaData( app: String, playing: Boolean, diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/domain/interactor/MediaCarouselInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/domain/interactor/MediaCarouselInteractorTest.kt index 39dbc7e8cb5b..c62195fafd8c 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/domain/interactor/MediaCarouselInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/domain/interactor/MediaCarouselInteractorTest.kt @@ -76,22 +76,20 @@ class MediaCarouselInteractorTest : SysuiTestCase() { testScope.runTest { val hasActiveMediaOrRecommendation by collectLastValue(underTest.hasActiveMediaOrRecommendation) - val hasActiveMedia by collectLastValue(underTest.hasActiveMedia) - val hasAnyMedia by collectLastValue(underTest.hasAnyMedia) val userMedia = MediaData(active = true) mediaFilterRepository.addSelectedUserMediaEntry(userMedia) assertThat(hasActiveMediaOrRecommendation).isTrue() - assertThat(hasActiveMedia).isTrue() - assertThat(hasAnyMedia).isTrue() + assertThat(underTest.hasActiveMedia()).isTrue() + assertThat(underTest.hasAnyMedia()).isTrue() mediaFilterRepository.addSelectedUserMediaEntry(userMedia.copy(active = false)) assertThat(hasActiveMediaOrRecommendation).isFalse() - assertThat(hasActiveMedia).isFalse() - assertThat(hasAnyMedia).isTrue() + assertThat(underTest.hasActiveMedia()).isFalse() + assertThat(underTest.hasAnyMedia()).isTrue() } @Test @@ -99,8 +97,6 @@ class MediaCarouselInteractorTest : SysuiTestCase() { testScope.runTest { val hasActiveMediaOrRecommendation by collectLastValue(underTest.hasActiveMediaOrRecommendation) - val hasActiveMedia by collectLastValue(underTest.hasActiveMedia) - val hasAnyMedia by collectLastValue(underTest.hasAnyMedia) val userMedia = MediaData(active = false) val instanceId = userMedia.instanceId @@ -109,8 +105,8 @@ class MediaCarouselInteractorTest : SysuiTestCase() { mediaFilterRepository.addMediaDataLoadingState(MediaDataLoadingModel.Loaded(instanceId)) assertThat(hasActiveMediaOrRecommendation).isFalse() - assertThat(hasActiveMedia).isFalse() - assertThat(hasAnyMedia).isTrue() + assertThat(underTest.hasActiveMedia()).isFalse() + assertThat(underTest.hasAnyMedia()).isTrue() assertThat(mediaFilterRepository.removeSelectedUserMediaEntry(instanceId, userMedia)) .isTrue() @@ -119,8 +115,8 @@ class MediaCarouselInteractorTest : SysuiTestCase() { ) assertThat(hasActiveMediaOrRecommendation).isFalse() - assertThat(hasActiveMedia).isFalse() - assertThat(hasAnyMedia).isFalse() + assertThat(underTest.hasActiveMedia()).isFalse() + assertThat(underTest.hasAnyMedia()).isFalse() } @Test @@ -147,6 +143,7 @@ class MediaCarouselInteractorTest : SysuiTestCase() { mediaFilterRepository.addSelectedUserMediaEntry(userMedia) mediaFilterRepository.addMediaDataLoadingState(mediaLoadingModel) + mediaFilterRepository.setOrderedMedia() assertThat(hasActiveMediaOrRecommendation).isTrue() assertThat(hasAnyMediaOrRecommendation).isTrue() @@ -202,7 +199,7 @@ class MediaCarouselInteractorTest : SysuiTestCase() { @Test fun hasAnyMedia_noMediaSet_returnsFalse() = - testScope.runTest { assertThat(underTest.hasAnyMedia.value).isFalse() } + testScope.runTest { assertThat(underTest.hasAnyMedia()).isFalse() } @Test fun hasAnyMediaOrRecommendation_noMediaSet_returnsFalse() = @@ -210,7 +207,7 @@ class MediaCarouselInteractorTest : SysuiTestCase() { @Test fun hasActiveMedia_noMediaSet_returnsFalse() = - testScope.runTest { assertThat(underTest.hasActiveMedia.value).isFalse() } + testScope.runTest { assertThat(underTest.hasActiveMedia()).isFalse() } @Test fun hasActiveMediaOrRecommendation_nothingSet_returnsFalse() = diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeSceneViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeSceneViewModelTest.kt index 9d8ec951dfe7..cb4e2d377048 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeSceneViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeSceneViewModelTest.kt @@ -31,6 +31,8 @@ import com.android.systemui.keyguard.data.repository.fakeDeviceEntryFingerprintA import com.android.systemui.keyguard.shared.model.SuccessFingerprintAuthenticationStatus import com.android.systemui.kosmos.testScope import com.android.systemui.scene.domain.interactor.sceneInteractor +import com.android.systemui.scene.domain.resolver.homeSceneFamilyResolver +import com.android.systemui.scene.shared.model.SceneFamilies import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.shade.ui.viewmodel.notificationsShadeSceneViewModel import com.android.systemui.testKosmos @@ -62,7 +64,9 @@ class NotificationsShadeSceneViewModelTest : SysuiTestCase() { val destinationScenes by collectLastValue(underTest.destinationScenes) lockDevice() - assertThat(destinationScenes?.get(Swipe.Up)?.toScene).isEqualTo(Scenes.Lockscreen) + assertThat(destinationScenes?.get(Swipe.Up)?.toScene).isEqualTo(SceneFamilies.Home) + assertThat(kosmos.homeSceneFamilyResolver.resolvedScene.value) + .isEqualTo(Scenes.Lockscreen) } @Test @@ -72,7 +76,8 @@ class NotificationsShadeSceneViewModelTest : SysuiTestCase() { lockDevice() unlockDevice() - assertThat(destinationScenes?.get(Swipe.Up)?.toScene).isEqualTo(Scenes.Gone) + assertThat(destinationScenes?.get(Swipe.Up)?.toScene).isEqualTo(SceneFamilies.Home) + assertThat(sceneInteractor.currentScene.value).isEqualTo(Scenes.Gone) } @Test @@ -85,7 +90,9 @@ class NotificationsShadeSceneViewModelTest : SysuiTestCase() { ) sceneInteractor.changeScene(Scenes.Lockscreen, "reason") - assertThat(destinationScenes?.get(Swipe.Up)?.toScene).isEqualTo(Scenes.Lockscreen) + assertThat(destinationScenes?.get(Swipe.Up)?.toScene).isEqualTo(SceneFamilies.Home) + assertThat(kosmos.homeSceneFamilyResolver.resolvedScene.value) + .isEqualTo(Scenes.Lockscreen) } @Test @@ -96,10 +103,12 @@ class NotificationsShadeSceneViewModelTest : SysuiTestCase() { kosmos.fakeAuthenticationRepository.setAuthenticationMethod( AuthenticationMethodModel.None ) + sceneInteractor // force the lazy; this will kick off StateFlows runCurrent() sceneInteractor.changeScene(Scenes.Gone, "reason") - assertThat(destinationScenes?.get(Swipe.Up)?.toScene).isEqualTo(Scenes.Gone) + assertThat(destinationScenes?.get(Swipe.Up)?.toScene).isEqualTo(SceneFamilies.Home) + assertThat(kosmos.homeSceneFamilyResolver.resolvedScene.value).isEqualTo(Scenes.Gone) } private fun TestScope.lockDevice() { 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 e87c8adc33a7..899122d4dd45 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 @@ -35,6 +35,7 @@ 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 @@ -72,11 +73,18 @@ class ScreenRecordTileUserActionInteractorTest : SysuiTestCase() { .thenReturn(dialog) } + private val screenRecordRepository = + ScreenRecordRepositoryImpl( + bgCoroutineContext = testScope.testScheduler, + recordingController = recordingController, + ) + private val underTest = ScreenRecordTileUserActionInteractor( context, testScope.testScheduler, testScope.testScheduler, + screenRecordRepository, recordingController, keyguardInteractor, keyguardDismissUtil, 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 7388d51a7cf0..b35b7bca4809 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 @@ -164,25 +164,16 @@ class QSSceneAdapterImplTest : SysuiTestCase() { with(qsImpl!!) { verify(this).setQsVisible(false) - verify(this, never()) + verify(this) .setQsExpansion( - /* expansion= */ anyFloat(), - /* panelExpansionFraction= */ anyFloat(), - /* proposedTranslation= */ anyFloat(), - /* squishinessFraction= */ anyFloat(), + /* expansion= */ 0f, + /* panelExpansionFraction= */ 1f, + /* proposedTranslation= */ 0f, + /* squishinessFraction= */ 1f, ) verify(this).setListening(false) verify(this).setExpanded(false) } - - underTest.applyLatestExpansionAndSquishiness() - verify(qsImpl!!) - .setQsExpansion( - /* expansion= */ 0f, - /* panelExpansionFraction= */ 1f, - /* proposedTranslation= */ 0f, - /* squishinessFraction= */ 1f, - ) } @Test diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt index 0b55befce932..5b6fea5a52f0 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt @@ -28,7 +28,6 @@ import com.android.systemui.authentication.data.repository.fakeAuthenticationRep import com.android.systemui.authentication.shared.model.AuthenticationMethodModel import com.android.systemui.coroutines.collectLastValue import com.android.systemui.deviceentry.data.repository.fakeDeviceEntryRepository -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 @@ -36,7 +35,6 @@ import com.android.systemui.keyguard.data.repository.fakeDeviceEntryFingerprintA import com.android.systemui.keyguard.shared.model.SuccessFingerprintAuthenticationStatus import com.android.systemui.kosmos.testScope import com.android.systemui.media.controls.data.repository.mediaFilterRepository -import com.android.systemui.media.controls.domain.pipeline.MediaDataManager import com.android.systemui.media.controls.domain.pipeline.interactor.mediaCarouselInteractor import com.android.systemui.media.controls.shared.model.MediaData import com.android.systemui.qs.FooterActionsController @@ -46,6 +44,8 @@ import com.android.systemui.res.R import com.android.systemui.scene.domain.interactor.sceneBackInteractor import com.android.systemui.scene.domain.interactor.sceneContainerStartable import com.android.systemui.scene.domain.interactor.sceneInteractor +import com.android.systemui.scene.domain.resolver.homeSceneFamilyResolver +import com.android.systemui.scene.shared.model.SceneFamilies import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.settings.brightness.ui.viewmodel.brightnessMirrorViewModel import com.android.systemui.shade.ui.viewmodel.shadeHeaderViewModel @@ -55,7 +55,6 @@ import com.android.systemui.util.mockito.any import com.android.systemui.util.mockito.mock import com.android.systemui.util.mockito.whenever import com.google.common.truth.Truth.assertThat -import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runTest import org.junit.Before import org.junit.Test @@ -82,11 +81,8 @@ class QuickSettingsSceneViewModelTest : SysuiTestCase() { private val sceneBackInteractor = kosmos.sceneBackInteractor private val sceneContainerStartable = kosmos.sceneContainerStartable - private val mediaDataManager = mock<MediaDataManager>() - private lateinit var underTest: QuickSettingsSceneViewModel - @OptIn(ExperimentalCoroutinesApi::class) @Before fun setUp() { kosmos.fakeFeatureFlagsClassic.set(Flags.NEW_NETWORK_SLICE_UI, false) @@ -95,7 +91,6 @@ class QuickSettingsSceneViewModelTest : SysuiTestCase() { underTest = QuickSettingsSceneViewModel( applicationScope = testScope.backgroundScope, - deviceEntryInteractor = kosmos.deviceEntryInteractor, brightnessMirrorViewModel = kosmos.brightnessMirrorViewModel, shadeHeaderViewModel = kosmos.shadeHeaderViewModel, qsSceneAdapter = qsFlexiglassAdapter, @@ -112,6 +107,7 @@ class QuickSettingsSceneViewModelTest : SysuiTestCase() { testScope.runTest { overrideResource(R.bool.config_use_split_notification_shade, false) val destinations by collectLastValue(underTest.destinationScenes) + val homeScene by collectLastValue(kosmos.homeSceneFamilyResolver.resolvedScene) qsFlexiglassAdapter.setCustomizing(false) kosmos.fakeAuthenticationRepository.setAuthenticationMethod( AuthenticationMethodModel.Pin @@ -128,9 +124,10 @@ class QuickSettingsSceneViewModelTest : SysuiTestCase() { Swipe( fromSource = Edge.Bottom, direction = SwipeDirection.Up, - ) to UserActionResult(Scenes.Gone) + ) to UserActionResult(SceneFamilies.Home) ) ) + assertThat(homeScene).isEqualTo(Scenes.Gone) } @Test @@ -142,6 +139,7 @@ class QuickSettingsSceneViewModelTest : SysuiTestCase() { val currentScene by collectLastValue(sceneInteractor.currentScene) val backScene by collectLastValue(sceneBackInteractor.backScene) + val homeScene by collectLastValue(kosmos.homeSceneFamilyResolver.resolvedScene) sceneInteractor.changeScene(Scenes.Lockscreen, "reason") sceneInteractor.changeScene(Scenes.QuickSettings, "reason") assertThat(currentScene).isEqualTo(Scenes.QuickSettings) @@ -155,9 +153,10 @@ class QuickSettingsSceneViewModelTest : SysuiTestCase() { Swipe( fromSource = Edge.Bottom, direction = SwipeDirection.Up, - ) to UserActionResult(Scenes.Lockscreen) + ) to UserActionResult(SceneFamilies.Home) ) ) + assertThat(homeScene).isEqualTo(Scenes.Lockscreen) } @Test @@ -165,6 +164,7 @@ class QuickSettingsSceneViewModelTest : SysuiTestCase() { testScope.runTest { overrideResource(R.bool.config_use_split_notification_shade, false) val destinations by collectLastValue(underTest.destinationScenes) + val homeScene by collectLastValue(kosmos.homeSceneFamilyResolver.resolvedScene) qsFlexiglassAdapter.setCustomizing(false) kosmos.fakeDeviceEntryRepository.setLockscreenEnabled(true) kosmos.fakeAuthenticationRepository.setAuthenticationMethod( @@ -179,9 +179,10 @@ class QuickSettingsSceneViewModelTest : SysuiTestCase() { Swipe( fromSource = Edge.Bottom, direction = SwipeDirection.Up, - ) to UserActionResult(Scenes.Lockscreen) + ) to UserActionResult(SceneFamilies.Home) ) ) + assertThat(homeScene).isEqualTo(Scenes.Lockscreen) } @Test @@ -199,6 +200,7 @@ class QuickSettingsSceneViewModelTest : SysuiTestCase() { testScope.runTest { overrideResource(R.bool.config_use_split_notification_shade, true) val destinations by collectLastValue(underTest.destinationScenes) + val homeScene by collectLastValue(kosmos.homeSceneFamilyResolver.resolvedScene) qsFlexiglassAdapter.setCustomizing(false) kosmos.fakeAuthenticationRepository.setAuthenticationMethod( AuthenticationMethodModel.Pin @@ -215,9 +217,10 @@ class QuickSettingsSceneViewModelTest : SysuiTestCase() { Swipe( fromSource = Edge.Bottom, direction = SwipeDirection.Up, - ) to UserActionResult(Scenes.Gone), + ) to UserActionResult(SceneFamilies.Home) ) ) + assertThat(homeScene).isEqualTo(Scenes.Gone) } @Test diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeSceneViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeSceneViewModelTest.kt index 034c2e9b6789..ac67ac8956be 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeSceneViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeSceneViewModelTest.kt @@ -31,6 +31,8 @@ import com.android.systemui.keyguard.data.repository.fakeDeviceEntryFingerprintA import com.android.systemui.keyguard.shared.model.SuccessFingerprintAuthenticationStatus import com.android.systemui.kosmos.testScope import com.android.systemui.scene.domain.interactor.sceneInteractor +import com.android.systemui.scene.domain.resolver.homeSceneFamilyResolver +import com.android.systemui.scene.shared.model.SceneFamilies import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.shade.ui.viewmodel.quickSettingsShadeSceneViewModel import com.android.systemui.testKosmos @@ -60,38 +62,45 @@ class QuickSettingsShadeSceneViewModelTest : SysuiTestCase() { fun upTransitionSceneKey_deviceLocked_lockscreen() = testScope.runTest { val destinationScenes by collectLastValue(underTest.destinationScenes) + val homeScene by collectLastValue(kosmos.homeSceneFamilyResolver.resolvedScene) lockDevice() - assertThat(destinationScenes?.get(Swipe.Up)?.toScene).isEqualTo(Scenes.Lockscreen) + assertThat(destinationScenes?.get(Swipe.Up)?.toScene).isEqualTo(SceneFamilies.Home) + assertThat(homeScene).isEqualTo(Scenes.Lockscreen) } @Test fun upTransitionSceneKey_deviceUnlocked_gone() = testScope.runTest { val destinationScenes by collectLastValue(underTest.destinationScenes) + val homeScene by collectLastValue(kosmos.homeSceneFamilyResolver.resolvedScene) lockDevice() unlockDevice() - assertThat(destinationScenes?.get(Swipe.Up)?.toScene).isEqualTo(Scenes.Gone) + assertThat(destinationScenes?.get(Swipe.Up)?.toScene).isEqualTo(SceneFamilies.Home) + assertThat(homeScene).isEqualTo(Scenes.Gone) } @Test fun upTransitionSceneKey_authMethodSwipe_lockscreenNotDismissed_goesToLockscreen() = testScope.runTest { val destinationScenes by collectLastValue(underTest.destinationScenes) + val homeScene by collectLastValue(kosmos.homeSceneFamilyResolver.resolvedScene) kosmos.fakeDeviceEntryRepository.setLockscreenEnabled(true) kosmos.fakeAuthenticationRepository.setAuthenticationMethod( AuthenticationMethodModel.None ) sceneInteractor.changeScene(Scenes.Lockscreen, "reason") - assertThat(destinationScenes?.get(Swipe.Up)?.toScene).isEqualTo(Scenes.Lockscreen) + assertThat(destinationScenes?.get(Swipe.Up)?.toScene).isEqualTo(SceneFamilies.Home) + assertThat(homeScene).isEqualTo(Scenes.Lockscreen) } @Test fun upTransitionSceneKey_authMethodSwipe_lockscreenDismissed_goesToGone() = testScope.runTest { val destinationScenes by collectLastValue(underTest.destinationScenes) + val homeScene by collectLastValue(kosmos.homeSceneFamilyResolver.resolvedScene) kosmos.fakeDeviceEntryRepository.setLockscreenEnabled(true) kosmos.fakeAuthenticationRepository.setAuthenticationMethod( AuthenticationMethodModel.None @@ -99,7 +108,8 @@ class QuickSettingsShadeSceneViewModelTest : SysuiTestCase() { runCurrent() sceneInteractor.changeScene(Scenes.Gone, "reason") - assertThat(destinationScenes?.get(Swipe.Up)?.toScene).isEqualTo(Scenes.Gone) + assertThat(destinationScenes?.get(Swipe.Up)?.toScene).isEqualTo(SceneFamilies.Home) + assertThat(homeScene).isEqualTo(Scenes.Gone) } private fun TestScope.lockDevice() { 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 9e7e766cb820..4d5d22c11f71 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt @@ -51,7 +51,6 @@ import com.android.systemui.flags.fakeFeatureFlagsClassic import com.android.systemui.keyguard.ui.viewmodel.KeyguardLongPressViewModel import com.android.systemui.keyguard.ui.viewmodel.LockscreenSceneViewModel import com.android.systemui.kosmos.testScope -import com.android.systemui.media.controls.domain.pipeline.MediaDataManager import com.android.systemui.media.controls.domain.pipeline.interactor.mediaCarouselInteractor import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAsleepForTest import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAwakeForTest @@ -61,6 +60,8 @@ import com.android.systemui.qs.footerActionsViewModelFactory import com.android.systemui.qs.ui.adapter.FakeQSSceneAdapter import com.android.systemui.scene.domain.interactor.sceneContainerStartable import com.android.systemui.scene.domain.interactor.sceneInteractor +import com.android.systemui.scene.domain.resolver.homeSceneFamilyResolver +import com.android.systemui.scene.shared.model.SceneFamilies import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.scene.shared.model.fakeSceneDataSource import com.android.systemui.scene.ui.viewmodel.SceneContainerViewModel @@ -91,7 +92,6 @@ import kotlinx.coroutines.test.runTest import org.junit.Before import org.junit.Test import org.junit.runner.RunWith -import org.mockito.Mock import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations @@ -169,8 +169,6 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() { private val qsFlexiglassAdapter = FakeQSSceneAdapter(inflateDelegate = { mock() }) - @Mock private lateinit var mediaDataManager: MediaDataManager - private lateinit var emergencyAffordanceManager: EmergencyAffordanceManager private lateinit var telecomManager: TelecomManager private val fakeSceneDataSource = kosmos.fakeSceneDataSource @@ -205,7 +203,6 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() { shadeSceneViewModel = ShadeSceneViewModel( applicationScope = testScope.backgroundScope, - deviceEntryInteractor = deviceEntryInteractor, shadeHeaderViewModel = kosmos.shadeHeaderViewModel, qsSceneAdapter = qsFlexiglassAdapter, notifications = kosmos.notificationsPlaceholderViewModel, @@ -280,6 +277,7 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() { fun swipeUpOnShadeScene_withAuthMethodSwipe_lockscreenNotDismissed_goesToLockscreen() = testScope.runTest { val destinationScenes by collectLastValue(shadeSceneViewModel.destinationScenes) + val homeScene by collectLastValue(kosmos.homeSceneFamilyResolver.resolvedScene) setAuthMethod(AuthenticationMethodModel.None, enableLockscreen = true) assertCurrentScene(Scenes.Lockscreen) @@ -288,9 +286,10 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() { assertCurrentScene(Scenes.Shade) val upDestinationSceneKey = destinationScenes?.get(Swipe.Up)?.toScene - assertThat(upDestinationSceneKey).isEqualTo(Scenes.Lockscreen) + assertThat(upDestinationSceneKey).isEqualTo(SceneFamilies.Home) + assertThat(homeScene).isEqualTo(Scenes.Lockscreen) emulateUserDrivenTransition( - to = upDestinationSceneKey, + to = homeScene, ) } @@ -299,6 +298,7 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() { testScope.runTest { val destinationScenes by collectLastValue(shadeSceneViewModel.destinationScenes) val canSwipeToEnter by collectLastValue(deviceEntryInteractor.canSwipeToEnter) + val homeScene by collectLastValue(kosmos.homeSceneFamilyResolver.resolvedScene) setAuthMethod(AuthenticationMethodModel.None, enableLockscreen = true) @@ -314,9 +314,10 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() { assertCurrentScene(Scenes.Shade) val upDestinationSceneKey = destinationScenes?.get(Swipe.Up)?.toScene - assertThat(upDestinationSceneKey).isEqualTo(Scenes.Gone) + assertThat(upDestinationSceneKey).isEqualTo(SceneFamilies.Home) + assertThat(homeScene).isEqualTo(Scenes.Gone) emulateUserDrivenTransition( - to = upDestinationSceneKey, + to = homeScene, ) } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt index 229a711d637d..ec7150b5af2d 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt @@ -21,23 +21,30 @@ package com.android.systemui.scene.domain.interactor import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.compose.animation.scene.ObservableTransitionState +import com.android.compose.animation.scene.SceneKey import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.collectLastValue import com.android.systemui.flags.EnableSceneContainer import com.android.systemui.keyguard.data.repository.fakeDeviceEntryFingerprintAuthRepository import com.android.systemui.keyguard.shared.model.SuccessFingerprintAuthenticationStatus 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.resolver.homeSceneFamilyResolver import com.android.systemui.scene.sceneContainerConfig import com.android.systemui.scene.sceneKeys -import com.android.systemui.scene.shared.model.SceneContainerConfig +import com.android.systemui.scene.shared.model.SceneFamilies import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.scene.shared.model.fakeSceneDataSource import com.android.systemui.testKosmos import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.flowOf +import kotlinx.coroutines.flow.toList import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest import org.junit.Test @@ -52,48 +59,27 @@ class SceneInteractorTest : SysuiTestCase() { private val testScope = kosmos.testScope private val fakeSceneDataSource = kosmos.fakeSceneDataSource - private lateinit var underTest: SceneInteractor + private val underTest = kosmos.sceneInteractor @Test fun allSceneKeys() { - underTest = kosmos.sceneInteractor assertThat(underTest.allSceneKeys()).isEqualTo(kosmos.sceneKeys) } @Test fun changeScene_toUnknownScene_doesNothing() = testScope.runTest { - val sceneKeys = - listOf( - Scenes.QuickSettings, - Scenes.Shade, - Scenes.Lockscreen, - Scenes.Gone, - Scenes.Communal, - ) - val navigationDistances = - mapOf( - Scenes.Gone to 0, - Scenes.Lockscreen to 0, - Scenes.Communal to 1, - Scenes.Shade to 2, - Scenes.QuickSettings to 3, - ) - kosmos.sceneContainerConfig = - SceneContainerConfig(sceneKeys, Scenes.Lockscreen, navigationDistances) - underTest = kosmos.sceneInteractor val currentScene by collectLastValue(underTest.currentScene) + val unknownScene = SceneKey("UNKNOWN") val previousScene = currentScene - assertThat(previousScene).isNotEqualTo(Scenes.Bouncer) - underTest.changeScene(Scenes.Bouncer, "reason") + assertThat(previousScene).isNotEqualTo(unknownScene) + underTest.changeScene(unknownScene, "reason") assertThat(currentScene).isEqualTo(previousScene) } @Test fun changeScene() = testScope.runTest { - underTest = kosmos.sceneInteractor - val currentScene by collectLastValue(underTest.currentScene) assertThat(currentScene).isEqualTo(Scenes.Lockscreen) @@ -104,8 +90,6 @@ class SceneInteractorTest : SysuiTestCase() { @Test fun changeScene_toGoneWhenUnl_doesNotThrow() = testScope.runTest { - underTest = kosmos.sceneInteractor - val currentScene by collectLastValue(underTest.currentScene) assertThat(currentScene).isEqualTo(Scenes.Lockscreen) @@ -120,15 +104,11 @@ class SceneInteractorTest : SysuiTestCase() { @Test(expected = IllegalStateException::class) fun changeScene_toGoneWhenStillLocked_throws() = - testScope.runTest { - underTest = kosmos.sceneInteractor - underTest.changeScene(Scenes.Gone, "reason") - } + testScope.runTest { underTest.changeScene(Scenes.Gone, "reason") } @Test fun changeScene_toGoneWhenTransitionToLockedFromGone() = testScope.runTest { - underTest = kosmos.sceneInteractor val currentScene by collectLastValue(underTest.currentScene) val transitionTo by collectLastValue(underTest.transitioningTo) kosmos.sceneContainerRepository.setTransitionState( @@ -151,39 +131,30 @@ class SceneInteractorTest : SysuiTestCase() { } @Test + fun changeScene_toHomeSceneFamily() = + testScope.runTest { + val currentScene by collectLastValue(underTest.currentScene) + + underTest.changeScene(SceneFamilies.Home, "reason") + runCurrent() + + assertThat(currentScene).isEqualTo(kosmos.homeSceneFamilyResolver.resolvedScene.value) + } + + @Test fun snapToScene_toUnknownScene_doesNothing() = testScope.runTest { - val sceneKeys = - listOf( - Scenes.QuickSettings, - Scenes.Shade, - Scenes.Lockscreen, - Scenes.Gone, - Scenes.Communal, - ) - val navigationDistances = - mapOf( - Scenes.Gone to 0, - Scenes.Lockscreen to 0, - Scenes.Communal to 1, - Scenes.Shade to 2, - Scenes.QuickSettings to 3, - ) - kosmos.sceneContainerConfig = - SceneContainerConfig(sceneKeys, Scenes.Lockscreen, navigationDistances) - underTest = kosmos.sceneInteractor val currentScene by collectLastValue(underTest.currentScene) val previousScene = currentScene - assertThat(previousScene).isNotEqualTo(Scenes.Bouncer) - underTest.snapToScene(Scenes.Bouncer, "reason") + val unknownScene = SceneKey("UNKNOWN") + assertThat(previousScene).isNotEqualTo(unknownScene) + underTest.snapToScene(unknownScene, "reason") assertThat(currentScene).isEqualTo(previousScene) } @Test fun snapToScene() = testScope.runTest { - underTest = kosmos.sceneInteractor - val currentScene by collectLastValue(underTest.currentScene) assertThat(currentScene).isEqualTo(Scenes.Lockscreen) @@ -194,8 +165,6 @@ class SceneInteractorTest : SysuiTestCase() { @Test fun snapToScene_toGoneWhenUnl_doesNotThrow() = testScope.runTest { - underTest = kosmos.sceneInteractor - val currentScene by collectLastValue(underTest.currentScene) assertThat(currentScene).isEqualTo(Scenes.Lockscreen) @@ -210,15 +179,22 @@ class SceneInteractorTest : SysuiTestCase() { @Test(expected = IllegalStateException::class) fun snapToScene_toGoneWhenStillLocked_throws() = + testScope.runTest { underTest.snapToScene(Scenes.Gone, "reason") } + + @Test + fun snapToScene_toHomeSceneFamily() = testScope.runTest { - underTest = kosmos.sceneInteractor - underTest.snapToScene(Scenes.Gone, "reason") + val currentScene by collectLastValue(underTest.currentScene) + + underTest.snapToScene(SceneFamilies.Home, "reason") + runCurrent() + + assertThat(currentScene).isEqualTo(kosmos.homeSceneFamilyResolver.resolvedScene.value) } @Test fun sceneChanged_inDataSource() = testScope.runTest { - underTest = kosmos.sceneInteractor val currentScene by collectLastValue(underTest.currentScene) assertThat(currentScene).isEqualTo(Scenes.Lockscreen) @@ -230,14 +206,14 @@ class SceneInteractorTest : SysuiTestCase() { @Test fun transitionState() = testScope.runTest { - underTest = kosmos.sceneInteractor - val underTest = kosmos.sceneContainerRepository + val sceneContainerRepository = kosmos.sceneContainerRepository val transitionState = MutableStateFlow<ObservableTransitionState>( ObservableTransitionState.Idle(Scenes.Lockscreen) ) - underTest.setTransitionState(transitionState) - val reflectedTransitionState by collectLastValue(underTest.transitionState) + sceneContainerRepository.setTransitionState(transitionState) + val reflectedTransitionState by + collectLastValue(sceneContainerRepository.transitionState) assertThat(reflectedTransitionState).isEqualTo(transitionState.value) val progress = MutableStateFlow(1f) @@ -258,7 +234,7 @@ class SceneInteractorTest : SysuiTestCase() { progress.value = 0.9f assertThat(reflectedTransitionState).isEqualTo(transitionState.value) - underTest.setTransitionState(null) + sceneContainerRepository.setTransitionState(null) assertThat(reflectedTransitionState) .isEqualTo( ObservableTransitionState.Idle(kosmos.sceneContainerConfig.initialSceneKey) @@ -268,7 +244,6 @@ class SceneInteractorTest : SysuiTestCase() { @Test fun transitioningTo() = testScope.runTest { - underTest = kosmos.sceneInteractor val transitionState = MutableStateFlow<ObservableTransitionState>( ObservableTransitionState.Idle(underTest.currentScene.value) @@ -306,7 +281,6 @@ class SceneInteractorTest : SysuiTestCase() { @Test fun isTransitionUserInputOngoing_idle_false() = testScope.runTest { - underTest = kosmos.sceneInteractor val transitionState = MutableStateFlow<ObservableTransitionState>( ObservableTransitionState.Idle(Scenes.Shade) @@ -321,7 +295,6 @@ class SceneInteractorTest : SysuiTestCase() { @Test fun isTransitionUserInputOngoing_transition_true() = testScope.runTest { - underTest = kosmos.sceneInteractor val transitionState = MutableStateFlow<ObservableTransitionState>( ObservableTransitionState.Transition( @@ -343,7 +316,6 @@ class SceneInteractorTest : SysuiTestCase() { @Test fun isTransitionUserInputOngoing_updateMidTransition_false() = testScope.runTest { - underTest = kosmos.sceneInteractor val transitionState = MutableStateFlow<ObservableTransitionState>( ObservableTransitionState.Transition( @@ -377,7 +349,6 @@ class SceneInteractorTest : SysuiTestCase() { @Test fun isTransitionUserInputOngoing_updateOnIdle_false() = testScope.runTest { - underTest = kosmos.sceneInteractor val transitionState = MutableStateFlow<ObservableTransitionState>( ObservableTransitionState.Transition( @@ -403,7 +374,6 @@ class SceneInteractorTest : SysuiTestCase() { @Test fun isVisible() = testScope.runTest { - underTest = kosmos.sceneInteractor val isVisible by collectLastValue(underTest.isVisible) assertThat(isVisible).isTrue() @@ -417,7 +387,6 @@ class SceneInteractorTest : SysuiTestCase() { @Test fun isVisible_duringRemoteUserInteraction_forcedVisible() = testScope.runTest { - underTest = kosmos.sceneInteractor underTest.setVisible(false, "reason") val isVisible by collectLastValue(underTest.isVisible) assertThat(isVisible).isFalse() @@ -428,4 +397,57 @@ class SceneInteractorTest : SysuiTestCase() { assertThat(isVisible).isFalse() } + + @Test + fun resolveSceneFamily_home() = + testScope.runTest { + assertThat(underTest.resolveSceneFamily(SceneFamilies.Home).first()) + .isEqualTo(kosmos.homeSceneFamilyResolver.resolvedScene.value) + } + + @Test + fun resolveSceneFamily_nonFamily() = + testScope.runTest { + val resolved = underTest.resolveSceneFamily(Scenes.Gone).toList() + assertThat(resolved).containsExactly(Scenes.Gone).inOrder() + } + + @Test + fun transitionValue_test_idle() = + testScope.runTest { + val transitionValue by collectLastValue(underTest.transitionProgress(Scenes.Gone)) + + kosmos.setSceneTransition(Idle(Scenes.Gone)) + assertThat(transitionValue).isEqualTo(1f) + + kosmos.setSceneTransition(Idle(Scenes.Lockscreen)) + assertThat(transitionValue).isEqualTo(0f) + } + + @Test + fun transitionValue_test_transitions() = + testScope.runTest { + val transitionValue by collectLastValue(underTest.transitionProgress(Scenes.Gone)) + val progress = MutableStateFlow(0f) + + kosmos.setSceneTransition( + Transition(from = Scenes.Lockscreen, to = Scenes.Gone, progress = progress) + ) + assertThat(transitionValue).isEqualTo(0f) + + progress.value = 0.4f + assertThat(transitionValue).isEqualTo(0.4f) + + kosmos.setSceneTransition( + Transition(from = Scenes.Gone, to = Scenes.Lockscreen, progress = progress) + ) + progress.value = 0.7f + assertThat(transitionValue).isEqualTo(0.3f) + + kosmos.setSceneTransition( + Transition(from = Scenes.Lockscreen, to = Scenes.Shade, progress = progress) + ) + progress.value = 0.9f + assertThat(transitionValue).isEqualTo(0f) + } } 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 ac66e6657a75..e40c8eecca0f 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 @@ -14,7 +14,7 @@ * limitations under the License. */ -@file:OptIn(ExperimentalCoroutinesApi::class, ExperimentalCoroutinesApi::class) +@file:OptIn(ExperimentalCoroutinesApi::class) package com.android.systemui.scene.domain.startable @@ -395,6 +395,7 @@ class SceneContainerStartableTest : SysuiTestCase() { ) assertThat(currentSceneKey).isEqualTo(Scenes.Gone) underTest.start() + runCurrent() kosmos.fakePowerRepository.updateWakefulness( rawState = WakefulnessState.STARTING_TO_SLEEP, @@ -1285,6 +1286,42 @@ 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) diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeBackActionInteractorImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeBackActionInteractorImplTest.kt index 468c39daa282..fa4da42fb958 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeBackActionInteractorImplTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeBackActionInteractorImplTest.kt @@ -30,12 +30,14 @@ import com.android.systemui.keyguard.data.repository.deviceEntryFingerprintAuthR import com.android.systemui.keyguard.shared.model.SuccessFingerprintAuthenticationStatus import com.android.systemui.kosmos.testScope import com.android.systemui.scene.domain.interactor.sceneInteractor +import com.android.systemui.scene.domain.resolver.homeSceneFamilyResolver import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.shared.recents.utilities.Utilities import com.android.systemui.testKosmos import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest @@ -77,6 +79,8 @@ class ShadeBackActionInteractorImplTest : SysuiTestCase() { @Test fun animateCollapseQs_fullyCollapse_entered() = testScope.runTest { + // Ensure that HomeSceneFamilyResolver is running + kosmos.homeSceneFamilyResolver.resolvedScene.launchIn(backgroundScope) val actual by collectLastValue(sceneInteractor.currentScene) enterDevice() setScene(Scenes.QuickSettings) diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModelTest.kt index f89f18ac29f1..3ded8a346ce9 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModelTest.kt @@ -6,15 +6,27 @@ import android.provider.Settings import android.telephony.SubscriptionManager.PROFILE_CLASS_UNSET import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest +import com.android.compose.animation.scene.ObservableTransitionState +import com.android.compose.animation.scene.SceneKey import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.collectLastValue +import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor +import com.android.systemui.flags.EnableSceneContainer +import com.android.systemui.keyguard.data.repository.fakeDeviceEntryFingerprintAuthRepository +import com.android.systemui.keyguard.shared.model.SuccessFingerprintAuthenticationStatus import com.android.systemui.kosmos.testScope import com.android.systemui.plugins.activityStarter +import com.android.systemui.scene.domain.interactor.sceneInteractor +import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.fakeMobileIconsInteractor import com.android.systemui.testKosmos import com.android.systemui.util.mockito.argThat import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.test.TestScope +import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest import org.junit.Before import org.junit.Test @@ -24,12 +36,16 @@ import org.mockito.ArgumentMatchers.anyInt import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations +@OptIn(ExperimentalCoroutinesApi::class) @SmallTest @RunWith(AndroidJUnit4::class) +@EnableSceneContainer class ShadeHeaderViewModelTest : SysuiTestCase() { private val kosmos = testKosmos() private val testScope = kosmos.testScope private val mobileIconsInteractor = kosmos.fakeMobileIconsInteractor + private val sceneInteractor = kosmos.sceneInteractor + private val deviceEntryInteractor = kosmos.deviceEntryInteractor private val underTest: ShadeHeaderViewModel = kosmos.shadeHeaderViewModel @@ -77,6 +93,30 @@ class ShadeHeaderViewModelTest : SysuiTestCase() { ) } + @Test + fun onSystemIconContainerClicked_locked_collapsesShadeToLockscreen() = + testScope.runTest { + setDeviceEntered(false) + setScene(Scenes.Shade) + + underTest.onSystemIconContainerClicked() + runCurrent() + + assertThat(sceneInteractor.currentScene.value).isEqualTo(Scenes.Lockscreen) + } + + @Test + fun onSystemIconContainerClicked_unlocked_collapsesShadeToGone() = + testScope.runTest { + setDeviceEntered(true) + setScene(Scenes.Shade) + + underTest.onSystemIconContainerClicked() + runCurrent() + + assertThat(sceneInteractor.currentScene.value).isEqualTo(Scenes.Gone) + } + companion object { private val SUB_1 = SubscriptionModel( @@ -93,6 +133,32 @@ class ShadeHeaderViewModelTest : SysuiTestCase() { profileClass = PROFILE_CLASS_UNSET, ) } + + private fun setScene(key: SceneKey) { + sceneInteractor.changeScene(key, "test") + sceneInteractor.setTransitionState( + MutableStateFlow<ObservableTransitionState>(ObservableTransitionState.Idle(key)) + ) + testScope.runCurrent() + } + + private fun TestScope.setDeviceEntered(isEntered: Boolean) { + if (isEntered) { + // Unlock the device marking the device has entered. + kosmos.fakeDeviceEntryFingerprintAuthRepository.setAuthenticationStatus( + SuccessFingerprintAuthenticationStatus(0, true) + ) + runCurrent() + } + setScene( + if (isEntered) { + Scenes.Gone + } else { + Scenes.Lockscreen + } + ) + assertThat(deviceEntryInteractor.isDeviceEntered.value).isEqualTo(isEntered) + } } private class IntentMatcherAction(private val action: String) : ArgumentMatcher<Intent> { diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt index 482dc5d992f0..c53cdf8cc44f 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt @@ -19,6 +19,8 @@ package com.android.systemui.shade.ui.viewmodel 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.compose.animation.scene.SceneKey import com.android.compose.animation.scene.Swipe import com.android.compose.animation.scene.SwipeDirection import com.android.systemui.SysuiTestCase @@ -41,6 +43,8 @@ import com.android.systemui.qs.footerActionsViewModelFactory import com.android.systemui.qs.ui.adapter.FakeQSSceneAdapter import com.android.systemui.res.R import com.android.systemui.scene.domain.interactor.sceneInteractor +import com.android.systemui.scene.domain.resolver.homeSceneFamilyResolver +import com.android.systemui.scene.shared.model.SceneFamilies import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.scene.shared.model.TransitionKeys.ToSplitShade import com.android.systemui.settings.brightness.ui.viewmodel.brightnessMirrorViewModel @@ -56,7 +60,9 @@ import com.android.systemui.util.mockito.mock import com.google.common.truth.Truth.assertThat import java.util.Locale import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest import org.junit.Before @@ -75,7 +81,6 @@ class ShadeSceneViewModelTest : SysuiTestCase() { private val kosmos = testKosmos() private val testScope = kosmos.testScope private val sceneInteractor by lazy { kosmos.sceneInteractor } - private val deviceEntryInteractor by lazy { kosmos.deviceEntryInteractor } private val shadeRepository by lazy { kosmos.shadeRepository } private val qsSceneAdapter = FakeQSSceneAdapter({ mock() }) @@ -91,7 +96,6 @@ class ShadeSceneViewModelTest : SysuiTestCase() { underTest = ShadeSceneViewModel( applicationScope = testScope.backgroundScope, - deviceEntryInteractor = deviceEntryInteractor, shadeHeaderViewModel = kosmos.shadeHeaderViewModel, qsSceneAdapter = qsSceneAdapter, notifications = kosmos.notificationsPlaceholderViewModel, @@ -109,33 +113,36 @@ class ShadeSceneViewModelTest : SysuiTestCase() { fun upTransitionSceneKey_deviceLocked_lockScreen() = testScope.runTest { val destinationScenes by collectLastValue(underTest.destinationScenes) + val homeScene by collectLastValue(kosmos.homeSceneFamilyResolver.resolvedScene) kosmos.fakeAuthenticationRepository.setAuthenticationMethod( AuthenticationMethodModel.Pin ) assertThat(destinationScenes?.get(Swipe(SwipeDirection.Up))?.toScene) - .isEqualTo(Scenes.Lockscreen) + .isEqualTo(SceneFamilies.Home) + assertThat(homeScene).isEqualTo(Scenes.Lockscreen) } @Test fun upTransitionSceneKey_deviceUnlocked_gone() = testScope.runTest { val destinationScenes by collectLastValue(underTest.destinationScenes) + val homeScene by collectLastValue(kosmos.homeSceneFamilyResolver.resolvedScene) kosmos.fakeAuthenticationRepository.setAuthenticationMethod( AuthenticationMethodModel.Pin ) - kosmos.fakeDeviceEntryFingerprintAuthRepository.setAuthenticationStatus( - SuccessFingerprintAuthenticationStatus(0, true) - ) + setDeviceEntered(true) assertThat(destinationScenes?.get(Swipe(SwipeDirection.Up))?.toScene) - .isEqualTo(Scenes.Gone) + .isEqualTo(SceneFamilies.Home) + assertThat(homeScene).isEqualTo(Scenes.Gone) } @Test fun upTransitionSceneKey_authMethodSwipe_lockscreenNotDismissed_goesToLockscreen() = testScope.runTest { val destinationScenes by collectLastValue(underTest.destinationScenes) + val homeScene by collectLastValue(kosmos.homeSceneFamilyResolver.resolvedScene) kosmos.fakeDeviceEntryRepository.setLockscreenEnabled(true) kosmos.fakeAuthenticationRepository.setAuthenticationMethod( AuthenticationMethodModel.None @@ -143,13 +150,15 @@ class ShadeSceneViewModelTest : SysuiTestCase() { sceneInteractor.changeScene(Scenes.Lockscreen, "reason") assertThat(destinationScenes?.get(Swipe(SwipeDirection.Up))?.toScene) - .isEqualTo(Scenes.Lockscreen) + .isEqualTo(SceneFamilies.Home) + assertThat(homeScene).isEqualTo(Scenes.Lockscreen) } @Test fun upTransitionSceneKey_authMethodSwipe_lockscreenDismissed_goesToGone() = testScope.runTest { val destinationScenes by collectLastValue(underTest.destinationScenes) + val homeScene by collectLastValue(kosmos.homeSceneFamilyResolver.resolvedScene) kosmos.fakeDeviceEntryRepository.setLockscreenEnabled(true) kosmos.fakeAuthenticationRepository.setAuthenticationMethod( AuthenticationMethodModel.None @@ -158,7 +167,8 @@ class ShadeSceneViewModelTest : SysuiTestCase() { sceneInteractor.changeScene(Scenes.Gone, "reason") assertThat(destinationScenes?.get(Swipe(SwipeDirection.Up))?.toScene) - .isEqualTo(Scenes.Gone) + .isEqualTo(SceneFamilies.Home) + assertThat(homeScene).isEqualTo(Scenes.Gone) } @Test @@ -189,9 +199,7 @@ class ShadeSceneViewModelTest : SysuiTestCase() { kosmos.fakeAuthenticationRepository.setAuthenticationMethod( AuthenticationMethodModel.Pin ) - kosmos.fakeDeviceEntryFingerprintAuthRepository.setAuthenticationStatus( - SuccessFingerprintAuthenticationStatus(0, true) - ) + setDeviceEntered(true) runCurrent() assertThat(isClickable).isFalse() @@ -338,6 +346,32 @@ class ShadeSceneViewModelTest : SysuiTestCase() { return maxTranslation } + private fun TestScope.setDeviceEntered(isEntered: Boolean) { + if (isEntered) { + // Unlock the device marking the device has entered. + kosmos.fakeDeviceEntryFingerprintAuthRepository.setAuthenticationStatus( + SuccessFingerprintAuthenticationStatus(0, true) + ) + runCurrent() + } + setScene( + if (isEntered) { + Scenes.Gone + } else { + Scenes.Lockscreen + } + ) + assertThat(kosmos.deviceEntryInteractor.isDeviceEntered.value).isEqualTo(isEntered) + } + + private fun TestScope.setScene(key: SceneKey) { + sceneInteractor.changeScene(key, "test") + sceneInteractor.setTransitionState( + MutableStateFlow<ObservableTransitionState>(ObservableTransitionState.Idle(key)) + ) + runCurrent() + } + private data class Translations( val start: Float, val end: Float, diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/events/PrivacyDotViewControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/events/PrivacyDotViewControllerTest.kt index 43409718c849..f12643280c26 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/events/PrivacyDotViewControllerTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/events/PrivacyDotViewControllerTest.kt @@ -18,14 +18,19 @@ package com.android.systemui.statusbar.events import android.graphics.Point import android.graphics.Rect +import android.platform.test.annotations.DisableFlags +import android.platform.test.annotations.EnableFlags import android.testing.TestableLooper.RunWithLooper import android.view.Display import android.view.DisplayAdjustments import android.view.View import android.widget.FrameLayout +import android.widget.FrameLayout.LayoutParams.UNSPECIFIED_GRAVITY 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.res.R import com.android.systemui.statusbar.FakeStatusBarStateController import com.android.systemui.statusbar.phone.StatusBarContentInsetsProvider import com.android.systemui.statusbar.policy.FakeConfigurationController @@ -291,14 +296,61 @@ class PrivacyDotViewControllerTest : SysuiTestCase() { assertThat(controller.currentViewState.designatedCorner).isEqualTo(bottomRightView) } + @Test + @EnableFlags(Flags.FLAG_PRIVACY_DOT_UNFOLD_WRONG_CORNER_FIX) + fun initialize_newViews_fixFlagEnabled_gravityIsUpdated() { + val newTopLeftView = initDotView() + val newTopRightView = initDotView() + val newBottomLeftView = initDotView() + val newBottomRightView = initDotView() + setRotation(ROTATION_LANDSCAPE) // Bottom right used in landscape + + val controller = createAndInitializeController() + // Re-init with different views, but same rotation + controller.initialize( + newTopLeftView, + newTopRightView, + newBottomLeftView, + newBottomRightView + ) + + assertThat((newBottomRightView.layoutParams as FrameLayout.LayoutParams).gravity) + .isNotEqualTo(UNSPECIFIED_GRAVITY) + } + + @Test + @DisableFlags(Flags.FLAG_PRIVACY_DOT_UNFOLD_WRONG_CORNER_FIX) + fun initialize_newViews_fixFlagDisabled_gravityIsNotUpdated() { + val newTopLeftView = initDotView() + val newTopRightView = initDotView() + val newBottomLeftView = initDotView() + val newBottomRightView = initDotView() + setRotation(ROTATION_LANDSCAPE) // Bottom right used in landscape + + val controller = createAndInitializeController() + // Re-init with different views, but same rotation + controller.initialize( + newTopLeftView, + newTopRightView, + newBottomLeftView, + newBottomRightView + ) + + assertThat((newBottomRightView.layoutParams as FrameLayout.LayoutParams).gravity) + .isEqualTo(UNSPECIFIED_GRAVITY) + } + private fun setRotation(rotation: Int) { whenever(mockDisplay.rotation).thenReturn(rotation) } - private fun initDotView(): View = - View(context).also { + private fun initDotView(): View { + val privacyDot = View(context).also { it.id = R.id.privacy_dot } + return FrameLayout(context).also { it.layoutParams = FrameLayout.LayoutParams(/* width = */ 0, /* height = */ 0) + it.addView(privacyDot) } + } private fun enableRtl() { configurationController.notifyLayoutDirectionChanged(isRtl = true) diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationStackAppearanceIntegrationTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationStackAppearanceIntegrationTest.kt index c4506f2d3caa..ca4434d233a6 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationStackAppearanceIntegrationTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationStackAppearanceIntegrationTest.kt @@ -121,10 +121,13 @@ class NotificationStackAppearanceIntegrationTest : SysuiTestCase() { sceneInteractor.setTransitionState(transitionState) val expandFraction by collectLastValue(scrollViewModel.expandFraction) assertThat(expandFraction).isEqualTo(0f) + + fakeSceneDataSource.changeScene(toScene = Scenes.Gone) val isScrollable by collectLastValue(scrollViewModel.isScrollable) assertThat(isScrollable).isFalse() fakeSceneDataSource.pause() + sceneInteractor.changeScene(Scenes.Shade, "reason") val transitionProgress = MutableStateFlow(0f) transitionState.value = @@ -159,8 +162,10 @@ class NotificationStackAppearanceIntegrationTest : SysuiTestCase() { sceneInteractor.setTransitionState(transitionState) val expandFraction by collectLastValue(scrollViewModel.expandFraction) assertThat(expandFraction).isEqualTo(1f) + + fakeSceneDataSource.changeScene(toScene = Scenes.Lockscreen) val isScrollable by collectLastValue(scrollViewModel.isScrollable) - assertThat(isScrollable).isFalse() + assertThat(isScrollable).isTrue() } @Test diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt index c35c165ba761..497484f90ca9 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt @@ -42,6 +42,7 @@ import com.android.systemui.keyguard.domain.interactor.keyguardInteractor import com.android.systemui.keyguard.shared.model.BurnInModel import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.shared.model.KeyguardState.AOD +import com.android.systemui.keyguard.shared.model.KeyguardState.GONE import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN import com.android.systemui.keyguard.shared.model.StatusBarState import com.android.systemui.keyguard.shared.model.TransitionState @@ -842,6 +843,30 @@ class SharedNotificationContainerViewModelTest(flags: FlagsParameterization) : S } @Test + @DisableSceneContainer + fun updateBounds_fromGone_withoutTransitions() = + testScope.runTest { + // Start step is already at 1.0 + val runningStep = TransitionStep(GONE, AOD, 1.0f, TransitionState.RUNNING) + val finishStep = TransitionStep(GONE, AOD, 1.0f, TransitionState.FINISHED) + + val bounds by collectLastValue(underTest.bounds) + val top = 123f + val bottom = 456f + + kosmos.fakeKeyguardTransitionRepository.sendTransitionStep(runningStep) + runCurrent() + kosmos.fakeKeyguardTransitionRepository.sendTransitionStep(finishStep) + runCurrent() + keyguardRootViewModel.onNotificationContainerBoundsChanged(top, bottom) + runCurrent() + + assertThat(bounds).isEqualTo( + NotificationContainerBounds(top = top, bottom = bottom) + ) + } + + @Test fun alphaOnFullQsExpansion() = testScope.runTest { val viewState = ViewStateAccessor() diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/BaseHeadsUpManagerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/BaseHeadsUpManagerTest.java index 88bef91d043f..206b39c28da3 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/BaseHeadsUpManagerTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/BaseHeadsUpManagerTest.java @@ -627,7 +627,7 @@ public class BaseHeadsUpManagerTest extends SysuiTestCase { hum.onEntryAdded(entryToPin); assertEquals(2, mUiEventLoggerFake.numLogs()); - assertEquals(AvalancheController.ThrottleEvent.SHOWN.getId(), + assertEquals(AvalancheController.ThrottleEvent.AVALANCHE_THROTTLING_HUN_SHOWN.getId(), mUiEventLoggerFake.eventId(0)); assertEquals(BaseHeadsUpManager.NotificationPeekEvent.NOTIFICATION_PEEK.getId(), mUiEventLoggerFake.eventId(1)); diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/domain/interactor/AudioOutputInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/domain/interactor/AudioOutputInteractorTest.kt index 10a4eb790b44..7385a4731401 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/domain/interactor/AudioOutputInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/domain/interactor/AudioOutputInteractorTest.kt @@ -71,6 +71,7 @@ class AudioOutputInteractorTest : SysuiTestCase() { addOverride(R.drawable.ic_headphone, testIcon) addOverride(R.drawable.ic_smartphone, testIcon) addOverride(R.drawable.ic_media_speaker_device, testIcon) + addOverride(R.drawable.ic_media_tablet, testIcon) addOverride(com.android.internal.R.drawable.ic_bt_hearing_aid, testIcon) diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputComponentInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputComponentInteractorTest.kt index 8921a23fda8d..0f56d0bcc7eb 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputComponentInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputComponentInteractorTest.kt @@ -65,6 +65,7 @@ class MediaOutputComponentInteractorTest : SysuiTestCase() { with(context.orCreateTestableResources) { addOverride(R.drawable.ic_smartphone, testIcon) + addOverride(R.drawable.ic_media_tablet, testIcon) addOverride(R.string.media_transfer_this_device_name_tv, builtInDeviceName) addOverride(R.string.media_transfer_this_device_name_tablet, builtInDeviceName) diff --git a/packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceDataPlugin.java b/packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceDataPlugin.java index 83658d3c1984..9ad4012cfd07 100644 --- a/packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceDataPlugin.java +++ b/packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceDataPlugin.java @@ -134,6 +134,12 @@ public interface BcSmartspaceDataPlugin extends Plugin { default void setScreenOn(boolean screenOn) {} /** + * Sets a delegate to handle clock event registration. Should be called immediately after + * the view is created. + */ + default void setTimeChangedDelegate(TimeChangedDelegate delegate) {} + + /** * Set if dozing is true or false */ default void setDozing(boolean dozing) {} @@ -228,4 +234,13 @@ public interface BcSmartspaceDataPlugin extends Plugin { /** Start the PendingIntent */ void startPendingIntent(View v, PendingIntent pi, boolean showOnLockscreen); } + + /** Interface for delegating time updates */ + interface TimeChangedDelegate { + /** Register the callback to be called when time is updated **/ + void register(Runnable callback); + + /** Unegister the callback **/ + void unregister(); + } } diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java index de659315e176..7cf56aa5c40e 100644 --- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java +++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java @@ -58,6 +58,19 @@ public interface ActivityStarter { @Nullable ActivityTransitionAnimator.Controller animationController); /** + * Similar to {@link #startPendingIntentMaybeDismissingKeyguard(PendingIntent, Runnable, + * ActivityTransitionAnimator.Controller)} but will always not dismiss the keyguard when + * launching activities. This should be avoided and other alternatives should be used. + */ + void startPendingIntentWithoutDismissing( + PendingIntent intent, + boolean dismissShade, + Runnable intentSentUiThreadCallback, + @Nullable ActivityTransitionAnimator.Controller animationController, + @Nullable Intent fillInIntent, + @Nullable Bundle extraOptions); + + /** * Similar to {@link #startPendingIntentDismissingKeyguard}, except that it supports launching * activities on top of the keyguard. If the activity supports {@code showOverLockscreen}, it * will show over keyguard without first dimissing it. If it doesn't support it, calling this diff --git a/packages/SystemUI/res/drawable/android15_patch_adaptive.xml b/packages/SystemUI/res/drawable/android15_patch_adaptive.xml new file mode 100644 index 000000000000..d9492000beeb --- /dev/null +++ b/packages/SystemUI/res/drawable/android15_patch_adaptive.xml @@ -0,0 +1,21 @@ +<?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. +--> +<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android"> + <background android:drawable="@drawable/android15_patch_adaptive_background"/> + <foreground android:drawable="@drawable/android15_patch_adaptive_foreground"/> + <monochrome android:drawable="@drawable/android15_patch_monochrome"/> +</adaptive-icon> diff --git a/packages/SystemUI/res/drawable/android15_patch_adaptive_background.xml b/packages/SystemUI/res/drawable/android15_patch_adaptive_background.xml new file mode 100644 index 000000000000..d4850d3d99c7 --- /dev/null +++ b/packages/SystemUI/res/drawable/android15_patch_adaptive_background.xml @@ -0,0 +1,75 @@ +<?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="108dp" + android:height="108dp" + android:viewportWidth="108" + android:viewportHeight="108"> + <!-- space (themed version) --> + <path + android:pathData="M0,0h108v108h-108z" + android:fillColor="@android:color/system_neutral1_800"/> + <!-- stars (themed version) --> + <group> + <path + android:pathData="M32,34 h1v1h-1z" + android:fillColor="@android:color/system_accent3_200"/> + <path + android:pathData="M33,61 h1v1h-1z" + android:fillColor="@android:color/system_accent3_200"/> + <path + android:pathData="M71,34 h1v1h-1z" + android:fillColor="@android:color/system_accent3_200"/> + <path + android:pathData="M62,56 h1v1h-1z" + android:fillColor="@android:color/system_accent3_200"/> + <path + android:pathData="M68,47 h1v1h-1z" + android:fillColor="@android:color/system_accent3_200"/> + <path + android:pathData="M72,55 h1v1h-1z" + android:fillColor="@android:color/system_accent3_200"/> + <path + android:pathData="M39,36 h1v1h-1z" + android:fillColor="@android:color/system_accent3_200"/> + + <path + android:pathData="M72,49 h2v2h-2z" + android:fillColor="@android:color/system_accent3_200"/> + <path + android:pathData="M46,53 h2v2h-2z" + android:fillColor="@android:color/system_accent3_200"/> + <path + android:pathData="M32,45 h2v2h-2z" + android:fillColor="@android:color/system_accent3_200"/> + <path + android:pathData="M43,37 h2v2h-2z" + android:fillColor="@android:color/system_accent3_200"/> + <path + android:pathData="M78,51 h2v2h-2z" + android:fillColor="@android:color/system_accent3_200"/> + <path + android:pathData="M34,51 h2v2h-2z" + android:fillColor="@android:color/system_accent3_200"/> + <path + android:pathData="M76,41 h2v2h-2z" + android:fillColor="@android:color/system_accent3_200"/> + <path + android:pathData="M39,46 h2v2h-2z" + android:fillColor="@android:color/system_accent3_200"/> + </group> +</vector> diff --git a/packages/SystemUI/res/drawable/android15_patch_adaptive_foreground.xml b/packages/SystemUI/res/drawable/android15_patch_adaptive_foreground.xml new file mode 100644 index 000000000000..34f6ee07a50b --- /dev/null +++ b/packages/SystemUI/res/drawable/android15_patch_adaptive_foreground.xml @@ -0,0 +1,60 @@ +<?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="108dp" + android:height="108dp" + android:viewportWidth="108" + android:viewportHeight="108"> + + <!-- zoomies (themed version) --> + <group> + <path + android:pathData="M53,42C52.21,50.58 46.46,68.95 32.11,74.63C19.22,79.75 5.77,82.32 1.19,83.19C0.68,83.29 0.28,83.36 0,83.42V108H54H108V83.42C107.72,83.36 107.32,83.29 106.81,83.19C102.23,82.32 88.78,79.75 75.89,74.63C61.54,68.95 55.79,50.58 55,42H54H53Z" + android:fillColor="@android:color/system_accent1_100" + android:fillType="evenOdd"/> + <path + android:pathData="M53.25,42C52.88,50.53 50.44,69.01 43.68,74.67C36.91,80.33 32.65,82.86 31.37,83.41L54,102.87L76.63,83.41C75.35,82.86 71.09,80.33 64.32,74.67C57.56,69.01 55.12,50.53 54.75,42H54H53.25Z" + android:fillColor="#ffffff" + android:fillType="evenOdd"/> + <path + android:pathData="M54,42m-1,0a1,1 0,1 1,2 0a1,1 0,1 1,-2 0" + android:fillColor="#ffffff"/> + </group> + <group> + <!-- head! it's like sputnik --> + <path + android:pathData="M54,94.25m-26.25,0a26.25,26.25 0,1 1,52.5 0a26.25,26.25 0,1 1,-52.5 0" + android:fillColor="#34A853"/> + <!-- ant --> + <path + android:pathData="M38,63.5L44.5,74.5" + android:strokeWidth="5" + android:fillColor="#00000000" + android:strokeColor="#34A853" + android:strokeLineCap="round"/> + <path + android:pathData="M70,63.5L63.5,74.5" + android:strokeWidth="5" + android:fillColor="#00000000" + android:strokeColor="#34A853" + android:strokeLineCap="round"/> + </group> + <!-- spaceship --> + <path + android:pathData="M54,34C52.34,34 51,35.29 51,36.88V40.44C51,40.75 51.25,41 51.56,41C51.87,41 52.13,40.75 52.13,40.44V39.48C52.13,38.87 52.63,38.37 53.25,38.37H54.75C55.37,38.37 55.87,38.87 55.87,39.48V40.44C55.87,40.75 56.13,41 56.44,41C56.75,41 57,40.75 57,40.44V36.88C57,35.29 55.66,34 54,34H54Z" + android:fillColor="#E9F3EB"/> +</vector> diff --git a/packages/SystemUI/res/drawable/android15_patch_monochrome.xml b/packages/SystemUI/res/drawable/android15_patch_monochrome.xml new file mode 100644 index 000000000000..a91cc864e7cd --- /dev/null +++ b/packages/SystemUI/res/drawable/android15_patch_monochrome.xml @@ -0,0 +1,119 @@ +<?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="108dp" + android:height="108dp" + android:viewportWidth="108" + android:viewportHeight="108"> + <group> + <path + android:pathData=" + M54,94.25 + m-26.25,0 + a26.25,26.25 0,1 1,52.5 0 + a26.25,26.25 0,1 1,-52.5 0 + " + android:fillColor="#000000"/> + <path + android:pathData="M38,63.5L44.5,74.5" + android:strokeWidth="5" + android:fillColor="#00000000" + android:strokeColor="#000000" + android:strokeLineCap="round"/> + <path + android:pathData="M70,63.5L63.5,74.5" + android:strokeWidth="5" + android:fillColor="#00000000" + android:strokeColor="#000000" + android:strokeLineCap="round"/> + + <path + android:pathData=" + M54,34 + C52.34,34 51,35.29 51,36.88 + V40.44 + C51,40.75 51.25,41 51.56,41 + C51.87,41 52.13,40.75 52.13,40.44 + V39.48 + C52.13,38.87 52.63,38.37 53.25,38.37 + H54.75 + C55.37,38.37 55.87,38.87 55.87,39.48 + V40.44 + C55.87,40.75 56.13,41 56.44,41 + C56.75,41 57,40.75 57,40.44 + V36.88 + C57,35.29 55.66,34 54,34 + H54 + Z + " + android:fillColor="#34A853"/> + <path + android:strokeWidth="1" + android:pathData="M54,40V67" + android:fillColor="#00000000" + android:strokeColor="#40FFFFFF" + /> + + <path + android:pathData="M32,34 h1v1h-1z" + android:fillColor="#ffffff"/> + <path + android:pathData="M33,61 h1v1h-1z" + android:fillColor="#ffffff"/> + <path + android:pathData="M71,34 h1v1h-1z" + android:fillColor="#ffffff"/> + <path + android:pathData="M62,56 h1v1h-1z" + android:fillColor="#ffffff"/> + <path + android:pathData="M68,47 h1v1h-1z" + android:fillColor="#ffffff"/> + <path + android:pathData="M72,55 h1v1h-1z" + android:fillColor="#ffffff"/> + <path + android:pathData="M39,36 h1v1h-1z" + android:fillColor="#ffffff"/> + + <path + android:pathData="M72,49 h2v2h-2z" + android:fillColor="#ffffff"/> + <path + android:pathData="M46,53 h2v2h-2z" + android:fillColor="#ffffff"/> + <path + android:pathData="M32,45 h2v2h-2z" + android:fillColor="#ffffff"/> + <path + android:pathData="M43,37 h2v2h-2z" + android:fillColor="#ffffff"/> + <path + android:pathData="M78,51 h2v2h-2z" + android:fillColor="#ffffff"/> + <path + android:pathData="M34,51 h2v2h-2z" + android:fillColor="#ffffff"/> + <path + android:pathData="M76,41 h2v2h-2z" + android:fillColor="#ffffff"/> + <path + android:pathData="M39,46 h2v2h-2z" + android:fillColor="#ffffff"/> + + </group> +</vector> diff --git a/packages/SystemUI/res/drawable/audio_bars_playing.xml b/packages/SystemUI/res/drawable/audio_bars_playing.xml new file mode 100644 index 000000000000..6a6706a73f64 --- /dev/null +++ b/packages/SystemUI/res/drawable/audio_bars_playing.xml @@ -0,0 +1,457 @@ +<!-- + ~ Copyright (C) 2024 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> +<animated-vector xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:aapt="http://schemas.android.com/aapt"> + <target android:name="_R_G_L_4_G_D_0_P_0"> + <aapt:attr name="android:animation"> + <set android:ordering="together"> + <objectAnimator + android:duration="283" + android:propertyName="pathData" + android:startOffset="0" + android:valueFrom="M-37.16 -5.87 C-33.94,-5.87 -31.32,-3.32 -31.2,-0.13 C-31.2,-0.06 -31.2,0.02 -31.2,0.09 C-31.2,0.16 -31.2,0.23 -31.2,0.29 C-31.31,3.49 -33.94,6.05 -37.16,6.05 C-40.39,6.05 -43.01,3.49 -43.12,0.29 C-43.12,0.23 -43.12,0.16 -43.12,0.09 C-43.12,0.01 -43.12,-0.07 -43.12,-0.15 C-42.99,-3.33 -40.37,-5.87 -37.16,-5.87c " + android:valueTo="M-37.16 -13.39 C-33.94,-13.39 -31.32,-10.83 -31.2,-7.64 C-31.2,-7.57 -31.2,0.02 -31.2,0.09 C-31.2,0.16 -31.2,6.79 -31.2,6.86 C-31.31,10.05 -33.94,12.61 -37.16,12.61 C-40.39,12.61 -43.01,10.05 -43.12,6.85 C-43.12,6.79 -43.12,0.16 -43.12,0.09 C-43.12,0.01 -43.12,-7.58 -43.12,-7.66 C-42.99,-10.84 -40.37,-13.39 -37.16,-13.39c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.667,1 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="433" + android:propertyName="pathData" + android:startOffset="283" + android:valueFrom="M-37.16 -13.39 C-33.94,-13.39 -31.32,-10.83 -31.2,-7.64 C-31.2,-7.57 -31.2,0.02 -31.2,0.09 C-31.2,0.16 -31.2,6.79 -31.2,6.86 C-31.31,10.05 -33.94,12.61 -37.16,12.61 C-40.39,12.61 -43.01,10.05 -43.12,6.85 C-43.12,6.79 -43.12,0.16 -43.12,0.09 C-43.12,0.01 -43.12,-7.58 -43.12,-7.66 C-42.99,-10.84 -40.37,-13.39 -37.16,-13.39c " + android:valueTo="M-37.16 -5.87 C-33.94,-5.87 -31.32,-3.32 -31.2,-0.13 C-31.2,-0.06 -31.2,0.02 -31.2,0.09 C-31.2,0.16 -31.2,0.23 -31.2,0.29 C-31.31,3.49 -33.94,6.05 -37.16,6.05 C-40.39,6.05 -43.01,3.49 -43.12,0.29 C-43.12,0.23 -43.12,0.16 -43.12,0.09 C-43.12,0.01 -43.12,-0.07 -43.12,-0.15 C-42.99,-3.33 -40.37,-5.87 -37.16,-5.87c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.667,1 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="417" + android:propertyName="pathData" + android:startOffset="717" + android:valueFrom="M-37.16 -5.87 C-33.94,-5.87 -31.32,-3.32 -31.2,-0.13 C-31.2,-0.06 -31.2,0.02 -31.2,0.09 C-31.2,0.16 -31.2,0.23 -31.2,0.29 C-31.31,3.49 -33.94,6.05 -37.16,6.05 C-40.39,6.05 -43.01,3.49 -43.12,0.29 C-43.12,0.23 -43.12,0.16 -43.12,0.09 C-43.12,0.01 -43.12,-0.07 -43.12,-0.15 C-42.99,-3.33 -40.37,-5.87 -37.16,-5.87c " + android:valueTo="M-37.16 -13.39 C-33.94,-13.39 -31.32,-10.83 -31.2,-7.64 C-31.2,-7.57 -31.2,0.02 -31.2,0.09 C-31.2,0.16 -31.2,6.79 -31.2,6.86 C-31.31,10.05 -33.94,12.61 -37.16,12.61 C-40.39,12.61 -43.01,10.05 -43.12,6.85 C-43.12,6.79 -43.12,0.16 -43.12,0.09 C-43.12,0.01 -43.12,-7.58 -43.12,-7.66 C-42.99,-10.84 -40.37,-13.39 -37.16,-13.39c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.667,1 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="467" + android:propertyName="pathData" + android:startOffset="1133" + android:valueFrom="M-37.16 -13.39 C-33.94,-13.39 -31.32,-10.83 -31.2,-7.64 C-31.2,-7.57 -31.2,0.02 -31.2,0.09 C-31.2,0.16 -31.2,6.79 -31.2,6.86 C-31.31,10.05 -33.94,12.61 -37.16,12.61 C-40.39,12.61 -43.01,10.05 -43.12,6.85 C-43.12,6.79 -43.12,0.16 -43.12,0.09 C-43.12,0.01 -43.12,-7.58 -43.12,-7.66 C-42.99,-10.84 -40.37,-13.39 -37.16,-13.39c " + android:valueTo="M-37.16 -5.87 C-33.94,-5.87 -31.32,-3.32 -31.2,-0.13 C-31.2,-0.06 -31.2,0.02 -31.2,0.09 C-31.2,0.16 -31.2,0.23 -31.2,0.29 C-31.31,3.49 -33.94,6.05 -37.16,6.05 C-40.39,6.05 -43.01,3.49 -43.12,0.29 C-43.12,0.23 -43.12,0.16 -43.12,0.09 C-43.12,0.01 -43.12,-0.07 -43.12,-0.15 C-42.99,-3.33 -40.37,-5.87 -37.16,-5.87c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.667,1 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="433" + android:propertyName="pathData" + android:startOffset="1600" + android:valueFrom="M-37.16 -5.87 C-33.94,-5.87 -31.32,-3.32 -31.2,-0.13 C-31.2,-0.06 -31.2,0.02 -31.2,0.09 C-31.2,0.16 -31.2,0.23 -31.2,0.29 C-31.31,3.49 -33.94,6.05 -37.16,6.05 C-40.39,6.05 -43.01,3.49 -43.12,0.29 C-43.12,0.23 -43.12,0.16 -43.12,0.09 C-43.12,0.01 -43.12,-0.07 -43.12,-0.15 C-42.99,-3.33 -40.37,-5.87 -37.16,-5.87c " + android:valueTo="M-37.16 -13.39 C-33.94,-13.39 -31.32,-10.83 -31.2,-7.64 C-31.2,-7.57 -31.2,0.02 -31.2,0.09 C-31.2,0.16 -31.2,6.79 -31.2,6.86 C-31.31,10.05 -33.94,12.61 -37.16,12.61 C-40.39,12.61 -43.01,10.05 -43.12,6.85 C-43.12,6.79 -43.12,0.16 -43.12,0.09 C-43.12,0.01 -43.12,-7.58 -43.12,-7.66 C-42.99,-10.84 -40.37,-13.39 -37.16,-13.39c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.667,1 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="517" + android:propertyName="pathData" + android:startOffset="2033" + android:valueFrom="M-37.16 -13.39 C-33.94,-13.39 -31.32,-10.83 -31.2,-7.64 C-31.2,-7.57 -31.2,0.02 -31.2,0.09 C-31.2,0.16 -31.2,6.79 -31.2,6.86 C-31.31,10.05 -33.94,12.61 -37.16,12.61 C-40.39,12.61 -43.01,10.05 -43.12,6.85 C-43.12,6.79 -43.12,0.16 -43.12,0.09 C-43.12,0.01 -43.12,-7.58 -43.12,-7.66 C-42.99,-10.84 -40.37,-13.39 -37.16,-13.39c " + android:valueTo="M-37.16 -5.87 C-33.94,-5.87 -31.32,-3.32 -31.2,-0.13 C-31.2,-0.06 -31.2,0.02 -31.2,0.09 C-31.2,0.16 -31.2,0.23 -31.2,0.29 C-31.31,3.49 -33.94,6.05 -37.16,6.05 C-40.39,6.05 -43.01,3.49 -43.12,0.29 C-43.12,0.23 -43.12,0.16 -43.12,0.09 C-43.12,0.01 -43.12,-0.07 -43.12,-0.15 C-42.99,-3.33 -40.37,-5.87 -37.16,-5.87c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.667,1 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + </set> + </aapt:attr> + </target> + <target android:name="_R_G_L_3_G_D_0_P_0"> + <aapt:attr name="android:animation"> + <set android:ordering="together"> + <objectAnimator + android:duration="367" + android:propertyName="pathData" + android:startOffset="0" + android:valueFrom="M-37.16 -5.87 C-33.94,-5.87 -31.32,-3.32 -31.2,-0.13 C-31.2,-0.06 -31.2,0.02 -31.2,0.09 C-31.2,0.16 -31.2,0.23 -31.2,0.29 C-31.31,3.49 -33.94,6.05 -37.16,6.05 C-40.39,6.05 -43.01,3.49 -43.12,0.29 C-43.12,0.23 -43.12,0.16 -43.12,0.09 C-43.12,0.01 -43.12,-0.07 -43.12,-0.15 C-42.99,-3.33 -40.37,-5.87 -37.16,-5.87c " + android:valueTo="M-37.16 -18.51 C-33.94,-18.51 -31.32,-15.96 -31.2,-12.77 C-31.2,-12.7 -31.2,0.02 -31.2,0.09 C-31.2,0.16 -31.2,13.16 -31.2,13.23 C-31.31,16.43 -33.94,18.99 -37.16,18.99 C-40.39,18.99 -43.01,16.43 -43.12,13.23 C-43.12,13.16 -43.12,0.16 -43.12,0.09 C-43.12,0.01 -43.12,-12.71 -43.12,-12.78 C-42.99,-15.97 -40.37,-18.51 -37.16,-18.51c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.667,1 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="433" + android:propertyName="pathData" + android:startOffset="367" + android:valueFrom="M-37.16 -18.51 C-33.94,-18.51 -31.32,-15.96 -31.2,-12.77 C-31.2,-12.7 -31.2,0.02 -31.2,0.09 C-31.2,0.16 -31.2,13.16 -31.2,13.23 C-31.31,16.43 -33.94,18.99 -37.16,18.99 C-40.39,18.99 -43.01,16.43 -43.12,13.23 C-43.12,13.16 -43.12,0.16 -43.12,0.09 C-43.12,0.01 -43.12,-12.71 -43.12,-12.78 C-42.99,-15.97 -40.37,-18.51 -37.16,-18.51c " + android:valueTo="M-37.16 -9.14 C-33.94,-9.14 -31.32,-6.58 -31.2,-3.39 C-31.2,-3.32 -31.2,0.02 -31.2,0.09 C-31.2,0.16 -31.2,3.79 -31.2,3.86 C-31.31,7.05 -33.94,9.61 -37.16,9.61 C-40.39,9.61 -43.01,7.05 -43.12,3.85 C-43.12,3.79 -43.12,0.16 -43.12,0.09 C-43.12,0.01 -43.12,-3.33 -43.12,-3.41 C-42.99,-6.59 -40.37,-9.14 -37.16,-9.14c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.667,1 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="417" + android:propertyName="pathData" + android:startOffset="800" + android:valueFrom="M-37.16 -9.14 C-33.94,-9.14 -31.32,-6.58 -31.2,-3.39 C-31.2,-3.32 -31.2,0.02 -31.2,0.09 C-31.2,0.16 -31.2,3.79 -31.2,3.86 C-31.31,7.05 -33.94,9.61 -37.16,9.61 C-40.39,9.61 -43.01,7.05 -43.12,3.85 C-43.12,3.79 -43.12,0.16 -43.12,0.09 C-43.12,0.01 -43.12,-3.33 -43.12,-3.41 C-42.99,-6.59 -40.37,-9.14 -37.16,-9.14c " + android:valueTo="M-37.16 -18.51 C-33.94,-18.51 -31.32,-15.96 -31.2,-12.77 C-31.2,-12.7 -31.2,0.02 -31.2,0.09 C-31.2,0.16 -31.2,13.16 -31.2,13.23 C-31.31,16.43 -33.94,18.99 -37.16,18.99 C-40.39,18.99 -43.01,16.43 -43.12,13.23 C-43.12,13.16 -43.12,0.16 -43.12,0.09 C-43.12,0.01 -43.12,-12.71 -43.12,-12.78 C-42.99,-15.97 -40.37,-18.51 -37.16,-18.51c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.667,1 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="467" + android:propertyName="pathData" + android:startOffset="1217" + android:valueFrom="M-37.16 -18.51 C-33.94,-18.51 -31.32,-15.96 -31.2,-12.77 C-31.2,-12.7 -31.2,0.02 -31.2,0.09 C-31.2,0.16 -31.2,13.16 -31.2,13.23 C-31.31,16.43 -33.94,18.99 -37.16,18.99 C-40.39,18.99 -43.01,16.43 -43.12,13.23 C-43.12,13.16 -43.12,0.16 -43.12,0.09 C-43.12,0.01 -43.12,-12.71 -43.12,-12.78 C-42.99,-15.97 -40.37,-18.51 -37.16,-18.51c " + android:valueTo="M-37.16 -9.14 C-33.94,-9.14 -31.32,-6.58 -31.2,-3.39 C-31.2,-3.32 -31.2,0.02 -31.2,0.09 C-31.2,0.16 -31.2,3.79 -31.2,3.86 C-31.31,7.05 -33.94,9.61 -37.16,9.61 C-40.39,9.61 -43.01,7.05 -43.12,3.85 C-43.12,3.79 -43.12,0.16 -43.12,0.09 C-43.12,0.01 -43.12,-3.33 -43.12,-3.41 C-42.99,-6.59 -40.37,-9.14 -37.16,-9.14c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.667,1 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="450" + android:propertyName="pathData" + android:startOffset="1683" + android:valueFrom="M-37.16 -9.14 C-33.94,-9.14 -31.32,-6.58 -31.2,-3.39 C-31.2,-3.32 -31.2,0.02 -31.2,0.09 C-31.2,0.16 -31.2,3.79 -31.2,3.86 C-31.31,7.05 -33.94,9.61 -37.16,9.61 C-40.39,9.61 -43.01,7.05 -43.12,3.85 C-43.12,3.79 -43.12,0.16 -43.12,0.09 C-43.12,0.01 -43.12,-3.33 -43.12,-3.41 C-42.99,-6.59 -40.37,-9.14 -37.16,-9.14c " + android:valueTo="M-37.16 -18.51 C-33.94,-18.51 -31.32,-15.96 -31.2,-12.77 C-31.2,-12.7 -31.2,0.02 -31.2,0.09 C-31.2,0.16 -31.2,13.16 -31.2,13.23 C-31.31,16.43 -33.94,18.99 -37.16,18.99 C-40.39,18.99 -43.01,16.43 -43.12,13.23 C-43.12,13.16 -43.12,0.16 -43.12,0.09 C-43.12,0.01 -43.12,-12.71 -43.12,-12.78 C-42.99,-15.97 -40.37,-18.51 -37.16,-18.51c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.667,1 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="417" + android:propertyName="pathData" + android:startOffset="2133" + android:valueFrom="M-37.16 -18.51 C-33.94,-18.51 -31.32,-15.96 -31.2,-12.77 C-31.2,-12.7 -31.2,0.02 -31.2,0.09 C-31.2,0.16 -31.2,13.16 -31.2,13.23 C-31.31,16.43 -33.94,18.99 -37.16,18.99 C-40.39,18.99 -43.01,16.43 -43.12,13.23 C-43.12,13.16 -43.12,0.16 -43.12,0.09 C-43.12,0.01 -43.12,-12.71 -43.12,-12.78 C-42.99,-15.97 -40.37,-18.51 -37.16,-18.51c " + android:valueTo="M-37.16 -5.87 C-33.94,-5.87 -31.32,-3.32 -31.2,-0.13 C-31.2,-0.06 -31.2,0.02 -31.2,0.09 C-31.2,0.16 -31.2,0.23 -31.2,0.29 C-31.31,3.49 -33.94,6.05 -37.16,6.05 C-40.39,6.05 -43.01,3.49 -43.12,0.29 C-43.12,0.23 -43.12,0.16 -43.12,0.09 C-43.12,0.01 -43.12,-0.07 -43.12,-0.15 C-42.99,-3.33 -40.37,-5.87 -37.16,-5.87c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.667,1 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + </set> + </aapt:attr> + </target> + <target android:name="_R_G_L_2_G_D_0_P_0"> + <aapt:attr name="android:animation"> + <set android:ordering="together"> + <objectAnimator + android:duration="433" + android:propertyName="pathData" + android:startOffset="0" + android:valueFrom="M-37.16 -5.87 C-33.94,-5.87 -31.32,-3.32 -31.2,-0.13 C-31.2,-0.06 -31.2,0.02 -31.2,0.09 C-31.2,0.16 -31.2,0.23 -31.2,0.29 C-31.31,3.49 -33.94,6.05 -37.16,6.05 C-40.39,6.05 -43.01,3.49 -43.12,0.29 C-43.12,0.23 -43.12,0.16 -43.12,0.09 C-43.12,0.01 -43.12,-0.07 -43.12,-0.15 C-42.99,-3.33 -40.37,-5.87 -37.16,-5.87c " + android:valueTo="M-37.16 -25.01 C-33.94,-25.01 -31.32,-22.46 -31.2,-19.27 C-31.2,-19.2 -31.2,0.02 -31.2,0.09 C-31.2,0.16 -31.2,19.54 -31.2,19.6 C-31.31,22.8 -33.94,25.36 -37.16,25.36 C-40.39,25.36 -43.01,22.8 -43.12,19.6 C-43.12,19.54 -43.12,0.16 -43.12,0.09 C-43.12,0.01 -43.12,-19.21 -43.12,-19.28 C-42.99,-22.47 -40.37,-25.01 -37.16,-25.01c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.667,1 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="450" + android:propertyName="pathData" + android:startOffset="433" + android:valueFrom="M-37.16 -25.01 C-33.94,-25.01 -31.32,-22.46 -31.2,-19.27 C-31.2,-19.2 -31.2,0.02 -31.2,0.09 C-31.2,0.16 -31.2,19.54 -31.2,19.6 C-31.31,22.8 -33.94,25.36 -37.16,25.36 C-40.39,25.36 -43.01,22.8 -43.12,19.6 C-43.12,19.54 -43.12,0.16 -43.12,0.09 C-43.12,0.01 -43.12,-19.21 -43.12,-19.28 C-42.99,-22.47 -40.37,-25.01 -37.16,-25.01c " + android:valueTo="M-37.16 -15.76 C-33.94,-15.76 -31.32,-13.21 -31.2,-10.02 C-31.2,-9.95 -31.2,0.02 -31.2,0.09 C-31.2,0.16 -31.2,10.54 -31.2,10.61 C-31.31,13.8 -33.94,16.36 -37.16,16.36 C-40.39,16.36 -43.01,13.8 -43.12,10.6 C-43.12,10.54 -43.12,0.16 -43.12,0.09 C-43.12,0.01 -43.12,-9.96 -43.12,-10.03 C-42.99,-13.22 -40.37,-15.76 -37.16,-15.76c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.833,1 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="417" + android:propertyName="pathData" + android:startOffset="883" + android:valueFrom="M-37.16 -15.76 C-33.94,-15.76 -31.32,-13.21 -31.2,-10.02 C-31.2,-9.95 -31.2,0.02 -31.2,0.09 C-31.2,0.16 -31.2,10.54 -31.2,10.61 C-31.31,13.8 -33.94,16.36 -37.16,16.36 C-40.39,16.36 -43.01,13.8 -43.12,10.6 C-43.12,10.54 -43.12,0.16 -43.12,0.09 C-43.12,0.01 -43.12,-9.96 -43.12,-10.03 C-42.99,-13.22 -40.37,-15.76 -37.16,-15.76c " + android:valueTo="M-37.16 -25.01 C-33.94,-25.01 -31.32,-22.46 -31.2,-19.27 C-31.2,-19.2 -31.2,0.02 -31.2,0.09 C-31.2,0.16 -31.2,19.54 -31.2,19.6 C-31.31,22.8 -33.94,25.36 -37.16,25.36 C-40.39,25.36 -43.01,22.8 -43.12,19.6 C-43.12,19.54 -43.12,0.16 -43.12,0.09 C-43.12,0.01 -43.12,-19.21 -43.12,-19.28 C-42.99,-22.47 -40.37,-25.01 -37.16,-25.01c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.667,1 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="467" + android:propertyName="pathData" + android:startOffset="1300" + android:valueFrom="M-37.16 -25.01 C-33.94,-25.01 -31.32,-22.46 -31.2,-19.27 C-31.2,-19.2 -31.2,0.02 -31.2,0.09 C-31.2,0.16 -31.2,19.54 -31.2,19.6 C-31.31,22.8 -33.94,25.36 -37.16,25.36 C-40.39,25.36 -43.01,22.8 -43.12,19.6 C-43.12,19.54 -43.12,0.16 -43.12,0.09 C-43.12,0.01 -43.12,-19.21 -43.12,-19.28 C-42.99,-22.47 -40.37,-25.01 -37.16,-25.01c " + android:valueTo="M-37.16 -15.76 C-33.94,-15.76 -31.32,-13.21 -31.2,-10.02 C-31.2,-9.95 -31.2,0.02 -31.2,0.09 C-31.2,0.16 -31.2,10.54 -31.2,10.61 C-31.31,13.8 -33.94,16.36 -37.16,16.36 C-40.39,16.36 -43.01,13.8 -43.12,10.6 C-43.12,10.54 -43.12,0.16 -43.12,0.09 C-43.12,0.01 -43.12,-9.96 -43.12,-10.03 C-42.99,-13.22 -40.37,-15.76 -37.16,-15.76c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.833,1 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="433" + android:propertyName="pathData" + android:startOffset="1767" + android:valueFrom="M-37.16 -15.76 C-33.94,-15.76 -31.32,-13.21 -31.2,-10.02 C-31.2,-9.95 -31.2,0.02 -31.2,0.09 C-31.2,0.16 -31.2,10.54 -31.2,10.61 C-31.31,13.8 -33.94,16.36 -37.16,16.36 C-40.39,16.36 -43.01,13.8 -43.12,10.6 C-43.12,10.54 -43.12,0.16 -43.12,0.09 C-43.12,0.01 -43.12,-9.96 -43.12,-10.03 C-42.99,-13.22 -40.37,-15.76 -37.16,-15.76c " + android:valueTo="M-37.16 -25.01 C-33.94,-25.01 -31.32,-22.46 -31.2,-19.27 C-31.2,-19.2 -31.2,0.02 -31.2,0.09 C-31.2,0.16 -31.2,19.54 -31.2,19.6 C-31.31,22.8 -33.94,25.36 -37.16,25.36 C-40.39,25.36 -43.01,22.8 -43.12,19.6 C-43.12,19.54 -43.12,0.16 -43.12,0.09 C-43.12,0.01 -43.12,-19.21 -43.12,-19.28 C-42.99,-22.47 -40.37,-25.01 -37.16,-25.01c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.667,1 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="350" + android:propertyName="pathData" + android:startOffset="2200" + android:valueFrom="M-37.16 -25.01 C-33.94,-25.01 -31.32,-22.46 -31.2,-19.27 C-31.2,-19.2 -31.2,0.02 -31.2,0.09 C-31.2,0.16 -31.2,19.54 -31.2,19.6 C-31.31,22.8 -33.94,25.36 -37.16,25.36 C-40.39,25.36 -43.01,22.8 -43.12,19.6 C-43.12,19.54 -43.12,0.16 -43.12,0.09 C-43.12,0.01 -43.12,-19.21 -43.12,-19.28 C-42.99,-22.47 -40.37,-25.01 -37.16,-25.01c " + android:valueTo="M-37.16 -5.87 C-33.94,-5.87 -31.32,-3.32 -31.2,-0.13 C-31.2,-0.06 -31.2,0.02 -31.2,0.09 C-31.2,0.16 -31.2,0.23 -31.2,0.29 C-31.31,3.49 -33.94,6.05 -37.16,6.05 C-40.39,6.05 -43.01,3.49 -43.12,0.29 C-43.12,0.23 -43.12,0.16 -43.12,0.09 C-43.12,0.01 -43.12,-0.07 -43.12,-0.15 C-42.99,-3.33 -40.37,-5.87 -37.16,-5.87c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.667,1 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + </set> + </aapt:attr> + </target> + <target android:name="_R_G_L_1_G_D_0_P_0"> + <aapt:attr name="android:animation"> + <set android:ordering="together"> + <objectAnimator + android:duration="500" + android:propertyName="pathData" + android:startOffset="0" + android:valueFrom="M-37.16 -5.87 C-33.94,-5.87 -31.32,-3.32 -31.2,-0.13 C-31.2,-0.06 -31.2,0.02 -31.2,0.09 C-31.2,0.16 -31.2,0.23 -31.2,0.29 C-31.31,3.49 -33.94,6.05 -37.16,6.05 C-40.39,6.05 -43.01,3.49 -43.12,0.29 C-43.12,0.23 -43.12,0.16 -43.12,0.09 C-43.12,0.01 -43.12,-0.07 -43.12,-0.15 C-42.99,-3.33 -40.37,-5.87 -37.16,-5.87c " + android:valueTo="M-37.16 -38.14 C-33.94,-38.14 -31.32,-35.58 -31.2,-32.39 C-31.2,-32.32 -31.2,0.02 -31.2,0.09 C-31.2,0.16 -31.2,32.04 -31.2,32.1 C-31.31,35.3 -33.94,37.86 -37.16,37.86 C-40.39,37.86 -43.01,35.3 -43.12,32.1 C-43.12,32.04 -43.12,0.16 -43.12,0.09 C-43.12,0.01 -43.12,-32.33 -43.12,-32.41 C-42.99,-35.59 -40.37,-38.14 -37.16,-38.14c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.667,1 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="467" + android:propertyName="pathData" + android:startOffset="500" + android:valueFrom="M-37.16 -38.14 C-33.94,-38.14 -31.32,-35.58 -31.2,-32.39 C-31.2,-32.32 -31.2,0.02 -31.2,0.09 C-31.2,0.16 -31.2,32.04 -31.2,32.1 C-31.31,35.3 -33.94,37.86 -37.16,37.86 C-40.39,37.86 -43.01,35.3 -43.12,32.1 C-43.12,32.04 -43.12,0.16 -43.12,0.09 C-43.12,0.01 -43.12,-32.33 -43.12,-32.41 C-42.99,-35.59 -40.37,-38.14 -37.16,-38.14c " + android:valueTo="M-37.16 -19.01 C-33.94,-19.01 -31.32,-16.46 -31.2,-13.27 C-31.2,-13.2 -31.2,0.02 -31.2,0.09 C-31.2,0.16 -31.2,13.66 -31.2,13.73 C-31.31,16.93 -33.94,19.49 -37.16,19.49 C-40.39,19.49 -43.01,16.93 -43.12,13.73 C-43.12,13.66 -43.12,0.16 -43.12,0.09 C-43.12,0.01 -43.12,-13.21 -43.12,-13.28 C-42.99,-16.47 -40.37,-19.01 -37.16,-19.01c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.833,1 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="383" + android:propertyName="pathData" + android:startOffset="967" + android:valueFrom="M-37.16 -19.01 C-33.94,-19.01 -31.32,-16.46 -31.2,-13.27 C-31.2,-13.2 -31.2,0.02 -31.2,0.09 C-31.2,0.16 -31.2,13.66 -31.2,13.73 C-31.31,16.93 -33.94,19.49 -37.16,19.49 C-40.39,19.49 -43.01,16.93 -43.12,13.73 C-43.12,13.66 -43.12,0.16 -43.12,0.09 C-43.12,0.01 -43.12,-13.21 -43.12,-13.28 C-42.99,-16.47 -40.37,-19.01 -37.16,-19.01c " + android:valueTo="M-37.16 -38.14 C-33.94,-38.14 -31.32,-35.58 -31.2,-32.39 C-31.2,-32.32 -31.2,0.02 -31.2,0.09 C-31.2,0.16 -31.2,32.04 -31.2,32.1 C-31.31,35.3 -33.94,37.86 -37.16,37.86 C-40.39,37.86 -43.01,35.3 -43.12,32.1 C-43.12,32.04 -43.12,0.16 -43.12,0.09 C-43.12,0.01 -43.12,-32.33 -43.12,-32.41 C-42.99,-35.59 -40.37,-38.14 -37.16,-38.14c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.667,1 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="467" + android:propertyName="pathData" + android:startOffset="1350" + android:valueFrom="M-37.16 -38.14 C-33.94,-38.14 -31.32,-35.58 -31.2,-32.39 C-31.2,-32.32 -31.2,0.02 -31.2,0.09 C-31.2,0.16 -31.2,32.04 -31.2,32.1 C-31.31,35.3 -33.94,37.86 -37.16,37.86 C-40.39,37.86 -43.01,35.3 -43.12,32.1 C-43.12,32.04 -43.12,0.16 -43.12,0.09 C-43.12,0.01 -43.12,-32.33 -43.12,-32.41 C-42.99,-35.59 -40.37,-38.14 -37.16,-38.14c " + android:valueTo="M-37.16 -19.01 C-33.94,-19.01 -31.32,-16.46 -31.2,-13.27 C-31.2,-13.2 -31.2,0.02 -31.2,0.09 C-31.2,0.16 -31.2,13.66 -31.2,13.73 C-31.31,16.93 -33.94,19.49 -37.16,19.49 C-40.39,19.49 -43.01,16.93 -43.12,13.73 C-43.12,13.66 -43.12,0.16 -43.12,0.09 C-43.12,0.01 -43.12,-13.21 -43.12,-13.28 C-42.99,-16.47 -40.37,-19.01 -37.16,-19.01c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.833,1 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="433" + android:propertyName="pathData" + android:startOffset="1817" + android:valueFrom="M-37.16 -19.01 C-33.94,-19.01 -31.32,-16.46 -31.2,-13.27 C-31.2,-13.2 -31.2,0.02 -31.2,0.09 C-31.2,0.16 -31.2,13.66 -31.2,13.73 C-31.31,16.93 -33.94,19.49 -37.16,19.49 C-40.39,19.49 -43.01,16.93 -43.12,13.73 C-43.12,13.66 -43.12,0.16 -43.12,0.09 C-43.12,0.01 -43.12,-13.21 -43.12,-13.28 C-42.99,-16.47 -40.37,-19.01 -37.16,-19.01c " + android:valueTo="M-37.16 -38.14 C-33.94,-38.14 -31.32,-35.58 -31.2,-32.39 C-31.2,-32.32 -31.2,0.02 -31.2,0.09 C-31.2,0.16 -31.2,32.04 -31.2,32.1 C-31.31,35.3 -33.94,37.86 -37.16,37.86 C-40.39,37.86 -43.01,35.3 -43.12,32.1 C-43.12,32.04 -43.12,0.16 -43.12,0.09 C-43.12,0.01 -43.12,-32.33 -43.12,-32.41 C-42.99,-35.59 -40.37,-38.14 -37.16,-38.14c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.667,1 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="300" + android:propertyName="pathData" + android:startOffset="2250" + android:valueFrom="M-37.16 -38.14 C-33.94,-38.14 -31.32,-35.58 -31.2,-32.39 C-31.2,-32.32 -31.2,0.02 -31.2,0.09 C-31.2,0.16 -31.2,32.04 -31.2,32.1 C-31.31,35.3 -33.94,37.86 -37.16,37.86 C-40.39,37.86 -43.01,35.3 -43.12,32.1 C-43.12,32.04 -43.12,0.16 -43.12,0.09 C-43.12,0.01 -43.12,-32.33 -43.12,-32.41 C-42.99,-35.59 -40.37,-38.14 -37.16,-38.14c " + android:valueTo="M-37.16 -5.87 C-33.94,-5.87 -31.32,-3.32 -31.2,-0.13 C-31.2,-0.06 -31.2,0.02 -31.2,0.09 C-31.2,0.16 -31.2,0.23 -31.2,0.29 C-31.31,3.49 -33.94,6.05 -37.16,6.05 C-40.39,6.05 -43.01,3.49 -43.12,0.29 C-43.12,0.23 -43.12,0.16 -43.12,0.09 C-43.12,0.01 -43.12,-0.07 -43.12,-0.15 C-42.99,-3.33 -40.37,-5.87 -37.16,-5.87c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.667,1 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + </set> + </aapt:attr> + </target> + <target android:name="_R_G_L_0_G_D_0_P_0"> + <aapt:attr name="android:animation"> + <set android:ordering="together"> + <objectAnimator + android:duration="533" + android:propertyName="pathData" + android:startOffset="0" + android:valueFrom="M-37.16 -5.87 C-33.94,-5.87 -31.32,-3.32 -31.2,-0.13 C-31.2,-0.06 -31.2,0.02 -31.2,0.09 C-31.2,0.16 -31.2,0.23 -31.2,0.29 C-31.31,3.49 -33.94,6.05 -37.16,6.05 C-40.39,6.05 -43.01,3.49 -43.12,0.29 C-43.12,0.23 -43.12,0.16 -43.12,0.09 C-43.12,0.01 -43.12,-0.07 -43.12,-0.15 C-42.99,-3.33 -40.37,-5.87 -37.16,-5.87c " + android:valueTo="M-37.16 -22.64 C-33.94,-22.64 -31.32,-20.08 -31.2,-16.89 C-31.2,-16.82 -31.2,0.02 -31.2,0.09 C-31.2,0.16 -31.2,17.41 -31.2,17.48 C-31.31,20.68 -33.94,23.24 -37.16,23.24 C-40.39,23.24 -43.01,20.68 -43.12,17.48 C-43.12,17.41 -43.12,0.16 -43.12,0.09 C-43.12,0.01 -43.12,-16.83 -43.12,-16.91 C-42.99,-20.09 -40.37,-22.64 -37.16,-22.64c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.667,1 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="433" + android:propertyName="pathData" + android:startOffset="533" + android:valueFrom="M-37.16 -22.64 C-33.94,-22.64 -31.32,-20.08 -31.2,-16.89 C-31.2,-16.82 -31.2,0.02 -31.2,0.09 C-31.2,0.16 -31.2,17.41 -31.2,17.48 C-31.31,20.68 -33.94,23.24 -37.16,23.24 C-40.39,23.24 -43.01,20.68 -43.12,17.48 C-43.12,17.41 -43.12,0.16 -43.12,0.09 C-43.12,0.01 -43.12,-16.83 -43.12,-16.91 C-42.99,-20.09 -40.37,-22.64 -37.16,-22.64c " + android:valueTo="M-37.18 -14.01 C-33.96,-14.01 -31.33,-11.46 -31.22,-8.27 C-31.22,-8.2 -31.2,0.02 -31.2,0.09 C-31.2,0.16 -31.2,8.54 -31.2,8.61 C-31.31,11.8 -33.94,14.36 -37.16,14.36 C-40.39,14.36 -43.01,11.8 -43.12,8.6 C-43.12,8.54 -43.12,0.16 -43.12,0.09 C-43.12,0.01 -43.14,-8.21 -43.13,-8.28 C-43.01,-11.47 -40.39,-14.01 -37.18,-14.01c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.833,1 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="417" + android:propertyName="pathData" + android:startOffset="967" + android:valueFrom="M-37.18 -14.01 C-33.96,-14.01 -31.33,-11.46 -31.22,-8.27 C-31.22,-8.2 -31.2,0.02 -31.2,0.09 C-31.2,0.16 -31.2,8.54 -31.2,8.61 C-31.31,11.8 -33.94,14.36 -37.16,14.36 C-40.39,14.36 -43.01,11.8 -43.12,8.6 C-43.12,8.54 -43.12,0.16 -43.12,0.09 C-43.12,0.01 -43.14,-8.21 -43.13,-8.28 C-43.01,-11.47 -40.39,-14.01 -37.18,-14.01c " + android:valueTo="M-37.16 -22.64 C-33.94,-22.64 -31.32,-20.08 -31.2,-16.89 C-31.2,-16.82 -31.2,0.02 -31.2,0.09 C-31.2,0.16 -31.2,17.41 -31.2,17.48 C-31.31,20.68 -33.94,23.24 -37.16,23.24 C-40.39,23.24 -43.01,20.68 -43.12,17.48 C-43.12,17.41 -43.12,0.16 -43.12,0.09 C-43.12,0.01 -43.12,-16.83 -43.12,-16.91 C-42.99,-20.09 -40.37,-22.64 -37.16,-22.64c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.667,1 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="483" + android:propertyName="pathData" + android:startOffset="1383" + android:valueFrom="M-37.16 -22.64 C-33.94,-22.64 -31.32,-20.08 -31.2,-16.89 C-31.2,-16.82 -31.2,0.02 -31.2,0.09 C-31.2,0.16 -31.2,17.41 -31.2,17.48 C-31.31,20.68 -33.94,23.24 -37.16,23.24 C-40.39,23.24 -43.01,20.68 -43.12,17.48 C-43.12,17.41 -43.12,0.16 -43.12,0.09 C-43.12,0.01 -43.12,-16.83 -43.12,-16.91 C-42.99,-20.09 -40.37,-22.64 -37.16,-22.64c " + android:valueTo="M-37.18 -14.01 C-33.96,-14.01 -31.33,-11.46 -31.22,-8.27 C-31.22,-8.2 -31.2,0.02 -31.2,0.09 C-31.2,0.16 -31.2,8.54 -31.2,8.61 C-31.31,11.8 -33.94,14.36 -37.16,14.36 C-40.39,14.36 -43.01,11.8 -43.12,8.6 C-43.12,8.54 -43.12,0.16 -43.12,0.09 C-43.12,0.01 -43.14,-8.21 -43.13,-8.28 C-43.01,-11.47 -40.39,-14.01 -37.18,-14.01c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.833,1 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="417" + android:propertyName="pathData" + android:startOffset="1867" + android:valueFrom="M-37.18 -14.01 C-33.96,-14.01 -31.33,-11.46 -31.22,-8.27 C-31.22,-8.2 -31.2,0.02 -31.2,0.09 C-31.2,0.16 -31.2,8.54 -31.2,8.61 C-31.31,11.8 -33.94,14.36 -37.16,14.36 C-40.39,14.36 -43.01,11.8 -43.12,8.6 C-43.12,8.54 -43.12,0.16 -43.12,0.09 C-43.12,0.01 -43.14,-8.21 -43.13,-8.28 C-43.01,-11.47 -40.39,-14.01 -37.18,-14.01c " + android:valueTo="M-37.16 -22.64 C-33.94,-22.64 -31.32,-20.08 -31.2,-16.89 C-31.2,-16.82 -31.2,0.02 -31.2,0.09 C-31.2,0.16 -31.2,17.41 -31.2,17.48 C-31.31,20.68 -33.94,23.24 -37.16,23.24 C-40.39,23.24 -43.01,20.68 -43.12,17.48 C-43.12,17.41 -43.12,0.16 -43.12,0.09 C-43.12,0.01 -43.12,-16.83 -43.12,-16.91 C-42.99,-20.09 -40.37,-22.64 -37.16,-22.64c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.667,1 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="267" + android:propertyName="pathData" + android:startOffset="2283" + android:valueFrom="M-37.16 -22.64 C-33.94,-22.64 -31.32,-20.08 -31.2,-16.89 C-31.2,-16.82 -31.2,0.02 -31.2,0.09 C-31.2,0.16 -31.2,17.41 -31.2,17.48 C-31.31,20.68 -33.94,23.24 -37.16,23.24 C-40.39,23.24 -43.01,20.68 -43.12,17.48 C-43.12,17.41 -43.12,0.16 -43.12,0.09 C-43.12,0.01 -43.12,-16.83 -43.12,-16.91 C-42.99,-20.09 -40.37,-22.64 -37.16,-22.64c " + android:valueTo="M-37.16 -5.87 C-33.94,-5.87 -31.32,-3.32 -31.2,-0.13 C-31.2,-0.06 -31.2,0.02 -31.2,0.09 C-31.2,0.16 -31.2,0.23 -31.2,0.29 C-31.31,3.49 -33.94,6.05 -37.16,6.05 C-40.39,6.05 -43.01,3.49 -43.12,0.29 C-43.12,0.23 -43.12,0.16 -43.12,0.09 C-43.12,0.01 -43.12,-0.07 -43.12,-0.15 C-42.99,-3.33 -40.37,-5.87 -37.16,-5.87c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.667,1 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + </set> + </aapt:attr> + </target> + <target android:name="time_group"> + <aapt:attr name="android:animation"> + <set android:ordering="together"> + <objectAnimator + android:duration="2567" + android:propertyName="translateX" + android:startOffset="0" + android:valueFrom="0" + android:valueTo="1" + android:valueType="floatType" /> + </set> + </aapt:attr> + </target> + <aapt:attr name="android:drawable"> + <vector + android:width="168dp" + android:height="168dp" + android:viewportHeight="168" + android:viewportWidth="168"> + <group android:name="_R_G"> + <group + android:name="_R_G_L_4_G" + android:translateX="84.411" + android:translateY="83.911"> + <path + android:name="_R_G_L_4_G_D_0_P_0" + android:fillAlpha="1" + android:fillColor="#ffffff" + android:fillType="nonZero" + android:pathData=" M-37.16 -5.87 C-33.94,-5.87 -31.32,-3.32 -31.2,-0.13 C-31.2,-0.06 -31.2,0.02 -31.2,0.09 C-31.2,0.16 -31.2,0.23 -31.2,0.29 C-31.31,3.49 -33.94,6.05 -37.16,6.05 C-40.39,6.05 -43.01,3.49 -43.12,0.29 C-43.12,0.23 -43.12,0.16 -43.12,0.09 C-43.12,0.01 -43.12,-0.07 -43.12,-0.15 C-42.99,-3.33 -40.37,-5.87 -37.16,-5.87c " /> + </group> + <group + android:name="_R_G_L_3_G" + android:translateX="121.161" + android:translateY="83.911"> + <path + android:name="_R_G_L_3_G_D_0_P_0" + android:fillAlpha="1" + android:fillColor="#ffffff" + android:fillType="nonZero" + android:pathData=" M-37.16 -5.87 C-33.94,-5.87 -31.32,-3.32 -31.2,-0.13 C-31.2,-0.06 -31.2,0.02 -31.2,0.09 C-31.2,0.16 -31.2,0.23 -31.2,0.29 C-31.31,3.49 -33.94,6.05 -37.16,6.05 C-40.39,6.05 -43.01,3.49 -43.12,0.29 C-43.12,0.23 -43.12,0.16 -43.12,0.09 C-43.12,0.01 -43.12,-0.07 -43.12,-0.15 C-42.99,-3.33 -40.37,-5.87 -37.16,-5.87c " /> + </group> + <group + android:name="_R_G_L_2_G" + android:translateX="102.911" + android:translateY="83.911"> + <path + android:name="_R_G_L_2_G_D_0_P_0" + android:fillAlpha="1" + android:fillColor="#ffffff" + android:fillType="nonZero" + android:pathData=" M-37.16 -5.87 C-33.94,-5.87 -31.32,-3.32 -31.2,-0.13 C-31.2,-0.06 -31.2,0.02 -31.2,0.09 C-31.2,0.16 -31.2,0.23 -31.2,0.29 C-31.31,3.49 -33.94,6.05 -37.16,6.05 C-40.39,6.05 -43.01,3.49 -43.12,0.29 C-43.12,0.23 -43.12,0.16 -43.12,0.09 C-43.12,0.01 -43.12,-0.07 -43.12,-0.15 C-42.99,-3.33 -40.37,-5.87 -37.16,-5.87c " /> + </group> + <group + android:name="_R_G_L_1_G" + android:translateX="139.661" + android:translateY="83.911"> + <path + android:name="_R_G_L_1_G_D_0_P_0" + android:fillAlpha="1" + android:fillColor="#ffffff" + android:fillType="nonZero" + android:pathData=" M-37.16 -5.87 C-33.94,-5.87 -31.32,-3.32 -31.2,-0.13 C-31.2,-0.06 -31.2,0.02 -31.2,0.09 C-31.2,0.16 -31.2,0.23 -31.2,0.29 C-31.31,3.49 -33.94,6.05 -37.16,6.05 C-40.39,6.05 -43.01,3.49 -43.12,0.29 C-43.12,0.23 -43.12,0.16 -43.12,0.09 C-43.12,0.01 -43.12,-0.07 -43.12,-0.15 C-42.99,-3.33 -40.37,-5.87 -37.16,-5.87c " /> + </group> + <group + android:name="_R_G_L_0_G" + android:translateX="157.911" + android:translateY="83.911"> + <path + android:name="_R_G_L_0_G_D_0_P_0" + android:fillAlpha="1" + android:fillColor="#ffffff" + android:fillType="nonZero" + android:pathData=" M-37.16 -5.87 C-33.94,-5.87 -31.32,-3.32 -31.2,-0.13 C-31.2,-0.06 -31.2,0.02 -31.2,0.09 C-31.2,0.16 -31.2,0.23 -31.2,0.29 C-31.31,3.49 -33.94,6.05 -37.16,6.05 C-40.39,6.05 -43.01,3.49 -43.12,0.29 C-43.12,0.23 -43.12,0.16 -43.12,0.09 C-43.12,0.01 -43.12,-0.07 -43.12,-0.15 C-42.99,-3.33 -40.37,-5.87 -37.16,-5.87c " /> + </group> + </group> + <group android:name="time_group" /> + </vector> + </aapt:attr> +</animated-vector>
\ No newline at end of file diff --git a/packages/SystemUI/res/drawable/brightness_mirror_background.xml b/packages/SystemUI/res/drawable/brightness_mirror_background.xml index b5c181bd896c..c4225f11f293 100644 --- a/packages/SystemUI/res/drawable/brightness_mirror_background.xml +++ b/packages/SystemUI/res/drawable/brightness_mirror_background.xml @@ -14,7 +14,15 @@ ~ See the License for the specific language governing permissions and ~ limitations under the License --> -<shape xmlns:android="http://schemas.android.com/apk/res/android"> - <solid android:color="?attr/underSurface" /> - <corners android:radius="@dimen/rounded_slider_background_rounded_corner" /> -</shape> +<layer-list xmlns:android="http://schemas.android.com/apk/res/android"> + <item + android:left="@dimen/rounded_slider_boundary_offset" + android:right="@dimen/rounded_slider_boundary_offset" + android:top="@dimen/rounded_slider_boundary_offset" + android:bottom="@dimen/rounded_slider_boundary_offset"> + <shape> + <solid android:color="?attr/underSurface" /> + <corners android:radius="@dimen/rounded_slider_background_rounded_corner" /> + </shape> + </item> +</layer-list> diff --git a/packages/SystemUI/res/drawable/immersive_cling_bg_circ.xml b/packages/SystemUI/res/drawable/brightness_slider_focus_bg.xml index 32e88ab22b91..22406ec52d00 100644 --- a/packages/SystemUI/res/drawable/immersive_cling_bg_circ.xml +++ b/packages/SystemUI/res/drawable/brightness_slider_focus_bg.xml @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="utf-8"?> <!-- - ~ Copyright (C) 2023 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. @@ -12,15 +12,18 @@ ~ distributed under the License is distributed on an "AS IS" BASIS, ~ WITHOUT 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. --> -<shape xmlns:android="http://schemas.android.com/apk/res/android" - android:shape="oval" > - <solid android:color="?android:attr/colorBackground" /> - - <size - android:height="56dp" - android:width="56dp" /> - -</shape> +<selector + xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"> + <item android:state_focused="true"> + <inset android:inset="-5dp"> + <shape> + <corners android:radius="16dp"/> + <stroke android:width="3dp" android:color="?androidprv:attr/materialColorSecondaryFixed"/> + </shape> + </inset> + </item> +</selector>
\ No newline at end of file diff --git a/libs/WindowManager/Shell/res/drawable/desktop_mode_header_background.xml b/packages/SystemUI/res/drawable/ic_bt_le_audio_sharing_18dp.xml index 50c5ca936245..dd3d9e3a8a9a 100644 --- a/libs/WindowManager/Shell/res/drawable/desktop_mode_header_background.xml +++ b/packages/SystemUI/res/drawable/ic_bt_le_audio_sharing_18dp.xml @@ -1,4 +1,5 @@ -<?xml version="1.0" encoding="utf-8"?><!-- +<?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"); @@ -13,16 +14,9 @@ ~ See the License for the specific language governing permissions and ~ limitations under the License. --> -<layer-list xmlns:android="http://schemas.android.com/apk/res/android"> - <item android:id="@+id/backLayer"> - <shape android:shape="rectangle"> - <solid android:color="#000000" /> - </shape> - </item> - - <item android:id="@+id/frontLayer"> - <shape android:shape="rectangle"> - <solid android:color="#000000" /> - </shape> - </item> +<layer-list xmlns:android="http://schemas.android.com/apk/res/android" > + <item + android:drawable="@drawable/ic_bt_le_audio_sharing" + android:width="18dp" + android:height="18dp" /> </layer-list>
\ No newline at end of file diff --git a/packages/SystemUI/res/drawable/ic_swipe_down.xml b/packages/SystemUI/res/drawable/ic_swipe_down.xml new file mode 100644 index 000000000000..15712d6fa4e9 --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_swipe_down.xml @@ -0,0 +1,26 @@ +<?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="24dp" + android:height="24dp" + android:viewportWidth="960" + android:viewportHeight="960" + android:tint="?attr/colorControlNormal"> + <path + android:fillColor="@android:color/white" + android:pathData="M180,600L40,460L82,418L152,488Q146,461 143,434Q140,407 140,380Q140,298 167,221Q194,144 245,80L288,123Q245,179 222.5,244.5Q200,310 200,380Q200,406 203,431.5Q206,457 213,482L278,418L320,460L180,600ZM658,833Q635,841 611.5,840.5Q588,840 566,829L304,707L322,667Q332,647 350,634.5Q368,622 390,620L458,615L346,308Q340,292 347,277.5Q354,263 370,257Q386,251 400.5,258Q415,265 421,281L569,688L469,695L600,756Q607,759 615,759.5Q623,760 630,758L787,701Q818,690 832,659.5Q846,629 835,598L780,448Q774,432 781,417.5Q788,403 804,397Q820,391 834.5,398Q849,405 855,421L910,571Q933,634 905.5,693.5Q878,753 815,776L658,833ZM568,568L514,417Q508,401 515,386.5Q522,372 538,366Q554,360 568.5,367Q583,374 589,390L644,540L568,568ZM681,527L640,414Q634,398 641,383.5Q648,369 664,363Q680,357 694.5,364Q709,371 715,387L756,499L681,527ZM689,605L689,605L689,605Q689,605 689,605Q689,605 689,605L689,605Q689,605 689,605Q689,605 689,605L689,605L689,605Z"/> +</vector>
\ No newline at end of file diff --git a/packages/SystemUI/res/drawable/immersive_cling_light_bg_circ.xml b/packages/SystemUI/res/drawable/immersive_cling_bg.xml index 12c3e23bf0a0..de29c32390e1 100644 --- a/packages/SystemUI/res/drawable/immersive_cling_light_bg_circ.xml +++ b/packages/SystemUI/res/drawable/immersive_cling_bg.xml @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="utf-8"?> <!-- - ~ Copyright (C) 2023 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. @@ -15,12 +15,10 @@ ~ limitations under the License --> <shape xmlns:android="http://schemas.android.com/apk/res/android" - android:shape="oval" > - - <solid android:color="?android:attr/colorBackground" /> - - <size - android:height="76dp" - android:width="76dp" /> - + xmlns:androidprv="http://schemas.android.com/apk/prv/res/android" + android:shape="rectangle"> + <corners + android:bottomLeftRadius="28dp" + android:bottomRightRadius="28dp"/> + <solid android:color="?androidprv:attr/materialColorSurfaceContainer" /> </shape> diff --git a/packages/SystemUI/res/drawable/immersive_cling_btn_bg.xml b/packages/SystemUI/res/drawable/immersive_cling_btn_bg.xml new file mode 100644 index 000000000000..df49e38b4f1e --- /dev/null +++ b/packages/SystemUI/res/drawable/immersive_cling_btn_bg.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. + --> +<inset xmlns:android="http://schemas.android.com/apk/res/android"> + <ripple android:color="?android:attr/colorControlHighlight"> + <item android:id="@android:id/mask"> + <shape android:shape="rectangle"> + <solid android:color="@android:color/white" /> + <corners android:radius="28dp" /> + </shape> + </item> + <item> + <shape android:shape="rectangle"> + <corners android:radius="28dp" /> + <solid android:color="?android:attr/colorAccent" /> + </shape> + </item> + </ripple> +</inset>
\ No newline at end of file diff --git a/packages/SystemUI/res/layout-land/volume_dialog.xml b/packages/SystemUI/res/layout-land/volume_dialog.xml index 5ce2601d407d..08edf59000b8 100644 --- a/packages/SystemUI/res/layout-land/volume_dialog.xml +++ b/packages/SystemUI/res/layout-land/volume_dialog.xml @@ -13,9 +13,7 @@ ~ See the License for the specific language governing permissions and ~ limitations under the License --> -<FrameLayout - xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:sysui="http://schemas.android.com/apk/res-auto" +<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" @@ -97,16 +95,18 @@ android:paddingLeft="@dimen/volume_dialog_ringer_rows_padding" android:paddingBottom="@dimen/volume_dialog_ringer_rows_padding" android:paddingRight="@dimen/volume_dialog_ringer_rows_padding"> + <com.android.keyguard.AlphaOptimizedImageButton android:id="@+id/settings" - android:src="@drawable/horizontal_ellipsis" android:layout_width="@dimen/volume_dialog_tap_target_size" android:layout_height="@dimen/volume_dialog_tap_target_size" android:layout_gravity="center" - android:contentDescription="@string/accessibility_volume_settings" android:background="@drawable/ripple_drawable_20dp" - android:tint="?androidprv:attr/colorAccent" - android:soundEffectsEnabled="false" /> + android:contentDescription="@string/accessibility_volume_settings" + android:scaleType="centerInside" + android:soundEffectsEnabled="false" + android:src="@drawable/horizontal_ellipsis" + android:tint="?androidprv:attr/colorAccent" /> </FrameLayout> </LinearLayout> diff --git a/packages/SystemUI/res/layout/dream_overlay_status_bar_view.xml b/packages/SystemUI/res/layout/ambient_status_bar_view.xml index ec2edb52a039..7d765ce7ac6f 100644 --- a/packages/SystemUI/res/layout/dream_overlay_status_bar_view.xml +++ b/packages/SystemUI/res/layout/ambient_status_bar_view.xml @@ -14,7 +14,7 @@ ~ See the License for the specific language governing permissions and ~ limitations under the License. --> -<com.android.systemui.dreams.DreamOverlayStatusBarView +<com.android.systemui.ambient.statusbar.ui.AmbientStatusBarView xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:id="@+id/dream_overlay_status_bar" @@ -118,4 +118,4 @@ android:contentDescription="@string/assistant_attention_content_description" /> </LinearLayout> -</com.android.systemui.dreams.DreamOverlayStatusBarView> +</com.android.systemui.ambient.statusbar.ui.AmbientStatusBarView> diff --git a/packages/SystemUI/res/layout/auth_container_view.xml b/packages/SystemUI/res/layout/auth_container_view.xml index 2a1ce1fc5ec6..cc5a27d60ec1 100644 --- a/packages/SystemUI/res/layout/auth_container_view.xml +++ b/packages/SystemUI/res/layout/auth_container_view.xml @@ -28,9 +28,9 @@ <View android:id="@+id/panel" + style="@style/AuthNonCredentialPanelStyle" android:layout_width="match_parent" android:layout_height="match_parent" - android:background="?androidprv:attr/materialColorSurfaceContainer" android:elevation="@dimen/biometric_dialog_elevation"/> <ScrollView diff --git a/packages/SystemUI/res/layout/biometric_prompt_button_bar.xml b/packages/SystemUI/res/layout/biometric_prompt_button_bar.xml index 9f4ad0ec8677..ce205ca56549 100644 --- a/packages/SystemUI/res/layout/biometric_prompt_button_bar.xml +++ b/packages/SystemUI/res/layout/biometric_prompt_button_bar.xml @@ -29,10 +29,11 @@ android:layout_gravity="center_vertical" android:layout_marginStart="24dp" android:layout_marginBottom="8dp" - android:ellipsize="end" - android:maxLines="2" android:visibility="invisible" + app:layout_constrainedWidth="true" app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintEnd_toEndOf="@id/button_center_guideline" + app:layout_constraintHorizontal_bias="0.0" app:layout_constraintStart_toStartOf="parent" /> <!-- Cancel Button, replaces negative button when biometric is accepted --> @@ -46,7 +47,10 @@ android:layout_marginBottom="8dp" android:text="@string/cancel" android:visibility="invisible" + app:layout_constrainedWidth="true" app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintEnd_toEndOf="@id/button_center_guideline" + app:layout_constraintHorizontal_bias="0.0" app:layout_constraintStart_toStartOf="parent" /> <!-- "Use Credential" Button, replaces if device credential is allowed --> @@ -59,7 +63,10 @@ android:layout_marginStart="24dp" android:layout_marginBottom="8dp" android:visibility="invisible" + app:layout_constrainedWidth="true" app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintEnd_toEndOf="@id/button_center_guideline" + app:layout_constraintHorizontal_bias="0.0" app:layout_constraintStart_toStartOf="parent" /> <!-- Positive Button --> @@ -71,12 +78,13 @@ android:layout_gravity="center_vertical" android:layout_marginEnd="24dp" android:layout_marginBottom="8dp" - android:ellipsize="end" - android:maxLines="2" android:text="@string/biometric_dialog_confirm" android:visibility="invisible" + app:layout_constrainedWidth="true" app:layout_constraintBottom_toBottomOf="parent" - app:layout_constraintEnd_toEndOf="parent" /> + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintHorizontal_bias="1.0" + app:layout_constraintStart_toStartOf="@id/button_center_guideline" /> <!-- Try Again Button --> <Button @@ -87,11 +95,19 @@ android:layout_gravity="center_vertical" android:layout_marginEnd="24dp" android:layout_marginBottom="8dp" - android:ellipsize="end" - android:maxLines="2" android:text="@string/biometric_dialog_try_again" android:visibility="invisible" + app:layout_constrainedWidth="true" app:layout_constraintBottom_toBottomOf="parent" - app:layout_constraintEnd_toEndOf="parent" /> + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintHorizontal_bias="1.0" + app:layout_constraintStart_toStartOf="@id/button_center_guideline" /> + + <androidx.constraintlayout.widget.Guideline + android:id="@+id/button_center_guideline" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:orientation="vertical" + app:layout_constraintGuide_percent="0.5" /> </androidx.constraintlayout.widget.ConstraintLayout> diff --git a/packages/SystemUI/res/layout/biometric_prompt_one_pane_layout.xml b/packages/SystemUI/res/layout/biometric_prompt_one_pane_layout.xml index 8d50bfa00fd5..4670f34f11a7 100644 --- a/packages/SystemUI/res/layout/biometric_prompt_one_pane_layout.xml +++ b/packages/SystemUI/res/layout/biometric_prompt_one_pane_layout.xml @@ -27,7 +27,6 @@ app:layout_constraintTop_toTopOf="@id/topBarrier" app:layout_constraintWidth_max="@dimen/biometric_prompt_panel_max_width" /> - <include android:id="@+id/button_bar" layout="@layout/biometric_prompt_button_bar" @@ -148,9 +147,10 @@ <TextView android:id="@+id/indicator" style="@style/TextAppearance.AuthCredential.Indicator" - android:layout_width="wrap_content" + android:layout_width="0dp" android:layout_height="wrap_content" android:layout_marginTop="24dp" + android:layout_marginHorizontal="24dp" android:accessibilityLiveRegion="assertive" android:fadingEdge="horizontal" android:gravity="center_horizontal" diff --git a/packages/SystemUI/res/layout/biometric_prompt_two_pane_layout.xml b/packages/SystemUI/res/layout/biometric_prompt_two_pane_layout.xml index 01b9f7e2e38a..c599f9e05b38 100644 --- a/packages/SystemUI/res/layout/biometric_prompt_two_pane_layout.xml +++ b/packages/SystemUI/res/layout/biometric_prompt_two_pane_layout.xml @@ -179,11 +179,13 @@ android:layout_height="match_parent"> android:fadingEdge="horizontal" android:gravity="center_horizontal" android:scrollHorizontally="true" + android:maxLines="2" app:layout_constraintBottom_toTopOf="@+id/button_bar" app:layout_constraintEnd_toEndOf="@+id/biometric_icon" app:layout_constraintStart_toStartOf="@+id/biometric_icon" app:layout_constraintTop_toBottomOf="@+id/biometric_icon" - app:layout_constraintVertical_bias="0.0" /> + app:layout_constraintVertical_bias="0.0" + app:layout_constraintWidth_max="@dimen/biometric_dialog_indicator_max_width" /> <include android:id="@+id/button_bar" diff --git a/packages/SystemUI/res/layout/bluetooth_tile_dialog.xml b/packages/SystemUI/res/layout/bluetooth_tile_dialog.xml index a598007833d7..27b80066e0f4 100644 --- a/packages/SystemUI/res/layout/bluetooth_tile_dialog.xml +++ b/packages/SystemUI/res/layout/bluetooth_tile_dialog.xml @@ -268,6 +268,12 @@ android:ellipsize="end" android:maxLines="1" android:text="@string/quick_settings_bluetooth_audio_sharing_button" + android:drawableStart="@drawable/ic_bt_le_audio_sharing_18dp" + android:drawablePadding="10dp" + android:drawableTint="?android:attr/textColorPrimary" + app:layout_constrainedWidth="true" + app:layout_constraintHorizontal_bias="0" + app:layout_constraintEnd_toStartOf="@+id/done_button" app:layout_constraintStart_toStartOf="parent" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintTop_toBottomOf="@+id/barrier" diff --git a/packages/SystemUI/res/layout/dream_overlay_container.xml b/packages/SystemUI/res/layout/dream_overlay_container.xml index 4234fca55e3c..dcd3fa66ef04 100644 --- a/packages/SystemUI/res/layout/dream_overlay_container.xml +++ b/packages/SystemUI/res/layout/dream_overlay_container.xml @@ -44,5 +44,5 @@ app:layout_constraintBottom_toBottomOf="parent" /> - <include layout="@layout/dream_overlay_status_bar_view" /> + <include layout="@layout/ambient_status_bar_view" /> </com.android.systemui.dreams.DreamOverlayContainerView>
\ No newline at end of file diff --git a/packages/SystemUI/res/layout/hearing_devices_preset_dropdown_item.xml b/packages/SystemUI/res/layout/hearing_devices_preset_dropdown_item.xml index 1d9307ba20ed..17c0222ef69e 100644 --- a/packages/SystemUI/res/layout/hearing_devices_preset_dropdown_item.xml +++ b/packages/SystemUI/res/layout/hearing_devices_preset_dropdown_item.xml @@ -22,4 +22,5 @@ android:minHeight="@dimen/hearing_devices_preset_spinner_height" android:paddingStart="@dimen/hearing_devices_preset_spinner_text_padding_start" android:gravity="center_vertical" + android:textDirection="locale" android:ellipsize="end" />
\ No newline at end of file diff --git a/packages/SystemUI/res/layout/hearing_devices_preset_spinner_selected.xml b/packages/SystemUI/res/layout/hearing_devices_preset_spinner_selected.xml index 77172ca8f90e..d512e7c3a433 100644 --- a/packages/SystemUI/res/layout/hearing_devices_preset_spinner_selected.xml +++ b/packages/SystemUI/res/layout/hearing_devices_preset_spinner_selected.xml @@ -32,6 +32,7 @@ android:fontFamily="@*android:string/config_headlineFontFamilyMedium" android:textSize="14sp" android:gravity="center_vertical" + android:textDirection="locale" android:layout_weight="1" /> <TextView android:id="@+id/hearing_devices_preset_option_text" @@ -42,5 +43,6 @@ android:gravity="center_vertical" android:ellipsize="end" android:maxLines="1" + android:textDirection="locale" android:layout_weight="1" /> </LinearLayout>
\ No newline at end of file diff --git a/packages/SystemUI/res/layout/immersive_mode_cling.xml b/packages/SystemUI/res/layout/immersive_mode_cling.xml index e6529b9aa9a1..20b7cd3add4b 100644 --- a/packages/SystemUI/res/layout/immersive_mode_cling.xml +++ b/packages/SystemUI/res/layout/immersive_mode_cling.xml @@ -14,78 +14,67 @@ limitations under the License. --> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:gravity="center_vertical" - android:paddingBottom="24dp"> - - <FrameLayout - android:id="@+id/immersive_cling_chevron" - android:layout_width="76dp" - android:layout_height="76dp" - android:layout_marginTop="-24dp" - android:layout_centerHorizontal="true"> + xmlns:androidprv="http://schemas.android.com/apk/prv/res/android" + android:theme="@android:style/Theme.DeviceDefault.DayNight" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:background="@drawable/immersive_cling_bg" + android:gravity="center_vertical" + android:padding="24dp"> - <ImageView - android:id="@+id/immersive_cling_back_bg_light" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:scaleType="center" - android:src="@drawable/immersive_cling_light_bg_circ" /> - - <ImageView - android:id="@+id/immersive_cling_back_bg" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:scaleType="center" - android:src="@drawable/immersive_cling_bg_circ" /> - - <ImageView - android:id="@+id/immersive_cling_ic_expand_more" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:paddingTop="8dp" - android:scaleType="center" - android:src="@drawable/ic_expand_more_48dp"/> - </FrameLayout> + <!-- The top margin of this icon can be adjusted to push the content down to prevent overlapping + with the display cutout. --> + <ImageView + android:id="@+id/immersive_cling_icon" + android:layout_width="32dp" + android:layout_height="32dp" + android:layout_centerHorizontal="true" + android:scaleType="fitXY" + android:src="@drawable/ic_swipe_down" + android:tint="?android:attr/colorAccent" + android:tintMode="src_in" /> <TextView - android:id="@+id/immersive_cling_title" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_below="@id/immersive_cling_chevron" - android:paddingEnd="48dp" - android:paddingStart="48dp" - android:paddingTop="40dp" - android:text="@string/immersive_cling_title" - android:textColor="?android:attr/textColorPrimaryInverse" - android:textSize="24sp" /> + android:id="@+id/immersive_cling_title" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_below="@id/immersive_cling_icon" + android:layout_marginTop="20dp" + android:gravity="center_horizontal" + android:text="@string/immersive_cling_title" + android:textColor="?androidprv:attr/materialColorOnSurface" + android:textSize="24sp" + android:fontFamily="google-sans" /> <TextView - android:id="@+id/immersive_cling_description" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_below="@id/immersive_cling_title" - android:paddingEnd="48dp" - android:paddingStart="48dp" - android:paddingTop="12.6dp" - android:text="@string/immersive_cling_description" - android:textColor="?android:attr/textColorPrimaryInverse" - android:textSize="16sp" /> + android:id="@+id/immersive_cling_description" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_below="@id/immersive_cling_title" + android:paddingTop="14dp" + android:gravity="center_horizontal" + android:text="@string/immersive_cling_description" + android:textColor="?androidprv:attr/materialColorOnSurfaceVariant" + android:textSize="14sp" + android:fontFamily="google-sans" /> <Button - android:id="@+id/ok" - style="@android:style/Widget.Material.Button.Borderless" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_alignParentEnd="true" - android:layout_below="@+id/immersive_cling_description" - android:layout_marginEnd="40dp" - android:layout_marginTop="18dp" - android:paddingEnd="8dp" - android:paddingStart="8dp" - android:text="@string/immersive_cling_positive" - android:textColor="?android:attr/textColorPrimaryInverse" - android:textSize="14sp" /> - + android:id="@+id/ok" + style="@android:style/Widget.Material.Button.Borderless.Colored" + android:background="@drawable/immersive_cling_btn_bg" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_alignParentEnd="true" + android:layout_below="@+id/immersive_cling_description" + android:layout_marginTop="24dp" + android:paddingStart="18dp" + android:paddingEnd="18dp" + android:minWidth="48dp" + android:minHeight="48dp" + android:text="@string/immersive_cling_positive" + android:textColor="?androidprv:attr/materialColorOnPrimary" + android:textAllCaps="false" + android:textSize="14sp" + android:textFontWeight="500" + android:fontFamily="google-sans" /> </RelativeLayout> diff --git a/packages/SystemUI/res/layout/quick_settings_brightness_dialog.xml b/packages/SystemUI/res/layout/quick_settings_brightness_dialog.xml index e95c6a79733c..62ceb07d5a4e 100644 --- a/packages/SystemUI/res/layout/quick_settings_brightness_dialog.xml +++ b/packages/SystemUI/res/layout/quick_settings_brightness_dialog.xml @@ -17,19 +17,20 @@ xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/brightness_slider" android:layout_width="match_parent" - android:layout_height="@dimen/brightness_mirror_height" + android:layout_height="wrap_content" android:layout_gravity="center" + android:clipChildren="false" + android:clipToPadding="false" android:contentDescription="@string/accessibility_brightness" android:importantForAccessibility="no" > <com.android.systemui.settings.brightness.ToggleSeekBar android:id="@+id/slider" android:layout_width="match_parent" - android:layout_height="wrap_content" + android:layout_height="@dimen/brightness_mirror_height" android:layout_gravity="center_vertical" android:minHeight="48dp" android:thumb="@null" - android:background="@null" android:paddingStart="0dp" android:paddingEnd="0dp" android:progressDrawable="@drawable/brightness_progress_drawable" diff --git a/packages/SystemUI/res/layout/screenshot_shelf.xml b/packages/SystemUI/res/layout/screenshot_shelf.xml index f3f472bf4d24..84ab0f1b6ee5 100644 --- a/packages/SystemUI/res/layout/screenshot_shelf.xml +++ b/packages/SystemUI/res/layout/screenshot_shelf.xml @@ -18,6 +18,7 @@ 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" + android:theme="@style/FloatingOverlay" android:layout_width="match_parent" android:layout_height="match_parent" android:clipChildren="false" diff --git a/packages/SystemUI/res/layout/super_notification_shade.xml b/packages/SystemUI/res/layout/super_notification_shade.xml index 221b791b8cb5..fbb07bed4b50 100644 --- a/packages/SystemUI/res/layout/super_notification_shade.xml +++ b/packages/SystemUI/res/layout/super_notification_shade.xml @@ -62,7 +62,9 @@ <com.android.systemui.keyguard.ui.view.KeyguardRootView android:id="@id/keyguard_root_view" android:layout_width="match_parent" - android:layout_height="match_parent" /> + android:layout_height="match_parent" + android:clipChildren="false" + /> <!-- Shared container for the notification stack. Can be positioned by either the keyguard_root_view or notification_panel --> diff --git a/packages/SystemUI/res/layout/volume_dialog.xml b/packages/SystemUI/res/layout/volume_dialog.xml index 39a1f1f9b85d..ad5621679681 100644 --- a/packages/SystemUI/res/layout/volume_dialog.xml +++ b/packages/SystemUI/res/layout/volume_dialog.xml @@ -13,9 +13,7 @@ See the License for the specific language governing permissions and limitations under the License. --> -<FrameLayout - xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:sysui="http://schemas.android.com/apk/res-auto" +<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" @@ -96,16 +94,18 @@ android:paddingLeft="@dimen/volume_dialog_ringer_rows_padding" android:paddingBottom="@dimen/volume_dialog_ringer_rows_padding" android:paddingRight="@dimen/volume_dialog_ringer_rows_padding"> + <com.android.keyguard.AlphaOptimizedImageButton android:id="@+id/settings" - android:src="@drawable/horizontal_ellipsis" android:layout_width="@dimen/volume_dialog_tap_target_size" android:layout_height="@dimen/volume_dialog_tap_target_size" android:layout_gravity="center" - android:contentDescription="@string/accessibility_volume_settings" android:background="@drawable/ripple_drawable_20dp" - android:tint="?androidprv:attr/colorAccent" - android:soundEffectsEnabled="false" /> + android:contentDescription="@string/accessibility_volume_settings" + android:scaleType="centerInside" + android:soundEffectsEnabled="false" + android:src="@drawable/horizontal_ellipsis" + android:tint="?androidprv:attr/colorAccent" /> </FrameLayout> </LinearLayout> diff --git a/packages/SystemUI/res/raw/face_dialog_authenticating.json b/packages/SystemUI/res/raw/face_dialog_authenticating.json new file mode 100644 index 000000000000..4e25e6d933c4 --- /dev/null +++ b/packages/SystemUI/res/raw/face_dialog_authenticating.json @@ -0,0 +1 @@ +{"v":"5.7.13","fr":60,"ip":0,"op":61,"w":64,"h":64,"nm":"face_scanning 3","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":".blue200","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[32,32,0],"ix":2,"l":2},"a":{"a":0,"k":[27.25,27.25,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":0,"s":[100,100,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":30,"s":[95,95,100]},{"t":60,"s":[100,100,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-1.243],[-1.244,0],[0,1.243],[1.242,0]],"o":[[0,1.243],[1.242,0],[0,-1.243],[-1.244,0]],"v":[[-2.249,0.001],[0.001,2.251],[2.249,0.001],[0.001,-2.251]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.658823529412,0.780392216701,0.980392216701,1],"ix":4},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":30,"s":[60]},{"t":60,"s":[100]}],"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[15.1,20.495],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-1.243],[-1.242,0],[0,1.243],[1.242,0]],"o":[[0,1.243],[1.242,0],[0,-1.243],[-1.242,0]],"v":[[-2.249,0],[0.001,2.25],[2.249,0],[0.001,-2.25]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.658823529412,0.780392216701,0.980392216701,1],"ix":4},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":30,"s":[60]},{"t":60,"s":[100]}],"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[39.4,20.495],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 2","np":2,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[2.814,3.523],[-2.814,3.523],[-2.814,1.363],[0.652,1.363],[0.652,-3.523],[2.814,-3.523]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.658823529412,0.780392216701,0.980392216701,1],"ix":4},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":30,"s":[60]},{"t":60,"s":[100]}],"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[27.791,28.479],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 3","np":2,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.154,0.15],[0,0],[0.117,-0.095],[0,0],[0.228,-0.121],[0.358,-0.103],[0.922,0.261],[0.3,0.16],[0.24,0.185],[0.14,0.139],[0.178,0.261],[0.143,0.451],[0,0],[0,0.494],[0,0],[-0.214,-0.676],[-0.392,-0.572],[-0.323,-0.317],[-0.228,-0.177],[-0.333,-0.179],[-0.503,-0.145],[-0.662,0],[-0.653,0.184],[-0.437,0.233],[-0.336,0.258],[0,0],[0,0]],"o":[[0,0],[-0.107,0.106],[0,0],[-0.24,0.185],[-0.301,0.16],[-0.92,0.261],[-0.357,-0.103],[-0.228,-0.121],[-0.158,-0.122],[-0.225,-0.221],[-0.272,-0.393],[0,0],[-0.147,-0.466],[0,0],[0,0.716],[0.206,0.656],[0.256,0.372],[0.204,0.201],[0.336,0.258],[0.436,0.233],[0.655,0.184],[0.662,0],[0.503,-0.145],[0.332,-0.179],[0,0],[0,0],[0.165,-0.136]],"v":[[6.094,1.465],[4.579,-0.076],[4.242,0.225],[4.124,0.315],[3.43,0.771],[2.439,1.165],[-0.342,1.165],[-1.331,0.771],[-2.027,0.315],[-2.48,-0.075],[-3.087,-0.801],[-3.712,-2.075],[-3.712,-2.075],[-3.934,-3.523],[-6.094,-3.523],[-5.771,-1.424],[-4.868,0.424],[-3.995,1.465],[-3.344,2.027],[-2.35,2.676],[-0.934,3.243],[1.049,3.523],[3.031,3.243],[4.449,2.676],[5.441,2.027],[5.482,1.997],[5.615,1.895]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.658823529412,0.780392216701,0.980392216701,1],"ix":4},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":30,"s":[60]},{"t":60,"s":[100]}],"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[26.201,40.411],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 4","np":2,"cix":2,"bm":0,"ix":4,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-13.398,0],[0,-13.4],[13.398,0],[0,13.4]],"o":[[13.398,0],[0,13.4],[-13.398,0],[0,-13.4]],"v":[[0,-24.3],[24.3,0],[0,24.3],[-24.3,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ind":1,"ty":"sh","ix":2,"ks":{"a":0,"k":{"i":[[14.904,0],[0,-14.904],[-14.904,0],[0,14.904]],"o":[[-14.904,0],[0,14.904],[14.904,0],[0,-14.904]],"v":[[0,-27],[-27,0],[0,27],[27,0]],"c":true},"ix":2},"nm":"Path 2","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"mm","mm":1,"nm":"Merge Paths 1","mn":"ADBE Vector Filter - Merge","hd":false},{"ty":"fl","c":{"a":0,"k":[0.658823529412,0.780392216701,0.980392216701,1],"ix":4},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":30,"s":[60]},{"t":60,"s":[100]}],"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[27.25,27.25],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 5","np":4,"cix":2,"bm":0,"ix":5,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":1200,"st":0,"bm":0}],"markers":[]}
\ No newline at end of file diff --git a/packages/SystemUI/res/raw/keep.xml b/packages/SystemUI/res/raw/keep_homecontrols_sq.xml index 645e3c50c9f9..645e3c50c9f9 100644 --- a/packages/SystemUI/res/raw/keep.xml +++ b/packages/SystemUI/res/raw/keep_homecontrols_sq.xml diff --git a/packages/SystemUI/res/raw/widget.rec b/packages/SystemUI/res/raw/widget.rec Binary files differdeleted file mode 100644 index a38b23b0078f..000000000000 --- a/packages/SystemUI/res/raw/widget.rec +++ /dev/null diff --git a/packages/SystemUI/res/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml index 63aa97f542ae..a7dfdaa658d1 100644 --- a/packages/SystemUI/res/values-af/strings.xml +++ b/packages/SystemUI/res/values-af/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Skakel dit môre outomaties weer aan"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Kenmerke soos Kitsdeel en Kry My Toestel gebruik Bluetooth"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth sal môreoggend aanskakel"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Oudiodeling"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Deel tans oudio"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> batterykrag"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Oudio"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Kopstuk"</string> @@ -361,19 +363,19 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Watter deel van jou toestelervaring is geraak?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Kies soort kwessie"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Skermopname"</string> - <string-array name="qs_record_issue_types"> - <item msgid="2947988124014085798">"Werkverrigting"</item> - <item msgid="1627504621139124393">"Gebruikerkoppelvlak"</item> - <item msgid="8309220355268900335">"Battery"</item> - </string-array> + <string name="performance" msgid="6552785217174378320">"Werkverrigting"</string> + <string name="user_interface" msgid="3712869377953950887">"Gebruikerkoppelvlak"</string> + <string name="thermal" msgid="6758074791325414831">"Termies"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Eenhandmodus"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Gehoortoestelle"</string> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Aktief"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Ontkoppel"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Gehoortoestelle"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Bind nuwe toestel saam"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Klik om nuwe toestel saam te bind"</string> <string name="hearing_devices_presets_error" msgid="350363093458408536">"Kon nie voorafstelling opdateer nie"</string> - <!-- no translation found for hearing_devices_preset_label (7878267405046232358) --> - <skip /> + <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Voorafstelling"</string> + <string name="live_caption_title" msgid="8916875614623730005">"Intydse Onderskrifte"</string> <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> @@ -727,11 +729,6 @@ <string name="keyboard_key_button_template" msgid="8005673627272051429">"Knoppie <xliff:g id="NAME">%1$s</xliff:g>"</string> <string name="keyboard_key_home" msgid="3734400625170020657">"Home"</string> <string name="keyboard_key_back" msgid="4185420465469481999">"Back"</string> - <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"Oppyl"</string> - <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"Afpyl"</string> - <string name="keyboard_key_dpad_left" msgid="144176368026538621">"Linkspyl"</string> - <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"Regspyl"</string> - <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"Middel"</string> <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string> <string name="keyboard_key_space" msgid="6980847564173394012">"Spasie"</string> <string name="keyboard_key_enter" msgid="8633362970109751646">"Enter"</string> @@ -1201,7 +1198,7 @@ <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Wi-fi sal vir nou nie outomaties koppel nie"</string> <string name="see_all_networks" msgid="3773666844913168122">"Sien alles"</string> <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Ontkoppel Ethernet om netwerke te wissel"</string> - <string name="wifi_scan_notify_message" msgid="3753839537448621794">"Om toestelervaring te verbeter, kan programme en dienste steeds enige tyd na wi‑fi-netwerke soek, selfs wanneer wi‑fi af is. Jy kan dit in Wi-fi-opsporinginstellings verander. "<annotation id="link">"Verander"</annotation></string> + <string name="wifi_scan_notify_message" msgid="3753839537448621794">"Om toestelervaring te verbeter, kan apps en dienste steeds enige tyd na wi‑fi-netwerke soek, selfs wanneer wi‑fi af is. Jy kan dit in Wi-fi-opsporinginstellings verander. "<annotation id="link">"Verander"</annotation></string> <string name="turn_off_airplane_mode" msgid="8425587763226548579">"Skakel vliegtuigmodus af"</string> <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> wil die volgende teël by Kitsinstellings voeg"</string> <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Voeg teël by"</string> @@ -1275,7 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"gevou"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"oopgevou"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s/%2$s"</string> - <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"<xliff:g id="PERCENTAGE">%s</xliff:g> batterykrag oor"</string> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Stilusbattery <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Koppel jou stilus aan ’n laaier"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"Stilus se battery is amper pap"</string> <string name="video_camera" msgid="7654002575156149298">"Videokamera"</string> @@ -1318,6 +1315,16 @@ <string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"Onlangs gebruik deur <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string> <string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"Word gebruik deur <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>)"</string> <string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"Onlangs gebruik deur <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>)"</string> + <string name="shortcut_helper_category_system" msgid="462110876978937359">"Stelsel"</string> + <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Verrigting van veelvuldige take"</string> + <string name="shortcut_helper_category_input" msgid="8674018654124839566">"Invoer"</string> + <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Appkortpaaie"</string> + <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Toeganklikheid"</string> + <string name="shortcut_helper_title" msgid="8567500639300970049">"Kortpadsleutels"</string> + <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Soekkortpaaie"</string> + <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Vou ikoon in"</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="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Sleutelbordlig"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"Vlak %1$d van %2$d"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"Huiskontroles"</string> diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml index 58940bb27d95..396837e405ee 100644 --- a/packages/SystemUI/res/values-am/strings.xml +++ b/packages/SystemUI/res/values-am/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"ነገ እንደገና በራስ-ሰር አስጀምር"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"እንደ ፈጣን ማጋራት እና የእኔን መሣሪያ አግኝ ያሉ ባህሪዎች ብሉቱዝን ይጠቀማሉ"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"ብሉቱዝ ነገ ጠዋት ይበራል"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"የድምጽ ማጋራት"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"ድምጽን በማጋራት ላይ"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> ባትሪ"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"ኦዲዮ"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"ማዳመጫ"</string> @@ -361,19 +363,19 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"የትኛው የመሣሪያዎ ተሞክሮ ክፍል ተጎድቷል?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"የችግሩን አይነት ይምረጡ"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"የማያ መቅረጫ"</string> - <string-array name="qs_record_issue_types"> - <item msgid="2947988124014085798">"አፈጻጸም"</item> - <item msgid="1627504621139124393">"የተጠቃሚ በይነገፅ"</item> - <item msgid="8309220355268900335">"ባትሪ"</item> - </string-array> + <string name="performance" msgid="6552785217174378320">"አፈጻጸም"</string> + <string name="user_interface" msgid="3712869377953950887">"የተጠቃሚ በይነገፅ"</string> + <string name="thermal" msgid="6758074791325414831">"ተርማል"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"የአንድ እጅ ሁነታ"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"የመስሚያ መሣሪያዎች"</string> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"ገቢር"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"ግንኙነት ተቋርጧል"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"የመስማት ችሎታ መሣሪያ"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"አዲስ መሣሪያ ያጣምሩ"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"አዲስ መሣሪያ ለማጣመር ጠቅ ያድርጉ"</string> <string name="hearing_devices_presets_error" msgid="350363093458408536">"ቅድመ-ቅምጥን ማዘመን አልተቻለም"</string> - <!-- no translation found for hearing_devices_preset_label (7878267405046232358) --> - <skip /> + <string name="hearing_devices_preset_label" msgid="7878267405046232358">"ቅድመ-ቅምጥ"</string> + <string name="live_caption_title" msgid="8916875614623730005">"የቀጥታ ስርጭት መግለጫ ጽሁፍ"</string> <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> @@ -727,11 +729,6 @@ <string name="keyboard_key_button_template" msgid="8005673627272051429">"አዝራር <xliff:g id="NAME">%1$s</xliff:g>"</string> <string name="keyboard_key_home" msgid="3734400625170020657">"መነሻ"</string> <string name="keyboard_key_back" msgid="4185420465469481999">"ተመለስ"</string> - <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"የላይ ቀስት"</string> - <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"የታች ቀስት"</string> - <string name="keyboard_key_dpad_left" msgid="144176368026538621">"የግራ ቀስት"</string> - <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"የቀኝ ቀስት"</string> - <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"መሃል"</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> @@ -1275,7 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"የታጠፈ"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"የተዘረጋ"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string> - <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"<xliff:g id="PERCENTAGE">%s</xliff:g> ባትሪ ይቀራል"</string> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"የብሮስፌ ባትሪ <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"ብሮስፌዎን ከኃይል መሙያ ጋር ያገናኙ"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"የብሮስፌ ባትሪ ዝቅተኛ ነው"</string> <string name="video_camera" msgid="7654002575156149298">"የቪድዮ ካሜራ"</string> @@ -1318,6 +1315,16 @@ <string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"በቅርብ ጊዜ በ<xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>) ጥቅም ላይ ውሏል"</string> <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>) ጥቅም ላይ እየዋለ ነው"</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>) ጥቅም ላይ ውሏል"</string> + <string name="shortcut_helper_category_system" msgid="462110876978937359">"ሥርዓት"</string> + <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"ብዙ ተግባራትን በተመሳሳይ ጊዜ ማከናወን"</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_a11y" msgid="6314444792641773464">"ተደራሽነት"</string> + <string name="shortcut_helper_title" msgid="8567500639300970049">"የቁልፍ ሰሌዳ አቋራጮች"</string> + <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"የፍለጋ አቋራጮች"</string> + <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"መሰብሰቢያ አዶ"</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="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> diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml index b74b6cc6759e..254549fdc1ba 100644 --- a/packages/SystemUI/res/values-ar/strings.xml +++ b/packages/SystemUI/res/values-ar/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"تفعيل البلوتوث تلقائيًا مرة أخرى غدًا"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"يُستخدَم البلوتوث في ميزات مثل Quick Share و\"العثور على جهازي\""</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"سيتم تفعيل البلوتوث صباح الغد"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"مشاركة الصوت"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"مشاركة الصوت"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"مستوى طاقة البطارية <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"صوت"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"سماعة الرأس"</string> @@ -301,11 +303,11 @@ <string name="quick_settings_user_title" msgid="8673045967216204537">"المستخدم"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string> <string name="quick_settings_internet_label" msgid="6603068555872455463">"الإنترنت"</string> - <string name="quick_settings_networks_available" msgid="1875138606855420438">"الشبكات متوفرة"</string> + <string name="quick_settings_networks_available" msgid="1875138606855420438">"تتوفّر شبكات"</string> <string name="quick_settings_networks_unavailable" msgid="1167847013337940082">"الشبكات غير متوفرة"</string> <string name="quick_settings_wifi_detail_empty_text" msgid="483130889414601732">"لا تتوفر أي شبكة Wi-Fi"</string> <string name="quick_settings_wifi_secondary_label_transient" msgid="7501659015509357887">"جارٍ التفعيل…"</string> - <string name="quick_settings_cast_title" msgid="3033553249449938182">"الإرسال"</string> + <string name="quick_settings_cast_title" msgid="3033553249449938182">"البث"</string> <string name="quick_settings_casting" msgid="1435880708719268055">"جارٍ الإرسال"</string> <string name="quick_settings_cast_device_default_name" msgid="6988469571141331700">"جهاز لا يحمل اسمًا"</string> <string name="quick_settings_cast_detail_empty_text" msgid="2846282280014617785">"لا يتوفر أي جهاز"</string> @@ -361,19 +363,19 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"ما هو الجانب الذي تأثّر في تجربة استخدام الجهاز؟"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"اختيار نوع المشكلة"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"تسجيل الشاشة"</string> - <string-array name="qs_record_issue_types"> - <item msgid="2947988124014085798">"الأداء"</item> - <item msgid="1627504621139124393">"واجهة المستخدم"</item> - <item msgid="8309220355268900335">"البطارية"</item> - </string-array> + <string name="performance" msgid="6552785217174378320">"الأداء"</string> + <string name="user_interface" msgid="3712869377953950887">"واجهة المستخدم"</string> + <string name="thermal" msgid="6758074791325414831">"الأداء الحراري"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"وضع \"التصفح بيد واحدة\""</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"سماعات الأذن الطبية"</string> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"متّصلة حاليًا"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"غير متّصلة"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"سماعات الأذن الطبية"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"إقران جهاز جديد"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"انقر لإقران جهاز جديد"</string> <string name="hearing_devices_presets_error" msgid="350363093458408536">"تعذَّر تعديل الإعداد المسبق"</string> - <!-- no translation found for hearing_devices_preset_label (7878267405046232358) --> - <skip /> + <string name="hearing_devices_preset_label" msgid="7878267405046232358">"الإعدادات المسبقة"</string> + <string name="live_caption_title" msgid="8916875614623730005">"النسخ النصي التلقائي"</string> <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> @@ -615,7 +617,7 @@ <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. انقر للتجاهل. قد يتم تجاهل خدمات \"سهولة الاستخدام\"."</string> <string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"%1$s. انقر للتعيين على الاهتزاز."</string> <string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s. انقر لكتم الصوت."</string> - <string name="volume_panel_noise_control_title" msgid="7413949943872304474">"التحكُّم في مستوى الضجيج"</string> + <string name="volume_panel_noise_control_title" msgid="7413949943872304474">"التحكُّم في مستوى الضوضاء"</string> <string name="volume_panel_spatial_audio_title" msgid="3367048857932040660">"الصوت المكاني"</string> <string name="volume_panel_spatial_audio_off" msgid="4177490084606772989">"غير مفعّل"</string> <string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"تفعيل"</string> @@ -727,11 +729,6 @@ <string name="keyboard_key_button_template" msgid="8005673627272051429">"الزر <xliff:g id="NAME">%1$s</xliff:g>"</string> <string name="keyboard_key_home" msgid="3734400625170020657">"Home"</string> <string name="keyboard_key_back" msgid="4185420465469481999">"Back"</string> - <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"سهم متّجه للأعلى"</string> - <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"سهم متّجه للأسفل"</string> - <string name="keyboard_key_dpad_left" msgid="144176368026538621">"سهم متّجه لليسار"</string> - <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"سهم متّجه لليمين"</string> - <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"وسط"</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">"Enter"</string> @@ -1275,7 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"مطوي"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"غير مطوي"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string> - <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"النسبة المئوية المتبقية من شحن البطارية: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"مستوى شحن بطارية قلم الشاشة: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"عليك توصيل قلم الشاشة بشاحن."</string> <string name="stylus_battery_low" msgid="7134370101603167096">"بطارية قلم الشاشة منخفضة"</string> <string name="video_camera" msgid="7654002575156149298">"كاميرا فيديو"</string> @@ -1318,6 +1315,16 @@ <string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"تم الاستخدام مؤخرًا في <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string> <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>)"</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>)"</string> + <string name="shortcut_helper_category_system" msgid="462110876978937359">"النظام"</string> + <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"تعدُّد المهام"</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_a11y" msgid="6314444792641773464">"تسهيل الاستخدام"</string> + <string name="shortcut_helper_title" msgid="8567500639300970049">"اختصارات لوحة المفاتيح"</string> + <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"اختصارات طلبات البحث"</string> + <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"رمز التصغير"</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="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> diff --git a/packages/SystemUI/res/values-as/strings.xml b/packages/SystemUI/res/values-as/strings.xml index e25a19e7f128..dd7102382983 100644 --- a/packages/SystemUI/res/values-as/strings.xml +++ b/packages/SystemUI/res/values-as/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"কাইলৈ পুনৰ স্বয়ংক্ৰিয়ভাৱে অন কৰক"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Quick Share আৰু Find My Deviceৰ দৰে সুবিধাসমূহে ব্লুটুথ ব্যৱহাৰ কৰে"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"কাইলৈ পুৱা ব্লুটুথ অন হ’ব"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"অডিঅ’ শ্বেয়াৰ কৰা"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"অডিঅ’ শ্বেয়াৰ কৰি থকা হৈছে"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"বেটাৰী <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"অডিঅ’"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"হেডছেট"</string> @@ -361,19 +363,19 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"আপোনাৰ ডিভাইচৰ অভিজ্ঞতাৰ কোনটো অংশ প্ৰভাৱিত হৈছিল?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"সমস্যাৰ প্ৰকাৰ বাছনি কৰক"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"স্ক্ৰীন ৰেকৰ্ড"</string> - <string-array name="qs_record_issue_types"> - <item msgid="2947988124014085798">"কাৰ্যদক্ষতা"</item> - <item msgid="1627504621139124393">"ব্যৱহাৰকাৰীৰ ইণ্টাৰফে’চ"</item> - <item msgid="8309220355268900335">"বেটাৰী"</item> - </string-array> + <string name="performance" msgid="6552785217174378320">"পাৰদৰ্শিতা"</string> + <string name="user_interface" msgid="3712869377953950887">"ব্যৱহাৰকাৰীৰ ইণ্টাৰফে’চ"</string> + <string name="thermal" msgid="6758074791325414831">"থাৰ্মেল"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"এখন হাতেৰে ব্যৱহাৰ কৰা ম’ড"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"শুনাৰ ডিভাইচ"</string> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"সক্ৰিয় হৈ আছে"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"সংযোগ বিচ্ছিন্ন কৰা হ’ল"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"শুনাৰ ডিভাইচ"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"নতুন ডিভাইচ পেয়াৰ কৰক"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"নতুন ডিভাইচ পেয়াৰ কৰিবলৈ ক্লিক কৰক"</string> <string name="hearing_devices_presets_error" msgid="350363093458408536">"প্ৰিছেট আপডে’ট কৰিব পৰা নগ’ল"</string> - <!-- no translation found for hearing_devices_preset_label (7878267405046232358) --> - <skip /> + <string name="hearing_devices_preset_label" msgid="7878267405046232358">"প্ৰিছেট"</string> + <string name="live_caption_title" msgid="8916875614623730005">"লাইভ কেপশ্বন"</string> <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> @@ -727,11 +729,6 @@ <string name="keyboard_key_button_template" msgid="8005673627272051429">"<xliff:g id="NAME">%1$s</xliff:g> বুটাম"</string> <string name="keyboard_key_home" msgid="3734400625170020657">"হ\'ম"</string> <string name="keyboard_key_back" msgid="4185420465469481999">"উভতি যাওক"</string> - <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"ওপৰলৈ নিৰ্দেশ কৰা কাঁড় চিহ্ন"</string> - <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"তললৈ নিৰ্দেশ কৰা কাঁড় চিহ্ন"</string> - <string name="keyboard_key_dpad_left" msgid="144176368026538621">"বাওঁফাললৈ নিৰ্দেশ কৰা কাঁড় চিহ্ন"</string> - <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"সোঁফাললৈ নিৰ্দেশ কৰা কাঁড় চিহ্ন"</string> - <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"স্ক্ৰীনৰ মাজত"</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> @@ -1268,14 +1265,14 @@ <string name="rear_display_bottom_sheet_confirm" msgid="1507591562761552899">"এতিয়াই স্ক্ৰীন সলনি কৰক"</string> <string name="rear_display_folded_bottom_sheet_title" msgid="3930008746560711990">"ফ’নটো আনফ’ল্ড কৰক"</string> <string name="rear_display_unfolded_bottom_sheet_title" msgid="6291111173057304055">"স্ক্ৰীন সলনি কৰিবনে?"</string> - <string name="rear_display_folded_bottom_sheet_description" msgid="6842767125783222695">"অধিক ৰিজ’লিউছনৰ বাবে, পিছফালৰ কেমেৰাটো ব্যৱহাৰ কৰক"</string> - <string name="rear_display_unfolded_bottom_sheet_description" msgid="7229961336309960201">"অধিক ৰিজ’লিউছনৰ বাবে, ফ’নটো লুটিয়াই দিয়ক"</string> + <string name="rear_display_folded_bottom_sheet_description" msgid="6842767125783222695">"অধিক ৰিজ’লিউশ্বনৰ বাবে, পিছফালৰ কেমেৰাটো ব্যৱহাৰ কৰক"</string> + <string name="rear_display_unfolded_bottom_sheet_description" msgid="7229961336309960201">"অধিক ৰিজ’লিউশ্বনৰ বাবে, ফ’নটো লুটিয়াই দিয়ক"</string> <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"জপাব পৰা ডিভাইচৰ জাপ খুলি থকা হৈছে"</string> <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"জপাব পৰা ডিভাইচৰ ওলোটাই থকা হৈছে"</string> <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"ফ’ল্ড কৰা"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"আনফ’ল্ড কৰা"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string> - <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"<xliff:g id="PERCENTAGE">%s</xliff:g> বেটাৰী বাকী আছে"</string> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"ষ্টাইলাছৰ বেটাৰী <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"আপোনাৰ ষ্টাইলাছ এটা চাৰ্জাৰৰ সৈতে সংযোগ কৰক"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"ষ্টাইলাছৰ বেটাৰী কম আছে"</string> <string name="video_camera" msgid="7654002575156149298">"ভিডিঅ’ কেমেৰা"</string> @@ -1318,6 +1315,16 @@ <string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"শেহতীয়াকৈ <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)এ ব্যৱহাৰ কৰিছে"</string> <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>)এ ব্যৱহাৰ কৰি আছে"</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>)এ ব্যৱহাৰ কৰিছে"</string> + <string name="shortcut_helper_category_system" msgid="462110876978937359">"ছিষ্টেম"</string> + <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"মাল্টিটাস্কিং"</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_a11y" msgid="6314444792641773464">"সাধ্য সুবিধা"</string> + <string name="shortcut_helper_title" msgid="8567500639300970049">"কীব’ৰ্ডৰ শ্বৰ্টকাট"</string> + <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"সন্ধানৰ শ্বৰ্টকাট"</string> + <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"সংকোচন কৰাৰ চিহ্ন"</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="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> diff --git a/packages/SystemUI/res/values-az/strings.xml b/packages/SystemUI/res/values-az/strings.xml index 29fb86e00fa3..592bc06de2fd 100644 --- a/packages/SystemUI/res/values-az/strings.xml +++ b/packages/SystemUI/res/values-az/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Sabah avtomatik aktiv edin"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Cəld Paylaşım və Cihazın Tapılması kimi funksiyalar Bluetooth istifadə edir"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth sabah səhər aktiv ediləcək"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Audio paylaşma"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Audio paylaşılır"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> batareya"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Qulaqlıq"</string> @@ -361,19 +363,19 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Cihaz istifadəsinə necə təsir etdi?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Problem növü seçin"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Ekran qeydəalma"</string> - <string-array name="qs_record_issue_types"> - <item msgid="2947988124014085798">"Performans"</item> - <item msgid="1627504621139124393">"İstifadəçi interfeysi"</item> - <item msgid="8309220355268900335">"Batareya"</item> - </string-array> + <string name="performance" msgid="6552785217174378320">"Performans"</string> + <string name="user_interface" msgid="3712869377953950887">"İstifadəçi interfeysi"</string> + <string name="thermal" msgid="6758074791325414831">"Termal"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Birəlli rejim"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Eşitmə cihazları"</string> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Aktiv"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Bağlantı kəsildi"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Eşitmə cihazları"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Yeni cihaz birləşdirin"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Yeni cihaz birləşdirmək üçün klikləyin"</string> <string name="hearing_devices_presets_error" msgid="350363093458408536">"Hazır ayar güncəllənmədi"</string> - <!-- no translation found for hearing_devices_preset_label (7878267405046232358) --> - <skip /> + <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Hazır Ayar"</string> + <string name="live_caption_title" msgid="8916875614623730005">"Avtomatik subtitrlər"</string> <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> @@ -727,11 +729,6 @@ <string name="keyboard_key_button_template" msgid="8005673627272051429">"Düymə <xliff:g id="NAME">%1$s</xliff:g>"</string> <string name="keyboard_key_home" msgid="3734400625170020657">"Əsas səhifə"</string> <string name="keyboard_key_back" msgid="4185420465469481999">"Geri"</string> - <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"Yuxarı ox"</string> - <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"Aşağı ox"</string> - <string name="keyboard_key_dpad_left" msgid="144176368026538621">"Sol ox"</string> - <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"Sağ ox"</string> - <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"Mərkəz"</string> <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string> <string name="keyboard_key_space" msgid="6980847564173394012">"Boşluq"</string> <string name="keyboard_key_enter" msgid="8633362970109751646">"Daxil olun"</string> @@ -1275,7 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"qatlanmış"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"açıq"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string> - <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"<xliff:g id="PERCENTAGE">%s</xliff:g> enerji qalıb"</string> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Stilusun enerjisi: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Qələmi adapterə qoşun"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"Qələm enerjisi azdır"</string> <string name="video_camera" msgid="7654002575156149298">"Videokamera"</string> @@ -1318,6 +1315,16 @@ <string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"Bu yaxınlarda <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>) istifadə edib"</string> <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>) istifadə edir"</string> <string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"Bu yaxınlarda <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>) istifadə edib"</string> + <string name="shortcut_helper_category_system" msgid="462110876978937359">"Sistem"</string> + <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Çoxsaylı tapşırıq icrası"</string> + <string name="shortcut_helper_category_input" msgid="8674018654124839566">"Daxiletmə"</string> + <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Tətbiq qısayolları"</string> + <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Xüsusi imkanlar"</string> + <string name="shortcut_helper_title" msgid="8567500639300970049">"Klaviatura qısayolları"</string> + <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Axtarış qısayolları"</string> + <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"İkonanı yığcamlaşdırın"</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="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Klaviatura işığı"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"Səviyyə %1$d/%2$d"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"Ev nizamlayıcıları"</string> diff --git a/packages/SystemUI/res/values-b+sr+Latn/strings.xml b/packages/SystemUI/res/values-b+sr+Latn/strings.xml index f7c252ea94e4..9b27943f5484 100644 --- a/packages/SystemUI/res/values-b+sr+Latn/strings.xml +++ b/packages/SystemUI/res/values-b+sr+Latn/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Automatski ponovo uključi sutra"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Funkcije kao što su Quick Share i Pronađi moj uređaj koriste Bluetooth"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth će se uključiti sutra ujutru"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Deljenje zvuka"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Deli se zvuk"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Nivo baterije je <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Slušalice"</string> @@ -361,19 +363,19 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Na koji deo doživljaja na uređaju je ovo uticalo?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Izaberite tip problema"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Snimanje ekrana"</string> - <string-array name="qs_record_issue_types"> - <item msgid="2947988124014085798">"Učinak"</item> - <item msgid="1627504621139124393">"Korisnički interfejs"</item> - <item msgid="8309220355268900335">"Baterija"</item> - </string-array> + <string name="performance" msgid="6552785217174378320">"Performanse"</string> + <string name="user_interface" msgid="3712869377953950887">"Korisnički interfejs"</string> + <string name="thermal" msgid="6758074791325414831">"Termalna kamera"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Režim jednom rukom"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Slušni aparati"</string> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Aktivno"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Veza je prekinuta"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Slušni aparati"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Upari novi uređaj"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Kliknite da biste uparili nov uređaj"</string> <string name="hearing_devices_presets_error" msgid="350363093458408536">"Ažuriranje zadatih podešavanja nije uspelo"</string> - <!-- no translation found for hearing_devices_preset_label (7878267405046232358) --> - <skip /> + <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Unapred određena podešavanja"</string> + <string name="live_caption_title" msgid="8916875614623730005">"Titl uživo"</string> <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> @@ -727,11 +729,6 @@ <string name="keyboard_key_button_template" msgid="8005673627272051429">"Dugme <xliff:g id="NAME">%1$s</xliff:g>"</string> <string name="keyboard_key_home" msgid="3734400625170020657">"Taster Početna"</string> <string name="keyboard_key_back" msgid="4185420465469481999">"Taster Nazad"</string> - <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"Strelica nagore"</string> - <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"Strelica nadole"</string> - <string name="keyboard_key_dpad_left" msgid="144176368026538621">"Strelica nalevo"</string> - <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"Strelica nadesno"</string> - <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"Taster sa centralnom strelicom"</string> <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> @@ -1275,7 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"zatvoreno"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"otvoreno"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s/%2$s"</string> - <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"Preostalo je još<xliff:g id="PERCENTAGE">%s</xliff:g> baterije"</string> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Baterija pisaljke je na <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Povežite pisaljku sa punjačem"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"Nizak nivo baterije pisaljke"</string> <string name="video_camera" msgid="7654002575156149298">"Video kamera"</string> @@ -1318,6 +1315,16 @@ <string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"Nedavno koristila aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string> <string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"Koriste <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>)"</string> <string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"Nedavno koristila aplikacija <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>)"</string> + <string name="shortcut_helper_category_system" msgid="462110876978937359">"Sistem"</string> + <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Obavljanje više zadataka istovremeno"</string> + <string name="shortcut_helper_category_input" msgid="8674018654124839566">"Unos"</string> + <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Prečice za aplikacije"</string> + <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Pristupačnost"</string> + <string name="shortcut_helper_title" msgid="8567500639300970049">"Tasterske prečice"</string> + <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Prečice pretrage"</string> + <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ikona za skupljanje"</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="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Pozadinsko osvetljenje tastature"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"%1$d. nivo od %2$d"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"Kontrole za dom"</string> diff --git a/packages/SystemUI/res/values-be/strings.xml b/packages/SystemUI/res/values-be/strings.xml index 60084389d9fb..ca763cfac724 100644 --- a/packages/SystemUI/res/values-be/strings.xml +++ b/packages/SystemUI/res/values-be/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Аўтаматычнае ўключэнне заўтра"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Bluetooth выкарыстоўваецца такімі функцыямі і сэрвісамі, як Хуткае абагульванне і Знайсці прыладу"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth уключыцца заўтра раніцай"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Абагульванне аўдыя"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Ідзе абагульванне аўдыя"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Узровень зараду: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Гук"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Гарнітура"</string> @@ -361,19 +363,19 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"З чым была звязана праблема, якая вам сустрэлася?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Выберыце тып праблемы"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Запіс экрана"</string> - <string-array name="qs_record_issue_types"> - <item msgid="2947988124014085798">"Прадукцыйнасць"</item> - <item msgid="1627504621139124393">"Карыстальніцкі інтэрфейс"</item> - <item msgid="8309220355268900335">"Акумулятар"</item> - </string-array> + <string name="performance" msgid="6552785217174378320">"Прадукцыйнасць"</string> + <string name="user_interface" msgid="3712869377953950887">"Карыстальніцкі інтэрфейс"</string> + <string name="thermal" msgid="6758074791325414831">"Тэрмальныя паказчыкі"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Рэжым кіравання адной рукой"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Слыхавыя апараты"</string> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Актыўныя"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Адключаны"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Слыхавыя апараты"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Спалучыць новую прыладу"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Націсніце, каб спалучыць новую прыладу"</string> <string name="hearing_devices_presets_error" msgid="350363093458408536">"Не ўдалося абнавіць набор налад"</string> - <!-- no translation found for hearing_devices_preset_label (7878267405046232358) --> - <skip /> + <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Набор налад"</string> + <string name="live_caption_title" msgid="8916875614623730005">"Аўтаматычныя субцітры"</string> <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> @@ -727,11 +729,6 @@ <string name="keyboard_key_button_template" msgid="8005673627272051429">"Кнопка <xliff:g id="NAME">%1$s</xliff:g>"</string> <string name="keyboard_key_home" msgid="3734400625170020657">"Home"</string> <string name="keyboard_key_back" msgid="4185420465469481999">"Назад"</string> - <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"Стрэлка ўверх"</string> - <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"Стрэлка ўніз"</string> - <string name="keyboard_key_dpad_left" msgid="144176368026538621">"Стрэлка ўлева"</string> - <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"Стрэлка ўправа"</string> - <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"Цэнтр"</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">"Enter"</string> @@ -1275,7 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"складзена"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"раскладзена"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string> - <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"Засталося зараду: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Зарад акумулятара пяра – <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Падключыце пяро да зараднай прылады"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"Нізкі ўзровень зараду пяра"</string> <string name="video_camera" msgid="7654002575156149298">"Відэакамера"</string> @@ -1318,6 +1315,16 @@ <string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"Нядаўна выкарыстоўваўся праграмай \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string> <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>)"</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>)"</string> + <string name="shortcut_helper_category_system" msgid="462110876978937359">"Сістэма"</string> + <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Шматзадачнасць"</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_a11y" msgid="6314444792641773464">"Спецыяльныя магчымасці"</string> + <string name="shortcut_helper_title" msgid="8567500639300970049">"Спалучэнні клавіш"</string> + <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Пошук спалучэнняў клавіш"</string> + <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Значок \"Згарнуць\""</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="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> diff --git a/packages/SystemUI/res/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml index efaa31f502bd..d50b11605743 100644 --- a/packages/SystemUI/res/values-bg/strings.xml +++ b/packages/SystemUI/res/values-bg/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Автоматично включване отново утре"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Функции като „Бързо споделяне“ и „Намиране на устройството ми“ използват Bluetooth"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth ще се включи утре сутрин"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Споделяне на звука"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Звукът се споделя"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Батерия: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Аудио"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Слушалки"</string> @@ -361,19 +363,19 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"С какво имахте проблем?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Изберете тип проблем"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Запис на екрана"</string> - <string-array name="qs_record_issue_types"> - <item msgid="2947988124014085798">"Ефективност"</item> - <item msgid="1627504621139124393">"Потребителски интерфейс"</item> - <item msgid="8309220355268900335">"Батерия"</item> - </string-array> + <string name="performance" msgid="6552785217174378320">"Ефективност"</string> + <string name="user_interface" msgid="3712869377953950887">"Потребителски интерфейс"</string> + <string name="thermal" msgid="6758074791325414831">"Термално"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Режим за работа с една ръка"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Слухови апарати"</string> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Активно"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Няма връзка"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Слухови апарати"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Сдвояване на ново устройство"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Кликнете за сдвояване на ново устройство"</string> <string name="hearing_devices_presets_error" msgid="350363093458408536">"Предварително зададените настройки не бяха актуализирани"</string> - <!-- no translation found for hearing_devices_preset_label (7878267405046232358) --> - <skip /> + <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Предварително зададено"</string> + <string name="live_caption_title" msgid="8916875614623730005">"Надписи на живо"</string> <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> @@ -727,11 +729,6 @@ <string name="keyboard_key_button_template" msgid="8005673627272051429">"Бутон „<xliff:g id="NAME">%1$s</xliff:g>“"</string> <string name="keyboard_key_home" msgid="3734400625170020657">"Начало"</string> <string name="keyboard_key_back" msgid="4185420465469481999">"Назад"</string> - <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"Стрелка за нагоре"</string> - <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"Стрелка за надолу"</string> - <string name="keyboard_key_dpad_left" msgid="144176368026538621">"Стрелка за наляво"</string> - <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"Стрелка за надясно"</string> - <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"Център"</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">"Enter"</string> @@ -1275,7 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"затворено"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"отворено"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s/%2$s"</string> - <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"Оставаща батерия: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Батерия на писалката: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Свържете писалката към зарядно устройство"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"Батерията на писалката е изтощена"</string> <string name="video_camera" msgid="7654002575156149298">"Видеокамера"</string> @@ -1318,6 +1315,16 @@ <string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"Наскоро използвано от <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string> <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>)"</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>)"</string> + <string name="shortcut_helper_category_system" msgid="462110876978937359">"Системни"</string> + <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Изпълняване на няколко задачи едновременно"</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_a11y" msgid="6314444792641773464">"Достъпност"</string> + <string name="shortcut_helper_title" msgid="8567500639300970049">"Клавишни комбинации"</string> + <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Търсете клавишни комбинации"</string> + <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Икона за свиване"</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="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> diff --git a/packages/SystemUI/res/values-bn/strings.xml b/packages/SystemUI/res/values-bn/strings.xml index 6decb6169419..bb41b1b14b29 100644 --- a/packages/SystemUI/res/values-bn/strings.xml +++ b/packages/SystemUI/res/values-bn/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"আগামীকাল আবার অটোমেটিক চালু হবে"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"দ্রুত শেয়ার ও Find My Device-এর মতো ফিচার ব্লুটুথ ব্যবহার করে"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"ব্লুটুথ আগামীকাল সকালে চালু হয়ে যাবে"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"অডিও শেয়ারিং"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"অডিও শেয়ার করা হচ্ছে"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"চার্জ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"অডিও"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"হেডসেট"</string> @@ -361,19 +363,19 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"ডিভাইস ব্যবহার করার সময় কোথায় অসুবিধা হয়েছিল?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"সমস্যার প্রকার বেছে নিন"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"স্ক্রিন রেকর্ড"</string> - <string-array name="qs_record_issue_types"> - <item msgid="2947988124014085798">"পারফর্ম্যান্স"</item> - <item msgid="1627504621139124393">"ইউজার ইন্টারফেস"</item> - <item msgid="8309220355268900335">"ব্যাটারি"</item> - </string-array> + <string name="performance" msgid="6552785217174378320">"পারফর্ম্যান্স"</string> + <string name="user_interface" msgid="3712869377953950887">"ইউজার ইন্টারফেস"</string> + <string name="thermal" msgid="6758074791325414831">"থার্মাল"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"এক হাতে ব্যবহার করার মোড"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"হিয়ারিং ডিভাইস"</string> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"অ্যাক্টিভ"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"ডিসকানেক্ট হয়ে গেছে"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"হিয়ারিং ডিভাইস"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"নতুন ডিভাইস পেয়ার করুন"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"নতুন ডিভাইস পেয়ার করতে ক্লিক করুন"</string> <string name="hearing_devices_presets_error" msgid="350363093458408536">"প্রিসেট আপডেট করা যায়নি"</string> - <!-- no translation found for hearing_devices_preset_label (7878267405046232358) --> - <skip /> + <string name="hearing_devices_preset_label" msgid="7878267405046232358">"প্রিসেট"</string> + <string name="live_caption_title" msgid="8916875614623730005">"লাইভ ক্যাপশন"</string> <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> @@ -727,11 +729,6 @@ <string name="keyboard_key_button_template" msgid="8005673627272051429">"<xliff:g id="NAME">%1$s</xliff:g> বোতাম"</string> <string name="keyboard_key_home" msgid="3734400625170020657">"হোম"</string> <string name="keyboard_key_back" msgid="4185420465469481999">"ফিরুন"</string> - <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"ঊর্ধমুখী তীরচিহ্ন"</string> - <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"নিম্নমুখী তীরচিহ্ন"</string> - <string name="keyboard_key_dpad_left" msgid="144176368026538621">"বাঁদিকের তীরচিহ্ন"</string> - <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"ডানদিকের তীরচিহ্ন"</string> - <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"কেন্দ্র"</string> <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">"এন্টার"</string> @@ -1275,7 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"ফোল্ড করা রয়েছে"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"ফোল্ড করা নেই"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string> - <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"<xliff:g id="PERCENTAGE">%s</xliff:g> ব্যাটারির চার্জ বাকি আছে"</string> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"স্টাইলাস ব্যাটারি <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"কোনও চার্জারের সাথে আপনার স্টাইলাস কানেক্ট করুন"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"স্টাইলাস ব্যাটারিতে চার্জ কম আছে"</string> <string name="video_camera" msgid="7654002575156149298">"ভিডিও ক্যামেরা"</string> @@ -1318,6 +1315,16 @@ <string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"সম্প্রতি <xliff:g id="APP_NAME">%1$s</xliff:g> অ্যাপে (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>) ব্যবহার করা হয়েছে"</string> <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>) ব্যবহার করা হচ্ছে"</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>) ব্যবহার করা হয়েছে"</string> + <string name="shortcut_helper_category_system" msgid="462110876978937359">"সিস্টেম"</string> + <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"মাল্টিটাস্কিং"</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_a11y" msgid="6314444792641773464">"অ্যাক্সেসিবিলিটি"</string> + <string name="shortcut_helper_title" msgid="8567500639300970049">"কীবোর্ড শর্টকাট"</string> + <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"সার্চ শর্টকাট"</string> + <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"আইকন আড়াল করুন"</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="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> diff --git a/packages/SystemUI/res/values-bs/strings.xml b/packages/SystemUI/res/values-bs/strings.xml index 1aea98254a2e..4916d80f5245 100644 --- a/packages/SystemUI/res/values-bs/strings.xml +++ b/packages/SystemUI/res/values-bs/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Automatski uključi ponovo sutra"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Funkcije kao što su Quick Share i Pronađi moj uređaj koriste Bluetooth"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth će se uključiti sutra ujutro"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Dijeljenje zvuka"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Dijeljenje zvuka"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> baterije"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Zvuk"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Slušalice"</string> @@ -361,19 +363,19 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Koji dio uređaja je imao problem?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Odaberite vrstu problema"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Snimanje ekrana"</string> - <string-array name="qs_record_issue_types"> - <item msgid="2947988124014085798">"Performanse"</item> - <item msgid="1627504621139124393">"Korisnički interfejs"</item> - <item msgid="8309220355268900335">"Baterija"</item> - </string-array> + <string name="performance" msgid="6552785217174378320">"Performanse"</string> + <string name="user_interface" msgid="3712869377953950887">"Korisnički interfejs"</string> + <string name="thermal" msgid="6758074791325414831">"Termalno"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Način rada jednom rukom"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Slušni aparati"</string> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Aktivno"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Veza je prekinuta"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Slušni aparati"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Uparite novi uređaj"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Kliknite da uparite novi uređaj"</string> <string name="hearing_devices_presets_error" msgid="350363093458408536">"Ažuriranje zadane postavke nije uspjelo"</string> - <!-- no translation found for hearing_devices_preset_label (7878267405046232358) --> - <skip /> + <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Zadana postavka"</string> + <string name="live_caption_title" msgid="8916875614623730005">"Automatski titlovi"</string> <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> @@ -727,11 +729,6 @@ <string name="keyboard_key_button_template" msgid="8005673627272051429">"Dugme <xliff:g id="NAME">%1$s</xliff:g>"</string> <string name="keyboard_key_home" msgid="3734400625170020657">"Tipka za početak"</string> <string name="keyboard_key_back" msgid="4185420465469481999">"Nazad"</string> - <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"Strelica nagore"</string> - <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"Strelica nadolje"</string> - <string name="keyboard_key_dpad_left" msgid="144176368026538621">"Strelica ulijevo"</string> - <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"Strelica udesno"</string> - <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"Sredina"</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> @@ -1201,7 +1198,7 @@ <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"WiFi se trenutno ne može automatski povezati"</string> <string name="see_all_networks" msgid="3773666844913168122">"Prikaži sve"</string> <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Da promijenite mrežu, isključite ethernet"</string> - <string name="wifi_scan_notify_message" msgid="3753839537448621794">"Radi poboljšanja iskustva s uređajem aplikacije i usluge i dalje mogu tražiti WiFi mreže bilo kada, čak i kada je WiFi isključen. Ovo možete promijeniti u Postavkama traženja WiFi mreže. "<annotation id="link">"Promijeni"</annotation></string> + <string name="wifi_scan_notify_message" msgid="3753839537448621794">"Radi poboljšanja iskustva s uređajem aplikacije i usluge i dalje mogu tražiti WiFi mreže bilo kada, čak i kada je WiFi isključen. Ovo možete promijeniti u postavkama traženja WiFi-ja. "<annotation id="link">"Promijeni"</annotation></string> <string name="turn_off_airplane_mode" msgid="8425587763226548579">"Isključi način rada u avionu"</string> <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> želi dodati sljedeću karticu u Brze postavke"</string> <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Dodaj karticu"</string> @@ -1210,7 +1207,7 @@ <string name="fgs_manager_footer_label" msgid="8276763570622288231">"{count,plural, =1{# aplikacija je aktivna}one{# aplikacija je aktivna}few{# aplikacije su aktivne}other{# aplikacija je aktivno}}"</string> <string name="fgs_dot_content_description" msgid="2865071539464777240">"Nove informacije"</string> <string name="fgs_manager_dialog_title" msgid="5879184257257718677">"Aktivne aplikacije"</string> - <string name="fgs_manager_dialog_message" msgid="2670045017200730076">"Ove aplikacije su aktivne i pokrenute, čak i kada ih ne koristite. Ovim se poboljšava njihova funkcionalnost, ali može uticati i na vijek trajanja baterije."</string> + <string name="fgs_manager_dialog_message" msgid="2670045017200730076">"Ove aplikacije su aktivne i pokrenute, čak i kada ih ne koristite. Ovim se poboljšava njihova funkcionalnost, ali to može uticati i na vijek trajanja baterije."</string> <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Zaustavi"</string> <string name="fgs_manager_app_item_stop_button_stopped_label" msgid="6950382004441263922">"Zaustavljeno"</string> <string name="clipboard_edit_text_done" msgid="4551887727694022409">"Gotovo"</string> @@ -1275,7 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"sklopljeno"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"otklopljeno"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string> - <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"Preostalo baterije: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Baterija pisaljke <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Priključite pisaljku na punjač"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"Baterija pisaljke je slaba"</string> <string name="video_camera" msgid="7654002575156149298">"Video kamera"</string> @@ -1318,6 +1315,16 @@ <string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"Nedavno je koristila aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string> <string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"Koristi aplikacija <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>)"</string> <string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"Nedavno je koristila aplikacija <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>)"</string> + <string name="shortcut_helper_category_system" msgid="462110876978937359">"Sistem"</string> + <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Multitasking"</string> + <string name="shortcut_helper_category_input" msgid="8674018654124839566">"Unos"</string> + <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Prečice aplikacije"</string> + <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Pristupačnost"</string> + <string name="shortcut_helper_title" msgid="8567500639300970049">"Prečice tastature"</string> + <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Prečica pretraživanja"</string> + <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ikona sužavanja"</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="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Pozadinsko osvjetljenje tastature"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"%1$d. nivo od %2$d"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"Kontrole za dom"</string> diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml index 8229e4b4c7ac..5afad1b93805 100644 --- a/packages/SystemUI/res/values-ca/strings.xml +++ b/packages/SystemUI/res/values-ca/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Torna\'l a activar automàticament demà"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Les funcions com Quick Share i Troba el meu dispositiu utilitzen el Bluetooth"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"El Bluetooth s\'activarà demà al matí"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Compartició d\'àudio"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"S\'està compartint l\'àudio"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> de bateria"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Àudio"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Auriculars"</string> @@ -361,19 +363,19 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"L\'experiència amb el dispositiu s\'ha vist afectada?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Selecciona el tipus de problema"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Gravació de pantalla"</string> - <string-array name="qs_record_issue_types"> - <item msgid="2947988124014085798">"Rendiment"</item> - <item msgid="1627504621139124393">"Interfície d\'usuari"</item> - <item msgid="8309220355268900335">"Bateria"</item> - </string-array> + <string name="performance" msgid="6552785217174378320">"Rendiment"</string> + <string name="user_interface" msgid="3712869377953950887">"Interfície d\'usuari"</string> + <string name="thermal" msgid="6758074791325414831">"Tèrmic"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Mode d\'una mà"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Audiòfons"</string> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Actiu"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Desconnectat"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Audiòfons"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Vincula un dispositiu nou"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Fes clic per vincular un dispositiu nou"</string> <string name="hearing_devices_presets_error" msgid="350363093458408536">"No s\'ha pogut actualitzar el valor predefinit"</string> - <!-- no translation found for hearing_devices_preset_label (7878267405046232358) --> - <skip /> + <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Valors predefinits"</string> + <string name="live_caption_title" msgid="8916875614623730005">"Subtítols instantanis"</string> <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> @@ -727,11 +729,6 @@ <string name="keyboard_key_button_template" msgid="8005673627272051429">"Botó <xliff:g id="NAME">%1$s</xliff:g>"</string> <string name="keyboard_key_home" msgid="3734400625170020657">"Inici"</string> <string name="keyboard_key_back" msgid="4185420465469481999">"Enrere"</string> - <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"Fletxa amunt"</string> - <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"Fletxa avall"</string> - <string name="keyboard_key_dpad_left" msgid="144176368026538621">"Fletxa esquerra"</string> - <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"Fletxa dreta"</string> - <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"Centre"</string> <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string> <string name="keyboard_key_space" msgid="6980847564173394012">"Espai"</string> <string name="keyboard_key_enter" msgid="8633362970109751646">"Retorn"</string> @@ -1275,7 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"plegat"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"desplegat"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string> - <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"Queda un <xliff:g id="PERCENTAGE">%s</xliff:g> de bateria"</string> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Bateria del llapis òptic: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Connecta el llapis òptic a un carregador"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"Bateria del llapis òptic baixa"</string> <string name="video_camera" msgid="7654002575156149298">"Càmera de vídeo"</string> @@ -1318,6 +1315,16 @@ <string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"Utilitzat recentment per <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string> <string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"En ús per <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>)"</string> <string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"Utilitzat recentment per <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>)"</string> + <string name="shortcut_helper_category_system" msgid="462110876978937359">"Sistema"</string> + <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Multitasca"</string> + <string name="shortcut_helper_category_input" msgid="8674018654124839566">"Entrada"</string> + <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Dreceres d\'aplicacions"</string> + <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Accessibilitat"</string> + <string name="shortcut_helper_title" msgid="8567500639300970049">"Tecles de drecera"</string> + <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Dreceres de cerca"</string> + <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Replega la icona"</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="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Retroil·luminació del teclat"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"Nivell %1$d de %2$d"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"Controls de la llar"</string> diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml index 85cef4b65426..b5293c0057a9 100644 --- a/packages/SystemUI/res/values-cs/strings.xml +++ b/packages/SystemUI/res/values-cs/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Zítra znovu automaticky zapnout"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Bluetooth využívají funkce jako Quick Share a Najdi moje zařízení."</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth se zapne zítra ráno."</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Sdílení zvuku"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Sdílení zvuku"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Baterie: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Zvuk"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Sluchátka"</string> @@ -361,19 +363,19 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Co v zařízení bylo ovlivněno?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Vyberte druh problém"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Záznam obrazovky"</string> - <string-array name="qs_record_issue_types"> - <item msgid="2947988124014085798">"Výkon"</item> - <item msgid="1627504621139124393">"Uživatelské rozhraní"</item> - <item msgid="8309220355268900335">"Baterie"</item> - </string-array> + <string name="performance" msgid="6552785217174378320">"Výkon"</string> + <string name="user_interface" msgid="3712869377953950887">"Uživatelské rozhraní"</string> + <string name="thermal" msgid="6758074791325414831">"Termovize"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Režim jedné ruky"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Naslouchátka"</string> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Aktivní"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Odpojeno"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Naslouchátka"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Spárovat nové zařízení"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Kliknutím spárujete nové zařízení"</string> <string name="hearing_devices_presets_error" msgid="350363093458408536">"Předvolbu nelze aktualizovat"</string> - <!-- no translation found for hearing_devices_preset_label (7878267405046232358) --> - <skip /> + <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Předvolba"</string> + <string name="live_caption_title" msgid="8916875614623730005">"Živý přepis"</string> <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> @@ -727,11 +729,6 @@ <string name="keyboard_key_button_template" msgid="8005673627272051429">"Tlačítko <xliff:g id="NAME">%1$s</xliff:g>"</string> <string name="keyboard_key_home" msgid="3734400625170020657">"Home"</string> <string name="keyboard_key_back" msgid="4185420465469481999">"Zpět"</string> - <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"Šipka nahoru"</string> - <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"Šipka dolů"</string> - <string name="keyboard_key_dpad_left" msgid="144176368026538621">"Šipka vlevo"</string> - <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"Šipka doprava"</string> - <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"Střed"</string> <string name="keyboard_key_tab" msgid="4592772350906496730">"TAB"</string> <string name="keyboard_key_space" msgid="6980847564173394012">"Mezerník"</string> <string name="keyboard_key_enter" msgid="8633362970109751646">"Enter"</string> @@ -789,8 +786,8 @@ <string name="system_multitasking_rhs" msgid="8714224917276297810">"Použít rozdělenou obrazovku se stávající aplikací vpravo"</string> <string name="system_multitasking_lhs" msgid="8402954791206308783">"Použít rozdělenou obrazovku se stávající aplikací vlevo"</string> <string name="system_multitasking_full_screen" msgid="336048080383640562">"Přepnout z rozdělené obrazovky na celou obrazovku"</string> - <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Přechod na aplikaci vpravo nebo dole v režimu rozdělené obrazovky"</string> - <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Přechod na aplikaci vlevo nebo nahoře v režimu rozdělené obrazovky"</string> + <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> <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> @@ -923,7 +920,7 @@ <string name="mobile_data" msgid="4564407557775397216">"Mobilní data"</string> <string name="mobile_data_text_format" msgid="6806501540022589786">"<xliff:g id="ID_1">%1$s</xliff:g> – <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="mobile_carrier_text_format" msgid="8912204177152950766">"<xliff:g id="CARRIER_NAME">%1$s</xliff:g>, <xliff:g id="MOBILE_DATA_TYPE">%2$s</xliff:g>"</string> - <string name="wifi_is_off" msgid="5389597396308001471">"Wi-Fi je vypnuta"</string> + <string name="wifi_is_off" msgid="5389597396308001471">"Wi-Fi je vypnutá"</string> <string name="bt_is_off" msgid="7436344904889461591">"Bluetooth je vypnuto"</string> <string name="dnd_is_off" msgid="3185706903793094463">"Režim Nerušit je vypnut"</string> <string name="dnd_is_on" msgid="7009368176361546279">"Režim Nerušit je zapnutý"</string> @@ -933,7 +930,7 @@ <string name="running_foreground_services_title" msgid="5137313173431186685">"Aplikace běžící na pozadí"</string> <string name="running_foreground_services_msg" msgid="3009459259222695385">"Klepnutím zobrazíte podrobnosti o využití baterie a dat"</string> <string name="mobile_data_disable_title" msgid="5366476131671617790">"Vypnout mobilní data?"</string> - <string name="mobile_data_disable_message" msgid="8604966027899770415">"Prostřednictvím <xliff:g id="CARRIER">%s</xliff:g> nebudete moci používat data ani internet. Internet bude dostupný pouze přes Wi-Fi."</string> + <string name="mobile_data_disable_message" msgid="8604966027899770415">"Prostřednictvím operátora <xliff:g id="CARRIER">%s</xliff:g> nebudete moct používat data ani internet. Internet bude dostupný pouze přes Wi-Fi."</string> <string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"vašeho operátora"</string> <string name="auto_data_switch_disable_title" msgid="5146527155665190652">"Přepnout zpět na operátora <xliff:g id="CARRIER">%s</xliff:g>?"</string> <string name="auto_data_switch_disable_message" msgid="5885533647399535852">"Mobilní data se nebudou automaticky přepínat podle dostupnosti"</string> @@ -1211,7 +1208,7 @@ <string name="fgs_dot_content_description" msgid="2865071539464777240">"Nové informace"</string> <string name="fgs_manager_dialog_title" msgid="5879184257257718677">"Aktivní aplikace"</string> <string name="fgs_manager_dialog_message" msgid="2670045017200730076">"Tyto aplikace jsou spuštěné a aktivní, i když je nepoužíváte. Zlepšuje to jejich funkčnost, ale může to mít dopad na výdrž baterie."</string> - <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Konec"</string> + <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Zastavit"</string> <string name="fgs_manager_app_item_stop_button_stopped_label" msgid="6950382004441263922">"Zastaveno"</string> <string name="clipboard_edit_text_done" msgid="4551887727694022409">"Hotovo"</string> <string name="clipboard_overlay_text_copied" msgid="1872624400464891363">"Zkopírováno"</string> @@ -1275,7 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"složené"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"rozložené"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string> - <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"Zbývá <xliff:g id="PERCENTAGE">%s</xliff:g> baterie"</string> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Baterie dotykového pera <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Připojte dotykové pero k nabíječce"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"Slabá baterie dotykového pera"</string> <string name="video_camera" msgid="7654002575156149298">"Videokamera"</string> @@ -1318,6 +1315,16 @@ <string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"Nedávno použila aplikace <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string> <string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"Právě používán aplikací <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>)"</string> <string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"Nedávno použila aplikace <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>)"</string> + <string name="shortcut_helper_category_system" msgid="462110876978937359">"Systém"</string> + <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Multitasking"</string> + <string name="shortcut_helper_category_input" msgid="8674018654124839566">"Vstup"</string> + <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Zkratky aplikací"</string> + <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Přístupnost"</string> + <string name="shortcut_helper_title" msgid="8567500639300970049">"Klávesové zkratky"</string> + <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Vyhledat zkratky"</string> + <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ikona sbalení"</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="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> diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml index 836fe26c7992..6e2323be3921 100644 --- a/packages/SystemUI/res/values-da/strings.xml +++ b/packages/SystemUI/res/values-da/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Aktivér automatisk igen i morgen"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Funktioner som f.eks. Quick Share og Find min enhed anvender Bluetooth"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth aktiveres i morgen tidlig"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Lyddeling"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Deler lyd"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> batteri"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Lyd"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Headset"</string> @@ -361,19 +363,19 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Hvilken del af din enhedsoplevelse blev påvirket?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Vælg problemtype"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Skærmoptagelse"</string> - <string-array name="qs_record_issue_types"> - <item msgid="2947988124014085798">"Ydeevne"</item> - <item msgid="1627504621139124393">"Brugerflade"</item> - <item msgid="8309220355268900335">"Batteri"</item> - </string-array> + <string name="performance" msgid="6552785217174378320">"Ydeevne"</string> + <string name="user_interface" msgid="3712869377953950887">"Brugerflade"</string> + <string name="thermal" msgid="6758074791325414831">"Termisk"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Enhåndstilstand"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Høreapparater"</string> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Aktivt"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Afbrudt"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Høreapparater"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Par ny enhed"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Klik for at parre en ny enhed"</string> <string name="hearing_devices_presets_error" msgid="350363093458408536">"Forindstillingen kunne ikke opdateres"</string> - <!-- no translation found for hearing_devices_preset_label (7878267405046232358) --> - <skip /> + <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Forindstilling"</string> + <string name="live_caption_title" msgid="8916875614623730005">"Livetekstning"</string> <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> @@ -727,11 +729,6 @@ <string name="keyboard_key_button_template" msgid="8005673627272051429">"<xliff:g id="NAME">%1$s</xliff:g>-knap"</string> <string name="keyboard_key_home" msgid="3734400625170020657">"Home"</string> <string name="keyboard_key_back" msgid="4185420465469481999">"Tilbage"</string> - <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"Pil op"</string> - <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"Pil ned"</string> - <string name="keyboard_key_dpad_left" msgid="144176368026538621">"Venstrepil"</string> - <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"Højrepil"</string> - <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"Midtertast"</string> <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string> <string name="keyboard_key_space" msgid="6980847564173394012">"Mellemrumstast"</string> <string name="keyboard_key_enter" msgid="8633362970109751646">"Enter"</string> @@ -1275,7 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"foldet"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"foldet ud"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s/%2$s"</string> - <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"<xliff:g id="PERCENTAGE">%s</xliff:g> batteri tilbage"</string> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Batteriniveau på styluspen: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Slut din styluspen til en oplader"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"Lavt batteriniveau på styluspen"</string> <string name="video_camera" msgid="7654002575156149298">"Videokamera"</string> @@ -1318,6 +1315,16 @@ <string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"Brugt for nylig af <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string> <string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"Bruges af <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>)"</string> <string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"Brugt for nylig af <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>)"</string> + <string name="shortcut_helper_category_system" msgid="462110876978937359">"System"</string> + <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Multitasking"</string> + <string name="shortcut_helper_category_input" msgid="8674018654124839566">"Input"</string> + <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Appgenveje"</string> + <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Hjælpefunktioner"</string> + <string name="shortcut_helper_title" msgid="8567500639300970049">"Tastaturgenveje"</string> + <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Genveje til søgning"</string> + <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ikon for Skjul"</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="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Tastaturets baggrundslys"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"Niveau %1$d af %2$d"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"Hjemmestyring"</string> diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml index 07a07a83e10f..bf28a88250a6 100644 --- a/packages/SystemUI/res/values-de/strings.xml +++ b/packages/SystemUI/res/values-de/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Morgen automatisch wieder aktivieren"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Funktionen wie „Quick Share“ und „Mein Gerät finden“ verwenden Bluetooth"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth wird morgen früh aktiviert"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Audiofreigabe"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Audioinhalte werden freigegeben"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Akkustand: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Headset"</string> @@ -361,19 +363,19 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Welche Bereiche des Geräts waren betroffen?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Art des Problems auswählen"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Bildschirmaufnahme"</string> - <string-array name="qs_record_issue_types"> - <item msgid="2947988124014085798">"Leistung"</item> - <item msgid="1627504621139124393">"Benutzeroberfläche"</item> - <item msgid="8309220355268900335">"Akku"</item> - </string-array> + <string name="performance" msgid="6552785217174378320">"Leistung"</string> + <string name="user_interface" msgid="3712869377953950887">"Benutzeroberfläche"</string> + <string name="thermal" msgid="6758074791325414831">"Überhitzung"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Einhandmodus"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Hörgeräte"</string> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Aktiv"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Nicht verbunden"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Hörgeräte"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Neues Gerät koppeln"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Klicken, um neues Gerät zu koppeln"</string> <string name="hearing_devices_presets_error" msgid="350363093458408536">"Voreinstellung konnte nicht aktualisiert werden"</string> - <!-- no translation found for hearing_devices_preset_label (7878267405046232358) --> - <skip /> + <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Voreinstellung"</string> + <string name="live_caption_title" msgid="8916875614623730005">"Automatische Untertitel"</string> <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> @@ -727,11 +729,6 @@ <string name="keyboard_key_button_template" msgid="8005673627272051429">"Taste <xliff:g id="NAME">%1$s</xliff:g>"</string> <string name="keyboard_key_home" msgid="3734400625170020657">"Pos1"</string> <string name="keyboard_key_back" msgid="4185420465469481999">"Zurück"</string> - <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"Aufwärtspfeil"</string> - <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"Abwärtspfeil"</string> - <string name="keyboard_key_dpad_left" msgid="144176368026538621">"Linkspfeil"</string> - <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"Rechtspfeil"</string> - <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"Zentrieren"</string> <string name="keyboard_key_tab" msgid="4592772350906496730">"Tabulatortaste"</string> <string name="keyboard_key_space" msgid="6980847564173394012">"Leertaste"</string> <string name="keyboard_key_enter" msgid="8633362970109751646">"Eingabetaste"</string> @@ -1177,7 +1174,7 @@ <string name="battery_state_unknown_notification_title" msgid="8464703640483773454">"Problem beim Lesen des Akkustands"</string> <string name="battery_state_unknown_notification_text" msgid="13720937839460899">"Für weitere Informationen tippen"</string> <string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"Kein Wecker gestellt"</string> - <string name="accessibility_bouncer" msgid="5896923685673320070">"Displaysperre-Anmeldedaten eingeben"</string> + <string name="accessibility_bouncer" msgid="5896923685673320070">"Displaysperre eingeben"</string> <string name="accessibility_fingerprint_label" msgid="5255731221854153660">"Fingerabdrucksensor"</string> <string name="accessibility_authenticate_hint" msgid="798914151813205721">"Authentifizieren"</string> <string name="accessibility_enter_hint" msgid="2617864063504824834">"Eingeben des Geräts"</string> @@ -1275,7 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"zugeklappt"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"aufgeklappt"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s/%2$s"</string> - <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"Akku bei <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Akkustand des Eingabestifts: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Schließe deinen Eingabestift an ein Ladegerät an"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"Stylus-Akkustand niedrig"</string> <string name="video_camera" msgid="7654002575156149298">"Videokamera"</string> @@ -1318,6 +1315,16 @@ <string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"Kürzlich von <xliff:g id="APP_NAME">%1$s</xliff:g> verwendet (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string> <string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"Verwendet von <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>)"</string> <string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"Kürzlich von <xliff:g id="APP_NAME">%1$s</xliff:g> verwendet (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string> + <string name="shortcut_helper_category_system" msgid="462110876978937359">"System"</string> + <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Multitasking"</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_a11y" msgid="6314444792641773464">"Bedienungshilfen"</string> + <string name="shortcut_helper_title" msgid="8567500639300970049">"Tastenkürzel"</string> + <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Tastenkürzel suchen"</string> + <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Symbol „Minimieren“"</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="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Tastaturbeleuchtung"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"Level %1$d von %2$d"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"Smart-Home-Steuerung"</string> diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml index a1dd5f920a39..3dd5fc0f1c8c 100644 --- a/packages/SystemUI/res/values-el/strings.xml +++ b/packages/SystemUI/res/values-el/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Αυτόματη ενεργοποίηση ξανά αύριο"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Λειτουργίες όπως το Quick Share και η Εύρεση συσκευής χρησιμοποιούν το Bluetooth"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Το Bluetooth θα ενεργοποιηθεί αύριο το πρωί"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Κοινή χρήση ήχου"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Κοινή χρήση ήχου"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Μπαταρία <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Ήχος"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Ακουστικά"</string> @@ -361,19 +363,19 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Ποιο κομμάτι της εμπειρίας συσκευής επηρεάστηκε;"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Επιλογή τύπου προβλήματος"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Εγγραφή οθόνης"</string> - <string-array name="qs_record_issue_types"> - <item msgid="2947988124014085798">"Απόδοση"</item> - <item msgid="1627504621139124393">"Διεπαφή χρήστη"</item> - <item msgid="8309220355268900335">"Μπαταρία"</item> - </string-array> + <string name="performance" msgid="6552785217174378320">"Απόδοση"</string> + <string name="user_interface" msgid="3712869377953950887">"Διεπαφή χρήστη"</string> + <string name="thermal" msgid="6758074791325414831">"Θερμικό"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Λειτουργία ενός χεριού"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Συσκευές ακοής"</string> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Ενεργά"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Αποσυνδεδεμένα"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Συσκευές ακοής"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Σύζευξη νέας συσκευής"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Κάντε κλικ για σύζευξη νέας συσκευής"</string> <string name="hearing_devices_presets_error" msgid="350363093458408536">"Δεν ήταν δυνατή η ενημέρωση της προεπιλογής"</string> - <!-- no translation found for hearing_devices_preset_label (7878267405046232358) --> - <skip /> + <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Προεπιλογή"</string> + <string name="live_caption_title" msgid="8916875614623730005">"Ζωντανοί υπότιτλοι"</string> <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> @@ -619,7 +621,7 @@ <string name="volume_panel_spatial_audio_title" msgid="3367048857932040660">"Χωρικός ήχος"</string> <string name="volume_panel_spatial_audio_off" msgid="4177490084606772989">"Ανενεργός"</string> <string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"Σταθερός"</string> - <string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Παρακ. κίνησ. κεφαλ."</string> + <string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Παρακ. κίνησ. κεφαλής"</string> <string name="volume_ringer_change" msgid="3574969197796055532">"Πατήστε για να αλλάξετε τη λειτουργία ειδοποίησης ήχου"</string> <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"σίγαση"</string> <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"κατάργηση σίγασης"</string> @@ -727,11 +729,6 @@ <string name="keyboard_key_button_template" msgid="8005673627272051429">"Κουμπί <xliff:g id="NAME">%1$s</xliff:g>"</string> <string name="keyboard_key_home" msgid="3734400625170020657">"Home"</string> <string name="keyboard_key_back" msgid="4185420465469481999">"Πίσω"</string> - <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"Επάνω βέλος"</string> - <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"Κάτω βέλος"</string> - <string name="keyboard_key_dpad_left" msgid="144176368026538621">"Αριστερό βέλος"</string> - <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"Δεξί βέλος"</string> - <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"Κέντρο"</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">"Enter"</string> @@ -1275,7 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"διπλωμένη"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"ξεδιπλωμένη"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s/%2$s"</string> - <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"Απομένει το <xliff:g id="PERCENTAGE">%s</xliff:g> της μπαταρίας"</string> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Μπαταρία γραφίδας <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Συνδέστε τη γραφίδα σε έναν φορτιστή"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"Χαμηλή στάθμη μπαταρίας γραφίδας"</string> <string name="video_camera" msgid="7654002575156149298">"Βιντεοκάμερα"</string> @@ -1318,6 +1315,16 @@ <string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"Χρησιμοποιήθηκε πρόσφατα από την εφαρμογή <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string> <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>)"</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>)"</string> + <string name="shortcut_helper_category_system" msgid="462110876978937359">"Σύστημα"</string> + <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Πολυδιεργασία"</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_a11y" msgid="6314444792641773464">"Προσβασιμότητα"</string> + <string name="shortcut_helper_title" msgid="8567500639300970049">"Συντομεύσεις πληκτρολογίου"</string> + <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Συντομεύσεις αναζήτησης"</string> + <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Εικονίδιο σύμπτυξης"</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="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> diff --git a/packages/SystemUI/res/values-en-rAU/strings.xml b/packages/SystemUI/res/values-en-rAU/strings.xml index 542d660c2f67..e830f3c2a8cd 100644 --- a/packages/SystemUI/res/values-en-rAU/strings.xml +++ b/packages/SystemUI/res/values-en-rAU/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Automatically turn on again tomorrow"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Features like Quick Share and Find My Device use Bluetooth"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth will turn on tomorrow morning"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Audio sharing"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Sharing audio"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> battery"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Headset"</string> @@ -361,19 +363,19 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"What part of your device experience was affected?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Select issue type"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Screen record"</string> - <string-array name="qs_record_issue_types"> - <item msgid="2947988124014085798">"Performance"</item> - <item msgid="1627504621139124393">"User interface"</item> - <item msgid="8309220355268900335">"Battery"</item> - </string-array> + <string name="performance" msgid="6552785217174378320">"Performance"</string> + <string name="user_interface" msgid="3712869377953950887">"User interface"</string> + <string name="thermal" msgid="6758074791325414831">"Thermal"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"One-handed mode"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Hearing devices"</string> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Active"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Disconnected"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Hearing devices"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Pair new device"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Click to pair new device"</string> <string name="hearing_devices_presets_error" msgid="350363093458408536">"Couldn\'t update preset"</string> - <!-- no translation found for hearing_devices_preset_label (7878267405046232358) --> - <skip /> + <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Preset"</string> + <string name="live_caption_title" msgid="8916875614623730005">"Live Caption"</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> @@ -727,11 +729,6 @@ <string name="keyboard_key_button_template" msgid="8005673627272051429">"Button <xliff:g id="NAME">%1$s</xliff:g>"</string> <string name="keyboard_key_home" msgid="3734400625170020657">"Home"</string> <string name="keyboard_key_back" msgid="4185420465469481999">"Back"</string> - <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"Up arrow"</string> - <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"Down arrow"</string> - <string name="keyboard_key_dpad_left" msgid="144176368026538621">"Left arrow"</string> - <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"Right arrow"</string> - <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"Centre"</string> <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> @@ -1275,7 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"folded"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"unfolded"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s/%2$s"</string> - <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"<xliff:g id="PERCENTAGE">%s</xliff:g> battery remaining"</string> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Stylus battery <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Connect your stylus to a charger"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"Stylus battery low"</string> <string name="video_camera" msgid="7654002575156149298">"Video camera"</string> @@ -1318,6 +1315,16 @@ <string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"Recently used by <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string> <string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"In use by <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>)"</string> <string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"Recently used by <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>)"</string> + <string name="shortcut_helper_category_system" msgid="462110876978937359">"System"</string> + <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Multitasking"</string> + <string name="shortcut_helper_category_input" msgid="8674018654124839566">"Input"</string> + <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"App shortcuts"</string> + <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Accessibility"</string> + <string name="shortcut_helper_title" msgid="8567500639300970049">"Keyboard shortcuts"</string> + <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Search shortcuts"</string> + <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Collapse icon"</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="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Keyboard backlight"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"Level %1$d of %2$d"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"Home controls"</string> diff --git a/packages/SystemUI/res/values-en-rCA/strings.xml b/packages/SystemUI/res/values-en-rCA/strings.xml index 4ac0efe0d853..046ce28b6488 100644 --- a/packages/SystemUI/res/values-en-rCA/strings.xml +++ b/packages/SystemUI/res/values-en-rCA/strings.xml @@ -281,8 +281,8 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Automatically turn on again tomorrow"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Features like Quick Share and Find My Device use Bluetooth"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth will turn on tomorrow morning"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Audio Sharing"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Sharing Audio"</string> + <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Share audio"</string> + <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Sharing audio"</string> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> battery"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Headset"</string> @@ -361,18 +361,19 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"What part of your device experience was affected?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Select issue type"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Screen record"</string> - <string-array name="qs_record_issue_types"> - <item msgid="2947988124014085798">"Performance"</item> - <item msgid="1627504621139124393">"User Interface"</item> - <item msgid="8309220355268900335">"Battery"</item> - </string-array> + <string name="performance" msgid="6552785217174378320">"Performance"</string> + <string name="user_interface" msgid="3712869377953950887">"User Interface"</string> + <string name="thermal" msgid="6758074791325414831">"Thermal"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"One-handed mode"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Hearing devices"</string> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Active"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Disconnected"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Hearing devices"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Pair new device"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Click to pair new device"</string> <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="live_caption_title" msgid="8916875614623730005">"Live Caption"</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> @@ -726,11 +727,6 @@ <string name="keyboard_key_button_template" msgid="8005673627272051429">"Button <xliff:g id="NAME">%1$s</xliff:g>"</string> <string name="keyboard_key_home" msgid="3734400625170020657">"Home"</string> <string name="keyboard_key_back" msgid="4185420465469481999">"Back"</string> - <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"Up arrow"</string> - <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"Down arrow"</string> - <string name="keyboard_key_dpad_left" msgid="144176368026538621">"Left arrow"</string> - <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"Right arrow"</string> - <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"Center"</string> <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> @@ -1274,7 +1270,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"folded"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"unfolded"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string> - <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"<xliff:g id="PERCENTAGE">%s</xliff:g> battery remaining"</string> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Stylus battery <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Connect your stylus to a charger"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"Stylus battery low"</string> <string name="video_camera" msgid="7654002575156149298">"Video camera"</string> @@ -1317,6 +1313,16 @@ <string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"Recently used by <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string> <string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"In use by <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>)"</string> <string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"Recently used by <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>)"</string> + <string name="shortcut_helper_category_system" msgid="462110876978937359">"System"</string> + <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Multitasking"</string> + <string name="shortcut_helper_category_input" msgid="8674018654124839566">"Input"</string> + <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"App shortcuts"</string> + <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Accessibility"</string> + <string name="shortcut_helper_title" msgid="8567500639300970049">"Keyboard shortcuts"</string> + <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Search shortcuts"</string> + <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Collapse icon"</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="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Keyboard backlight"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"Level %1$d of %2$d"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"Home Controls"</string> diff --git a/packages/SystemUI/res/values-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml index 542d660c2f67..e830f3c2a8cd 100644 --- a/packages/SystemUI/res/values-en-rGB/strings.xml +++ b/packages/SystemUI/res/values-en-rGB/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Automatically turn on again tomorrow"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Features like Quick Share and Find My Device use Bluetooth"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth will turn on tomorrow morning"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Audio sharing"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Sharing audio"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> battery"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Headset"</string> @@ -361,19 +363,19 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"What part of your device experience was affected?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Select issue type"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Screen record"</string> - <string-array name="qs_record_issue_types"> - <item msgid="2947988124014085798">"Performance"</item> - <item msgid="1627504621139124393">"User interface"</item> - <item msgid="8309220355268900335">"Battery"</item> - </string-array> + <string name="performance" msgid="6552785217174378320">"Performance"</string> + <string name="user_interface" msgid="3712869377953950887">"User interface"</string> + <string name="thermal" msgid="6758074791325414831">"Thermal"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"One-handed mode"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Hearing devices"</string> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Active"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Disconnected"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Hearing devices"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Pair new device"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Click to pair new device"</string> <string name="hearing_devices_presets_error" msgid="350363093458408536">"Couldn\'t update preset"</string> - <!-- no translation found for hearing_devices_preset_label (7878267405046232358) --> - <skip /> + <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Preset"</string> + <string name="live_caption_title" msgid="8916875614623730005">"Live Caption"</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> @@ -727,11 +729,6 @@ <string name="keyboard_key_button_template" msgid="8005673627272051429">"Button <xliff:g id="NAME">%1$s</xliff:g>"</string> <string name="keyboard_key_home" msgid="3734400625170020657">"Home"</string> <string name="keyboard_key_back" msgid="4185420465469481999">"Back"</string> - <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"Up arrow"</string> - <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"Down arrow"</string> - <string name="keyboard_key_dpad_left" msgid="144176368026538621">"Left arrow"</string> - <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"Right arrow"</string> - <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"Centre"</string> <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> @@ -1275,7 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"folded"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"unfolded"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s/%2$s"</string> - <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"<xliff:g id="PERCENTAGE">%s</xliff:g> battery remaining"</string> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Stylus battery <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Connect your stylus to a charger"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"Stylus battery low"</string> <string name="video_camera" msgid="7654002575156149298">"Video camera"</string> @@ -1318,6 +1315,16 @@ <string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"Recently used by <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string> <string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"In use by <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>)"</string> <string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"Recently used by <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>)"</string> + <string name="shortcut_helper_category_system" msgid="462110876978937359">"System"</string> + <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Multitasking"</string> + <string name="shortcut_helper_category_input" msgid="8674018654124839566">"Input"</string> + <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"App shortcuts"</string> + <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Accessibility"</string> + <string name="shortcut_helper_title" msgid="8567500639300970049">"Keyboard shortcuts"</string> + <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Search shortcuts"</string> + <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Collapse icon"</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="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Keyboard backlight"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"Level %1$d of %2$d"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"Home controls"</string> diff --git a/packages/SystemUI/res/values-en-rIN/strings.xml b/packages/SystemUI/res/values-en-rIN/strings.xml index 542d660c2f67..e830f3c2a8cd 100644 --- a/packages/SystemUI/res/values-en-rIN/strings.xml +++ b/packages/SystemUI/res/values-en-rIN/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Automatically turn on again tomorrow"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Features like Quick Share and Find My Device use Bluetooth"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth will turn on tomorrow morning"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Audio sharing"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Sharing audio"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> battery"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Headset"</string> @@ -361,19 +363,19 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"What part of your device experience was affected?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Select issue type"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Screen record"</string> - <string-array name="qs_record_issue_types"> - <item msgid="2947988124014085798">"Performance"</item> - <item msgid="1627504621139124393">"User interface"</item> - <item msgid="8309220355268900335">"Battery"</item> - </string-array> + <string name="performance" msgid="6552785217174378320">"Performance"</string> + <string name="user_interface" msgid="3712869377953950887">"User interface"</string> + <string name="thermal" msgid="6758074791325414831">"Thermal"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"One-handed mode"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Hearing devices"</string> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Active"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Disconnected"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Hearing devices"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Pair new device"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Click to pair new device"</string> <string name="hearing_devices_presets_error" msgid="350363093458408536">"Couldn\'t update preset"</string> - <!-- no translation found for hearing_devices_preset_label (7878267405046232358) --> - <skip /> + <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Preset"</string> + <string name="live_caption_title" msgid="8916875614623730005">"Live Caption"</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> @@ -727,11 +729,6 @@ <string name="keyboard_key_button_template" msgid="8005673627272051429">"Button <xliff:g id="NAME">%1$s</xliff:g>"</string> <string name="keyboard_key_home" msgid="3734400625170020657">"Home"</string> <string name="keyboard_key_back" msgid="4185420465469481999">"Back"</string> - <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"Up arrow"</string> - <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"Down arrow"</string> - <string name="keyboard_key_dpad_left" msgid="144176368026538621">"Left arrow"</string> - <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"Right arrow"</string> - <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"Centre"</string> <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> @@ -1275,7 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"folded"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"unfolded"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s/%2$s"</string> - <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"<xliff:g id="PERCENTAGE">%s</xliff:g> battery remaining"</string> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Stylus battery <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Connect your stylus to a charger"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"Stylus battery low"</string> <string name="video_camera" msgid="7654002575156149298">"Video camera"</string> @@ -1318,6 +1315,16 @@ <string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"Recently used by <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string> <string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"In use by <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>)"</string> <string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"Recently used by <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>)"</string> + <string name="shortcut_helper_category_system" msgid="462110876978937359">"System"</string> + <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Multitasking"</string> + <string name="shortcut_helper_category_input" msgid="8674018654124839566">"Input"</string> + <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"App shortcuts"</string> + <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Accessibility"</string> + <string name="shortcut_helper_title" msgid="8567500639300970049">"Keyboard shortcuts"</string> + <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Search shortcuts"</string> + <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Collapse icon"</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="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Keyboard backlight"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"Level %1$d of %2$d"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"Home controls"</string> diff --git a/packages/SystemUI/res/values-en-rXC/strings.xml b/packages/SystemUI/res/values-en-rXC/strings.xml index 67c332eef5f0..07907a640da4 100644 --- a/packages/SystemUI/res/values-en-rXC/strings.xml +++ b/packages/SystemUI/res/values-en-rXC/strings.xml @@ -281,8 +281,8 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Automatically turn on again tomorrow"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Features like Quick Share and Find My Device use Bluetooth"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth will turn on tomorrow morning"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Audio Sharing"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Sharing Audio"</string> + <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Share audio"</string> + <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Sharing audio"</string> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> battery"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Headset"</string> @@ -361,18 +361,19 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"What part of your device experience was affected?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Select issue type"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Screen record"</string> - <string-array name="qs_record_issue_types"> - <item msgid="2947988124014085798">"Performance"</item> - <item msgid="1627504621139124393">"User Interface"</item> - <item msgid="8309220355268900335">"Battery"</item> - </string-array> + <string name="performance" msgid="6552785217174378320">"Performance"</string> + <string name="user_interface" msgid="3712869377953950887">"User Interface"</string> + <string name="thermal" msgid="6758074791325414831">"Thermal"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"One-handed mode"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Hearing devices"</string> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Active"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Disconnected"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Hearing devices"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Pair new device"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Click to pair new device"</string> <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="live_caption_title" msgid="8916875614623730005">"Live Caption"</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> @@ -726,11 +727,6 @@ <string name="keyboard_key_button_template" msgid="8005673627272051429">"Button <xliff:g id="NAME">%1$s</xliff:g>"</string> <string name="keyboard_key_home" msgid="3734400625170020657">"Home"</string> <string name="keyboard_key_back" msgid="4185420465469481999">"Back"</string> - <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"Up arrow"</string> - <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"Down arrow"</string> - <string name="keyboard_key_dpad_left" msgid="144176368026538621">"Left arrow"</string> - <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"Right arrow"</string> - <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"Center"</string> <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> @@ -1274,7 +1270,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"folded"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"unfolded"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string> - <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"<xliff:g id="PERCENTAGE">%s</xliff:g> battery remaining"</string> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Stylus battery <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Connect your stylus to a charger"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"Stylus battery low"</string> <string name="video_camera" msgid="7654002575156149298">"Video camera"</string> @@ -1317,6 +1313,16 @@ <string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"Recently used by <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string> <string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"In use by <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>)"</string> <string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"Recently used by <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>)"</string> + <string name="shortcut_helper_category_system" msgid="462110876978937359">"System"</string> + <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Multitasking"</string> + <string name="shortcut_helper_category_input" msgid="8674018654124839566">"Input"</string> + <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"App shortcuts"</string> + <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Accessibility"</string> + <string name="shortcut_helper_title" msgid="8567500639300970049">"Keyboard shortcuts"</string> + <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Search shortcuts"</string> + <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Collapse icon"</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="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Keyboard backlight"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"Level %1$d of %2$d"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"Home Controls"</string> diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml index c6d206b8a746..c622ebec0c84 100644 --- a/packages/SystemUI/res/values-es-rUS/strings.xml +++ b/packages/SystemUI/res/values-es-rUS/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Activar automáticamente mañana"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Las funciones como Quick Share y Encontrar mi dispositivo usan Bluetooth"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"El Bluetooth se activará mañana a la mañana"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Uso compartido de audio"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Compartiendo audio"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> de batería"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Auriculares"</string> @@ -361,19 +363,19 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"¿Qué parte de tu exp. del disp. se vio afectada?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Seleccionar tipo de problema"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Grabadora de pant."</string> - <string-array name="qs_record_issue_types"> - <item msgid="2947988124014085798">"Rendimiento"</item> - <item msgid="1627504621139124393">"Interfaz de usuario"</item> - <item msgid="8309220355268900335">"Batería"</item> - </string-array> + <string name="performance" msgid="6552785217174378320">"Rendimiento"</string> + <string name="user_interface" msgid="3712869377953950887">"Interfaz de usuario"</string> + <string name="thermal" msgid="6758074791325414831">"Térmico"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Modo una mano"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Dispositivos auditivos"</string> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Activos"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Desconectados"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Dispositivos auditivos"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Vincular dispositivo nuevo"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Haz clic para vincular un dispositivo nuevo"</string> <string name="hearing_devices_presets_error" msgid="350363093458408536">"No se pudo actualizar el ajuste predeterminado"</string> - <!-- no translation found for hearing_devices_preset_label (7878267405046232358) --> - <skip /> + <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Ajuste predeterminado"</string> + <string name="live_caption_title" msgid="8916875614623730005">"Subtitulado instantáneo"</string> <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> @@ -727,11 +729,6 @@ <string name="keyboard_key_button_template" msgid="8005673627272051429">"Botón <xliff:g id="NAME">%1$s</xliff:g>"</string> <string name="keyboard_key_home" msgid="3734400625170020657">"Inicio"</string> <string name="keyboard_key_back" msgid="4185420465469481999">"Atrás"</string> - <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"Flecha hacia arriba"</string> - <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"Flecha hacia abajo"</string> - <string name="keyboard_key_dpad_left" msgid="144176368026538621">"Flecha a la izquierda"</string> - <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"Flecha a la derecha"</string> - <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"Centro"</string> <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string> <string name="keyboard_key_space" msgid="6980847564173394012">"Espacio"</string> <string name="keyboard_key_enter" msgid="8633362970109751646">"Intro"</string> @@ -1275,7 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"plegado"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"desplegado"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s/%2$s"</string> - <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"<xliff:g id="PERCENTAGE">%s</xliff:g> de batería restante"</string> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Batería de la pluma stylus: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Conecta tu pluma stylus a un cargador"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"La pluma stylus tiene poca batería"</string> <string name="video_camera" msgid="7654002575156149298">"Videocámara"</string> @@ -1318,8 +1315,18 @@ <string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"Uso reciente en <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string> <string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"En uso por <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>)"</string> <string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"Uso reciente en <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>)"</string> + <string name="shortcut_helper_category_system" msgid="462110876978937359">"Sistema"</string> + <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Tareas múltiples"</string> + <string name="shortcut_helper_category_input" msgid="8674018654124839566">"Entrada"</string> + <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Accesos directos a aplicaciones"</string> + <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Accesibilidad"</string> + <string name="shortcut_helper_title" msgid="8567500639300970049">"Combinaciones de teclas"</string> + <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Buscar combinaciones de teclas"</string> + <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ícono de contraer"</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="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Retroiluminación del teclado"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"Nivel %1$d de %2$d"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"Controles de la casa"</string> - <string name="home_controls_dream_description" msgid="4644150952104035789">"Accede rápidamente a controles de la casa como prot. de pantalla"</string> + <string name="home_controls_dream_description" msgid="4644150952104035789">"Usa rápidamente los controles de la casa como protector de pantalla"</string> </resources> diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml index 76effd15c665..c021b456e2fc 100644 --- a/packages/SystemUI/res/values-es/strings.xml +++ b/packages/SystemUI/res/values-es/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Volver a activar automáticamente mañana"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Las funciones como Quick Share y Encontrar mi dispositivo usan Bluetooth"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"El Bluetooth se activará mañana por la mañana"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Compartir audio"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Compartiendo audio"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> de batería"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Auriculares"</string> @@ -361,19 +363,19 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"¿Qué parte de tu experiencia se ha visto afectada?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Selecciona el tipo de problema"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Grabar pantalla"</string> - <string-array name="qs_record_issue_types"> - <item msgid="2947988124014085798">"Rendimiento"</item> - <item msgid="1627504621139124393">"Interfaz de usuario"</item> - <item msgid="8309220355268900335">"Batería"</item> - </string-array> + <string name="performance" msgid="6552785217174378320">"Rendimiento"</string> + <string name="user_interface" msgid="3712869377953950887">"Interfaz de usuario"</string> + <string name="thermal" msgid="6758074791325414831">"Térmico"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Modo Una mano"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Audífonos"</string> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Activos"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Desconectados"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Audífonos"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Emparejar nuevo dispositivo"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Haz clic para emparejar un nuevo dispositivo"</string> <string name="hearing_devices_presets_error" msgid="350363093458408536">"No se ha podido actualizar el preajuste"</string> - <!-- no translation found for hearing_devices_preset_label (7878267405046232358) --> - <skip /> + <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Preajuste"</string> + <string name="live_caption_title" msgid="8916875614623730005">"Subtítulos automáticos"</string> <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> @@ -727,11 +729,6 @@ <string name="keyboard_key_button_template" msgid="8005673627272051429">"Botón <xliff:g id="NAME">%1$s</xliff:g>"</string> <string name="keyboard_key_home" msgid="3734400625170020657">"Inicio"</string> <string name="keyboard_key_back" msgid="4185420465469481999">"Atrás"</string> - <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"Flecha hacia arriba"</string> - <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"Flecha hacia abajo"</string> - <string name="keyboard_key_dpad_left" msgid="144176368026538621">"Flecha izquierda"</string> - <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"Flecha derecha"</string> - <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"Centro"</string> <string name="keyboard_key_tab" msgid="4592772350906496730">"Tabulador"</string> <string name="keyboard_key_space" msgid="6980847564173394012">"Espacio"</string> <string name="keyboard_key_enter" msgid="8633362970109751646">"Intro"</string> @@ -1275,7 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"plegado"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"desplegado"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s/%2$s"</string> - <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"Batería restante: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Batería del lápiz óptico al <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Conecta tu lápiz óptico a un cargador"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"Batería del lápiz óptico baja"</string> <string name="video_camera" msgid="7654002575156149298">"Videocámara"</string> @@ -1318,6 +1315,16 @@ <string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"Usado recientemente por <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string> <string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"En uso por <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>)"</string> <string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"Usado recientemente por <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>)"</string> + <string name="shortcut_helper_category_system" msgid="462110876978937359">"Sistema"</string> + <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Multitarea"</string> + <string name="shortcut_helper_category_input" msgid="8674018654124839566">"Entrada"</string> + <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Accesos directos a aplicaciones"</string> + <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Accesibilidad"</string> + <string name="shortcut_helper_title" msgid="8567500639300970049">"Combinaciones de teclas"</string> + <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Atajos de búsqueda"</string> + <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Icono de contraer"</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="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Retroiluminación del teclado"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"Nivel %1$d de %2$d"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"Controles de la casa"</string> diff --git a/packages/SystemUI/res/values-et/strings.xml b/packages/SystemUI/res/values-et/strings.xml index 3d15aab3e216..003c92577d80 100644 --- a/packages/SystemUI/res/values-et/strings.xml +++ b/packages/SystemUI/res/values-et/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Lülita automaatselt homme uuesti sisse"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Sellised funktsioonid nagu Kiirjagamine ja Leia mu seade kasutavad Bluetoothi."</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth lülitub sisse homme hommikul"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Heli jagamine"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Heli jagamine"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> akut"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Heli"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Peakomplekt"</string> @@ -361,19 +363,19 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Millist seadme kasutuskogemuse osa see mõjutas?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Valige probleemi tüüp"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Ekraanisalvestus"</string> - <string-array name="qs_record_issue_types"> - <item msgid="2947988124014085798">"Toimivus"</item> - <item msgid="1627504621139124393">"Kasutajaliides"</item> - <item msgid="8309220355268900335">"Aku"</item> - </string-array> + <string name="performance" msgid="6552785217174378320">"Jõudlus"</string> + <string name="user_interface" msgid="3712869377953950887">"Kasutajaliides"</string> + <string name="thermal" msgid="6758074791325414831">"Soojus"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Ühekäerežiim"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Kuuldeseadmed"</string> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Ühendatud"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Ühendus on katkestatud"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Kuuldeseadmed"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Uue seadme sidumine"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Uue seadme sidumiseks klõpsake"</string> <string name="hearing_devices_presets_error" msgid="350363093458408536">"Eelseadistust ei saanud värskendada"</string> - <!-- no translation found for hearing_devices_preset_label (7878267405046232358) --> - <skip /> + <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Eelseadistus"</string> + <string name="live_caption_title" msgid="8916875614623730005">"Reaalajas subtiitrid"</string> <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> @@ -727,11 +729,6 @@ <string name="keyboard_key_button_template" msgid="8005673627272051429">"Nupp <xliff:g id="NAME">%1$s</xliff:g>"</string> <string name="keyboard_key_home" msgid="3734400625170020657">"Avakuva"</string> <string name="keyboard_key_back" msgid="4185420465469481999">"Tagasi"</string> - <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"Ülesnool"</string> - <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"Allanool"</string> - <string name="keyboard_key_dpad_left" msgid="144176368026538621">"Vasaknool"</string> - <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"Paremnool"</string> - <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"Keskele"</string> <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string> <string name="keyboard_key_space" msgid="6980847564173394012">"Tühik"</string> <string name="keyboard_key_enter" msgid="8633362970109751646">"Sisestusklahv"</string> @@ -1275,7 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"kokku volditud"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"lahti volditud"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s/%2$s"</string> - <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"Akutase on <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Elektronpliiatsi aku <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Ühendage elektronpliiats laadijaga"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"Elektronpliiatsi akutase on madal"</string> <string name="video_camera" msgid="7654002575156149298">"Videokaamera"</string> @@ -1318,6 +1315,16 @@ <string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"Kasutas hiljuti rakendus <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string> <string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"Seda kasutab <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>)"</string> <string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"Kasutas hiljuti rakendus <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>)"</string> + <string name="shortcut_helper_category_system" msgid="462110876978937359">"Süsteem"</string> + <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Multitegumtöö"</string> + <string name="shortcut_helper_category_input" msgid="8674018654124839566">"Sisend"</string> + <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Rakenduse otseteed"</string> + <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Juurdepääsetavus"</string> + <string name="shortcut_helper_title" msgid="8567500639300970049">"Klaviatuuri otseteed"</string> + <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Otsingu otseteed"</string> + <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ahendamisikoon"</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="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Klaviatuuri taustavalgustus"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"Tase %1$d/%2$d"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"Kodu juhtelemendid"</string> diff --git a/packages/SystemUI/res/values-eu/strings.xml b/packages/SystemUI/res/values-eu/strings.xml index db0e8e96502f..66b79b80fcbf 100644 --- a/packages/SystemUI/res/values-eu/strings.xml +++ b/packages/SystemUI/res/values-eu/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Aktibatu automatikoki berriro bihar"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Quick Share, Bilatu nire gailua eta beste eginbide batzuek Bluetootha erabiltzen dute"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bihar goizean aktibatuko da Bluetootha"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Audioa partekatzea"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Audioa partekatzen"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Bateria: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audioa"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Entzungailua"</string> @@ -361,19 +363,19 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Gailuaren erabileraren zer alderdiri eragin dio?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Hautatu arazo mota"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Pantaila-grabaketa"</string> - <string-array name="qs_record_issue_types"> - <item msgid="2947988124014085798">"Errendimendua"</item> - <item msgid="1627504621139124393">"Erabiltzaile-interfazea"</item> - <item msgid="8309220355268900335">"Bateria"</item> - </string-array> + <string name="performance" msgid="6552785217174378320">"Errendimendua"</string> + <string name="user_interface" msgid="3712869377953950887">"Erabiltzaile-interfazea"</string> + <string name="thermal" msgid="6758074791325414831">"Termikoa"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Esku bakarreko modua"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Entzumen-gailuak"</string> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Aktibo"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Deskonektatuta"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Entzumen-gailuak"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Parekatu beste gailu bat"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Egin klik beste gailu bat parekatzeko"</string> <string name="hearing_devices_presets_error" msgid="350363093458408536">"Ezin izan da eguneratu aurrezarpena"</string> - <!-- no translation found for hearing_devices_preset_label (7878267405046232358) --> - <skip /> + <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Aurrezarpena"</string> + <string name="live_caption_title" msgid="8916875614623730005">"Istanteko azpitituluak"</string> <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> @@ -727,11 +729,6 @@ <string name="keyboard_key_button_template" msgid="8005673627272051429">"<xliff:g id="NAME">%1$s</xliff:g> botoia"</string> <string name="keyboard_key_home" msgid="3734400625170020657">"Hasiera"</string> <string name="keyboard_key_back" msgid="4185420465469481999">"Atzera"</string> - <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"Gora egiteko gezi-tekla"</string> - <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"Behera egiteko gezi-tekla"</string> - <string name="keyboard_key_dpad_left" msgid="144176368026538621">"Ezkerrera egiteko gezi-tekla"</string> - <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"Eskuinera egiteko gezi-tekla"</string> - <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"Erdiratu"</string> <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string> <string name="keyboard_key_space" msgid="6980847564173394012">"Zuriunea"</string> <string name="keyboard_key_enter" msgid="8633362970109751646">"Sartu"</string> @@ -1275,7 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"tolestuta"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"tolestu gabe"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s/%2$s"</string> - <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"Bateriaren <xliff:g id="PERCENTAGE">%s</xliff:g> geratzen da"</string> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Arkatzaren bateria: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Konektatu arkatza kargagailu batera"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"Arkatzak bateria gutxi du"</string> <string name="video_camera" msgid="7654002575156149298">"Bideokamera"</string> @@ -1318,6 +1315,16 @@ <string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"<xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>) aplikazioak erabili du duela gutxi"</string> <string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"<xliff:g id="APP_NAME">%1$s</xliff:g> aplikazioak darabil (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</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>) aplikazioak erabili du duela gutxi"</string> + <string name="shortcut_helper_category_system" msgid="462110876978937359">"Sistema"</string> + <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Zeregin bat baino gehiago aldi berean exekutatzea"</string> + <string name="shortcut_helper_category_input" msgid="8674018654124839566">"Sarrera"</string> + <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Aplikazioetarako lasterbideak"</string> + <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Erabilerraztasuna"</string> + <string name="shortcut_helper_title" msgid="8567500639300970049">"Lasterbideak"</string> + <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Bilatu lasterbideak"</string> + <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Tolesteko ikonoa"</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="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Teklatuaren hondoko argia"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"%1$d/%2$d maila"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"Etxeko gailuen kontrola"</string> diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml index f2372a6d61d5..cdbb6851acc0 100644 --- a/packages/SystemUI/res/values-fa/strings.xml +++ b/packages/SystemUI/res/values-fa/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"فردا دوباره بهطور خودکار روشن شود"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"ویژگیهایی مثل «همرسانی سریع» و «پیدا کردن دستگاهم» از بلوتوث استفاده میکنند"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"بلوتوث فردا صبح روشن خواهد شد"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"اشتراک صدا"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"درحال اشتراکگذاری صدا"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"شارژ باتری <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"صوت"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"هدست"</string> @@ -361,19 +363,19 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"کدام بخش تجربه استفاده از دستگاه تحتتأثیر قرار گرفت؟"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"انتخاب نوع مشکل"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"ضبط صفحهنمایش"</string> - <string-array name="qs_record_issue_types"> - <item msgid="2947988124014085798">"عملکرد"</item> - <item msgid="1627504621139124393">"واسط کاربر"</item> - <item msgid="8309220355268900335">"باتری"</item> - </string-array> + <string name="performance" msgid="6552785217174378320">"عملکرد"</string> + <string name="user_interface" msgid="3712869377953950887">"میانای کاربر"</string> + <string name="thermal" msgid="6758074791325414831">"حرارتی"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"حالت یکدستی"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"سمعک"</string> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"فعال"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"اتصال قطع شد"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"سمعک"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"جفت کردن دستگاه جدید"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"برای جفت کردن دستگاه جدید، کلیک کنید"</string> <string name="hearing_devices_presets_error" msgid="350363093458408536">"پیشتنظیم بهروزرسانی نشد"</string> - <!-- no translation found for hearing_devices_preset_label (7878267405046232358) --> - <skip /> + <string name="hearing_devices_preset_label" msgid="7878267405046232358">"پیشتنظیم"</string> + <string name="live_caption_title" msgid="8916875614623730005">"زیرنویس ناشنوایان زنده"</string> <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> @@ -727,11 +729,6 @@ <string name="keyboard_key_button_template" msgid="8005673627272051429">"دکمه <xliff:g id="NAME">%1$s</xliff:g>"</string> <string name="keyboard_key_home" msgid="3734400625170020657">"ابتدا"</string> <string name="keyboard_key_back" msgid="4185420465469481999">"برگشت"</string> - <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"پیکان رو بهبالا"</string> - <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"پیکان روبهپایین"</string> - <string name="keyboard_key_dpad_left" msgid="144176368026538621">"پیکان چپ"</string> - <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"پیکان راست"</string> - <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"مرکز"</string> <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> @@ -1275,7 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"تاشده"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"تانشده"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string> - <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"<xliff:g id="PERCENTAGE">%s</xliff:g> باتری باقی مانده است"</string> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"شارژ باتری قلم <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"قلم را به شارژر وصل کنید"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"باتری قلم ضعیف است"</string> <string name="video_camera" msgid="7654002575156149298">"دوربین ویدیویی"</string> @@ -1318,6 +1315,16 @@ <string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"اخیراً <xliff:g id="APP_NAME">%1$s</xliff:g> از آن استفاده کرده است (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string> <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>)"</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>)"</string> + <string name="shortcut_helper_category_system" msgid="462110876978937359">"سیستم"</string> + <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"چندوظیفگی"</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_a11y" msgid="6314444792641773464">"دسترسپذیری"</string> + <string name="shortcut_helper_title" msgid="8567500639300970049">"میانبرهای صفحهکلید"</string> + <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"جستجوی میانبرها"</string> + <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"نماد جمع کردن"</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="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> diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml index 9172a9acdbe4..bea39cb6e704 100644 --- a/packages/SystemUI/res/values-fi/strings.xml +++ b/packages/SystemUI/res/values-fi/strings.xml @@ -201,7 +201,7 @@ <string name="fingerprint_reenroll_failure_dialog_content" msgid="4733768492747300666">"Sormenjälkiavauksen määritys epäonnistui. Siirry asetuksiin ja yritä uudelleen."</string> <string name="face_re_enroll_notification_title" msgid="1850838867718410520">"Ota kasvojentunnistusavaus uudelleen käyttöön"</string> <string name="face_re_enroll_notification_name" msgid="7384545252206120659">"Kasvojentunnistusavaus"</string> - <string name="face_re_enroll_dialog_title" msgid="6392173708176069994">"Kasvojentunnistusavauksen käyttöönotto"</string> + <string name="face_re_enroll_dialog_title" msgid="6392173708176069994">"Kasvojen&shy;tunnistus&shy;avauksen käyttöönotto"</string> <string name="face_re_enroll_dialog_content" msgid="7353502359464038511">"Jos haluat ottaa kasvojentunnistusavauksen uudelleen käyttöön, nykyinen kasvomalli poistetaan.\n\nJos haluat avata puhelimen lukituksen kasvoilla, sinun on otettava ominaisuus uudelleen käyttöön."</string> <string name="face_reenroll_failure_dialog_content" msgid="7073947334397236935">"Kasvojentunnistusavauksen käyttöönotto epäonnistui. Siirry asetuksiin ja yritä uudelleen."</string> <string name="fingerprint_dialog_touch_sensor" msgid="2817887108047658975">"Kosketa sormenjälkitunnistinta"</string> @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Laita automaattisesti päälle taas huomenna"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Quick Share, Paikanna laite ja tietyt muut ominaisuudet käyttävät Bluetoothia"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth menee päälle huomisaamuna"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Audionjako"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Audiota jaetaan"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Akun taso <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Ääni"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Headset"</string> @@ -361,19 +363,19 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Mitä osaa käyttökokemuksesta ongelma koski?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Valitse ongelman tyyppi"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Näytön tallentaja"</string> - <string-array name="qs_record_issue_types"> - <item msgid="2947988124014085798">"Suorituskyky"</item> - <item msgid="1627504621139124393">"Käyttöliittymä"</item> - <item msgid="8309220355268900335">"Akku"</item> - </string-array> + <string name="performance" msgid="6552785217174378320">"Suorituskyky"</string> + <string name="user_interface" msgid="3712869377953950887">"Käyttöliittymä"</string> + <string name="thermal" msgid="6758074791325414831">"Lämpökamera"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Yhden käden moodi"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Kuulolaitteet"</string> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Aktiivinen"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Yhteys katkaistu"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Kuulolaitteet"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Muodosta uusi laitepari"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Muodosta uusi laitepari klikkaamalla"</string> <string name="hearing_devices_presets_error" msgid="350363093458408536">"Esiasetusta ei voitu muuttaa"</string> - <!-- no translation found for hearing_devices_preset_label (7878267405046232358) --> - <skip /> + <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Esiasetus"</string> + <string name="live_caption_title" msgid="8916875614623730005">"Livetekstitys"</string> <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> @@ -727,11 +729,6 @@ <string name="keyboard_key_button_template" msgid="8005673627272051429">"Painike <xliff:g id="NAME">%1$s</xliff:g>"</string> <string name="keyboard_key_home" msgid="3734400625170020657">"Home"</string> <string name="keyboard_key_back" msgid="4185420465469481999">"Takaisin"</string> - <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"Ylänuoli"</string> - <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"Alanuoli"</string> - <string name="keyboard_key_dpad_left" msgid="144176368026538621">"Vasen nuoli"</string> - <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"Oikea nuoli"</string> - <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"Keskelle"</string> <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string> <string name="keyboard_key_space" msgid="6980847564173394012">"Välilyönti"</string> <string name="keyboard_key_enter" msgid="8633362970109751646">"Enter"</string> @@ -1275,7 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"taitettu"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"taittamaton"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string> - <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"Akkua jäljellä <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Näyttökynän akku <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Yhdistä näyttökynä laturiin"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"Näyttökynän akku vähissä"</string> <string name="video_camera" msgid="7654002575156149298">"Videokamera"</string> @@ -1318,6 +1315,16 @@ <string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"<xliff:g id="APP_NAME">%1$s</xliff:g> käytti tätä äskettäin (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string> <string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"Tämän käytössä: <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>)"</string> <string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"<xliff:g id="APP_NAME">%1$s</xliff:g> käytti tätä äskettäin (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string> + <string name="shortcut_helper_category_system" msgid="462110876978937359">"Järjestelmä"</string> + <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Multitaskaus"</string> + <string name="shortcut_helper_category_input" msgid="8674018654124839566">"Syöte"</string> + <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Sovellusten pikakuvakkeet"</string> + <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Saavutettavuus"</string> + <string name="shortcut_helper_title" msgid="8567500639300970049">"Pikanäppäimet"</string> + <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Pikahaut"</string> + <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Tiivistyskuvake"</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="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> diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml index f2bae1b1a488..95b25936f8a4 100644 --- a/packages/SystemUI/res/values-fr-rCA/strings.xml +++ b/packages/SystemUI/res/values-fr-rCA/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Activer le Bluetooth automatiquement demain"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Les fonctionnalités comme Partage rapide et Localiser mon appareil utilisent le Bluetooth"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Le Bluetooth s\'activera demain matin"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Partage audio"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Partage de l\'audio en cours…"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Pile : <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Écouteurs"</string> @@ -361,19 +363,19 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Quelle composante de l\'appareil a été affectée?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Sélectionner un type"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Enregistrement écran"</string> - <string-array name="qs_record_issue_types"> - <item msgid="2947988124014085798">"Performance"</item> - <item msgid="1627504621139124393">"Interface utilisateur"</item> - <item msgid="8309220355268900335">"Pile"</item> - </string-array> + <string name="performance" msgid="6552785217174378320">"Performance"</string> + <string name="user_interface" msgid="3712869377953950887">"Interface utilisateur"</string> + <string name="thermal" msgid="6758074791325414831">"Thermique"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Mode Une main"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Appareils auditifs"</string> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Actives"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Déconnectées"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Appareils auditifs"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Associer un nouvel appareil"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Cliquez ici pour associer un nouvel appareil"</string> <string name="hearing_devices_presets_error" msgid="350363093458408536">"Impossible de mettre à jour le préréglage"</string> - <!-- no translation found for hearing_devices_preset_label (7878267405046232358) --> - <skip /> + <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Préréglage"</string> + <string name="live_caption_title" msgid="8916875614623730005">"Sous-titres instantanés"</string> <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> @@ -727,11 +729,6 @@ <string name="keyboard_key_button_template" msgid="8005673627272051429">"Bouton <xliff:g id="NAME">%1$s</xliff:g>"</string> <string name="keyboard_key_home" msgid="3734400625170020657">"Accueil"</string> <string name="keyboard_key_back" msgid="4185420465469481999">"Précédent"</string> - <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"Flèche vers le haut"</string> - <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"Flèche vers le bas"</string> - <string name="keyboard_key_dpad_left" msgid="144176368026538621">"Flèche vers la gauche"</string> - <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"Flèche vers la droite"</string> - <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"Centrer"</string> <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string> <string name="keyboard_key_space" msgid="6980847564173394012">"Espace"</string> <string name="keyboard_key_enter" msgid="8633362970109751646">"Entrée"</string> @@ -1209,8 +1206,8 @@ <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Choisir l\'utilisateur"</string> <string name="fgs_manager_footer_label" msgid="8276763570622288231">"{count,plural, =1{# application est active}one{# application est active}many{# d\'applications sont actives}other{# applications sont actives}}"</string> <string name="fgs_dot_content_description" msgid="2865071539464777240">"Nouvelle information"</string> - <string name="fgs_manager_dialog_title" msgid="5879184257257718677">"Applications actives"</string> - <string name="fgs_manager_dialog_message" msgid="2670045017200730076">"Ces applications sont actives et s\'exécutent même lorsque vous ne les utilisez pas. Cela améliore leur fonctionnalité, mais peut également affecter l\'autonomie de la pile."</string> + <string name="fgs_manager_dialog_title" msgid="5879184257257718677">"Applis actives"</string> + <string name="fgs_manager_dialog_message" msgid="2670045017200730076">"Ces applis sont actives et s\'exécutent même lorsque vous ne les utilisez pas. Cela améliore leur fonctionnalité, mais peut également affecter l\'autonomie de la pile."</string> <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Arrêter"</string> <string name="fgs_manager_app_item_stop_button_stopped_label" msgid="6950382004441263922">"Arrêtée"</string> <string name="clipboard_edit_text_done" msgid="4551887727694022409">"OK"</string> @@ -1275,7 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"plié"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"déplié"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s/%2$s"</string> - <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"Charge restante de la pile : <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Pile du stylet <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Connectez votre stylet à un chargeur"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"Pile du stylet faible"</string> <string name="video_camera" msgid="7654002575156149298">"Caméra"</string> @@ -1318,6 +1315,16 @@ <string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"Récemment utilisé par <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string> <string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"Utilisé par <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>)"</string> <string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"Récemment utilisé par <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>)"</string> + <string name="shortcut_helper_category_system" msgid="462110876978937359">"Système"</string> + <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Multitâche"</string> + <string name="shortcut_helper_category_input" msgid="8674018654124839566">"Entrée"</string> + <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Raccourcis des applis"</string> + <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Accessibilité"</string> + <string name="shortcut_helper_title" msgid="8567500639300970049">"Raccourcis-clavier"</string> + <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Recherchez des raccourcis"</string> + <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Icône Réduire"</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="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Rétroéclairage du clavier"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"Niveau %1$d de %2$d"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"Domotique"</string> diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml index e216c8453b07..1b8ddc102220 100644 --- a/packages/SystemUI/res/values-fr/strings.xml +++ b/packages/SystemUI/res/values-fr/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Réactiver automatiquement demain"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Certaines fonctionnalités, telles que Quick Share et Localiser mon appareil, utilisent le Bluetooth"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Le Bluetooth sera activé demain matin"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Partage audio"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Audio partagé"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> de batterie"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Casque"</string> @@ -361,19 +363,19 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Quel problème avez-vous rencontré avec votre appareil ?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Sélectionnez un type de problème"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Enregistrement de l\'écran"</string> - <string-array name="qs_record_issue_types"> - <item msgid="2947988124014085798">"Performances"</item> - <item msgid="1627504621139124393">"Interface utilisateur"</item> - <item msgid="8309220355268900335">"Batterie"</item> - </string-array> + <string name="performance" msgid="6552785217174378320">"Performances"</string> + <string name="user_interface" msgid="3712869377953950887">"Interface utilisateur"</string> + <string name="thermal" msgid="6758074791325414831">"Thermique"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Mode une main"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Appareils auditifs"</string> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Actifs"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Déconnectés"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Appareils auditifs"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Associer un nouvel appareil"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Cliquer pour associer un nouvel appareil"</string> <string name="hearing_devices_presets_error" msgid="350363093458408536">"Impossible de mettre à jour les préréglages"</string> - <!-- no translation found for hearing_devices_preset_label (7878267405046232358) --> - <skip /> + <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Préréglage"</string> + <string name="live_caption_title" msgid="8916875614623730005">"Sous-titres instantanés"</string> <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> @@ -727,11 +729,6 @@ <string name="keyboard_key_button_template" msgid="8005673627272051429">"Bouton <xliff:g id="NAME">%1$s</xliff:g>"</string> <string name="keyboard_key_home" msgid="3734400625170020657">"Accueil"</string> <string name="keyboard_key_back" msgid="4185420465469481999">"Précédent"</string> - <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"Flèche vers le haut"</string> - <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"Flèche vers le bas"</string> - <string name="keyboard_key_dpad_left" msgid="144176368026538621">"Flèche vers la gauche"</string> - <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"Flèche vers la droite"</string> - <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"Centre"</string> <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string> <string name="keyboard_key_space" msgid="6980847564173394012">"Espace"</string> <string name="keyboard_key_enter" msgid="8633362970109751646">"Entrée"</string> @@ -1212,7 +1209,7 @@ <string name="fgs_manager_dialog_title" msgid="5879184257257718677">"Applis actives"</string> <string name="fgs_manager_dialog_message" msgid="2670045017200730076">"Ces applis sont actives et s\'exécutent même lorsque vous ne les utilisez pas. Cela améliore leur fonctionnement, mais peut également affecter l\'autonomie de la batterie."</string> <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Arrêter"</string> - <string name="fgs_manager_app_item_stop_button_stopped_label" msgid="6950382004441263922">"Arrêtée"</string> + <string name="fgs_manager_app_item_stop_button_stopped_label" msgid="6950382004441263922">"Arrêté"</string> <string name="clipboard_edit_text_done" msgid="4551887727694022409">"OK"</string> <string name="clipboard_overlay_text_copied" msgid="1872624400464891363">"Copié"</string> <string name="clipboard_edit_source" msgid="9156488177277788029">"De <xliff:g id="APPNAME">%1$s</xliff:g>"</string> @@ -1275,7 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"plié"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"déplié"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s/%2$s"</string> - <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"<xliff:g id="PERCENTAGE">%s</xliff:g> de batterie restante"</string> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Batterie du stylet à <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Connectez votre stylet à un chargeur"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"La batterie du stylet est faible"</string> <string name="video_camera" msgid="7654002575156149298">"Caméra"</string> @@ -1318,6 +1315,16 @@ <string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"Récemment utilisé par <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string> <string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"En cours d\'utilisation par <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>)"</string> <string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"Récemment utilisé par <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>)"</string> + <string name="shortcut_helper_category_system" msgid="462110876978937359">"Système"</string> + <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Multitâche"</string> + <string name="shortcut_helper_category_input" msgid="8674018654124839566">"Entrée"</string> + <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Raccourcis d\'application"</string> + <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Accessibilité"</string> + <string name="shortcut_helper_title" msgid="8567500639300970049">"Raccourcis clavier"</string> + <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Raccourcis de recherche"</string> + <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Icône Réduire"</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="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Rétroéclairage du clavier"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"Niveau %1$d sur %2$d"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"Contrôle de la maison"</string> diff --git a/packages/SystemUI/res/values-fr/tiles_states_strings.xml b/packages/SystemUI/res/values-fr/tiles_states_strings.xml index 23c124cce37a..fcdd9f0e6e3a 100644 --- a/packages/SystemUI/res/values-fr/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-fr/tiles_states_strings.xml @@ -58,8 +58,8 @@ </string-array> <string-array name="tile_states_flashlight"> <item msgid="3465257127433353857">"Indisponible"</item> - <item msgid="5044688398303285224">"Désactivée"</item> - <item msgid="8527389108867454098">"Activée"</item> + <item msgid="5044688398303285224">"Désactivé"</item> + <item msgid="8527389108867454098">"Activé"</item> </string-array> <string-array name="tile_states_rotation"> <item msgid="4578491772376121579">"Indisponible"</item> diff --git a/packages/SystemUI/res/values-gl/strings.xml b/packages/SystemUI/res/values-gl/strings.xml index 1ccab5d8dd60..1d0f4c476f1e 100644 --- a/packages/SystemUI/res/values-gl/strings.xml +++ b/packages/SystemUI/res/values-gl/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Volver activar automaticamente mañá"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"As funcións como Quick Share e Localizar o meu dispositivo utilizan o Bluetooth"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"O Bluetooth activarase mañá á mañá"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Audio compartido"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Compartindo audio"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> de batería"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Auriculares"</string> @@ -361,19 +363,19 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Cal foi o problema na experiencia co dispositivo?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Selecciona o tipo de problema"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Gravación de pant."</string> - <string-array name="qs_record_issue_types"> - <item msgid="2947988124014085798">"Rendemento"</item> - <item msgid="1627504621139124393">"Interface de usuario"</item> - <item msgid="8309220355268900335">"Batería"</item> - </string-array> + <string name="performance" msgid="6552785217174378320">"Rendemento"</string> + <string name="user_interface" msgid="3712869377953950887">"Interface de usuario"</string> + <string name="thermal" msgid="6758074791325414831">"Térmico"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Modo dunha soa man"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Dispositivos auditivos"</string> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Activos"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Desconectados"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Dispositivos auditivos"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Vincular dispositivo novo"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Fai clic para vincular un novo dispositivo"</string> <string name="hearing_devices_presets_error" msgid="350363093458408536">"Non se puido actualizar a configuración predeterminada"</string> - <!-- no translation found for hearing_devices_preset_label (7878267405046232358) --> - <skip /> + <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Configuración predeterminada"</string> + <string name="live_caption_title" msgid="8916875614623730005">"Subtítulos instantáneos"</string> <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> @@ -617,7 +619,7 @@ <string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s. Toca para silenciar."</string> <string name="volume_panel_noise_control_title" msgid="7413949943872304474">"Control de ruído"</string> <string name="volume_panel_spatial_audio_title" msgid="3367048857932040660">"Audio espacial"</string> - <string name="volume_panel_spatial_audio_off" msgid="4177490084606772989">"Desactivar"</string> + <string name="volume_panel_spatial_audio_off" msgid="4177490084606772989">"Desactivado"</string> <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> @@ -727,11 +729,6 @@ <string name="keyboard_key_button_template" msgid="8005673627272051429">"Botón <xliff:g id="NAME">%1$s</xliff:g>"</string> <string name="keyboard_key_home" msgid="3734400625170020657">"Inicio"</string> <string name="keyboard_key_back" msgid="4185420465469481999">"Volver"</string> - <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"Frecha arriba"</string> - <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"Frecha abaixo"</string> - <string name="keyboard_key_dpad_left" msgid="144176368026538621">"Frecha esquerda"</string> - <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"Frecha dereita"</string> - <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"Centro"</string> <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string> <string name="keyboard_key_space" msgid="6980847564173394012">"Espazo"</string> <string name="keyboard_key_enter" msgid="8633362970109751646">"Intro"</string> @@ -1275,7 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"dispositivo pregado"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"dispositivo despregado"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s/%2$s"</string> - <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"Batería restante: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Batería do lapis óptico: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Conecta o lapis óptico a un cargador"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"O lapis óptico ten pouca batería"</string> <string name="video_camera" msgid="7654002575156149298">"Videocámara"</string> @@ -1318,6 +1315,16 @@ <string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"En uso recentemente por <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string> <string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"En uso por <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>)"</string> <string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"En uso recentemente por <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>)"</string> + <string name="shortcut_helper_category_system" msgid="462110876978937359">"Sistema"</string> + <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Multitarefa"</string> + <string name="shortcut_helper_category_input" msgid="8674018654124839566">"Entrada"</string> + <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Atallos de aplicacións"</string> + <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Accesibilidade"</string> + <string name="shortcut_helper_title" msgid="8567500639300970049">"Atallos de teclado"</string> + <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Atallos de busca"</string> + <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Icona de contraer"</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="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Retroiluminación do teclado"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"Nivel %1$d de %2$d"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"Controis domóticos"</string> diff --git a/packages/SystemUI/res/values-gu/strings.xml b/packages/SystemUI/res/values-gu/strings.xml index e096fff98221..7a4cfd1c91ce 100644 --- a/packages/SystemUI/res/values-gu/strings.xml +++ b/packages/SystemUI/res/values-gu/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"આવતીકાલે ફરીથી ઑટોમૅટિક રીતે ચાલુ કરો"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"ક્વિક શેર અને Find My Device જેવી સુવિધાઓ બ્લૂટૂથનો ઉપયોગ કરે છે"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"બ્લૂટૂથ આવતીકાલે સવારે ચાલુ થશે"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"ઑડિયો શેરિંગ"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"ઑડિયો શેર કરી રહ્યાં છીએ"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> બૅટરી"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"ઑડિયો"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"હૅડસેટ"</string> @@ -361,19 +363,19 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"ડિવાઇસ સંબંધી તમારા અનુભવના કયા ભાગને અસર થઈ હતી?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"સમસ્યાનો પ્રકાર પસંદ કરો"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"સ્ક્રીન રેકોર્ડ કરો"</string> - <string-array name="qs_record_issue_types"> - <item msgid="2947988124014085798">"પર્ફોર્મન્સ"</item> - <item msgid="1627504621139124393">"યૂઝર ઇન્ટરફેસ"</item> - <item msgid="8309220355268900335">"બૅટરી"</item> - </string-array> + <string name="performance" msgid="6552785217174378320">"પર્ફોર્મન્સ"</string> + <string name="user_interface" msgid="3712869377953950887">"યૂઝર ઇન્ટરફેસ"</string> + <string name="thermal" msgid="6758074791325414831">"થર્મલ"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"એક-હાથે વાપરો મોડ"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"સાંભળવામાં મદદ આપતા ડિવાઇસ"</string> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"સક્રિય"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"ડિસ્કનેક્ટેડ છે"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"સાંભળવામાં મદદ આપતા ડિવાઇસ"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"નવા ડિવાઇસ સાથે જોડાણ કરો"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"નવા ડિવાઇસ સાથે જોડાણ કરવા માટે ક્લિક કરો"</string> <string name="hearing_devices_presets_error" msgid="350363093458408536">"પ્રીસેટ અપડેટ કરી શક્યા નથી"</string> - <!-- no translation found for hearing_devices_preset_label (7878267405046232358) --> - <skip /> + <string name="hearing_devices_preset_label" msgid="7878267405046232358">"પ્રીસેટ"</string> + <string name="live_caption_title" msgid="8916875614623730005">"લાઇવ કૅપ્શન"</string> <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> @@ -727,11 +729,6 @@ <string name="keyboard_key_button_template" msgid="8005673627272051429">"બટન <xliff:g id="NAME">%1$s</xliff:g>"</string> <string name="keyboard_key_home" msgid="3734400625170020657">"Home"</string> <string name="keyboard_key_back" msgid="4185420465469481999">"Back"</string> - <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"ઉપરની ઍરો કી"</string> - <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"નીચેની ઍરો કી"</string> - <string name="keyboard_key_dpad_left" msgid="144176368026538621">"ડાબી ઍરો કી"</string> - <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"જમણી ઍરો કી"</string> - <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"Center"</string> <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> @@ -1275,7 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"ફોલ્ડ કરેલું"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"અનફોલ્ડ કરેલું"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string> - <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"<xliff:g id="PERCENTAGE">%s</xliff:g> બૅટરી બાકી છે"</string> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"સ્ટાઇલસની બૅટરી <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"તમારા સ્ટાઇલસને ચાર્જર સાથે કનેક્ટ કરો"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"સ્ટાઇલસની બૅટરીમાં ચાર્જ ઓછો છે"</string> <string name="video_camera" msgid="7654002575156149298">"વીડિયો કૅમેરા"</string> @@ -1318,6 +1315,16 @@ <string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"<xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>) દ્વારા તાજેતરમાં ઉપયોગ કરવામાં આવ્યો"</string> <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>) દ્વારા ઉપયોગ ચાલુ છે"</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>) દ્વારા તાજેતરમાં ઉપયોગ કરવામાં આવ્યો"</string> + <string name="shortcut_helper_category_system" msgid="462110876978937359">"સિસ્ટમ"</string> + <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"એકથી વધુ કાર્યો કરવા"</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_a11y" msgid="6314444792641773464">"ઍક્સેસિબિલિટી"</string> + <string name="shortcut_helper_title" msgid="8567500639300970049">"કીબોર્ડ શૉર્ટકટ"</string> + <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"શૉર્ટકટ શોધો"</string> + <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"\'નાનું કરો\'નું આઇકન"</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="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> diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml index d8c7dcd67f18..896af2487564 100644 --- a/packages/SystemUI/res/values-hi/strings.xml +++ b/packages/SystemUI/res/values-hi/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"कल फिर से अपने-आप चालू हो जाए"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"क्विक शेयर और Find My Device जैसी सुविधाएं, ब्लूटूथ का इस्तेमाल करती हैं"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"ब्लूटूथ कल सुबह चालू होगा"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"ऑडियो शेयर करने की सुविधा"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"ऑडियो शेयर किया जा रहा है"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> बैटरी"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"ऑडियो"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"हेडसेट"</string> @@ -295,7 +297,7 @@ <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"स्क्रीन सेवर"</string> <string name="quick_settings_camera_label" msgid="5612076679385269339">"कैमरे का ऐक्सेस"</string> <string name="quick_settings_mic_label" msgid="8392773746295266375">"माइक्रोफ़ोन का ऐक्सेस"</string> - <string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"उपलब्ध"</string> + <string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"उपलब्ध है"</string> <string name="quick_settings_camera_mic_blocked" msgid="4710884905006788281">"ब्लॉक किया गया है"</string> <string name="quick_settings_media_device_label" msgid="8034019242363789941">"मीडिया डिवाइस"</string> <string name="quick_settings_user_title" msgid="8673045967216204537">"उपयोगकर्ता"</string> @@ -361,21 +363,21 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"आपके डिवाइस की कौनसी सुविधा पर असर पड़ा था?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"समस्या का टाइप चुनें"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"स्क्रीन रिकॉर्डर"</string> - <string-array name="qs_record_issue_types"> - <item msgid="2947988124014085798">"परफ़ॉर्मेंस"</item> - <item msgid="1627504621139124393">"यूज़र इंटरफ़ेस"</item> - <item msgid="8309220355268900335">"बैटरी"</item> - </string-array> + <string name="performance" msgid="6552785217174378320">"परफ़ॉर्मेंस"</string> + <string name="user_interface" msgid="3712869377953950887">"यूज़र इंटरफ़ेस"</string> + <string name="thermal" msgid="6758074791325414831">"थर्मल"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"वन-हैंडेड मोड"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"कान की मशीनें"</string> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"ऐक्टिव"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"डिसकनेक्ट हो गया"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"कान की मशीनें"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"नया डिवाइस जोड़ें"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"नया डिवाइस जोड़ने के लिए क्लिक करें"</string> <string name="hearing_devices_presets_error" msgid="350363093458408536">"प्रीसेट अपडेट नहीं किया जा सका"</string> - <!-- no translation found for hearing_devices_preset_label (7878267405046232358) --> - <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="hearing_devices_preset_label" msgid="7878267405046232358">"प्रीसेट"</string> + <string name="live_caption_title" msgid="8916875614623730005">"लाइव कैप्शन"</string> + <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> <string name="sensor_privacy_start_use_mic_dialog_content" msgid="1624701280680913717">"ऐसा करने से, सभी ऐप्लिकेशन और सेवाओं के लिए माइक्रोफ़ोन का ऐक्सेस अनब्लॉक हो जाएगा और वे इसका इस्तेमाल कर पाएंगी."</string> <string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"ऐसा करने से, सभी ऐप्लिकेशन और सेवाओं के लिए कैमरे का ऐक्सेस अनब्लॉक हो जाएगा और वे इसका इस्तेमाल कर पाएंगी."</string> @@ -727,11 +729,6 @@ <string name="keyboard_key_button_template" msgid="8005673627272051429">"बटन <xliff:g id="NAME">%1$s</xliff:g>"</string> <string name="keyboard_key_home" msgid="3734400625170020657">"Home"</string> <string name="keyboard_key_back" msgid="4185420465469481999">"Back"</string> - <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"अप ऐरो"</string> - <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"डाउन ऐरो"</string> - <string name="keyboard_key_dpad_left" msgid="144176368026538621">"लेफ़्ट ऐरो"</string> - <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"राइट ऐरो"</string> - <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"मध्य तीर"</string> <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> @@ -997,7 +994,7 @@ <string name="accessibility_magnification_right_handle" msgid="9055988237319397605">"दायां हैंडल"</string> <string name="accessibility_magnification_bottom_handle" msgid="6531646968813821258">"नीचे का हैंडल"</string> <string name="accessibility_magnification_settings_panel_description" msgid="8174187340747846953">"ज़ूम करने की सुविधा वाली सेटिंग"</string> - <string name="accessibility_magnifier_size" msgid="3038755600030422334">"ज़ूम करने की सुविधा का साइज़"</string> + <string name="accessibility_magnifier_size" msgid="3038755600030422334">"ज़ूम का साइज़"</string> <string name="accessibility_magnification_zoom" msgid="4222088982642063979">"ज़ूम करें"</string> <string name="accessibility_magnification_medium" msgid="6994632616884562625">"मध्यम"</string> <string name="accessibility_magnification_small" msgid="8144502090651099970">"छोटा"</string> @@ -1112,7 +1109,7 @@ <string name="media_output_dialog_multiple_devices" msgid="1093771040315422350">"<xliff:g id="COUNT">%1$d</xliff:g> डिवाइस चुने गए"</string> <string name="media_output_dialog_disconnected" msgid="7090512852817111185">"(डिसकनेक्ट हो गया)"</string> <string name="media_output_dialog_connect_failed" msgid="3080972621975339387">"स्विच नहीं किया जा सकता. फिर से कोशिश करने के लिए टैप करें."</string> - <string name="media_output_dialog_pairing_new" msgid="5098212763195577270">"किसी डिवाइस को कनेक्ट करें"</string> + <string name="media_output_dialog_pairing_new" msgid="5098212763195577270">"कोई डिवाइस कनेक्ट करें"</string> <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"इस सेशन को कास्ट करने के लिए, कृपया ऐप्लिकेशन खोलें."</string> <string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"अनजान ऐप्लिकेशन"</string> <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"कास्ट करना बंद करें"</string> @@ -1275,7 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"डिवाइस फ़ोल्ड किया गया"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"डिवाइस अनफ़ोल्ड किया गया"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string> - <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"<xliff:g id="PERCENTAGE">%s</xliff:g> बैटरी बची है"</string> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"स्टाइलस की बैटरी <xliff:g id="PERCENTAGE">%s</xliff:g> है"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"अपने स्टाइलस को चार्ज करें"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"स्टाइलस की बैटरी कम है"</string> <string name="video_camera" msgid="7654002575156149298">"वीडियो कैमरा"</string> @@ -1318,6 +1315,16 @@ <string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"हाल ही में, <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>) ने इस्तेमाल किया"</string> <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>) पर इस्तेमाल किया जा रहा है"</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>) ने इस्तेमाल किया"</string> + <string name="shortcut_helper_category_system" msgid="462110876978937359">"सिस्टम"</string> + <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"मल्टीटास्किंग (एक साथ कई काम करना)"</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_a11y" msgid="6314444792641773464">"सुलभता"</string> + <string name="shortcut_helper_title" msgid="8567500639300970049">"कीबोर्ड शॉर्टकट"</string> + <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"सर्च शॉर्टकट"</string> + <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"छोटा करने का आइकॉन"</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="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> diff --git a/packages/SystemUI/res/values-hr/strings.xml b/packages/SystemUI/res/values-hr/strings.xml index 19625bd0c887..acf253dd00a5 100644 --- a/packages/SystemUI/res/values-hr/strings.xml +++ b/packages/SystemUI/res/values-hr/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Automatski uključi ponovno sutra"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Značajke kao što su brzo dijeljenje i Pronađi moj uređaj koriste Bluetooth"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth će se uključiti sutra ujutro"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Zajedničko slušanje"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Zajedničko slušanje"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> baterije"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Slušalice"</string> @@ -361,19 +363,19 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Na koji dio doživljaja na uređaju to utjecalo?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Odaberite vrstu problema"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Snimanje zaslona"</string> - <string-array name="qs_record_issue_types"> - <item msgid="2947988124014085798">"Izvedba"</item> - <item msgid="1627504621139124393">"Korisničko sučelje"</item> - <item msgid="8309220355268900335">"Baterija"</item> - </string-array> + <string name="performance" msgid="6552785217174378320">"Izvedba"</string> + <string name="user_interface" msgid="3712869377953950887">"Korisničko sučelje"</string> + <string name="thermal" msgid="6758074791325414831">"Termalno"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Način rada jednom rukom"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Slušna pomagala"</string> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Aktivno"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Nije povezano"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Slušna pomagala"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Uparite novi uređaj"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Kliknite da biste uparili novi uređaj"</string> <string name="hearing_devices_presets_error" msgid="350363093458408536">"Ažuriranje unaprijed definiranih postavki nije uspjelo"</string> - <!-- no translation found for hearing_devices_preset_label (7878267405046232358) --> - <skip /> + <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Unaprijed definirana postavka"</string> + <string name="live_caption_title" msgid="8916875614623730005">"Automatski titlovi"</string> <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> @@ -727,11 +729,6 @@ <string name="keyboard_key_button_template" msgid="8005673627272051429">"Tipka <xliff:g id="NAME">%1$s</xliff:g>"</string> <string name="keyboard_key_home" msgid="3734400625170020657">"Početak"</string> <string name="keyboard_key_back" msgid="4185420465469481999">"Natrag"</string> - <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"Strelica prema gore"</string> - <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"Strelica prema dolje"</string> - <string name="keyboard_key_dpad_left" msgid="144176368026538621">"Strelica lijevo"</string> - <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"Strelica desno"</string> - <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"Sredina"</string> <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string> <string name="keyboard_key_space" msgid="6980847564173394012">"Razmaknica"</string> <string name="keyboard_key_enter" msgid="8633362970109751646">"Unos"</string> @@ -932,7 +929,7 @@ <string name="qs_dnd_prompt_auto_rule_app" msgid="1841469944118486580">"Način Ne uznemiravaj uključilo je automatsko pravilo ili aplikacija."</string> <string name="running_foreground_services_title" msgid="5137313173431186685">"Izvođenje aplikacija u pozadini"</string> <string name="running_foreground_services_msg" msgid="3009459259222695385">"Dodirnite da biste vidjeli pojedinosti o potrošnji baterije i podatkovnom prometu"</string> - <string name="mobile_data_disable_title" msgid="5366476131671617790">"Isključiti mobilne podatke?"</string> + <string name="mobile_data_disable_title" msgid="5366476131671617790">"Želite li isključiti mobilne podatke?"</string> <string name="mobile_data_disable_message" msgid="8604966027899770415">"Nećete imati pristup mobilnim podacima ili internetu putem operatera <xliff:g id="CARRIER">%s</xliff:g>. Internet će biti dostupan samo putem Wi-Fija."</string> <string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"vaš mobilni operater"</string> <string name="auto_data_switch_disable_title" msgid="5146527155665190652">"Vratiti se na mobilnog operatera <xliff:g id="CARRIER">%s</xliff:g>?"</string> @@ -1275,7 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"zatvoreno"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"otvoreno"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s/%2$s"</string> - <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"Preostalo je <xliff:g id="PERCENTAGE">%s</xliff:g> baterije"</string> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Baterija olovke <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Priključite pisaljku na punjač"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"Slaba baterija pisaljke"</string> <string name="video_camera" msgid="7654002575156149298">"Videokamera"</string> @@ -1318,6 +1315,16 @@ <string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"Nedavno koristila aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string> <string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"Koristi: <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>)"</string> <string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"Nedavno koristila aplikacija <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>)"</string> + <string name="shortcut_helper_category_system" msgid="462110876978937359">"Sustav"</string> + <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Obavljanje više zadataka"</string> + <string name="shortcut_helper_category_input" msgid="8674018654124839566">"Unos"</string> + <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Prečaci aplikacija"</string> + <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Pristupačnost"</string> + <string name="shortcut_helper_title" msgid="8567500639300970049">"Tipkovni prečaci"</string> + <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Prečaci za pretraživanje"</string> + <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ikona za sažimanje"</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="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Pozadinsko osvjetljenje tipkovnice"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"Razina %1$d od %2$d"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"Upravljanje uređajima"</string> diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml index 70990336e93f..c73511e18677 100644 --- a/packages/SystemUI/res/values-hu/strings.xml +++ b/packages/SystemUI/res/values-hu/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Automatikus visszakapcsolás holnap"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Egyes funkciók (például a Quick Share és a Készülékkereső) Bluetootht használnak"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"A Bluetooth holnap reggel bekapcsol"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Hang megosztása"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Hang megosztása…"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Akkumulátor: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Hang"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Headset"</string> @@ -292,7 +294,7 @@ <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> - <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"Képernyővédő"</string> + <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"Képernyőkímélő"</string> <string name="quick_settings_camera_label" msgid="5612076679385269339">"Hozzáférés a kamerához"</string> <string name="quick_settings_mic_label" msgid="8392773746295266375">"Mikrofonelérés"</string> <string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"Rendelkezésre áll"</string> @@ -361,19 +363,19 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Az eszközhasználati élmény mely része érintett?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Problématípus kiválasztása"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Képernyőrögzítés"</string> - <string-array name="qs_record_issue_types"> - <item msgid="2947988124014085798">"Teljesítmény"</item> - <item msgid="1627504621139124393">"Kezelőfelület"</item> - <item msgid="8309220355268900335">"Akkumulátor"</item> - </string-array> + <string name="performance" msgid="6552785217174378320">"Teljesítmény"</string> + <string name="user_interface" msgid="3712869377953950887">"Kezelőfelület"</string> + <string name="thermal" msgid="6758074791325414831">"Termikus"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Egykezes mód"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Hallókészülékek"</string> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Aktív"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Leválasztva"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Hallókészülékek"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Új eszköz párosítása"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Kattintson új eszköz párosításához"</string> <string name="hearing_devices_presets_error" msgid="350363093458408536">"Nem sikerült frissíteni a beállításkészletet"</string> - <!-- no translation found for hearing_devices_preset_label (7878267405046232358) --> - <skip /> + <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Beállításkészlet"</string> + <string name="live_caption_title" msgid="8916875614623730005">"Élő feliratozás"</string> <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> @@ -596,7 +598,7 @@ <string name="screen_pinning_exit" msgid="4553787518387346893">"Alkalmazás kitűzése megszüntetve"</string> <string name="stream_voice_call" msgid="7468348170702375660">"Telefonhívás"</string> <string name="stream_system" msgid="7663148785370565134">"Rendszer"</string> - <string name="stream_ring" msgid="7550670036738697526">"Csörgetés"</string> + <string name="stream_ring" msgid="7550670036738697526">"Csörgés"</string> <string name="stream_music" msgid="2188224742361847580">"Média"</string> <string name="stream_alarm" msgid="16058075093011694">"Ébresztő"</string> <string name="stream_notification" msgid="7930294049046243939">"Értesítés"</string> @@ -727,11 +729,6 @@ <string name="keyboard_key_button_template" msgid="8005673627272051429">"<xliff:g id="NAME">%1$s</xliff:g> gomb"</string> <string name="keyboard_key_home" msgid="3734400625170020657">"Kezdőképernyő"</string> <string name="keyboard_key_back" msgid="4185420465469481999">"Vissza"</string> - <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"Felfelé nyíl"</string> - <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"Lefelé nyíl"</string> - <string name="keyboard_key_dpad_left" msgid="144176368026538621">"Balra nyíl"</string> - <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"Jobbra nyíl"</string> - <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"Középre"</string> <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string> <string name="keyboard_key_space" msgid="6980847564173394012">"Szóköz"</string> <string name="keyboard_key_enter" msgid="8633362970109751646">"Enter"</string> @@ -1275,7 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"összehajtva"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"kihajtva"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string> - <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"Akkumulátor töltöttségi szintje: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Érintőceruza töltöttségi szintje: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Tegye töltőre az érintőceruzát"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"Az érintőceruza töltöttsége alacsony"</string> <string name="video_camera" msgid="7654002575156149298">"Videokamera"</string> @@ -1318,6 +1315,16 @@ <string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"Legutóbb használta: <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string> <string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"Használatban a következő által: <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>)"</string> <string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"Legutóbb használta: <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>)"</string> + <string name="shortcut_helper_category_system" msgid="462110876978937359">"Rendszer"</string> + <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Multitasking"</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_a11y" msgid="6314444792641773464">"Kisegítő lehetőségek"</string> + <string name="shortcut_helper_title" msgid="8567500639300970049">"Billentyűparancsok"</string> + <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Billentyűparancsok keresése"</string> + <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Összecsukás ikon"</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="keyboard_backlight_dialog_title" msgid="8273102932345564724">"A billentyűzet háttérvilágítása"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"Fényerő: %2$d/%1$d"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"Otthon vezérlése"</string> diff --git a/packages/SystemUI/res/values-hy/strings.xml b/packages/SystemUI/res/values-hy/strings.xml index c539407f4efd..f1462be4261a 100644 --- a/packages/SystemUI/res/values-hy/strings.xml +++ b/packages/SystemUI/res/values-hy/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Վաղը նորից ավտոմատ միացնել"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Bluetooth-ն օգտագործում են, օրինակ, Quick Share և «Գտնել իմ սարքը» գործառույթները"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth-ը կմիանա վաղն առավոտյան"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Աուդիոյի փոխանցում"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Աուդիոն փոխանցվում է"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Մարտկոցի լիցքը՝ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Աուդիո"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Ականջակալ"</string> @@ -361,19 +363,19 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Սարքի ո՞ր մասի հետ է կապված խնդիրը։"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Ընտրեք խնդրի տեսակը"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Էկրանի տեսագրում"</string> - <string-array name="qs_record_issue_types"> - <item msgid="2947988124014085798">"Աշխատանք"</item> - <item msgid="1627504621139124393">"Միջերես"</item> - <item msgid="8309220355268900335">"Մարտկոց"</item> - </string-array> + <string name="performance" msgid="6552785217174378320">"Արդյունավետություն"</string> + <string name="user_interface" msgid="3712869377953950887">"Օգտատիրական ինտերֆեյս"</string> + <string name="thermal" msgid="6758074791325414831">"Ջերմատեսիլ"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Մեկ ձեռքի ռեժիմ"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Լսողական սարքեր"</string> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Ակտիվ է"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Անջատված է"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Լսողական սարքեր"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Նոր սարքի զուգակցում"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Սեղմեք՝ նոր սարք զուգակցելու համար"</string> <string name="hearing_devices_presets_error" msgid="350363093458408536">"Չհաջողվեց թարմացնել կարգավորումների հավաքածուն"</string> - <!-- no translation found for hearing_devices_preset_label (7878267405046232358) --> - <skip /> + <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Կարգավորումների հավաքածու"</string> + <string name="live_caption_title" msgid="8916875614623730005">"Կենդանի ենթագրեր"</string> <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> @@ -594,9 +596,9 @@ <string name="screen_pinning_negative" msgid="6882816864569211666">"Ոչ"</string> <string name="screen_pinning_start" msgid="7483998671383371313">"Հավելվածն ամրացվեց"</string> <string name="screen_pinning_exit" msgid="4553787518387346893">"Հավելվածն ապամրացվեց"</string> - <string name="stream_voice_call" msgid="7468348170702375660">"Զանգ"</string> + <string name="stream_voice_call" msgid="7468348170702375660">"Զանգելը"</string> <string name="stream_system" msgid="7663148785370565134">"Համակարգ"</string> - <string name="stream_ring" msgid="7550670036738697526">"Զանգ"</string> + <string name="stream_ring" msgid="7550670036738697526">"Զանգ ստանալը"</string> <string name="stream_music" msgid="2188224742361847580">"Մեդիա"</string> <string name="stream_alarm" msgid="16058075093011694">"Զարթուցիչ"</string> <string name="stream_notification" msgid="7930294049046243939">"Ծանուցում"</string> @@ -727,11 +729,6 @@ <string name="keyboard_key_button_template" msgid="8005673627272051429">"<xliff:g id="NAME">%1$s</xliff:g> կոճակ"</string> <string name="keyboard_key_home" msgid="3734400625170020657">"Գլխավոր էջ"</string> <string name="keyboard_key_back" msgid="4185420465469481999">"Հետ"</string> - <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"Վերև սլաք"</string> - <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"Ներքև սլաք"</string> - <string name="keyboard_key_dpad_left" msgid="144176368026538621">"Ձախ սլաք"</string> - <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"Աջ սլաք"</string> - <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"Կենտրոն"</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> @@ -1275,7 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"ծալված"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"բացված"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string> - <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"Մարտկոցի լիցքը՝ <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Ստիլուսի մարտկոցի լիցքը՝ <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Ձեր ստիլուսը միացրեք լիցքավորիչի"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"Ստիլուսի մարտկոցի լիցքի ցածր մակարդակ"</string> <string name="video_camera" msgid="7654002575156149298">"Տեսախցիկ"</string> @@ -1318,6 +1315,16 @@ <string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"Վերջերս օգտագործվել է <xliff:g id="APP_NAME">%1$s</xliff:g> հավելվածի կողմից (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string> <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>)"</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>)"</string> + <string name="shortcut_helper_category_system" msgid="462110876978937359">"Համակարգ"</string> + <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Բազմախնդրություն"</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_a11y" msgid="6314444792641773464">"Հատուկ գործառույթներ"</string> + <string name="shortcut_helper_title" msgid="8567500639300970049">"Ստեղնային դյուրանցումներ"</string> + <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Դյուրանցումների որոնում"</string> + <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ծալել պատկերակը"</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="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> diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml index 5af06e8f6f11..7525e9aea79a 100644 --- a/packages/SystemUI/res/values-in/strings.xml +++ b/packages/SystemUI/res/values-in/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Otomatis aktifkan lagi besok"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Fitur seperti Quick Share dan Temukan Perangkat Saya menggunakan Bluetooth"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth akan dinyalakan besok pagi"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Berbagi Audio"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Berbagi Audio"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Baterai <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Headset"</string> @@ -361,19 +363,19 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Bagian pengalaman perangkat mana yang terpengaruh?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Pilih jenis masalah"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Perekaman layar"</string> - <string-array name="qs_record_issue_types"> - <item msgid="2947988124014085798">"Performa"</item> - <item msgid="1627504621139124393">"Antarmuka Pengguna"</item> - <item msgid="8309220355268900335">"Baterai"</item> - </string-array> + <string name="performance" msgid="6552785217174378320">"Performa"</string> + <string name="user_interface" msgid="3712869377953950887">"Antarmuka Pengguna"</string> + <string name="thermal" msgid="6758074791325414831">"Termal"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Mode satu tangan"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Alat bantu dengar"</string> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Aktif"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Terputus"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Alat bantu dengar"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Sambungkan perangkat baru"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Klik untuk menyambungkan perangkat baru"</string> <string name="hearing_devices_presets_error" msgid="350363093458408536">"Tidak dapat memperbarui preset"</string> - <!-- no translation found for hearing_devices_preset_label (7878267405046232358) --> - <skip /> + <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Preset"</string> + <string name="live_caption_title" msgid="8916875614623730005">"Teks Otomatis"</string> <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> @@ -727,11 +729,6 @@ <string name="keyboard_key_button_template" msgid="8005673627272051429">"Tombol <xliff:g id="NAME">%1$s</xliff:g>"</string> <string name="keyboard_key_home" msgid="3734400625170020657">"Home"</string> <string name="keyboard_key_back" msgid="4185420465469481999">"Back"</string> - <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"Panah atas"</string> - <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"Panah bawah"</string> - <string name="keyboard_key_dpad_left" msgid="144176368026538621">"Panah kiri"</string> - <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"Panah kanan"</string> - <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"Center"</string> <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> @@ -988,7 +985,7 @@ <string name="magnification_close_settings_click_label" msgid="4642477260651704517">"Tutup setelan pembesaran"</string> <string name="magnification_exit_edit_mode_click_label" msgid="1664818325144887117">"Keluar dari mode edit"</string> <string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"Tarik pojok persegi untuk mengubah ukuran"</string> - <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"Izinkan scrolling diagonal"</string> + <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"Izinkan scroll diagonal"</string> <string name="accessibility_resize" msgid="5733759136600611551">"Ubah ukuran"</string> <string name="accessibility_change_magnification_type" msgid="666000085077432421">"Ubah jenis pembesaran"</string> <string name="accessibility_magnification_end_resizing" msgid="4881690585800302628">"Akhiri pengubahan ukuran"</string> @@ -1275,7 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"ditutup"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"dibuka"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string> - <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"Baterai tersisa <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Baterai stilus <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Hubungkan stilus ke pengisi daya"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"Baterai stilus lemah"</string> <string name="video_camera" msgid="7654002575156149298">"Kamera video"</string> @@ -1318,6 +1315,16 @@ <string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"Baru saja digunakan oleh <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string> <string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"Sedang digunakan oleh <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>)"</string> <string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"Baru saja digunakan oleh <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>)"</string> + <string name="shortcut_helper_category_system" msgid="462110876978937359">"Sistem"</string> + <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Multitasking"</string> + <string name="shortcut_helper_category_input" msgid="8674018654124839566">"Input"</string> + <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Pintasan aplikasi"</string> + <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Aksesibilitas"</string> + <string name="shortcut_helper_title" msgid="8567500639300970049">"Pintasan keyboard"</string> + <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Pintasan penelusuran"</string> + <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ikon ciutkan"</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="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Lampu latar keyboard"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"Tingkat %1$d dari %2$d"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"Kontrol Rumah"</string> diff --git a/packages/SystemUI/res/values-is/strings.xml b/packages/SystemUI/res/values-is/strings.xml index ee99882d6b7c..38507900ebac 100644 --- a/packages/SystemUI/res/values-is/strings.xml +++ b/packages/SystemUI/res/values-is/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Kveikja sjálfkrafa aftur á morgun"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Eiginleikar eins og Flýtideiling og Finna tækið mitt nota Bluetooth"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Kveikt verður á Bluetooth í fyrramálið"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Hljóði deilt"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Deilir hljóði"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> rafhlöðuhleðsla"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Hljóð"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Höfuðtól"</string> @@ -361,19 +363,19 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Hvað í tækjaupplifuninni varð fyrir áhrifum?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Veldu gerð vandamáls"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Skjáupptaka"</string> - <string-array name="qs_record_issue_types"> - <item msgid="2947988124014085798">"Afköst"</item> - <item msgid="1627504621139124393">"Notandaviðmót"</item> - <item msgid="8309220355268900335">"Rafhlaða"</item> - </string-array> + <string name="performance" msgid="6552785217174378320">"Afköst"</string> + <string name="user_interface" msgid="3712869377953950887">"Notandaviðmót"</string> + <string name="thermal" msgid="6758074791325414831">"Varmi"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Einhent stilling"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Heyrnartæki"</string> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Virk"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Aftengd"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Heyrnartæki"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Para nýtt tæki"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Smelltu til að para nýtt tæki"</string> <string name="hearing_devices_presets_error" msgid="350363093458408536">"Tókst ekki að uppfæra forstillingu"</string> - <!-- no translation found for hearing_devices_preset_label (7878267405046232358) --> - <skip /> + <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Forstilling"</string> + <string name="live_caption_title" msgid="8916875614623730005">"Skjátextar í rauntíma"</string> <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> @@ -727,11 +729,6 @@ <string name="keyboard_key_button_template" msgid="8005673627272051429">"Hnappur <xliff:g id="NAME">%1$s</xliff:g>"</string> <string name="keyboard_key_home" msgid="3734400625170020657">"Home"</string> <string name="keyboard_key_back" msgid="4185420465469481999">"Til baka"</string> - <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"Ör upp"</string> - <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"Ör niður"</string> - <string name="keyboard_key_dpad_left" msgid="144176368026538621">"Ör til vinstri"</string> - <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"Ör til hægri"</string> - <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"Miðja"</string> <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string> <string name="keyboard_key_space" msgid="6980847564173394012">"Bilslá"</string> <string name="keyboard_key_enter" msgid="8633362970109751646">"Enter"</string> @@ -1275,7 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"samanbrotið"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"opið"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string> - <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"<xliff:g id="PERCENTAGE">%s</xliff:g> hleðsla eftir á rafhlöðu"</string> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Pennarafhlaða <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Tengdu pennann við hleðslutæki"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"Rafhlaða pennans er að tæmast"</string> <string name="video_camera" msgid="7654002575156149298">"Kvikmyndatökuvél"</string> @@ -1318,6 +1315,16 @@ <string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"Nýlega notað af <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string> <string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"Í notkun í <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>)"</string> <string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"Nýlega notað af <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>)"</string> + <string name="shortcut_helper_category_system" msgid="462110876978937359">"Kerfi"</string> + <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Fjölvinnsla"</string> + <string name="shortcut_helper_category_input" msgid="8674018654124839566">"Inntak"</string> + <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Flýtileiðir forrita"</string> + <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Aðgengi"</string> + <string name="shortcut_helper_title" msgid="8567500639300970049">"Flýtilyklar"</string> + <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Leitarflýtileiðir"</string> + <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Minnka tákn"</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="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Baklýsing lyklaborðs"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"Stig %1$d af %2$d"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"Heimastýringar"</string> diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml index b42bd95f32e0..a0d54c267c7b 100644 --- a/packages/SystemUI/res/values-it/strings.xml +++ b/packages/SystemUI/res/values-it/strings.xml @@ -76,8 +76,7 @@ <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"è stata inviata un\'immagine"</string> <string name="screenshot_saving_title" msgid="2298349784913287333">"Salvataggio screenshot…"</string> <string name="screenshot_saving_work_profile_title" msgid="5332829607308450880">"Salvataggio screenshot nel profilo di lavoro…"</string> - <!-- no translation found for screenshot_saving_private_profile (8934706048497093297) --> - <skip /> + <string name="screenshot_saving_private_profile" msgid="8934706048497093297">"Salvataggio screenshot nel profilo privato in corso…"</string> <string name="screenshot_saved_title" msgid="8893267638659083153">"Screenshot salvato"</string> <string name="screenshot_failed_title" msgid="3259148215671936891">"Impossibile salvare lo screenshot"</string> <string name="screenshot_failed_external_display_indication" msgid="6555673132061101936">"Display esterno"</string> @@ -282,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Riattiva automaticamente domani"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Funzionalità come Quick Share e Trova il mio dispositivo usano il Bluetooth"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Il Bluetooth verrà attivato domani mattina"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Condivisione audio"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Condivisione audio in corso…"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Batteria: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Auricolare"</string> @@ -362,19 +363,19 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Quali problemi ha l\'esperienza del dispositivo?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Seleziona il tipo di problema"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Regis. dello schermo"</string> - <string-array name="qs_record_issue_types"> - <item msgid="2947988124014085798">"Prestazioni"</item> - <item msgid="1627504621139124393">"Interfaccia utente"</item> - <item msgid="8309220355268900335">"Batteria"</item> - </string-array> + <string name="performance" msgid="6552785217174378320">"Prestazioni"</string> + <string name="user_interface" msgid="3712869377953950887">"Interfaccia utente"</string> + <string name="thermal" msgid="6758074791325414831">"Termico"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Modalità a una mano"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Apparecchi acustici"</string> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Attivi"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Disconnessi"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Protesi uditive"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Accoppia nuovo dispositivo"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Fai clic per accoppiare un nuovo dispositivo"</string> <string name="hearing_devices_presets_error" msgid="350363093458408536">"Impossibile aggiornare preset"</string> - <!-- no translation found for hearing_devices_preset_label (7878267405046232358) --> - <skip /> + <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Preset"</string> + <string name="live_caption_title" msgid="8916875614623730005">"Sottotitoli in tempo reale"</string> <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> @@ -581,11 +582,11 @@ <string name="csd_button_keep_listening" product="default" msgid="4093794049149286784">"Continua ad ascoltare"</string> <string name="csd_button_lower_volume" product="default" msgid="5347210412376264579">"Abbassa il volume"</string> <string name="screen_pinning_title" msgid="9058007390337841305">"L\'app è bloccata sullo schermo"</string> - <string name="screen_pinning_description" msgid="8699395373875667743">"La schermata rimane visibile finché non viene sganciata. Per sganciarla, tieni premuto Indietro e Panoramica."</string> - <string name="screen_pinning_description_recents_invisible" msgid="4564466648700390037">"La schermata rimane visibile finché non viene disattivato il blocco su schermo. Per disattivarlo, tocca e tieni premuto Indietro e Home."</string> + <string name="screen_pinning_description" msgid="8699395373875667743">"La schermata rimane visibile finché non viene sbloccata. Per sbloccarla, tieni premuto Indietro e Panoramica."</string> + <string name="screen_pinning_description_recents_invisible" msgid="4564466648700390037">"La schermata rimane visibile finché non viene sbloccata. Per sbloccarla, tocca e tieni premuto Indietro e Home."</string> <string name="screen_pinning_description_gestural" msgid="7246323931831232068">"Rimarrà visibile finché non viene sbloccata. Scorri verso l\'alto e tieni premuto per sbloccarla."</string> - <string name="screen_pinning_description_accessible" msgid="7386449191953535332">"La schermata rimane visibile finché non viene sganciata. Per sganciarla, tieni premuto Panoramica."</string> - <string name="screen_pinning_description_recents_invisible_accessible" msgid="2857071808674481986">"La schermata rimane visibile finché non viene disattivato il blocco su schermo. Per disattivarlo, tocca e tieni premuto Home."</string> + <string name="screen_pinning_description_accessible" msgid="7386449191953535332">"La schermata rimane visibile finché non viene sbloccata. Per sbloccarla, tieni premuto Panoramica."</string> + <string name="screen_pinning_description_recents_invisible_accessible" msgid="2857071808674481986">"La schermata rimane visibile finché non viene sbloccata. Per sbloccarla, tocca e tieni premuto Home."</string> <string name="screen_pinning_exposes_personal_data" msgid="8189852022981524789">"I dati personali potrebbero essere accessibili (ad esempio i contatti e i contenuti delle email)."</string> <string name="screen_pinning_can_open_other_apps" msgid="7529756813231421455">"L\'app bloccata sullo schermo potrebbe aprire altre app."</string> <string name="screen_pinning_toast" msgid="8177286912533744328">"Per sbloccare questa app, tocca e tieni premuti i pulsanti Indietro e Panoramica"</string> @@ -728,11 +729,6 @@ <string name="keyboard_key_button_template" msgid="8005673627272051429">"Pulsante <xliff:g id="NAME">%1$s</xliff:g>"</string> <string name="keyboard_key_home" msgid="3734400625170020657">"Home page"</string> <string name="keyboard_key_back" msgid="4185420465469481999">"Indietro"</string> - <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"Freccia su"</string> - <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"Freccia giù"</string> - <string name="keyboard_key_dpad_left" msgid="144176368026538621">"Freccia sinistra"</string> - <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"Freccia destra"</string> - <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"Al centro"</string> <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string> <string name="keyboard_key_space" msgid="6980847564173394012">"Spazio"</string> <string name="keyboard_key_enter" msgid="8633362970109751646">"Invio"</string> @@ -1276,7 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"Piegato"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"Non piegato"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string> - <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"<xliff:g id="PERCENTAGE">%s</xliff:g> batteria rimanente"</string> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Batteria dello stilo: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Connetti lo stilo a un caricabatterie"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"Batteria stilo in esaurimento"</string> <string name="video_camera" msgid="7654002575156149298">"Videocamera"</string> @@ -1319,6 +1315,16 @@ <string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"Recentemente in uso da <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string> <string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"In uso da <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>)"</string> <string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"Recentemente in uso da <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>)"</string> + <string name="shortcut_helper_category_system" msgid="462110876978937359">"Sistema"</string> + <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Multitasking"</string> + <string name="shortcut_helper_category_input" msgid="8674018654124839566">"Input"</string> + <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Scorciatoie app"</string> + <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Accessibilità"</string> + <string name="shortcut_helper_title" msgid="8567500639300970049">"Scorciatoie da tastiera"</string> + <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Scorciatoie per la ricerca"</string> + <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Icona Comprimi"</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="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Retroilluminazione della tastiera"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"Livello %1$d di %2$d"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"Controlli della casa"</string> diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml index 71d0c6be20b6..f773b84a0e67 100644 --- a/packages/SystemUI/res/values-iw/strings.xml +++ b/packages/SystemUI/res/values-iw/strings.xml @@ -278,11 +278,13 @@ <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"נשמר"</string> <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"ניתוק"</string> <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"הפעלה"</string> - <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"החיבור יופעל שוב אוטומטית מחר"</string> + <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"יופעל שוב אוטומטית מחר"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"תכונות כמו \'שיתוף מהיר\' ו\'איפה המכשיר שלי\' משתמשות ב-Bluetooth"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"חיבור ה-Bluetooth יופעל מחר בבוקר"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"שיתוף אודיו"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"מתבצע שיתוף של האודיו"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> סוללה"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"אודיו"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"אוזניות"</string> @@ -361,19 +363,19 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"איזה חלק בחוויית השימוש שלך במכשיר הושפע?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"בחירה בסוג הבעיה"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"הקלטת המסך"</string> - <string-array name="qs_record_issue_types"> - <item msgid="2947988124014085798">"ביצועים"</item> - <item msgid="1627504621139124393">"ממשק משתמש"</item> - <item msgid="8309220355268900335">"סוללה"</item> - </string-array> + <string name="performance" msgid="6552785217174378320">"ביצועים"</string> + <string name="user_interface" msgid="3712869377953950887">"ממשק משתמש"</string> + <string name="thermal" msgid="6758074791325414831">"תרמי"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"מצב שימוש ביד אחת"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"מכשירי שמיעה"</string> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"פעיל"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"מנותק"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"מכשירי שמיעה"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"התאמה של מכשיר חדש"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"צריך ללחוץ כדי להתאים מכשיר חדש"</string> <string name="hearing_devices_presets_error" msgid="350363093458408536">"לא ניתן לעדכן את ההגדרה הקבועה מראש"</string> - <!-- no translation found for hearing_devices_preset_label (7878267405046232358) --> - <skip /> + <string name="hearing_devices_preset_label" msgid="7878267405046232358">"הגדרה קבועה מראש"</string> + <string name="live_caption_title" msgid="8916875614623730005">"כתוביות מיידיות"</string> <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> @@ -618,7 +620,7 @@ <string name="volume_panel_noise_control_title" msgid="7413949943872304474">"בקרת רעש"</string> <string name="volume_panel_spatial_audio_title" msgid="3367048857932040660">"אודיו מרחבי"</string> <string name="volume_panel_spatial_audio_off" msgid="4177490084606772989">"השבתה"</string> - <string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"מצב קבוע"</string> + <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_hint_mute" msgid="4263821214125126614">"השתקה"</string> @@ -727,11 +729,6 @@ <string name="keyboard_key_button_template" msgid="8005673627272051429">"לחצן <xliff:g id="NAME">%1$s</xliff:g>"</string> <string name="keyboard_key_home" msgid="3734400625170020657">"דף הבית"</string> <string name="keyboard_key_back" msgid="4185420465469481999">"הקודם"</string> - <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"חץ למעלה"</string> - <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"חץ למטה"</string> - <string name="keyboard_key_dpad_left" msgid="144176368026538621">"חץ שמאלה"</string> - <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"חץ ימינה"</string> - <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"מרכז"</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">"Enter"</string> @@ -1201,7 +1198,7 @@ <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"ה-Wi-Fi לא יתחבר באופן אוטומטי בינתיים"</string> <string name="see_all_networks" msgid="3773666844913168122">"הצגת הכול"</string> <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"כדי לעבור בין רשתות, צריך לנתק את האתרנט"</string> - <string name="wifi_scan_notify_message" msgid="3753839537448621794">"כדי לשפר את חוויית השימוש במכשיר, אפליקציות ושירותים יוכלו לחפש רשתות Wi-Fi בכל שלב, גם כאשר ה-Wi-Fi כבוי. אפשר לשנות זאת בהגדרות של חיפוש נקודות Wi-Fi. "<annotation id="link">"שינוי"</annotation></string> + <string name="wifi_scan_notify_message" msgid="3753839537448621794">"כדי לשפר את חוויית השימוש במכשיר, אפליקציות ושירותים יוכלו לחפש רשתות Wi-Fi בכל שלב, גם כאשר ה-Wi-Fi כבוי. אפשר לשנות את זה בהגדרות של חיפוש נקודות Wi-Fi. "<annotation id="link">"שינוי"</annotation></string> <string name="turn_off_airplane_mode" msgid="8425587763226548579">"השבתה של מצב טיסה"</string> <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"אפליקציית <xliff:g id="APPNAME">%1$s</xliff:g> מבקשת להוסיף להגדרות המהירות את הלחצן הבא"</string> <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"להוסיף לחצן"</string> @@ -1275,7 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"מצב מקופל"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"מצב לא מקופל"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string> - <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"רמת הטעינה שנותרה בסוללה: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"רמת הטעינה בסוללה של הסטיילוס: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"כדאי לחבר את הסטיילוס למטען"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"הסוללה של הסטיילוס חלשה"</string> <string name="video_camera" msgid="7654002575156149298">"מצלמת וידאו"</string> @@ -1318,6 +1315,16 @@ <string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"נעשה שימוש לאחרונה על ידי <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string> <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>)"</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>)"</string> + <string name="shortcut_helper_category_system" msgid="462110876978937359">"מערכת"</string> + <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"ריבוי משימות"</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_a11y" msgid="6314444792641773464">"נגישות"</string> + <string name="shortcut_helper_title" msgid="8567500639300970049">"מקשי קיצור"</string> + <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"קיצורי דרך לחיפוש"</string> + <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"סמל הכיווץ"</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="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> diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml index d6dc93c8887f..6b5d08241582 100644 --- a/packages/SystemUI/res/values-ja/strings.xml +++ b/packages/SystemUI/res/values-ja/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"明日自動的に ON に戻す"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Quick Share や「デバイスを探す」などの機能は Bluetooth を使用します"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"明日の朝に Bluetooth が ON になります"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"音声の共有"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"音声を共有中"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"バッテリー <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"オーディオ"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"ヘッドセット"</string> @@ -361,19 +363,19 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"デバイスのどの部分が影響を受けましたか?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"問題の種類を選択する"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"スクリーン レコード"</string> - <string-array name="qs_record_issue_types"> - <item msgid="2947988124014085798">"パフォーマンス"</item> - <item msgid="1627504621139124393">"ユーザー インターフェース"</item> - <item msgid="8309220355268900335">"バッテリー"</item> - </string-array> + <string name="performance" msgid="6552785217174378320">"パフォーマンス"</string> + <string name="user_interface" msgid="3712869377953950887">"ユーザー インターフェース"</string> + <string name="thermal" msgid="6758074791325414831">"温度"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"片手モード"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"補聴器"</string> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"アクティブ"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"未接続"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"補聴器"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"新しいデバイスとペア設定"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"クリックすると、新しいデバイスをペア設定できます"</string> <string name="hearing_devices_presets_error" msgid="350363093458408536">"プリセットを更新できませんでした"</string> - <!-- no translation found for hearing_devices_preset_label (7878267405046232358) --> - <skip /> + <string name="hearing_devices_preset_label" msgid="7878267405046232358">"プリセット"</string> + <string name="live_caption_title" msgid="8916875614623730005">"自動字幕起こし"</string> <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> @@ -727,11 +729,6 @@ <string name="keyboard_key_button_template" msgid="8005673627272051429">"<xliff:g id="NAME">%1$s</xliff:g> ボタン"</string> <string name="keyboard_key_home" msgid="3734400625170020657">"Home"</string> <string name="keyboard_key_back" msgid="4185420465469481999">"戻る"</string> - <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"上矢印"</string> - <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"下矢印"</string> - <string name="keyboard_key_dpad_left" msgid="144176368026538621">"左矢印"</string> - <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"右矢印"</string> - <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"中央"</string> <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> @@ -1275,7 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"折りたたんだ状態"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"広げた状態"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s/%2$s"</string> - <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"バッテリー残量 <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"タッチペンのバッテリー残量 <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"タッチペンを充電器に接続してください"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"タッチペンのバッテリー残量が少なくなっています"</string> <string name="video_camera" msgid="7654002575156149298">"ビデオカメラ"</string> @@ -1318,6 +1315,16 @@ <string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"<xliff:g id="APP_NAME">%1$s</xliff:g> が最近使用(<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string> <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>)"</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>)"</string> + <string name="shortcut_helper_category_system" msgid="462110876978937359">"システム"</string> + <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"マルチタスク"</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_a11y" msgid="6314444792641773464">"ユーザー補助"</string> + <string name="shortcut_helper_title" msgid="8567500639300970049">"キーボード ショートカット"</string> + <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"検索ショートカット"</string> + <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"閉じるアイコン"</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="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> diff --git a/packages/SystemUI/res/values-ka/strings.xml b/packages/SystemUI/res/values-ka/strings.xml index 2670e4542344..399c1f0dc4c4 100644 --- a/packages/SystemUI/res/values-ka/strings.xml +++ b/packages/SystemUI/res/values-ka/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"ხელახლა ავტომატურად ჩართვა ხვალ"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"ისეთი ფუნქციები, როგორიცაა სწრაფი გაზიარება და ჩემი მოწყობილობის პოვნა, იყენებს Bluetooth-ს"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth ჩაირთვება ხვალ დილით"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"აუდიოს გაზიარება"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"აუდიოს გაზიარება"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> ბატარეა"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"აუდიო"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"ყურსაცვამი"</string> @@ -361,19 +363,19 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"თქვენი მოწყობილობის გამოცდილების რა ნაწილზე მოხდა ზეგავლენა?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"აირჩიეთ პრობლემის ტიპი"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"ეკრანის ჩანაწერი"</string> - <string-array name="qs_record_issue_types"> - <item msgid="2947988124014085798">"ეფექტურობა"</item> - <item msgid="1627504621139124393">"სამომხმარებლო ინტერფეისი"</item> - <item msgid="8309220355268900335">"ბატარეა"</item> - </string-array> + <string name="performance" msgid="6552785217174378320">"ეფექტურობა"</string> + <string name="user_interface" msgid="3712869377953950887">"სამომხმარებლო ინტერფეისი"</string> + <string name="thermal" msgid="6758074791325414831">"თერმული"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"ცალი ხელის რეჟიმი"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"სმენის აპარატები"</string> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"აქტიური"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"კავშირი გაწყვეტილია"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"სმენის აპარატები"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"ახალი მოწყობილობის დაწყვილება"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"დააწკაპუნეთ ახალი მოწყობილობის დასაწყვილებლად"</string> <string name="hearing_devices_presets_error" msgid="350363093458408536">"წინასწარ დაყენებული პარამეტრების განახლება ვერ მოხერხდა"</string> - <!-- no translation found for hearing_devices_preset_label (7878267405046232358) --> - <skip /> + <string name="hearing_devices_preset_label" msgid="7878267405046232358">"წინასწარ დაყენებული"</string> + <string name="live_caption_title" msgid="8916875614623730005">"ავტოსუბტიტრები"</string> <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> @@ -727,11 +729,6 @@ <string name="keyboard_key_button_template" msgid="8005673627272051429">"ღილაკი „<xliff:g id="NAME">%1$s</xliff:g>“"</string> <string name="keyboard_key_home" msgid="3734400625170020657">"Home"</string> <string name="keyboard_key_back" msgid="4185420465469481999">"უკან"</string> - <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"ზემოთ მიმართული ისარი"</string> - <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"ქვემოთ მიმართული ისარი"</string> - <string name="keyboard_key_dpad_left" msgid="144176368026538621">"მარცხნივ მიმართული ისარი"</string> - <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"მარჯვნივ მიმართული ისარი"</string> - <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"ცენტრში"</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">"Enter"</string> @@ -1275,7 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"დაკეცილი"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"გაშლილი"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string> - <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"დარჩენილია ბატარეის <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"სტილუსის ბატარეა <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"დააკავშირეთ თქვენი სტილუსი დამტენს"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"სტილუსის ბატარეა დაცლის პირასაა"</string> <string name="video_camera" msgid="7654002575156149298">"ვიდეოკამერა"</string> @@ -1318,6 +1315,16 @@ <string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"ახლახან გამოყენებულია <xliff:g id="APP_NAME">%1$s</xliff:g>-ის მიერ (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string> <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>)"</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>)"</string> + <string name="shortcut_helper_category_system" msgid="462110876978937359">"სისტემა"</string> + <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"მრავალამოცანიანი რეჟიმი"</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_a11y" msgid="6314444792641773464">"მისაწვდომობა"</string> + <string name="shortcut_helper_title" msgid="8567500639300970049">"კლავიატურის მალსახმობები"</string> + <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"ძიების მალსახმობები"</string> + <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"ხატულის ჩაკეცვა"</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="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> diff --git a/packages/SystemUI/res/values-kk/strings.xml b/packages/SystemUI/res/values-kk/strings.xml index c709bbec2b1f..a3e61790692a 100644 --- a/packages/SystemUI/res/values-kk/strings.xml +++ b/packages/SystemUI/res/values-kk/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Ертең автоматты түрде қосылсын"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Quick Share және Find My Device сияқты функциялар Bluetooth-ты пайдаланады."</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth ертең таңертең қосылады."</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Аудио бөлісу"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Аудио бөлісу әрекеті орындалып жатыр."</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Батарея деңгейі: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Aудио"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Гарнитура"</string> @@ -361,19 +363,19 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Құрылғы қызметінің қандай түріне әсер етті?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Мәселе түрін таңдаңыз."</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Экранды жазу"</string> - <string-array name="qs_record_issue_types"> - <item msgid="2947988124014085798">"Өнімділік"</item> - <item msgid="1627504621139124393">"Пайдаланушы интерфейсі"</item> - <item msgid="8309220355268900335">"Батарея"</item> - </string-array> + <string name="performance" msgid="6552785217174378320">"Өнімділік режимі"</string> + <string name="user_interface" msgid="3712869377953950887">"Пайдаланушы интерфейсі"</string> + <string name="thermal" msgid="6758074791325414831">"Термовизия"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Бір қолмен басқару режимі"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Есту құрылғылары"</string> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Қосулы"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Ажыратулы"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Есту құрылғылары"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Жаңа құрылғыны жұптау"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Жаңа құрылғыны жұптау үшін басыңыз."</string> <string name="hearing_devices_presets_error" msgid="350363093458408536">"Параметрлер жинағын жаңарту мүмкін болмады."</string> - <!-- no translation found for hearing_devices_preset_label (7878267405046232358) --> - <skip /> + <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Параметрлер жинағы"</string> + <string name="live_caption_title" msgid="8916875614623730005">"Live Caption"</string> <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> @@ -596,7 +598,7 @@ <string name="screen_pinning_exit" msgid="4553787518387346893">"Қолданба босатылды."</string> <string name="stream_voice_call" msgid="7468348170702375660">"Қоңырау шалу"</string> <string name="stream_system" msgid="7663148785370565134">"Жүйе"</string> - <string name="stream_ring" msgid="7550670036738697526">"Шылдырлау"</string> + <string name="stream_ring" msgid="7550670036738697526">"Шылдыр"</string> <string name="stream_music" msgid="2188224742361847580">"Мультимедиа"</string> <string name="stream_alarm" msgid="16058075093011694">"Дабыл"</string> <string name="stream_notification" msgid="7930294049046243939">"Хабарландыру"</string> @@ -727,11 +729,6 @@ <string name="keyboard_key_button_template" msgid="8005673627272051429">"<xliff:g id="NAME">%1$s</xliff:g> түймесі"</string> <string name="keyboard_key_home" msgid="3734400625170020657">"Home"</string> <string name="keyboard_key_back" msgid="4185420465469481999">"Артқа"</string> - <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"Жоғары бағыт пернесі"</string> - <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"Төмен бағыт пернесі"</string> - <string name="keyboard_key_dpad_left" msgid="144176368026538621">"Сол бағыт пернесі"</string> - <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"Оң бағыт пернесі"</string> - <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"Орталық"</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">"Enter"</string> @@ -1275,7 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"жабық"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"ашық"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s/%2$s"</string> - <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"Қалған батарея заряды: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Стилус батареясы: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Стилусты зарядтағышқа жалғаңыз."</string> <string name="stylus_battery_low" msgid="7134370101603167096">"Стилус батареясының заряды аз"</string> <string name="video_camera" msgid="7654002575156149298">"Бейнекамера"</string> @@ -1318,6 +1315,16 @@ <string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"Соңғы рет <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>) қолданбасы пайдаланды."</string> <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>) қолданбасы пайдаланып жатыр"</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>) қолданбасы пайдаланды."</string> + <string name="shortcut_helper_category_system" msgid="462110876978937359">"Жүйе"</string> + <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Мультитаскинг"</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_a11y" msgid="6314444792641773464">"Арнайы мүмкіндіктер"</string> + <string name="shortcut_helper_title" msgid="8567500639300970049">"Перне тіркесімдері"</string> + <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Іздеу жылдам пәрмендері"</string> + <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Жию белгішесі"</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="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> diff --git a/packages/SystemUI/res/values-km/strings.xml b/packages/SystemUI/res/values-km/strings.xml index 042e7481370c..7c6f12720cd6 100644 --- a/packages/SystemUI/res/values-km/strings.xml +++ b/packages/SystemUI/res/values-km/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"បើកដោយស្វ័យប្រវត្តិម្ដងទៀតនៅថ្ងៃស្អែក"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"មុខងារដូចជា Quick Share និង \"រកឧបករណ៍របស់ខ្ញុំ\" ប្រើប៊្លូធូស"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"ប៊្លូធូសនឹងបើកនៅព្រឹកស្អែក"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"ការស្ដាប់សំឡេងរួមគ្នា"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"កំពុងស្ដាប់សំឡេងរួមគ្នា"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"ថ្ម <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"សំឡេង"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"កាស"</string> @@ -361,19 +363,19 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"តើផ្នែកអ្វីនៃបទពិសោធប្រើប្រាស់ឧបករណ៍របស់អ្នកបានរងការប៉ះពាល់?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"ជ្រើសរើសប្រភេទបញ្ហា"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"ការថតវីដេអូអេក្រង់"</string> - <string-array name="qs_record_issue_types"> - <item msgid="2947988124014085798">"ប្រតិបត្តិការ"</item> - <item msgid="1627504621139124393">"ផ្ទៃប៉ះ"</item> - <item msgid="8309220355268900335">"ថ្ម"</item> - </string-array> + <string name="performance" msgid="6552785217174378320">"ប្រតិបត្តិការ"</string> + <string name="user_interface" msgid="3712869377953950887">"ផ្ទៃប៉ះ"</string> + <string name="thermal" msgid="6758074791325414831">"កម្ដៅ"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"មុខងារប្រើដៃម្ខាង"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"ឧបករណ៍ជំនួយការស្ដាប់"</string> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"សកម្ម"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"បានផ្ដាច់"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"ឧបករណ៍ជំនួយការស្ដាប់"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"ផ្គូផ្គងឧបករណ៍ថ្មី"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"ចុច ដើម្បីផ្គូផ្គងឧបករណ៍ថ្មី"</string> <string name="hearing_devices_presets_error" msgid="350363093458408536">"មិនអាចប្ដូរការកំណត់ជាមុនបានទេ"</string> - <!-- no translation found for hearing_devices_preset_label (7878267405046232358) --> - <skip /> + <string name="hearing_devices_preset_label" msgid="7878267405046232358">"កំណត់ជាមុន"</string> + <string name="live_caption_title" msgid="8916875614623730005">"អក្សររត់ក្នុងពេលជាក់ស្ដែង"</string> <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> @@ -727,11 +729,6 @@ <string name="keyboard_key_button_template" msgid="8005673627272051429">"ប៊ូតុង <xliff:g id="NAME">%1$s</xliff:g>"</string> <string name="keyboard_key_home" msgid="3734400625170020657">"Home"</string> <string name="keyboard_key_back" msgid="4185420465469481999">"Back"</string> - <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"ព្រួញឡើងលើ"</string> - <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"ព្រួញចុះក្រោម"</string> - <string name="keyboard_key_dpad_left" msgid="144176368026538621">"ព្រួញទៅឆ្វេង"</string> - <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"ព្រួញទៅស្ដាំ"</string> - <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"Center"</string> <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> @@ -1201,7 +1198,7 @@ <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Wi-Fi នឹងមិនភ្ជាប់ដោយស្វ័យប្រវត្តិក្នុងពេលនេះទេ"</string> <string name="see_all_networks" msgid="3773666844913168122">"មើលទាំងអស់"</string> <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"ដើម្បីប្ដូរបណ្ដាញ សូមផ្ដាច់អ៊ីសឺរណិត"</string> - <string name="wifi_scan_notify_message" msgid="3753839537448621794">"ដើម្បីធ្វើឱ្យបទពិសោធន៍ប្រើប្រាស់ឧបករណ៍ប្រសើរឡើង កម្មវិធី និងសេវាកម្មនៅតែអាចស្កេនរកបណ្ដាញ Wi‑Fi បានគ្រប់ពេល ទោះបីជានៅពេលដែលបិទ Wi‑Fi ក៏ដោយ។ អ្នកអាចប្ដូរវាបាននៅក្នុងការកំណត់ការស្កេន Wi‑Fi។ "<annotation id="link">"ប្ដូរ"</annotation></string> + <string name="wifi_scan_notify_message" msgid="3753839537448621794">"ដើម្បីធ្វើឱ្យបទពិសោធប្រើប្រាស់ឧបករណ៍ប្រសើរឡើង កម្មវិធី និងសេវាកម្មនៅតែអាចស្កេនរកបណ្ដាញ Wi‑Fi បានគ្រប់ពេល ទោះបីជានៅពេលដែលបិទ Wi‑Fi ក៏ដោយ។ អ្នកអាចប្ដូរវាបាននៅក្នុងការកំណត់ការស្កេន Wi‑Fi។ "<annotation id="link">"ប្ដូរ"</annotation></string> <string name="turn_off_airplane_mode" msgid="8425587763226548579">"បិទមុខងារពេលជិះយន្តហោះ"</string> <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> ចង់បញ្ចូលប្រអប់ខាងក្រោមទៅក្នុងការកំណត់រហ័ស"</string> <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"បញ្ចូលប្រអប់"</string> @@ -1275,7 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"បត់"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"លា"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string> - <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"ថ្មនៅសល់ <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"ថ្មប៊ិក <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"ភ្ជាប់ប៊ិករបស់អ្នកជាមួយឆ្នាំងសាក"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"ថ្មប៊ិកនៅសល់តិច"</string> <string name="video_camera" msgid="7654002575156149298">"កាមេរ៉ាវីដេអូ"</string> @@ -1318,6 +1315,16 @@ <string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"បានប្រើនាពេលថ្មីៗនេះដោយ <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string> <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>)"</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>)"</string> + <string name="shortcut_helper_category_system" msgid="462110876978937359">"ប្រព័ន្ធ"</string> + <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"ការដំណើរការបានច្រើន"</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_a11y" msgid="6314444792641773464">"ភាពងាយស្រួល"</string> + <string name="shortcut_helper_title" msgid="8567500639300970049">"ផ្លូវកាត់ក្ដារចុច"</string> + <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"ផ្លូវកាត់ការស្វែងរក"</string> + <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"រូបតំណាង \"បង្រួម\""</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="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> diff --git a/packages/SystemUI/res/values-kn/strings.xml b/packages/SystemUI/res/values-kn/strings.xml index 8b1ab5f3b7e3..31aa87504ca3 100644 --- a/packages/SystemUI/res/values-kn/strings.xml +++ b/packages/SystemUI/res/values-kn/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"ನಾಳೆ ಪುನಃ ಸ್ವಯಂಚಾಲಿತವಾಗಿ ಆನ್ ಮಾಡಿ"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"ಕ್ವಿಕ್ ಶೇರ್ ಮತ್ತು Find My Device ನಂತಹ ಫೀಚರ್ಗಳು ಬ್ಲೂಟೂತ್ ಅನ್ನು ಬಳಸುತ್ತವೆ"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"ಬ್ಲೂಟೂತ್ ನಾಳೆ ಬೆಳಗ್ಗೆ ಆನ್ ಆಗುತ್ತದೆ"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"ಆಡಿಯೋ ಹಂಚಿಕೊಳ್ಳುವಿಕೆ"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"ಆಡಿಯೋವನ್ನು ಹಂಚಿಕೊಳ್ಳಲಾಗುತ್ತಿದೆ"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> ಬ್ಯಾಟರಿ"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"ಆಡಿಯೋ"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"ಹೆಡ್ಸೆಟ್"</string> @@ -361,19 +363,19 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"ಸಾಧನ ಬಳಸುವಾಗ ನೀವು ಯಾವ ರೀತಿಯ ಸಮಸ್ಯೆ ಎದುರಿಸುತ್ತೀರಿ?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"ಸಮಸ್ಯೆಯ ಪ್ರಕಾರವನ್ನು ಆಯ್ಕೆಮಾಡಿ"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"ಸ್ಕ್ರೀನ್ ರೆಕಾರ್ಡ್"</string> - <string-array name="qs_record_issue_types"> - <item msgid="2947988124014085798">"ಕಾರ್ಯಕ್ಷಮತೆ"</item> - <item msgid="1627504621139124393">"ಬಳಕೆದಾರರ ಇಂಟರ್ಫೇಸ್"</item> - <item msgid="8309220355268900335">"ಬ್ಯಾಟರಿ"</item> - </string-array> + <string name="performance" msgid="6552785217174378320">"ಪರ್ಫಾರ್ಮೆನ್ಸ್"</string> + <string name="user_interface" msgid="3712869377953950887">"ಬಳಕೆದಾರ ಇಂಟರ್ಫೇಸ್"</string> + <string name="thermal" msgid="6758074791325414831">"ಥರ್ಮಲ್"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"ಒಂದು ಕೈ ಮೋಡ್"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"ಶ್ರವಣ ಸಾಧನಗಳು"</string> - <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"ಹಿಯರಿಂಗ್ ಸಾಧನಗಳು"</string> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"ಸಕ್ರಿಯವಾಗಿದೆ"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"ಡಿಸ್ಕನೆಕ್ಟ್ ಆಗಿದೆ"</string> + <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"ಶ್ರವಣ ಸಾಧನಗಳು"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"ಹೊಸ ಸಾಧನವನ್ನು ಪೇರ್ ಮಾಡಿ"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"ಹೊಸ ಸಾಧನವನ್ನು ಜೋಡಿಸಲು ಕ್ಲಿಕ್ ಮಾಡಿ"</string> <string name="hearing_devices_presets_error" msgid="350363093458408536">"ಪ್ರಿಸೆಟ್ ಅನ್ನು ಅಪ್ಡೇಟ್ ಮಾಡಲು ಸಾಧ್ಯವಾಗಲಿಲ್ಲ"</string> - <!-- no translation found for hearing_devices_preset_label (7878267405046232358) --> - <skip /> + <string name="hearing_devices_preset_label" msgid="7878267405046232358">"ಪ್ರಿಸೆಟ್"</string> + <string name="live_caption_title" msgid="8916875614623730005">"ಲೈವ್ ಕ್ಯಾಪ್ಶನ್"</string> <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> @@ -617,7 +619,7 @@ <string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s. ಮ್ಯೂಟ್ ಮಾಡಲು ಟ್ಯಾಪ್ ಮಾಡಿ."</string> <string name="volume_panel_noise_control_title" msgid="7413949943872304474">"ಗದ್ದಲ ನಿಯಂತ್ರಣ"</string> <string name="volume_panel_spatial_audio_title" msgid="3367048857932040660">"ಸ್ಪೇಷಿಯಲ್ ಆಡಿಯೋ"</string> - <string name="volume_panel_spatial_audio_off" msgid="4177490084606772989">"ಆಫ್ ಮಾಡಿ"</string> + <string name="volume_panel_spatial_audio_off" msgid="4177490084606772989">"ಆಫ್"</string> <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> @@ -727,11 +729,6 @@ <string name="keyboard_key_button_template" msgid="8005673627272051429">"<xliff:g id="NAME">%1$s</xliff:g> ಬಟನ್"</string> <string name="keyboard_key_home" msgid="3734400625170020657">"Home"</string> <string name="keyboard_key_back" msgid="4185420465469481999">"ಹಿಂದೆ"</string> - <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"ಅಪ್ ಆ್ಯರೋ"</string> - <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"ಡೌನ್ ಆ್ಯರೋ"</string> - <string name="keyboard_key_dpad_left" msgid="144176368026538621">"ಲೆಫ್ಟ್ ಆ್ಯರೋ"</string> - <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"ರೈಟ್ ಆ್ಯರೋ"</string> - <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"ಮಧ್ಯ"</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">"Enter"</string> @@ -1275,7 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"ಫೋಲ್ಡ್ ಮಾಡಿರುವುದು"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"ಅನ್ಫೋಲ್ಡ್ ಮಾಡಿರುವುದು"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string> - <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"<xliff:g id="PERCENTAGE">%s</xliff:g> ಬ್ಯಾಟರಿ ಉಳಿದಿದೆ"</string> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"ಸ್ಟೈಲಸ್ ಬ್ಯಾಟರಿ <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"ನಿಮ್ಮ ಸ್ಟೈಲಸ್ ಅನ್ನು ಚಾರ್ಜರ್ಗೆ ಕನೆಕ್ಟ್ ಮಾಡಿ"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"ಸ್ಟೈಲಸ್ ಬ್ಯಾಟರಿ ಕಡಿಮೆಯಿದೆ"</string> <string name="video_camera" msgid="7654002575156149298">"ವೀಡಿಯೊ ಕ್ಯಾಮರಾ"</string> @@ -1318,6 +1315,16 @@ <string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"ಇತ್ತೀಚೆಗೆ <xliff:g id="APP_NAME">%1$s</xliff:g> ಇದನ್ನು ಬಳಸಿದೆ (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string> <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>)"</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>)"</string> + <string name="shortcut_helper_category_system" msgid="462110876978937359">"ಸಿಸ್ಟಂ"</string> + <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"ಮಲ್ಟಿಟಾಸ್ಕಿಂಗ್"</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_a11y" msgid="6314444792641773464">"ಆ್ಯಕ್ಸೆಸಿಬಿಲಿಟಿ"</string> + <string name="shortcut_helper_title" msgid="8567500639300970049">"ಕೀಬೋರ್ಡ್ ಶಾರ್ಟ್ಕಟ್ಗಳು"</string> + <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"ಹುಡುಕಾಟದ ಶಾರ್ಟ್ಕಟ್ಗಳು"</string> + <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"ಕುಗ್ಗಿಸುವ ಐಕಾನ್"</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="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> diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml index 8d34d00d5c11..91266b7aea10 100644 --- a/packages/SystemUI/res/values-ko/strings.xml +++ b/packages/SystemUI/res/values-ko/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"내일 다시 자동으로 사용 설정"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Quick Share, 내 기기 찾기 등의 기능에서 블루투스를 사용합니다."</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"블루투스가 내일 아침에 켜집니다."</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"오디오 공유"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"오디오 공유 중"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"배터리 <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"오디오"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"헤드셋"</string> @@ -361,19 +363,19 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"기기 경험의 어떤 부분에 영향이 있었나요?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"문제 유형 선택"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"화면 녹화"</string> - <string-array name="qs_record_issue_types"> - <item msgid="2947988124014085798">"성능"</item> - <item msgid="1627504621139124393">"사용자 인터페이스"</item> - <item msgid="8309220355268900335">"배터리"</item> - </string-array> + <string name="performance" msgid="6552785217174378320">"성능"</string> + <string name="user_interface" msgid="3712869377953950887">"사용자 인터페이스"</string> + <string name="thermal" msgid="6758074791325414831">"열화상"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"한 손 사용 모드"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"청각 보조 기기"</string> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"활성"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"연결 끊김"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"청각 보조 기기"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"새 기기와 페어링"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"새 기기와 페어링하려면 클릭하세요"</string> <string name="hearing_devices_presets_error" msgid="350363093458408536">"사전 설정을 업데이트할 수 없음"</string> - <!-- no translation found for hearing_devices_preset_label (7878267405046232358) --> - <skip /> + <string name="hearing_devices_preset_label" msgid="7878267405046232358">"미리 설정"</string> + <string name="live_caption_title" msgid="8916875614623730005">"실시간 자막"</string> <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> @@ -727,11 +729,6 @@ <string name="keyboard_key_button_template" msgid="8005673627272051429">"<xliff:g id="NAME">%1$s</xliff:g> 버튼"</string> <string name="keyboard_key_home" msgid="3734400625170020657">"Home"</string> <string name="keyboard_key_back" msgid="4185420465469481999">"뒤로"</string> - <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"위쪽 화살표"</string> - <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"아래쪽 화살표"</string> - <string name="keyboard_key_dpad_left" msgid="144176368026538621">"왼쪽 화살표"</string> - <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"오른쪽 화살표"</string> - <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"중앙"</string> <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> @@ -1275,7 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"접은 상태"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"펼친 상태"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string> - <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"배터리 <xliff:g id="PERCENTAGE">%s</xliff:g> 남음"</string> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"스타일러스 배터리 <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"스타일러스를 충전기에 연결하세요"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"스타일러스 배터리 부족"</string> <string name="video_camera" msgid="7654002575156149298">"비디오 카메라"</string> @@ -1318,6 +1315,16 @@ <string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"최근 <xliff:g id="APP_NAME">%1$s</xliff:g>에서 사용됨(<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string> <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>)"</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>)"</string> + <string name="shortcut_helper_category_system" msgid="462110876978937359">"시스템"</string> + <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"멀티태스킹"</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_a11y" msgid="6314444792641773464">"접근성"</string> + <string name="shortcut_helper_title" msgid="8567500639300970049">"단축키"</string> + <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"검색 바로가기"</string> + <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"접기 아이콘"</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="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> diff --git a/packages/SystemUI/res/values-ky/strings.xml b/packages/SystemUI/res/values-ky/strings.xml index 975faf128eed..a3fd0a2e1f1f 100644 --- a/packages/SystemUI/res/values-ky/strings.xml +++ b/packages/SystemUI/res/values-ky/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Эртең автоматтык түрдө кайра күйгүзүү"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Bluetooth Тез бөлүшүү жана Түзмөгүм кайда? сыяктуу функцияларда колдонулат"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth эртең таңда күйөт"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Чогуу угуу"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Чогуу угулууда"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Батареянын деңгээли <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Аудио"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Гарнитура"</string> @@ -361,19 +363,19 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Түзмөгүңүздүн кайсы бөлүгүнө кедергиси тийди?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Маселенин түрүн тандоо"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Экрандан видео жаздырып алуу"</string> - <string-array name="qs_record_issue_types"> - <item msgid="2947988124014085798">"Иштин майнаптуулугу"</item> - <item msgid="1627504621139124393">"Колдонуучунун интерфейси"</item> - <item msgid="8309220355268900335">"Батарея"</item> - </string-array> + <string name="performance" msgid="6552785217174378320">"Иштин майнаптуулугу"</string> + <string name="user_interface" msgid="3712869377953950887">"Колдонуучунун интерфейси"</string> + <string name="thermal" msgid="6758074791325414831">"Жылуулук"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Бир кол режими"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Угуу аппараттары"</string> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Жигердүү"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Ажыратылды"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Угуу аппараттары"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Жаңы түзмөк кошуу"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Жаңы түзмөк кошуу үчүн басыңыз"</string> <string name="hearing_devices_presets_error" msgid="350363093458408536">"Алдын ала коюлган параметрлер жаңыртылган жок"</string> - <!-- no translation found for hearing_devices_preset_label (7878267405046232358) --> - <skip /> + <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Алдын ала коюлган параметрлер"</string> + <string name="live_caption_title" msgid="8916875614623730005">"Ыкчам коштомо жазуулар"</string> <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> @@ -727,11 +729,6 @@ <string name="keyboard_key_button_template" msgid="8005673627272051429">"<xliff:g id="NAME">%1$s</xliff:g> баскычы"</string> <string name="keyboard_key_home" msgid="3734400625170020657">"Башкы бет"</string> <string name="keyboard_key_back" msgid="4185420465469481999">"Артка"</string> - <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"Өйдө жебе"</string> - <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"Ылдый жебе"</string> - <string name="keyboard_key_dpad_left" msgid="144176368026538621">"Солго жебе"</string> - <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"Оңго жебе"</string> - <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"Ортолотуу"</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">"Enter"</string> @@ -1275,7 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"бүктөлгөн"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"ачылган"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string> - <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"Батареянын кубаты: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Стилустун батареясы: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Стилусту кубаттаңыз"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"Стилустун батареясы отурайын деп калды"</string> <string name="video_camera" msgid="7654002575156149298">"Видео камера"</string> @@ -1318,6 +1315,16 @@ <string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"Акыркы жолу <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>) колдонмосунда иштетилди"</string> <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>)"</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>) колдонмосунда иштетилди"</string> + <string name="shortcut_helper_category_system" msgid="462110876978937359">"Система"</string> + <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Бир нече тапшырма аткаруу"</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_a11y" msgid="6314444792641773464">"Атайын мүмкүнчүлүктөр"</string> + <string name="shortcut_helper_title" msgid="8567500639300970049">"Ыкчам баскычтар"</string> + <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Ыкчам баскычтарды издөө"</string> + <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Жыйыштыруу сүрөтчөсү"</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="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> diff --git a/packages/SystemUI/res/values-land/dimens.xml b/packages/SystemUI/res/values-land/dimens.xml index aea79e84f7e8..235015b5286f 100644 --- a/packages/SystemUI/res/values-land/dimens.xml +++ b/packages/SystemUI/res/values-land/dimens.xml @@ -34,7 +34,7 @@ <dimen name="volume_row_slider_height">128dp</dimen> <!-- width of ImmersiveModeConfirmation (-1 for match_parent) --> - <dimen name="immersive_mode_cling_width">380dp</dimen> + <dimen name="immersive_mode_cling_width">500dp</dimen> <dimen name="controls_activity_view_top_offset">25dp</dimen> diff --git a/packages/SystemUI/res/values-lo/strings.xml b/packages/SystemUI/res/values-lo/strings.xml index 22a64f86f26f..31182fd7f740 100644 --- a/packages/SystemUI/res/values-lo/strings.xml +++ b/packages/SystemUI/res/values-lo/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"ເປີດໃຊ້ໂດຍອັດຕະໂນມັດອີກຄັ້ງມື້ອື່ນ"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"ຄຸນສົມບັດຕ່າງໆໃຊ້ Bluetooth ເຊັ່ນ: ການແຊຣ໌ດ່ວນ ແລະ ຊອກຫາອຸປະກອນຂອງຂ້ອຍ"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth ຈະເປີດມື້ອື່ນເຊົ້າ"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"ການແບ່ງປັນສຽງ"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"ກຳລັງແບ່ງປັນສຽງ"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"ແບັດເຕີຣີ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"ສຽງ"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"ຊຸດຫູຟັງ"</string> @@ -361,19 +363,19 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"ສ່ວນໃດຂອງປະສົບການອຸປະກອນຂອງທ່ານໄດ້ຮັບຜົນກະທົບ?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"ເລືອກປະເພດບັນຫາ"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"ບັນທຶກໜ້າຈໍ"</string> - <string-array name="qs_record_issue_types"> - <item msgid="2947988124014085798">"ປະສິດທິພາບ"</item> - <item msgid="1627504621139124393">"ສ່ວນຕິດຕໍ່ຜູ້ໃຊ້"</item> - <item msgid="8309220355268900335">"ແບັດເຕີຣີ"</item> - </string-array> + <string name="performance" msgid="6552785217174378320">"ປະສິດທິພາບ"</string> + <string name="user_interface" msgid="3712869377953950887">"ສ່ວນຕິດຕໍ່ຜູ້ໃຊ້"</string> + <string name="thermal" msgid="6758074791325414831">"ຄວາມຮ້ອນ"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"ໂໝດມືດຽວ"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"ອຸປະກອນຊ່ວຍຟັງ"</string> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"ນຳໃຊ້ຢູ່"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"ຕັດການເຊື່ອມຕໍ່ແລ້ວ"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"ອຸປະກອນຊ່ວຍຟັງ"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"ຈັບຄູ່ອຸປະກອນໃໝ່"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"ຄລິກເພື່ອຈັບຄູ່ອຸປະກອນໃໝ່"</string> <string name="hearing_devices_presets_error" msgid="350363093458408536">"ບໍ່ສາມາດອັບເດດການຕັ້ງຄ່າລ່ວງໜ້າໄດ້"</string> - <!-- no translation found for hearing_devices_preset_label (7878267405046232358) --> - <skip /> + <string name="hearing_devices_preset_label" msgid="7878267405046232358">"ຄ່າທີ່ກຳນົດລ່ວງໜ້າ"</string> + <string name="live_caption_title" msgid="8916875614623730005">"ຄຳບັນຍາຍສົດ"</string> <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> @@ -727,11 +729,6 @@ <string name="keyboard_key_button_template" msgid="8005673627272051429">"ປຸ່ມ <xliff:g id="NAME">%1$s</xliff:g>"</string> <string name="keyboard_key_home" msgid="3734400625170020657">"Home"</string> <string name="keyboard_key_back" msgid="4185420465469481999">"ກັບຄືນ"</string> - <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"ລູກສອນຂຶ້ນ"</string> - <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"ລູກສອນລົງ"</string> - <string name="keyboard_key_dpad_left" msgid="144176368026538621">"ລູກສອນຊ້າຍ"</string> - <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"ລູກສອນຂວາ"</string> - <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"ເຄິ່ງກາງ"</string> <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> @@ -1275,7 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"ພັບແລ້ວ"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"ກາງອອກແລ້ວ"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string> - <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"ແບັດເຕີຣີເຫຼືອ <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"ແບັດເຕີຣີປາກກາ <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"ເຊື່ອມຕໍ່ປາກກາຂອງທ່ານກັບສາຍສາກ"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"ແບັດເຕີຣີປາກກາເຫຼືອໜ້ອຍ"</string> <string name="video_camera" msgid="7654002575156149298">"ກ້ອງວິດີໂອ"</string> @@ -1318,6 +1315,16 @@ <string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"ໃຊ້ຫຼ້າສຸດໂດຍ <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string> <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>)"</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>)"</string> + <string name="shortcut_helper_category_system" msgid="462110876978937359">"ລະບົບ"</string> + <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"ການເຮັດຫຼາຍໜ້າວຽກພ້ອມກັນ"</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_a11y" msgid="6314444792641773464">"ການຊ່ວຍເຂົ້າເຖິງ"</string> + <string name="shortcut_helper_title" msgid="8567500639300970049">"ຄີລັດ"</string> + <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"ທາງລັດການຊອກຫາ"</string> + <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"ໄອຄອນຫຍໍ້ລົງ"</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="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> diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml index bec3cc1e61f2..62c291135b07 100644 --- a/packages/SystemUI/res/values-lt/strings.xml +++ b/packages/SystemUI/res/values-lt/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Automatiškai vėl įjungti rytoj"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Tokioms funkcijoms kaip „Spartusis bendrinimas“ ir „Rasti įrenginį“ naudojamas „Bluetooth“ ryšys"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"„Bluetooth“ ryšys bus įjungtas rytoj ryte"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Garso įrašų bendrinimas"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Bendrinamas garso įrašas"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Akumuliatorius: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Garsas"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Virtualiosios realybės įrenginys"</string> @@ -361,19 +363,19 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Kuri įrenginio funkcija buvo paveikta?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Pasirinkite problemos tipą"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Ekrano įrašas"</string> - <string-array name="qs_record_issue_types"> - <item msgid="2947988124014085798">"Našumas"</item> - <item msgid="1627504621139124393">"Naudotojo sąsaja"</item> - <item msgid="8309220355268900335">"Akumuliatorius"</item> - </string-array> + <string name="performance" msgid="6552785217174378320">"Našumas"</string> + <string name="user_interface" msgid="3712869377953950887">"Naudotojo sąsaja"</string> + <string name="thermal" msgid="6758074791325414831">"Šiluminis"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Vienos rankos režimas"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Klausos įrenginiai"</string> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Aktyvus"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Atjungtas"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Klausos įrenginiai"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Susieti naują įrenginį"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Spustelėkite, kad susietumėte naują įrenginį"</string> <string name="hearing_devices_presets_error" msgid="350363093458408536">"Išankstinių nustatymų atnaujinti nepavyko"</string> - <!-- no translation found for hearing_devices_preset_label (7878267405046232358) --> - <skip /> + <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Išankstiniai nustatymai"</string> + <string name="live_caption_title" msgid="8916875614623730005">"Subtitrai realiuoju laiku"</string> <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> @@ -727,11 +729,6 @@ <string name="keyboard_key_button_template" msgid="8005673627272051429">"Mygtukas <xliff:g id="NAME">%1$s</xliff:g>"</string> <string name="keyboard_key_home" msgid="3734400625170020657">"Pagrindinis"</string> <string name="keyboard_key_back" msgid="4185420465469481999">"Atgal"</string> - <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"Rodyklė aukštyn"</string> - <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"Rodyklė žemyn"</string> - <string name="keyboard_key_dpad_left" msgid="144176368026538621">"Rodyklė kairėn"</string> - <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"Rodyklė dešinėn"</string> - <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"Centras"</string> <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string> <string name="keyboard_key_space" msgid="6980847564173394012">"Tarpas"</string> <string name="keyboard_key_enter" msgid="8633362970109751646">"Įvesti"</string> @@ -1275,7 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"sulenkta"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"nesulenkta"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string> - <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"Liko akumuliatoriaus įkrovos: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Liko rašiklio akumuliatoriaus energijos: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Prijunkite rašiklį prie kroviklio"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"Senka rašiklio akumuliatorius"</string> <string name="video_camera" msgid="7654002575156149298">"Vaizdo kamera"</string> @@ -1318,6 +1315,16 @@ <string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"Neseniai naudojo „<xliff:g id="APP_NAME">%1$s</xliff:g>“ (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string> <string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"Naudoja <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>)"</string> <string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"Neseniai naudojo „<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>)"</string> + <string name="shortcut_helper_category_system" msgid="462110876978937359">"Sistema"</string> + <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Kelių užduočių atlikimas"</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_a11y" msgid="6314444792641773464">"Pritaikomumas"</string> + <string name="shortcut_helper_title" msgid="8567500639300970049">"Spartieji klavišai"</string> + <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Paieškos šaukiniai"</string> + <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Sutraukimo piktograma"</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="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Klaviatūros foninis apšvietimas"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"%1$d lygis iš %2$d"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"Namų sistemos valdymas"</string> diff --git a/packages/SystemUI/res/values-lv/strings.xml b/packages/SystemUI/res/values-lv/strings.xml index 001f6a027ce4..8d0b43c9a925 100644 --- a/packages/SystemUI/res/values-lv/strings.xml +++ b/packages/SystemUI/res/values-lv/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Automātiski atkal ieslēgt rīt"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Tādas funkcijas kā “Ātrā kopīgošana” un “Atrast ierīci” izmanto Bluetooth savienojumu"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth savienojums tiks ieslēgts rīt no rīta"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Kopīgot audio"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Notiek audio kopīgošana"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Akumulators: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Austiņas"</string> @@ -361,19 +363,19 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Kuras ierīces funkcijas tika ietekmētas?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Atlasiet problēmas veidu"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Ekrāna ierakstīšana"</string> - <string-array name="qs_record_issue_types"> - <item msgid="2947988124014085798">"Veiktspēja"</item> - <item msgid="1627504621139124393">"Lietotāja saskarne"</item> - <item msgid="8309220355268900335">"Akumulators"</item> - </string-array> + <string name="performance" msgid="6552785217174378320">"Veiktspēja"</string> + <string name="user_interface" msgid="3712869377953950887">"Lietotāja saskarne"</string> + <string name="thermal" msgid="6758074791325414831">"Ierīces temperatūra"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Vienas rokas režīms"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Dzirdes aparāti"</string> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Aktīvs"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Atvienots"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Dzirdes aparāti"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Savienot pārī jaunu ierīci"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Noklikšķiniet, lai savienotu pārī jaunu ierīci"</string> <string name="hearing_devices_presets_error" msgid="350363093458408536">"Nevarēja atjaunināt pirmsiestatījumu"</string> - <!-- no translation found for hearing_devices_preset_label (7878267405046232358) --> - <skip /> + <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Pirmsiestatījums"</string> + <string name="live_caption_title" msgid="8916875614623730005">"Subtitri reāllaikā"</string> <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> @@ -617,7 +619,7 @@ <string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s. Pieskarieties, lai izslēgtu skaņu."</string> <string name="volume_panel_noise_control_title" msgid="7413949943872304474">"Trokšņu kontrole"</string> <string name="volume_panel_spatial_audio_title" msgid="3367048857932040660">"Telpiskais audio"</string> - <string name="volume_panel_spatial_audio_off" msgid="4177490084606772989">"Izslēgta"</string> + <string name="volume_panel_spatial_audio_off" msgid="4177490084606772989">"Izslēgts"</string> <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> @@ -727,11 +729,6 @@ <string name="keyboard_key_button_template" msgid="8005673627272051429">"Poga <xliff:g id="NAME">%1$s</xliff:g>"</string> <string name="keyboard_key_home" msgid="3734400625170020657">"Sākumvietas taustiņš"</string> <string name="keyboard_key_back" msgid="4185420465469481999">"Atpakaļ"</string> - <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"Augšupvērstā bultiņa"</string> - <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"Lejupvērstā bultiņa"</string> - <string name="keyboard_key_dpad_left" msgid="144176368026538621">"Kreisā bultiņa"</string> - <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"Labā bultiņa"</string> - <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"Centrā"</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> @@ -1275,7 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"aizvērta"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"atvērta"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s/%2$s"</string> - <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"Atlikušais uzlādes līmenis: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Skārienekrāna pildspalvas akumulatora uzlādes līmenis: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Pievienojiet skārienekrāna pildspalvu lādētājam"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"Zems skārienekrāna pildspalvas akumulatora līmenis"</string> <string name="video_camera" msgid="7654002575156149298">"Videokamera"</string> @@ -1318,6 +1315,16 @@ <string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"Nesen to izmantoja lietotne <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string> <string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"To izmanto lietotne <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>)"</string> <string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"Nesen to izmantoja lietotne <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>)"</string> + <string name="shortcut_helper_category_system" msgid="462110876978937359">"Sistēma"</string> + <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Vairākuzdevumu režīms"</string> + <string name="shortcut_helper_category_input" msgid="8674018654124839566">"Ievade"</string> + <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Lietotņu saīsnes"</string> + <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Pieejamība"</string> + <string name="shortcut_helper_title" msgid="8567500639300970049">"Īsinājumtaustiņi"</string> + <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Meklēšanas saīsnes"</string> + <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Sakļaušanas ikona"</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="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Tastatūras fona apgaismojums"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"Līmenis numur %1$d, kopā ir %2$d"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"Mājas kontrolierīces"</string> diff --git a/packages/SystemUI/res/values-mk/strings.xml b/packages/SystemUI/res/values-mk/strings.xml index 12bf5689525b..1c889e6b6b68 100644 --- a/packages/SystemUI/res/values-mk/strings.xml +++ b/packages/SystemUI/res/values-mk/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Автоматски вклучи повторно утре"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Функциите како „Брзо споделување“ и „Најди го мојот уред“ користат Bluetooth"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth ќе се вклучи утре наутро"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Споделување аудио"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Се споделува аудио"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Батерија: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Аудио"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Слушалки"</string> @@ -301,7 +303,7 @@ <string name="quick_settings_user_title" msgid="8673045967216204537">"Корисник"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string> <string name="quick_settings_internet_label" msgid="6603068555872455463">"Интернет"</string> - <string name="quick_settings_networks_available" msgid="1875138606855420438">"Мрежите се достапни"</string> + <string name="quick_settings_networks_available" msgid="1875138606855420438">"Достапни се мрежи"</string> <string name="quick_settings_networks_unavailable" msgid="1167847013337940082">"Не се достапни мрежи"</string> <string name="quick_settings_wifi_detail_empty_text" msgid="483130889414601732">"Нема достапни Wi-Fi мрежи"</string> <string name="quick_settings_wifi_secondary_label_transient" msgid="7501659015509357887">"Се вклучува…"</string> @@ -361,19 +363,19 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Кој дел од доживувањето на уредот беше засегнат?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Изберете тип проблем"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Снимање екран"</string> - <string-array name="qs_record_issue_types"> - <item msgid="2947988124014085798">"Изведба"</item> - <item msgid="1627504621139124393">"Кориснички интерфејс"</item> - <item msgid="8309220355268900335">"Батерија"</item> - </string-array> + <string name="performance" msgid="6552785217174378320">"Изведба"</string> + <string name="user_interface" msgid="3712869377953950887">"Кориснички интерфејс"</string> + <string name="thermal" msgid="6758074791325414831">"Термално"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Режим со една рака"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Слушни апарати"</string> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Активно"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Не е поврзано"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Слушни апарати"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Спари нов уред"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Кликнете за да спарите нов уред"</string> <string name="hearing_devices_presets_error" msgid="350363093458408536">"Не можеше да се ажурира зададената вредност"</string> - <!-- no translation found for hearing_devices_preset_label (7878267405046232358) --> - <skip /> + <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Зададени вредности"</string> + <string name="live_caption_title" msgid="8916875614623730005">"Автоматски титлови"</string> <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> @@ -684,8 +686,8 @@ <string name="notification_automatic_title" msgid="3745465364578762652">"Автоматски"</string> <string name="notification_channel_summary_low" msgid="4860617986908931158">"Без звук или вибрации"</string> <string name="notification_conversation_summary_low" msgid="1734433426085468009">"Без звук или вибрации и се појавува подолу во делот со разговори"</string> - <string name="notification_channel_summary_default" msgid="777294388712200605">"Може да ѕвони или вибрира во зависност од поставките за уредот"</string> - <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"Може да ѕвони или вибрира во зависност од поставките на уредот. Стандардно, разговорите од <xliff:g id="APP_NAME">%1$s</xliff:g> се во балончиња."</string> + <string name="notification_channel_summary_default" msgid="777294388712200605">"Може да ѕвони или да вибрира во зависност од поставките за уредот"</string> + <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"Може да ѕвони или да вибрира во зависност од поставките за уредот. Стандардно, разговорите од <xliff:g id="APP_NAME">%1$s</xliff:g> се во балончиња."</string> <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Дозволете системот да определи дали известувањево треба да испушти звук или да вибрира"</string> <string name="notification_channel_summary_automatic_alerted" msgid="954166812246932240">"<b>Статус:</b> поставено на „Стандардно“"</string> <string name="notification_channel_summary_automatic_silenced" msgid="7403004439649872047">"<b>Статус:</b> намалено на „Тивко“"</string> @@ -727,11 +729,6 @@ <string name="keyboard_key_button_template" msgid="8005673627272051429">"Копче <xliff:g id="NAME">%1$s</xliff:g>"</string> <string name="keyboard_key_home" msgid="3734400625170020657">"Home-копче"</string> <string name="keyboard_key_back" msgid="4185420465469481999">"Назад"</string> - <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"Стрелка нагоре"</string> - <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"Стрелка надолу"</string> - <string name="keyboard_key_dpad_left" msgid="144176368026538621">"Стрелка налево"</string> - <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"Стрелка надесно"</string> - <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"Центар"</string> <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> @@ -988,7 +985,7 @@ <string name="magnification_close_settings_click_label" msgid="4642477260651704517">"Затворете ги поставките за зголемување"</string> <string name="magnification_exit_edit_mode_click_label" msgid="1664818325144887117">"Излегување од „Режим на изменување“"</string> <string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"Повлечете на аголот за да ја промените големината"</string> - <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"Дозволете дијагонално лизгање"</string> + <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"Дозволи дијагонално лизгање"</string> <string name="accessibility_resize" msgid="5733759136600611551">"Промени големина"</string> <string name="accessibility_change_magnification_type" msgid="666000085077432421">"Променете го типот на зголемување"</string> <string name="accessibility_magnification_end_resizing" msgid="4881690585800302628">"Заврши ја промената на големина"</string> @@ -997,7 +994,7 @@ <string name="accessibility_magnification_right_handle" msgid="9055988237319397605">"Десна рачка"</string> <string name="accessibility_magnification_bottom_handle" msgid="6531646968813821258">"Долна рачка"</string> <string name="accessibility_magnification_settings_panel_description" msgid="8174187340747846953">"Поставки за зголемување"</string> - <string name="accessibility_magnifier_size" msgid="3038755600030422334">"Големина на лупа"</string> + <string name="accessibility_magnifier_size" msgid="3038755600030422334">"Големина на лупата"</string> <string name="accessibility_magnification_zoom" msgid="4222088982642063979">"Зум"</string> <string name="accessibility_magnification_medium" msgid="6994632616884562625">"Средно"</string> <string name="accessibility_magnification_small" msgid="8144502090651099970">"Мало"</string> @@ -1210,7 +1207,7 @@ <string name="fgs_manager_footer_label" msgid="8276763570622288231">"{count,plural, =1{Активна е # апликација}one{Активни се # апликација}other{Активни се # апликации}}"</string> <string name="fgs_dot_content_description" msgid="2865071539464777240">"Нови информации"</string> <string name="fgs_manager_dialog_title" msgid="5879184257257718677">"Активни апликации"</string> - <string name="fgs_manager_dialog_message" msgid="2670045017200730076">"Овие апликации се активни и работат, дури и кога не ги користите. Ова ја подобрува нивната функционалност, но може да влијае и на траењето на батеријата."</string> + <string name="fgs_manager_dialog_message" msgid="2670045017200730076">"Овие апликации се активни и работат дури и кога не ги користите. Ова ја подобрува нивната функционалност, но може и да влијае на траењето на батеријата."</string> <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Запри"</string> <string name="fgs_manager_app_item_stop_button_stopped_label" msgid="6950382004441263922">"Запрено"</string> <string name="clipboard_edit_text_done" msgid="4551887727694022409">"Готово"</string> @@ -1275,7 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"затворен"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"отворен"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s/%2$s"</string> - <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"Преостаната батерија: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Батерија на пенкало: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Поврзете го пенкалото со полнач"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"Слаба батерија на пенкало"</string> <string name="video_camera" msgid="7654002575156149298">"Видеокамера"</string> @@ -1318,6 +1315,16 @@ <string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"Неодамна користено од <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string> <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>)"</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>)"</string> + <string name="shortcut_helper_category_system" msgid="462110876978937359">"Систем"</string> + <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Мултитаскинг"</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_a11y" msgid="6314444792641773464">"Пристапност"</string> + <string name="shortcut_helper_title" msgid="8567500639300970049">"Кратенки од тастатура"</string> + <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Кратенки за пребарување"</string> + <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Икона за собирање"</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="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> diff --git a/packages/SystemUI/res/values-ml/strings.xml b/packages/SystemUI/res/values-ml/strings.xml index d165242e49e1..3dd91f7cfd2b 100644 --- a/packages/SystemUI/res/values-ml/strings.xml +++ b/packages/SystemUI/res/values-ml/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"നാളെ വീണ്ടും സ്വയമേവ ഓണാക്കുക"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"ക്വിക്ക് ഷെയർ, Find My Device പോലുള്ള ഫീച്ചറുകൾ Bluetooth ഉപയോഗിക്കുന്നു"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth നാളെ രാവിലെ ഓണാകും"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"ഓഡിയോ പങ്കിടൽ"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"ഓഡിയോ പങ്കിടുന്നു"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> ബാറ്ററി"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"ഓഡിയോ"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"ഹെഡ്സെറ്റ്"</string> @@ -361,19 +363,19 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"നിങ്ങളുടെ ഉപകരണ അനുഭവത്തിന്റെ ഏത് ഭാഗമാണ് ബാധിച്ചത്?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"പ്രശ്ന തരം തിരഞ്ഞെടുക്കുക"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"സ്ക്രീൻ റെക്കോർഡ്"</string> - <string-array name="qs_record_issue_types"> - <item msgid="2947988124014085798">"പ്രകടനം"</item> - <item msgid="1627504621139124393">"ഉപയോക്തൃ ഇന്റര്ഫേസ്"</item> - <item msgid="8309220355268900335">"ബാറ്ററി"</item> - </string-array> + <string name="performance" msgid="6552785217174378320">"പ്രകടനം"</string> + <string name="user_interface" msgid="3712869377953950887">"ഉപയോക്തൃ ഇന്റർഫേസ്"</string> + <string name="thermal" msgid="6758074791325414831">"തെർമൽ"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"ഒറ്റക്കൈ മോഡ്"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"കേൾവിക്കുള്ള ഉപകരണങ്ങൾ"</string> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"സജീവം"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"വിച്ഛേദിച്ചിരിക്കുന്നു"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"കേൾവിക്കുള്ള ഉപകരണങ്ങൾ"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"പുതിയ ഉപകരണം ജോടിയാക്കുക"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"പുതിയ ഉപകരണം ജോടിയാക്കാൻ ക്ലിക്ക് ചെയ്യുക"</string> <string name="hearing_devices_presets_error" msgid="350363093458408536">"പ്രീസെറ്റ് അപ്ഡേറ്റ് ചെയ്യാനായില്ല"</string> - <!-- no translation found for hearing_devices_preset_label (7878267405046232358) --> - <skip /> + <string name="hearing_devices_preset_label" msgid="7878267405046232358">"പ്രീസെറ്റ്"</string> + <string name="live_caption_title" msgid="8916875614623730005">"തത്സമയ ക്യാപ്ഷനുകൾ"</string> <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> @@ -727,11 +729,6 @@ <string name="keyboard_key_button_template" msgid="8005673627272051429">"ബട്ടൺ <xliff:g id="NAME">%1$s</xliff:g>"</string> <string name="keyboard_key_home" msgid="3734400625170020657">"ഹോം"</string> <string name="keyboard_key_back" msgid="4185420465469481999">"ബാക്ക്"</string> - <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"മുകളിലേക്കുള്ള അമ്പടയാളം"</string> - <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"താഴേക്കുള്ള അമ്പടയാളം"</string> - <string name="keyboard_key_dpad_left" msgid="144176368026538621">"ഇടത് അമ്പടയാളം"</string> - <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"വലത് അമ്പടയാളം"</string> - <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"മധ്യം"</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> @@ -1275,7 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"ഫോൾഡ് ചെയ്തത്"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"അൺഫോൾഡ് ചെയ്തത്"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string> - <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"<xliff:g id="PERCENTAGE">%s</xliff:g> ബാറ്ററി ചാർജ് ശേഷിക്കുന്നു"</string> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"സ്റ്റൈലസ് ബാറ്ററി <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"നിങ്ങളുടെ സ്റ്റൈലസ് ചാർജറുമായി കണക്റ്റ് ചെയ്യുക"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"സ്റ്റൈലസിന്റെ ബാറ്ററി ചാർജ് കുറവാണ്"</string> <string name="video_camera" msgid="7654002575156149298">"വീഡിയോ ക്യാമറ"</string> @@ -1318,6 +1315,16 @@ <string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"<xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>) അടുത്തിടെ ഉപയോഗിച്ചു"</string> <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>) ഉപയോഗിക്കുന്നു"</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>) അടുത്തിടെ ഉപയോഗിച്ചു"</string> + <string name="shortcut_helper_category_system" msgid="462110876978937359">"സിസ്റ്റം"</string> + <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"മൾട്ടിടാസ്കിംഗ്"</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_a11y" msgid="6314444792641773464">"ഉപയോഗസഹായി"</string> + <string name="shortcut_helper_title" msgid="8567500639300970049">"കീബോഡ് കുറുക്കുവഴികൾ"</string> + <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"തിരയൽ കുറുക്കുവഴികൾ"</string> + <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"ചുരുക്കൽ ഐക്കൺ"</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="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> diff --git a/packages/SystemUI/res/values-mn/strings.xml b/packages/SystemUI/res/values-mn/strings.xml index 2a0b70a30253..553926383476 100644 --- a/packages/SystemUI/res/values-mn/strings.xml +++ b/packages/SystemUI/res/values-mn/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Маргааш автоматаар дахин асаах"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Түргэн хуваалцах, Миний төхөөрөмжийг олох зэрэг онцлогууд Bluetooth-г ашигладаг"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth-г маргааш өглөө асаана"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Аудио хуваалцах"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Аудио хуваалцаж байна"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> батарей"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Аудио"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Чихэвч"</string> @@ -361,19 +363,19 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Таны төхөөрөмжийн хэрэглээний аль хэсэгт нөлөөлсөн бэ?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Асуудлын төрөл сонгоно уу"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Дэлгэцийн бичлэг"</string> - <string-array name="qs_record_issue_types"> - <item msgid="2947988124014085798">"Гүйцэтгэл"</item> - <item msgid="1627504621139124393">"Хэрэглэгчийн интерфейс"</item> - <item msgid="8309220355268900335">"Батарей"</item> - </string-array> + <string name="performance" msgid="6552785217174378320">"Гүйцэтгэл"</string> + <string name="user_interface" msgid="3712869377953950887">"Хэрэглэгчийн интерфейс"</string> + <string name="thermal" msgid="6758074791325414831">"Дулааны"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Нэг гарын горим"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Сонсголын төхөөрөмжүүд"</string> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Идэвхтэй"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Салсан"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Сонсголын төхөөрөмжүүд"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Шинэ төхөөрөмж хослуулах"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Шинэ төхөөрөмж хослуулахын тулд товшино уу"</string> <string name="hearing_devices_presets_error" msgid="350363093458408536">"Урьдчилсан тохируулгыг шинэчилж чадсангүй"</string> - <!-- no translation found for hearing_devices_preset_label (7878267405046232358) --> - <skip /> + <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Урьдчилсан тохируулга"</string> + <string name="live_caption_title" msgid="8916875614623730005">"Шууд тайлбар"</string> <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> @@ -727,11 +729,6 @@ <string name="keyboard_key_button_template" msgid="8005673627272051429">"<xliff:g id="NAME">%1$s</xliff:g> товчлуур"</string> <string name="keyboard_key_home" msgid="3734400625170020657">"Нүүр хуудас"</string> <string name="keyboard_key_back" msgid="4185420465469481999">"Буцах"</string> - <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"Дээш сум"</string> - <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"Доош сум"</string> - <string name="keyboard_key_dpad_left" msgid="144176368026538621">"Зүүн сум"</string> - <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"Баруун сум"</string> - <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"Гол хэсэг"</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> @@ -1275,7 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"эвхсэн"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"дэлгэсэн"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string> - <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"<xliff:g id="PERCENTAGE">%s</xliff:g> батарей үлдлээ"</string> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Мэдрэгч үзгийн батарей <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Мэдрэгч үзгээ цэнэглэгчтэй холбоорой"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"Мэдрэгч үзэгний батарей бага байна"</string> <string name="video_camera" msgid="7654002575156149298">"Видео камер"</string> @@ -1318,6 +1315,16 @@ <string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"Саяхан <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>) ашигласан"</string> <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>) ашиглаж байна"</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>) ашигласан"</string> + <string name="shortcut_helper_category_system" msgid="462110876978937359">"Систем"</string> + <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Олон ажил зэрэг хийх"</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_a11y" msgid="6314444792641773464">"Хандалт"</string> + <string name="shortcut_helper_title" msgid="8567500639300970049">"Товчлуурын шууд холбоос"</string> + <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Товчлолууд хайх"</string> + <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Хураах дүрс тэмдэг"</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="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> diff --git a/packages/SystemUI/res/values-mr/strings.xml b/packages/SystemUI/res/values-mr/strings.xml index 017a6c8067a5..aea4d5a4cf50 100644 --- a/packages/SystemUI/res/values-mr/strings.xml +++ b/packages/SystemUI/res/values-mr/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"उद्या पुन्हा आपोआप सुरू करा"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"क्विक शेअर आणि Find My Device यांसारखी वैशिष्ट्ये ब्लूटूथ वापरतात"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"ब्लूटूथ उद्या सकाळी सुरू होईल"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"ऑडिओ शेअरिंग"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"ऑडिओ शेअर करत आहे"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> बॅटरी"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"ऑडिओ"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"हेडसेट"</string> @@ -361,19 +363,19 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"तुमच्या डिव्हाइसबाबत कोणत्या अनुभवावर परिणाम झाला?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"समस्येचा प्रकार निवडा"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"स्क्रीन रेकॉर्ड"</string> - <string-array name="qs_record_issue_types"> - <item msgid="2947988124014085798">"परफॉर्मन्स"</item> - <item msgid="1627504621139124393">"यूझर इंटरफेस"</item> - <item msgid="8309220355268900335">"बॅटरी"</item> - </string-array> + <string name="performance" msgid="6552785217174378320">"परफॉर्मन्स"</string> + <string name="user_interface" msgid="3712869377953950887">"यूझर इंटरफेस"</string> + <string name="thermal" msgid="6758074791325414831">"थर्मल"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"एकहाती मोड"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"श्रवणयंत्रे"</string> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"ॲक्टिव्ह आहे"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"डिस्कनेक्ट केले आहे"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"श्रवणयंत्रे"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"नवीन डिव्हाइस पेअर करा"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"नवीन डिव्हाइस पेअर करण्यासाठी क्लिक करा"</string> <string name="hearing_devices_presets_error" msgid="350363093458408536">"प्रीसेट अपडेट करता आले नाही"</string> - <!-- no translation found for hearing_devices_preset_label (7878267405046232358) --> - <skip /> + <string name="hearing_devices_preset_label" msgid="7878267405046232358">"प्रीसेट"</string> + <string name="live_caption_title" msgid="8916875614623730005">"लाइव्ह कॅप्शन"</string> <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> @@ -594,9 +596,9 @@ <string name="screen_pinning_negative" msgid="6882816864569211666">"नाही, नको"</string> <string name="screen_pinning_start" msgid="7483998671383371313">"ॲप पिन केले"</string> <string name="screen_pinning_exit" msgid="4553787518387346893">"ॲप अनपिन केले"</string> - <string name="stream_voice_call" msgid="7468348170702375660">"कॉल करा"</string> + <string name="stream_voice_call" msgid="7468348170702375660">"कॉल"</string> <string name="stream_system" msgid="7663148785370565134">"सिस्टम"</string> - <string name="stream_ring" msgid="7550670036738697526">"रिंग करा"</string> + <string name="stream_ring" msgid="7550670036738697526">"रिंग"</string> <string name="stream_music" msgid="2188224742361847580">"मीडिया"</string> <string name="stream_alarm" msgid="16058075093011694">"अलार्म"</string> <string name="stream_notification" msgid="7930294049046243939">"सूचना"</string> @@ -727,11 +729,6 @@ <string name="keyboard_key_button_template" msgid="8005673627272051429">"बटण <xliff:g id="NAME">%1$s</xliff:g>"</string> <string name="keyboard_key_home" msgid="3734400625170020657">"Home"</string> <string name="keyboard_key_back" msgid="4185420465469481999">"परत"</string> - <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"अप अॅरो"</string> - <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"डाउन अॅरो"</string> - <string name="keyboard_key_dpad_left" msgid="144176368026538621">"लेफ्ट अॅरो"</string> - <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"राइट अॅरो"</string> - <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"मध्यवर्ती"</string> <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> @@ -1275,7 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"फोल्ड केलेले"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"फोल्ड न केलेले"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string> - <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"<xliff:g id="PERCENTAGE">%s</xliff:g> बॅटरी शिल्लक आहे"</string> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"स्टायलस बॅटरी <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"तुमचे स्टायलस चार्जरशी कनेक्ट करा"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"स्टायलस बॅटरी कमी आहे"</string> <string name="video_camera" msgid="7654002575156149298">"व्हिडिओ कॅमेरा"</string> @@ -1318,6 +1315,16 @@ <string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"अलीकडे <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>) ने वापरले"</string> <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>) द्वारे वापरले जात आहे"</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>) ने वापरले"</string> + <string name="shortcut_helper_category_system" msgid="462110876978937359">"सिस्टीम"</string> + <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"मल्टिटास्किंग"</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_a11y" msgid="6314444792641773464">"अॅक्सेसिबिलिटी"</string> + <string name="shortcut_helper_title" msgid="8567500639300970049">"कीबोर्ड शॉर्टकट"</string> + <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"शोधण्यासाठी शॉर्टकट"</string> + <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"कोलॅप्स करा आयकन"</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="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> diff --git a/packages/SystemUI/res/values-ms/strings.xml b/packages/SystemUI/res/values-ms/strings.xml index 23e4559d97a0..01310982f331 100644 --- a/packages/SystemUI/res/values-ms/strings.xml +++ b/packages/SystemUI/res/values-ms/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Dihidupkan lagi esok secara automatik"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Ciri seperti Quick Share dan Find My Device menggunakan Bluetooth"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth akan dihidupkan esok pagi"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Perkongsian Audio"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Berkongsi Audio"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> bateri"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Set Kepala"</string> @@ -361,24 +363,24 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Pengalaman peranti yang manakah yang terjejas?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Pilih jenis masalah"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Rakam skrin"</string> - <string-array name="qs_record_issue_types"> - <item msgid="2947988124014085798">"Prestasi"</item> - <item msgid="1627504621139124393">"Antara Muka Pengguna"</item> - <item msgid="8309220355268900335">"Bateri"</item> - </string-array> + <string name="performance" msgid="6552785217174378320">"Prestasi"</string> + <string name="user_interface" msgid="3712869377953950887">"Antara Muka Pengguna"</string> + <string name="thermal" msgid="6758074791325414831">"Terma"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Mod sebelah tangan"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Peranti pendengaran"</string> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Aktif"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Diputuskan sambungan"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Peranti pendengaran"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Gandingkan peranti baharu"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Klik untuk menggandingkan peranti baharu"</string> <string name="hearing_devices_presets_error" msgid="350363093458408536">"Tidak dapat mengemaskinikan pratetapan"</string> - <!-- no translation found for hearing_devices_preset_label (7878267405046232358) --> - <skip /> + <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Pratetapan"</string> + <string name="live_caption_title" msgid="8916875614623730005">"Sari Kata Langsung"</string> <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> - <string name="sensor_privacy_start_use_mic_dialog_content" msgid="1624701280680913717">"Tindakan ini menyahsekat akses bagi semua apl dan perkhidmatan yang dibenarkan untuk menggunakan mikrofon anda."</string> - <string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"Tindakan ini menyahsekat akses bagi semua apl dan perkhidmatan yang dibenarkan untuk menggunakan kamera anda."</string> + <string name="sensor_privacy_start_use_mic_dialog_content" msgid="1624701280680913717">"Tindakan ini menyahsekat akses bagi semua apl dan perkhidmatan yang dibenarkan untuk menggunakan mikrofon."</string> + <string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"Tindakan ini menyahsekat akses bagi semua apl dan perkhidmatan yang dibenarkan untuk menggunakan kamera."</string> <string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"Tindakan ini menyahsekat akses bagi semua apl dan perkhidmatan yang dibenarkan untuk menggunakan kamera atau mikrofon anda."</string> <string name="sensor_privacy_start_use_mic_blocked_dialog_title" msgid="2640140287496469689">"Mikrofon disekat"</string> <string name="sensor_privacy_start_use_camera_blocked_dialog_title" msgid="7398084286822440384">"Kamera disekat"</string> @@ -727,11 +729,6 @@ <string name="keyboard_key_button_template" msgid="8005673627272051429">"Butang <xliff:g id="NAME">%1$s</xliff:g>"</string> <string name="keyboard_key_home" msgid="3734400625170020657">"Skrin Utama"</string> <string name="keyboard_key_back" msgid="4185420465469481999">"Kembali"</string> - <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"Anak panah ke atas"</string> - <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"Anak panah ke bawah"</string> - <string name="keyboard_key_dpad_left" msgid="144176368026538621">"Anak panah ke kiri"</string> - <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"Anak panah ke kanan"</string> - <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"Tengah"</string> <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> @@ -933,7 +930,7 @@ <string name="running_foreground_services_title" msgid="5137313173431186685">"Apl yang berjalan di latar belakang"</string> <string name="running_foreground_services_msg" msgid="3009459259222695385">"Ketik untuk mendapatkan butiran tentang penggunaan kuasa bateri dan data"</string> <string name="mobile_data_disable_title" msgid="5366476131671617790">"Matikan data mudah alih?"</string> - <string name="mobile_data_disable_message" msgid="8604966027899770415">"Anda tidak akan mempunyai akses kepada data atau Internet melalui <xliff:g id="CARRIER">%s</xliff:g>. Internet hanya tersedia melaui Wi-Fi."</string> + <string name="mobile_data_disable_message" msgid="8604966027899770415">"Anda tidak akan dapat mengakses data atau Internet melalui <xliff:g id="CARRIER">%s</xliff:g>. Internet hanya akan tersedia melalui Wi-Fi."</string> <string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"pembawa anda"</string> <string name="auto_data_switch_disable_title" msgid="5146527155665190652">"Tukar kembali kepada <xliff:g id="CARRIER">%s</xliff:g>?"</string> <string name="auto_data_switch_disable_message" msgid="5885533647399535852">"Data mudah alih tidak akan ditukar secara automatik berdasarkan ketersediaan"</string> @@ -1275,7 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"terlipat"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"tidak terlipat"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string> - <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"Bateri tinggal <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Bateri stilus <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Sambungkan stilus anda kepada pengecas"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"Bateri stilus lemah"</string> <string name="video_camera" msgid="7654002575156149298">"Kamera video"</string> @@ -1318,6 +1315,16 @@ <string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"Digunakan baru-baru ini oleh <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string> <string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"Digunakan oleh <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>)"</string> <string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"Digunakan baru-baru ini oleh <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>)"</string> + <string name="shortcut_helper_category_system" msgid="462110876978937359">"Sistem"</string> + <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Berbilang tugas"</string> + <string name="shortcut_helper_category_input" msgid="8674018654124839566">"Input"</string> + <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Pintasan apl"</string> + <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Kebolehaksesan"</string> + <string name="shortcut_helper_title" msgid="8567500639300970049">"Pintasan papan kekunci"</string> + <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Pintasan carian"</string> + <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Kuncupkan ikon"</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="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Cahaya latar papan kekunci"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"Tahap %1$d daripada %2$d"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"Kawalan Rumah"</string> diff --git a/packages/SystemUI/res/values-my/strings.xml b/packages/SystemUI/res/values-my/strings.xml index c3c42052d689..9b19b62c73db 100644 --- a/packages/SystemUI/res/values-my/strings.xml +++ b/packages/SystemUI/res/values-my/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"မနက်ဖြန် အလိုအလျောက် ပြန်ဖွင့်ရန်"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"‘အမြန် မျှဝေပါ’ နှင့် Find My Device ကဲ့သို့ တူးလ်များသည် ဘလူးတုသ်သုံးသည်"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"မနက်ဖြန်နံနက်တွင် ဘလူးတုသ် ပွင့်ပါမည်"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"အော်ဒီယို မျှဝေခြင်း"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"အော်ဒီယို မျှဝေနေသည်"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> ဘက်ထရီ"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"အသံ"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"မိုက်ခွက်ပါနားကြပ်"</string> @@ -361,19 +363,19 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"စက်အသုံးပြုမှု၏ မည်သည့်အပိုင်းကို သက်ရောက်သလဲ။"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"ပြဿနာအမျိုးအစား ရွေးရန်"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"ဖန်သားပြင်ရိုက်ကူးရန်"</string> - <string-array name="qs_record_issue_types"> - <item msgid="2947988124014085798">"စွမ်းဆောင်ရည်"</item> - <item msgid="1627504621139124393">"သုံးသူအတွက် ကြားခံစနစ်"</item> - <item msgid="8309220355268900335">"ဘက်ထရီ"</item> - </string-array> + <string name="performance" msgid="6552785217174378320">"စွမ်းဆောင်ရည်"</string> + <string name="user_interface" msgid="3712869377953950887">"သုံးသူအတွက် ကြားခံစနစ်"</string> + <string name="thermal" msgid="6758074791325414831">"အပူဓာတ်"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"လက်တစ်ဖက်သုံးမုဒ်"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"နားကြားကိရိယာ"</string> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"သုံးနေသည်"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"ချိတ်ဆက်မထားပါ"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"နားကြားကိရိယာ"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"စက်အသစ်တွဲချိတ်ရန်"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"စက်အသစ် တွဲချိတ်ရန် နှိပ်ပါ"</string> <string name="hearing_devices_presets_error" msgid="350363093458408536">"အသင့်သုံးကို အပ်ဒိတ်လုပ်၍မရပါ"</string> - <!-- no translation found for hearing_devices_preset_label (7878267405046232358) --> - <skip /> + <string name="hearing_devices_preset_label" msgid="7878267405046232358">"ကြိုတင်သတ်မှတ်ချက်"</string> + <string name="live_caption_title" msgid="8916875614623730005">"တိုက်ရိုက်စာတန်း"</string> <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> @@ -727,11 +729,6 @@ <string name="keyboard_key_button_template" msgid="8005673627272051429">"ခလုတ် <xliff:g id="NAME">%1$s</xliff:g>"</string> <string name="keyboard_key_home" msgid="3734400625170020657">"ပင်မ"</string> <string name="keyboard_key_back" msgid="4185420465469481999">"နောက်သို့"</string> - <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"အပေါ်ညွှန်မြား"</string> - <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"အောက်ညွှန်မြား"</string> - <string name="keyboard_key_dpad_left" msgid="144176368026538621">"ဘယ်ညွှန်မြား"</string> - <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"ညာညွှန်မြား"</string> - <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"ဌာန"</string> <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> @@ -1275,7 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"ခေါက်ထားသည်"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"ဖြန့်ထားသည်"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string> - <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"ဘက်ထရီ <xliff:g id="PERCENTAGE">%s</xliff:g> ကျန်သေးသည်"</string> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"စတိုင်လပ်စ် ဘက်ထရီ <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"စတိုင်လပ်စ်ကို အားသွင်းကိရိယာနှင့် ချိတ်ဆက်ခြင်း"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"စတိုင်လပ်စ် ဘက်ထရီ အားနည်းနေသည်"</string> <string name="video_camera" msgid="7654002575156149298">"ဗီဒီယိုကင်မရာ"</string> @@ -1318,6 +1315,16 @@ <string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"<xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>) က လတ်တလောသုံးထားသည်"</string> <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>) က သုံးနေသည်"</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>) က လတ်တလောသုံးထားသည်"</string> + <string name="shortcut_helper_category_system" msgid="462110876978937359">"စနစ်"</string> + <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"တစ်ပြိုင်နက် များစွာလုပ်ခြင်း"</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_a11y" msgid="6314444792641773464">"အများသုံးနိုင်မှု"</string> + <string name="shortcut_helper_title" msgid="8567500639300970049">"လက်ကွက်ဖြတ်လမ်းများ"</string> + <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"ရှာဖွေစာလုံး ဖြတ်လမ်း"</string> + <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"လျှော့ပြရန် သင်္ကေတ"</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="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> diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml index dbcfaa366656..a8befb36d6c8 100644 --- a/packages/SystemUI/res/values-nb/strings.xml +++ b/packages/SystemUI/res/values-nb/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Slå på igjen i morgen automatisk"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Funksjoner som Quick Share og Finn enheten min bruker Bluetooth"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth slås på i morgen tidlig"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Lyddeling"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Deler lyd"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> batteri"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Lyd"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Hodetelefoner"</string> @@ -361,19 +363,19 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Hvilken del av enhetsopplevelsen din ble påvirket?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Velg problemtype"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Skjermopptak"</string> - <string-array name="qs_record_issue_types"> - <item msgid="2947988124014085798">"Ytelse"</item> - <item msgid="1627504621139124393">"Brukergrensesnitt"</item> - <item msgid="8309220355268900335">"Batteri"</item> - </string-array> + <string name="performance" msgid="6552785217174378320">"Ytelse"</string> + <string name="user_interface" msgid="3712869377953950887">"Brukergrensesnitt"</string> + <string name="thermal" msgid="6758074791325414831">"Termisk"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Enhåndsmodus"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Høreapparater"</string> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Aktiv"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Frakoblet"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Høreapparater"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Koble til en ny enhet"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Klikk for å koble til en ny enhet"</string> <string name="hearing_devices_presets_error" msgid="350363093458408536">"Kunne ikke oppdatere forhåndsinnstillingen"</string> - <!-- no translation found for hearing_devices_preset_label (7878267405046232358) --> - <skip /> + <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Forhåndsinnstilling"</string> + <string name="live_caption_title" msgid="8916875614623730005">"Direkteteksting"</string> <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> @@ -727,11 +729,6 @@ <string name="keyboard_key_button_template" msgid="8005673627272051429">"<xliff:g id="NAME">%1$s</xliff:g>-knappen"</string> <string name="keyboard_key_home" msgid="3734400625170020657">"Startskjerm"</string> <string name="keyboard_key_back" msgid="4185420465469481999">"Tilbake"</string> - <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"Oppoverpil"</string> - <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"Nedoverpil"</string> - <string name="keyboard_key_dpad_left" msgid="144176368026538621">"Venstrepil"</string> - <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"Høyrepil"</string> - <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"Midttasten"</string> <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string> <string name="keyboard_key_space" msgid="6980847564173394012">"Mellomrom"</string> <string name="keyboard_key_enter" msgid="8633362970109751646">"Enter"</string> @@ -1275,7 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"lagt sammen"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"åpen"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s/%2$s"</string> - <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"<xliff:g id="PERCENTAGE">%s</xliff:g> batteri gjenstår"</string> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Batteri i pekepennen: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Koble pekepennen til en lader"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"Det er lite batteri i pekepennen"</string> <string name="video_camera" msgid="7654002575156149298">"Videokamera"</string> @@ -1318,6 +1315,16 @@ <string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"Nylig brukt av <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string> <string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"I bruk av <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>)"</string> <string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"Nylig brukt av <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>)"</string> + <string name="shortcut_helper_category_system" msgid="462110876978937359">"System"</string> + <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Multitasking"</string> + <string name="shortcut_helper_category_input" msgid="8674018654124839566">"Inndata"</string> + <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"App-snarveier"</string> + <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Tilgjengelighet"</string> + <string name="shortcut_helper_title" msgid="8567500639300970049">"Hurtigtaster"</string> + <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Snarveier til søk"</string> + <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Skjul-ikon"</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="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Bakgrunnslys for tastatur"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"Nivå %1$d av %2$d"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"Hjemkontroller"</string> diff --git a/packages/SystemUI/res/values-ne/strings.xml b/packages/SystemUI/res/values-ne/strings.xml index aa7e3140b635..c26ea3e039dd 100644 --- a/packages/SystemUI/res/values-ne/strings.xml +++ b/packages/SystemUI/res/values-ne/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"भोलि फेरि स्वतः अन गर्नुहोस्"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"क्विक सेयर र Find My Device जस्ता सुविधाहरू प्रयोग गर्न ब्लुटुथ चाहिन्छ"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"ब्लुटुथ भोलि बिहान अन हुने छ"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"अडियो सेयरिङ"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"अडियो सेयर गरिँदै छ"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> ब्याट्री"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"अडियो"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"हेडसेट"</string> @@ -361,25 +363,25 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"तपाईंको डिभाइसको कुन चाहिँ सुविधा प्रभावित भएको छ?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"समस्याको प्रकार चयन गर्नुहोस्"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"स्क्रिन रेकर्ड"</string> - <string-array name="qs_record_issue_types"> - <item msgid="2947988124014085798">"पर्फर्मेन्स"</item> - <item msgid="1627504621139124393">"युजर इन्टरफेस"</item> - <item msgid="8309220355268900335">"ब्याट्री"</item> - </string-array> + <string name="performance" msgid="6552785217174378320">"पर्फर्मेन्स"</string> + <string name="user_interface" msgid="3712869377953950887">"युजर इन्टरफेस"</string> + <string name="thermal" msgid="6758074791325414831">"थर्मल"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"एक हाते मोड"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"हियरिङ डिभाइसहरू"</string> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"सक्रिय छ"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"डिस्कनेक्ट गरिएको छ"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"हियरिङ डिभाइसहरू"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"नयाँ डिभाइस कनेक्ट गर्नुहोस्"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"नयाँ डिभाइसमा कनेक्ट गर्न क्लिक गर्नुहोस्"</string> <string name="hearing_devices_presets_error" msgid="350363093458408536">"प्रिसेट अपडेट गर्न सकिएन"</string> - <!-- no translation found for hearing_devices_preset_label (7878267405046232358) --> - <skip /> + <string name="hearing_devices_preset_label" msgid="7878267405046232358">"पूर्वनिर्धारित"</string> + <string name="live_caption_title" msgid="8916875614623730005">"लाइभ क्याप्सन"</string> <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> - <string name="sensor_privacy_start_use_mic_dialog_content" msgid="1624701280680913717">"यसो गर्नुभयो भने माइक्रोफोन प्रयोग गर्ने अनुमति दिइएका सबै एप तथा सेवाहरूका लागि सो अनुमति अनब्लक गरिन्छ।"</string> - <string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"यसो गर्नुभयो भने क्यामेरा प्रयोग गर्ने अनुमति दिइएका सबै एप तथा सेवाहरूका लागि सो अनुमति अनब्लक गरिन्छ।"</string> - <string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"यसो गर्नुभयो भने क्यामेरा वा माइक्रोफोन प्रयोग गर्ने अनुमति दिइएका सबै एप तथा सेवाहरूका लागि सो अनुमति अनब्लक गरिन्छ।"</string> + <string name="sensor_privacy_start_use_mic_dialog_content" msgid="1624701280680913717">"यसो गर्नुभयो भने माइक्रोफोन प्रयोग गर्ने अनुमति दिइएका सबै एप तथा सेवाको हकमा यो अनुमति अनब्लक गरिन्छ।"</string> + <string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"यसो गर्नुभयो भने क्यामेरा प्रयोग गर्ने अनुमति दिइएका सबै एप तथा सेवाको हकमा यो अनुमति अनब्लक गरिन्छ।"</string> + <string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"यसो गर्नुभयो भने क्यामेरा वा माइक्रोफोन प्रयोग गर्ने अनुमति दिइएका सबै एप तथा सेवाको हकमा यो अनुमति अनब्लक गरिन्छ।"</string> <string name="sensor_privacy_start_use_mic_blocked_dialog_title" msgid="2640140287496469689">"माइक्रोफोन ब्लक गरिएको छ"</string> <string name="sensor_privacy_start_use_camera_blocked_dialog_title" msgid="7398084286822440384">"क्यामेरा ब्लक गरिएको छ"</string> <string name="sensor_privacy_start_use_mic_camera_blocked_dialog_title" msgid="195236134743281973">"माइक र क्यामेरा ब्लक गरिएको छ"</string> @@ -727,11 +729,6 @@ <string name="keyboard_key_button_template" msgid="8005673627272051429">"<xliff:g id="NAME">%1$s</xliff:g> बटन"</string> <string name="keyboard_key_home" msgid="3734400625170020657">"Home"</string> <string name="keyboard_key_back" msgid="4185420465469481999">"पछाडि"</string> - <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"अप एरो"</string> - <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"डाउन एरो"</string> - <string name="keyboard_key_dpad_left" msgid="144176368026538621">"लेफ्ट एरो"</string> - <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"राइट एरो"</string> - <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"केन्द्र"</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">"Enter"</string> @@ -932,7 +929,7 @@ <string name="qs_dnd_prompt_auto_rule_app" msgid="1841469944118486580">"कुनै स्वचालित नियम वा एपले बाधा नपुऱ्याउनुहोस् नामक विकल्पलाई सक्रिय गऱ्यो।"</string> <string name="running_foreground_services_title" msgid="5137313173431186685">"पृष्ठभूमिमा चल्ने एपहरू"</string> <string name="running_foreground_services_msg" msgid="3009459259222695385">"ब्याट्री र डेटाका प्रयोग सम्बन्धी विवरणहरूका लागि ट्याप गर्नुहोस्"</string> - <string name="mobile_data_disable_title" msgid="5366476131671617790">"मोबाइल डेटा निष्क्रिय पार्ने हो?"</string> + <string name="mobile_data_disable_title" msgid="5366476131671617790">"मोबाइल डेटा अफ गर्ने हो?"</string> <string name="mobile_data_disable_message" msgid="8604966027899770415">"तपाईं <xliff:g id="CARRIER">%s</xliff:g> मार्फत डेटा वा इन्टरनेट प्रयोग गर्न सक्नुहुने छैन। Wi-Fi मार्फत मात्र इन्टरनेट उपलब्ध हुने छ।"</string> <string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"तपाईंको सेवा प्रदायक"</string> <string name="auto_data_switch_disable_title" msgid="5146527155665190652">"फेरि <xliff:g id="CARRIER">%s</xliff:g> को मोबाइल डेटा अन गर्ने हो?"</string> @@ -1275,7 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"फोल्ड गरिएको"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"अनफोल्ड गरिएको"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string> - <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"<xliff:g id="PERCENTAGE">%s</xliff:g> ब्याट्री बाँकी छ"</string> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"स्टाइलसको ब्याट्री <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"आफ्नो स्टाइलस चार्जरमा कनेक्ट गर्नुहोस्"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"स्टाइलसको ब्याट्री लो छ"</string> <string name="video_camera" msgid="7654002575156149298">"भिडियो क्यामेरा"</string> @@ -1318,6 +1315,16 @@ <string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"<xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>) ले हालसालै प्रयोग गरेको"</string> <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>) ले प्रयोग गरिरहेको छ"</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>) ले हालसालै प्रयोग गरेको"</string> + <string name="shortcut_helper_category_system" msgid="462110876978937359">"सिस्टम"</string> + <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"एकै पटक एकभन्दा बढी एप चलाउन मिल्ने सुविधा"</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_a11y" msgid="6314444792641773464">"सर्वसुलभता"</string> + <string name="shortcut_helper_title" msgid="8567500639300970049">"किबोर्डका सर्टकटहरू"</string> + <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"खोजका सर्टकटहरू"</string> + <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"\"कोल्याप्स गर्नुहोस्\" आइकन"</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="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> diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml index 3b460e51f7a5..3eba8a8a1324 100644 --- a/packages/SystemUI/res/values-nl/strings.xml +++ b/packages/SystemUI/res/values-nl/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Morgen weer automatisch aanzetten"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Functies zoals Quick Share en Vind mijn apparaat gebruiken bluetooth"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth wordt morgenochtend aangezet"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Audio delen"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Audio delen…"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> batterijniveau"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Headset"</string> @@ -361,19 +363,19 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Op welk onderdeel van de apparaatfunctionaliteit had dit effect?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Probleemtype selecteren"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Schermopname"</string> - <string-array name="qs_record_issue_types"> - <item msgid="2947988124014085798">"Prestaties"</item> - <item msgid="1627504621139124393">"Gebruikersinterface"</item> - <item msgid="8309220355268900335">"Batterij"</item> - </string-array> + <string name="performance" msgid="6552785217174378320">"Prestaties"</string> + <string name="user_interface" msgid="3712869377953950887">"Gebruikersinterface"</string> + <string name="thermal" msgid="6758074791325414831">"Thermisch"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Bediening met 1 hand"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Hoortoestellen"</string> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Actief"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Ontkoppeld"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Hoortoestellen"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Nieuw apparaat koppelen"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Klik om nieuw apparaat te koppelen"</string> <string name="hearing_devices_presets_error" msgid="350363093458408536">"Kan voorinstelling niet updaten"</string> - <!-- no translation found for hearing_devices_preset_label (7878267405046232358) --> - <skip /> + <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Voorinstelling"</string> + <string name="live_caption_title" msgid="8916875614623730005">"Live ondertiteling"</string> <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> @@ -727,11 +729,6 @@ <string name="keyboard_key_button_template" msgid="8005673627272051429">"Knop <xliff:g id="NAME">%1$s</xliff:g>"</string> <string name="keyboard_key_home" msgid="3734400625170020657">"Home"</string> <string name="keyboard_key_back" msgid="4185420465469481999">"Terug"</string> - <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"Pijl-omhoog"</string> - <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"Pijl-omlaag"</string> - <string name="keyboard_key_dpad_left" msgid="144176368026538621">"Pijl-links"</string> - <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"Pijl-rechts"</string> - <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"Midden"</string> <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string> <string name="keyboard_key_space" msgid="6980847564173394012">"Spatiebalk"</string> <string name="keyboard_key_enter" msgid="8633362970109751646">"Enter"</string> @@ -1201,7 +1198,7 @@ <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Wifi maakt momenteel niet automatisch verbinding"</string> <string name="see_all_networks" msgid="3773666844913168122">"Alles tonen"</string> <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Verbreek de ethernetverbinding om van netwerk te wisselen"</string> - <string name="wifi_scan_notify_message" msgid="3753839537448621794">"Apps en services kunnen nog steeds op elk gewenst moment zoeken naar apparaten in de buurt om de apparaatfunctionaliteit te verbeteren, zelfs als bluetooth uitstaat. Je kunt dit aanpassen in de instellingen voor wifi-scannen. "<annotation id="link">"Wijzigen"</annotation></string> + <string name="wifi_scan_notify_message" msgid="3753839537448621794">"Voor een betere apparaatfunctionaliteit kunnen apps en services nog steeds op elk moment zoeken naar wifi-netwerken, zelfs als wifi uitstaat. Je kunt dit aanpassen in de instellingen voor wifi-scannen. "<annotation id="link">"Wijzigen"</annotation></string> <string name="turn_off_airplane_mode" msgid="8425587763226548579">"Vliegtuigmodus uitzetten"</string> <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> wil de volgende tegel toevoegen aan Snelle instellingen"</string> <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Tegel toevoegen"</string> @@ -1275,7 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"dichtgevouwen"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"opengevouwen"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s/%2$s"</string> - <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"Nog <xliff:g id="PERCENTAGE">%s</xliff:g> batterijlading"</string> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Stylusbatterij <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Verbind je stylus met een oplader"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"Batterij van stylus bijna leeg"</string> <string name="video_camera" msgid="7654002575156149298">"Videocamera"</string> @@ -1318,8 +1315,18 @@ <string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"Recent gebruikt door <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string> <string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"Gebruikt door <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>)"</string> <string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"Recent gebruikt door <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>)"</string> + <string name="shortcut_helper_category_system" msgid="462110876978937359">"Systeem"</string> + <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Multitasking"</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_a11y" msgid="6314444792641773464">"Toegankelijkheid"</string> + <string name="shortcut_helper_title" msgid="8567500639300970049">"Sneltoetsen"</string> + <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Snelkoppelingen voor zoekopdrachten"</string> + <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Icoon voor samenvouwen"</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="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Achtergrondverlichting van toetsenbord"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"Niveau %1$d van %2$d"</string> - <string name="home_controls_dream_label" msgid="6567105701292324257">"Huisbediening"</string> - <string name="home_controls_dream_description" msgid="4644150952104035789">"Snel toegang tot je huisbediening als screensaver"</string> + <string name="home_controls_dream_label" msgid="6567105701292324257">"Bediening voor in huis"</string> + <string name="home_controls_dream_description" msgid="4644150952104035789">"Gebruik bediening voor in huis als screensaver"</string> </resources> diff --git a/packages/SystemUI/res/values-or/strings.xml b/packages/SystemUI/res/values-or/strings.xml index 29a5e7302530..1e3ebab86b77 100644 --- a/packages/SystemUI/res/values-or/strings.xml +++ b/packages/SystemUI/res/values-or/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"ଆସନ୍ତାକାଲି ସ୍ୱତଃ ପୁଣି ଚାଲୁ ହେବ"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Quick Share ଏବଂ Find My Device ପରି ଫିଚରଗୁଡ଼ିକ ବ୍ଲୁଟୁଥ ବ୍ୟବହାର କରେ"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"ବ୍ଲୁଟୁଥ ଆସନ୍ତା କାଲି ସକାଳେ ଚାଲୁ ହେବ"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"ଅଡିଓ ସେୟାରିଂ"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"ଅଡିଓ ସେୟାର କରାଯାଉଛି"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> ବ୍ୟାଟେରୀ"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"ଅଡିଓ"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"ହେଡସେଟ୍"</string> @@ -361,19 +363,19 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"ଆପଣଙ୍କ ଡିଭାଇସ ଅନୁଭୂତିର କେଉଁ ଅଂଶ ପ୍ରଭାବିତ ହୋଇଛି?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"ସମସ୍ୟାର ପ୍ରକାର ଚୟନ କରନ୍ତୁ"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"ସ୍କ୍ରିନ ରେକର୍ଡ"</string> - <string-array name="qs_record_issue_types"> - <item msgid="2947988124014085798">"ପରଫରମାନ୍ସ"</item> - <item msgid="1627504621139124393">"ୟୁଜର ଇଣ୍ଟରଫେସ"</item> - <item msgid="8309220355268900335">"ବେଟେରୀ"</item> - </string-array> + <string name="performance" msgid="6552785217174378320">"ପରଫରମାନ୍ସ"</string> + <string name="user_interface" msgid="3712869377953950887">"ୟୁଜର ଇଣ୍ଟରଫେସ"</string> + <string name="thermal" msgid="6758074791325414831">"ଥର୍ମାଲ"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"ଏକ-ହାତ ମୋଡ"</string> - <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"ହିଅରିଂ ଡିଭାଇସଗୁଡ଼ିକ"</string> - <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"ହିଅରିଂ ଡିଭାଇସଗୁଡ଼ିକ"</string> + <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"ଶ୍ରବଣ ଡିଭାଇସଗୁଡ଼ିକ"</string> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"ସକ୍ରିୟ"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"ଡିସକନେକ୍ଟ ହୋଇଛି"</string> + <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"ଶ୍ରବଣ ଡିଭାଇସଗୁଡ଼ିକ"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"ନୂଆ ଡିଭାଇସ ପେୟାର କରନ୍ତୁ"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"ନୂଆ ଡିଭାଇସ ପେୟାର କରିବାକୁ କ୍ଲିକ କରନ୍ତୁ"</string> <string name="hearing_devices_presets_error" msgid="350363093458408536">"ପ୍ରିସେଟକୁ ଅପଡେଟ କରାଯାଇପାରିଲା ନାହିଁ"</string> - <!-- no translation found for hearing_devices_preset_label (7878267405046232358) --> - <skip /> + <string name="hearing_devices_preset_label" msgid="7878267405046232358">"ପ୍ରିସେଟ"</string> + <string name="live_caption_title" msgid="8916875614623730005">"ଲାଇଭ କେପ୍ସନ"</string> <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> @@ -727,11 +729,6 @@ <string name="keyboard_key_button_template" msgid="8005673627272051429">"ବଟନ୍ <xliff:g id="NAME">%1$s</xliff:g>"</string> <string name="keyboard_key_home" msgid="3734400625170020657">"ହୋମ"</string> <string name="keyboard_key_back" msgid="4185420465469481999">"ଫେରନ୍ତୁ"</string> - <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"ଅପ ତୀର କୀ"</string> - <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"ଡାଉନ ଆରୋ"</string> - <string name="keyboard_key_dpad_left" msgid="144176368026538621">"ବାମ ତୀର କୀ"</string> - <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"ଡାହାଣ ତୀର କୀ"</string> - <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"କେନ୍ଦ୍ର"</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> @@ -933,7 +930,7 @@ <string name="running_foreground_services_title" msgid="5137313173431186685">"ବ୍ୟାକଗ୍ରାଉଣ୍ଡରେ ଆପ୍ ଚାଲୁଛି"</string> <string name="running_foreground_services_msg" msgid="3009459259222695385">"ବ୍ୟାଟେରୀ ଏବଂ ଡାଟା ବ୍ୟବହାର ଉପରେ ବିବରଣୀ ପାଇଁ ଟାପ୍ କରନ୍ତୁ"</string> <string name="mobile_data_disable_title" msgid="5366476131671617790">"ମୋବାଇଲ୍ ଡାଟା ବନ୍ଦ କରିବେ?"</string> - <string name="mobile_data_disable_message" msgid="8604966027899770415">"ଡାଟା କିମ୍ବା ଇଣ୍ଟରନେଟ୍କୁ <xliff:g id="CARRIER">%s</xliff:g> ଦ୍ଵାରା ଆପଣଙ୍କର ଆକ୍ସେସ୍ ରହିବ ନାହିଁ। ଇଣ୍ଟରନେଟ୍ କେବଳ ୱାଇ-ଫାଇ ମାଧ୍ୟମରେ ଉପଲବ୍ଧ ହେବ।"</string> + <string name="mobile_data_disable_message" msgid="8604966027899770415">"ଡାଟା କିମ୍ବା ଇଣ୍ଟର୍ନେଟକୁ <xliff:g id="CARRIER">%s</xliff:g> ଦ୍ଵାରା ଆପଣଙ୍କର ଆକ୍ସେସ ରହିବ ନାହିଁ। ଇଣ୍ଟର୍ନେଟ କେବଳ ୱାଇ-ଫାଇ ମାଧ୍ୟମରେ ଉପଲବ୍ଧ ହେବ।"</string> <string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"ଆପଣଙ୍କ କେରିଅର୍"</string> <string name="auto_data_switch_disable_title" msgid="5146527155665190652">"<xliff:g id="CARRIER">%s</xliff:g>କୁ ପୁଣି ସ୍ୱିଚ କରିବେ?"</string> <string name="auto_data_switch_disable_message" msgid="5885533647399535852">"ଉପଲବ୍ଧତା ଆଧାରରେ ମୋବାଇଲ ଡାଟା ସ୍ୱତଃ ସ୍ୱିଚ ହେବ ନାହିଁ"</string> @@ -1210,7 +1207,7 @@ <string name="fgs_manager_footer_label" msgid="8276763570622288231">"{count,plural, =1{#ଟି ଆପ ସକ୍ରିୟ ଅଛି}other{#ଟି ଆପ ସକ୍ରିୟ ଅଛି}}"</string> <string name="fgs_dot_content_description" msgid="2865071539464777240">"ନୂଆ ସୂଚନା"</string> <string name="fgs_manager_dialog_title" msgid="5879184257257718677">"ସକ୍ରିୟ ଆପ୍ସ"</string> - <string name="fgs_manager_dialog_message" msgid="2670045017200730076">"ଆପଣ ଏହି ଆପ୍ସକୁ ବ୍ୟବହାର କରୁନଥିଲେ ମଧ୍ୟ ସେଗୁଡ଼ିକ ସକ୍ରିୟ ରହିଥାଏ ଏବଂ ଚାଲୁଥାଏ। ଏହା ସେଗୁଡ଼ିକର କାର୍ଯ୍ୟକ୍ଷମତାକୁ ଉନ୍ନତ କରେ, କିନ୍ତୁ ଏହା ମଧ୍ୟ ବ୍ୟାଟେରୀ ଲାଇଫକୁ ପ୍ରଭାବିତ କରିପାରେ।"</string> + <string name="fgs_manager_dialog_message" msgid="2670045017200730076">"ଆପଣ ଏହି ଆପ୍ସକୁ ବ୍ୟବହାର କରୁନଥିଲେ ମଧ୍ୟ ସେଗୁଡ଼ିକ ସକ୍ରିୟ ରହିଥାଏ ଏବଂ ଚାଲୁଥାଏ। ଏହା ସେଗୁଡ଼ିକର କାର୍ଯ୍ୟକ୍ଷମତାକୁ ଉନ୍ନତ କରେ, କିନ୍ତୁ ଏହା ମଧ୍ୟ ବେଟେରୀ ଲାଇଫକୁ ପ୍ରଭାବିତ କରିପାରେ।"</string> <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"ବନ୍ଦ କରନ୍ତୁ"</string> <string name="fgs_manager_app_item_stop_button_stopped_label" msgid="6950382004441263922">"ବନ୍ଦ ହୋଇଛି"</string> <string name="clipboard_edit_text_done" msgid="4551887727694022409">"ହୋଇଗଲା"</string> @@ -1275,7 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"ଫୋଲ୍ଡେଡ"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"ଅନଫୋଲ୍ଡେଡ"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string> - <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"<xliff:g id="PERCENTAGE">%s</xliff:g> ବେଟେରୀ ଚାର୍ଜ ବାକି ଅଛି"</string> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"ଷ୍ଟାଇଲସ ବେଟେରୀ <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"ଏକ ଚାର୍ଜର ସହ ଆପଣଙ୍କ ଷ୍ଟାଇଲସକୁ କନେକ୍ଟ କରନ୍ତୁ"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"ଷ୍ଟାଇଲସ ବେଟେରୀର ଚାର୍ଜ କମ ଅଛି"</string> <string name="video_camera" msgid="7654002575156149298">"ଭିଡିଓ କେମେରା"</string> @@ -1318,6 +1315,16 @@ <string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"ଏବେ <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>) ଦ୍ୱାରା ବ୍ୟବହାର କରାଯାଉଛି"</string> <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>)"</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>) ଦ୍ୱାରା ବ୍ୟବହାର କରାଯାଉଛି"</string> + <string name="shortcut_helper_category_system" msgid="462110876978937359">"ସିଷ୍ଟମ"</string> + <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"ମଲ୍ଟିଟାସ୍କିଂ"</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_a11y" msgid="6314444792641773464">"ଆକ୍ସେସିବିଲିଟୀ"</string> + <string name="shortcut_helper_title" msgid="8567500639300970049">"କୀବୋର୍ଡ ସର୍ଟକଟ"</string> + <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"ସର୍ଚ୍ଚ ସର୍ଟକଟ"</string> + <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"ଆଇକନକୁ ସଙ୍କୁଚିତ କରନ୍ତୁ"</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="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> diff --git a/packages/SystemUI/res/values-pa/strings.xml b/packages/SystemUI/res/values-pa/strings.xml index 624557597677..dddda72d53e3 100644 --- a/packages/SystemUI/res/values-pa/strings.xml +++ b/packages/SystemUI/res/values-pa/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"ਕੱਲ੍ਹ ਨੂੰ ਆਪਣੇ ਆਪ ਚਾਲੂ ਹੋ ਜਾਵੇਗਾ"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"ਕਵਿੱਕ ਸ਼ੇਅਰ ਅਤੇ Find My Device ਵਰਗੀਆਂ ਵਿਸ਼ੇਸ਼ਤਾਵਾਂ ਬਲੂਟੁੱਥ ਵਰਤਦੀਆਂ ਹਨ"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"ਬਲੂਟੁੱਥ ਕੱਲ੍ਹ ਸਵੇਰੇ ਚਾਲੂ ਹੋ ਜਾਵੇਗਾ"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"ਆਡੀਓ ਸਾਂਝਾਕਰਨ"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"ਆਡੀਓ ਨੂੰ ਸਾਂਝਾ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> ਬੈਟਰੀ"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"ਆਡੀਓ"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"ਹੈੱਡਸੈੱਟ"</string> @@ -361,19 +363,19 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"ਤੁਹਾਡੇ ਡੀਵਾਈਸ ਦੀ ਕਿਹੜੀ ਸੁਵਿਧਾ ਪ੍ਰਭਾਵਿਤ ਹੋਈ ਸੀ?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"ਸਮੱਸਿਆ ਦੀ ਕਿਸਮ ਚੁਣੋ"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"ਸਕ੍ਰੀਨ ਰਿਕਾਰਡ"</string> - <string-array name="qs_record_issue_types"> - <item msgid="2947988124014085798">"ਕਾਰਗੁਜ਼ਾਰੀ"</item> - <item msgid="1627504621139124393">"ਯੂਜ਼ਰ ਇੰਟਰਫ਼ੇਸ"</item> - <item msgid="8309220355268900335">"ਬੈਟਰੀ"</item> - </string-array> + <string name="performance" msgid="6552785217174378320">"ਕਾਰਗੁਜ਼ਾਰੀ"</string> + <string name="user_interface" msgid="3712869377953950887">"ਯੂਜ਼ਰ ਇੰਟਰਫ਼ੇਸ"</string> + <string name="thermal" msgid="6758074791325414831">"ਥਰਮਲ"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"ਇੱਕ ਹੱਥ ਮੋਡ"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"ਸੁਣਨ ਵਾਲੇ ਡੀਵਾਈਸ"</string> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"ਕਿਰਿਆਸ਼ੀਲ"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"ਡਿਸਕਨੈਕਟ ਹੈ"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"ਸੁਣਨ ਵਾਲੇ ਡੀਵਾਈਸ"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"ਨਵਾਂ ਡੀਵਾਈਸ ਜੋੜਾਬੱਧ ਕਰੋ"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"\'ਨਵਾਂ ਡੀਵਾਈਸ ਜੋੜਾਬੱਧ ਕਰੋ\' \'ਤੇ ਕਲਿੱਕ ਕਰੋ"</string> <string name="hearing_devices_presets_error" msgid="350363093458408536">"ਪ੍ਰੀਸੈੱਟ ਨੂੰ ਅੱਪਡੇਟ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਿਆ"</string> - <!-- no translation found for hearing_devices_preset_label (7878267405046232358) --> - <skip /> + <string name="hearing_devices_preset_label" msgid="7878267405046232358">"ਪ੍ਰੀਸੈੱਟ"</string> + <string name="live_caption_title" msgid="8916875614623730005">"ਲਾਈਵ ਸੁਰਖੀਆਂ"</string> <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> @@ -727,11 +729,6 @@ <string name="keyboard_key_button_template" msgid="8005673627272051429">"ਬਟਨ <xliff:g id="NAME">%1$s</xliff:g>"</string> <string name="keyboard_key_home" msgid="3734400625170020657">"Home"</string> <string name="keyboard_key_back" msgid="4185420465469481999">"Back"</string> - <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"ਉੱਪਰ ਤੀਰ"</string> - <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"ਹੇਠਾਂ ਤੀਰ"</string> - <string name="keyboard_key_dpad_left" msgid="144176368026538621">"ਖੱਬਾ ਤੀਰ"</string> - <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"ਸੱਜਾ ਤੀਰ"</string> - <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"Center"</string> <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> @@ -1275,7 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"ਫੋਲਡਯੋਗ ਡੀਵਾਈਸ"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"ਅਣਫੋਲਡਯੋਗ ਡੀਵਾਈਸ"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string> - <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"<xliff:g id="PERCENTAGE">%s</xliff:g> ਬੈਟਰੀ ਬਾਕੀ"</string> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"ਸਟਾਈਲਸ ਦੀ ਬੈਟਰੀ <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"ਆਪਣੇ ਸਟਾਈਲਸ ਨੂੰ ਚਾਰਜਰ ਨਾਲ ਕਨੈਕਟ ਕਰੋ"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"ਸਟਾਈਲਸ ਦੀ ਬੈਟਰੀ ਘੱਟ ਹੈ"</string> <string name="video_camera" msgid="7654002575156149298">"ਵੀਡੀਓ ਕੈਮਰਾ"</string> @@ -1318,6 +1315,16 @@ <string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"ਹਾਲ ਹੀ ਵਿੱਚ <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>) ਵੱਲੋਂ ਵਰਤਿਆ ਗਿਆ"</string> <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>) ਵੱਲੋਂ ਵਰਤੋਂ ਕੀਤੀ ਜਾ ਰਹੀ ਹੈ"</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>) ਵੱਲੋਂ ਵਰਤਿਆ ਗਿਆ"</string> + <string name="shortcut_helper_category_system" msgid="462110876978937359">"ਸਿਸਟਮ"</string> + <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"ਮਲਟੀਟਾਸਕਿੰਗ"</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_a11y" msgid="6314444792641773464">"ਪਹੁੰਚਯੋਗਤਾ"</string> + <string name="shortcut_helper_title" msgid="8567500639300970049">"ਕੀ-ਬੋਰਡ ਸ਼ਾਰਟਕੱਟ"</string> + <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"ਖੋਜ ਸੰਬੰਧੀ ਸ਼ਾਰਟਕੱਟ"</string> + <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"ਪ੍ਰਤੀਕ ਨੂੰ ਸਮੇਟੋ"</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="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> diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml index e8e6f09d0d41..2dbcf2028c50 100644 --- a/packages/SystemUI/res/values-pl/strings.xml +++ b/packages/SystemUI/res/values-pl/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Automatycznie włącz ponownie jutro"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Bluetootha używają funkcje takie jak szybkie udostępnianie czy Znajdź moje urządzenie"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth włączy się jutro rano"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Udostępnianie dźwięku"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Udostępniam dźwięk"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> naładowania baterii"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Dźwięk"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Zestaw słuchawkowy"</string> @@ -361,19 +363,19 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Którego aspektu korzystania z urządzenia dotyczył problem?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Wybierz typ problemu"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Nagrywanie ekranu"</string> - <string-array name="qs_record_issue_types"> - <item msgid="2947988124014085798">"Wydajność"</item> - <item msgid="1627504621139124393">"Interfejs"</item> - <item msgid="8309220355268900335">"Bateria"</item> - </string-array> + <string name="performance" msgid="6552785217174378320">"Wydajność"</string> + <string name="user_interface" msgid="3712869377953950887">"Interfejs"</string> + <string name="thermal" msgid="6758074791325414831">"Termografia"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Tryb jednej ręki"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Urządzenia słuchowe"</string> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Aktywny"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Rozłączono"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Urządzenia słuchowe"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Sparuj nowe urządzenie"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Kliknij, aby sparować nowe urządzenie"</string> <string name="hearing_devices_presets_error" msgid="350363093458408536">"Nie udało się zaktualizować gotowego ustawienia"</string> - <!-- no translation found for hearing_devices_preset_label (7878267405046232358) --> - <skip /> + <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Gotowe ustawienie"</string> + <string name="live_caption_title" msgid="8916875614623730005">"Napisy na żywo"</string> <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> @@ -727,11 +729,6 @@ <string name="keyboard_key_button_template" msgid="8005673627272051429">"Przycisk <xliff:g id="NAME">%1$s</xliff:g>"</string> <string name="keyboard_key_home" msgid="3734400625170020657">"Home"</string> <string name="keyboard_key_back" msgid="4185420465469481999">"Wstecz"</string> - <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"Strzałka w górę"</string> - <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"Strzałka w dół"</string> - <string name="keyboard_key_dpad_left" msgid="144176368026538621">"Strzałka w lewo"</string> - <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"Strzałka w prawo"</string> - <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"Do środka"</string> <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string> <string name="keyboard_key_space" msgid="6980847564173394012">"Spacja"</string> <string name="keyboard_key_enter" msgid="8633362970109751646">"Enter"</string> @@ -877,7 +874,7 @@ <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Otwórz ustawienia."</string> <string name="accessibility_quick_settings_expand" msgid="2609275052412521467">"Otwórz szybkie ustawienia."</string> <string name="accessibility_quick_settings_collapse" msgid="4674876336725041982">"Zamknij szybkie ustawienia."</string> - <string name="accessibility_quick_settings_user" msgid="505821942882668619">"Zalogowany użytkownik: <xliff:g id="ID_1">%s</xliff:g>"</string> + <string name="accessibility_quick_settings_user" msgid="505821942882668619">"Używasz konta: <xliff:g id="ID_1">%s</xliff:g>"</string> <string name="accessibility_quick_settings_choose_user_action" msgid="4554388498186576087">"wybrać użytkownika"</string> <string name="data_connection_no_internet" msgid="691058178914184544">"Brak internetu"</string> <string name="accessibility_quick_settings_open_settings" msgid="536838345505030893">"Otwórz ustawienia: <xliff:g id="ID_1">%s</xliff:g>."</string> @@ -1275,7 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"po zamknięciu"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"po otwarciu"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string> - <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"Pozostało <xliff:g id="PERCENTAGE">%s</xliff:g> baterii"</string> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Bateria rysika: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Podłącz rysik do ładowarki"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"Słaba bateria w rysiku"</string> <string name="video_camera" msgid="7654002575156149298">"Kamera"</string> @@ -1318,6 +1315,16 @@ <string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"Ostatnio używany przez aplikację <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string> <string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"Używany przez aplikację <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>)"</string> <string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"Ostatnio używany przez aplikację <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>)"</string> + <string name="shortcut_helper_category_system" msgid="462110876978937359">"System"</string> + <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Wielozadaniowość"</string> + <string name="shortcut_helper_category_input" msgid="8674018654124839566">"Wprowadzanie"</string> + <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Skróty do aplikacji"</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> + <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Skróty do wyszukiwania"</string> + <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ikona zwijania"</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="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Podświetlenie klawiatury"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"Poziom %1$d z %2$d"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"Sterowanie domem"</string> diff --git a/packages/SystemUI/res/values-pt-rBR/strings.xml b/packages/SystemUI/res/values-pt-rBR/strings.xml index e72b45a4742d..992db40d04a6 100644 --- a/packages/SystemUI/res/values-pt-rBR/strings.xml +++ b/packages/SystemUI/res/values-pt-rBR/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Ativar automaticamente de novo amanhã"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Recursos como o Quick Share e o Encontre Meu Dispositivo usam Bluetooth"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"O Bluetooth será ativado amanhã de manhã"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Compartilhamento de áudio"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Compartilhando áudio"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Bateria: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Áudio"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Fone de ouvido"</string> @@ -361,19 +363,19 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Que parte da sua experiência no dispositivo foi afetada?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Selecionar tipo de problema"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Gravação de tela"</string> - <string-array name="qs_record_issue_types"> - <item msgid="2947988124014085798">"Desempenho"</item> - <item msgid="1627504621139124393">"Interface do usuário"</item> - <item msgid="8309220355268900335">"Bateria"</item> - </string-array> + <string name="performance" msgid="6552785217174378320">"Desempenho"</string> + <string name="user_interface" msgid="3712869377953950887">"Interface do usuário"</string> + <string name="thermal" msgid="6758074791325414831">"Térmico"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Modo 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> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Aparelhos auditivos"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Parear novo dispositivo"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Clique para parear o novo dispositivo"</string> <string name="hearing_devices_presets_error" msgid="350363093458408536">"Não foi possível atualizar a predefinição"</string> - <!-- no translation found for hearing_devices_preset_label (7878267405046232358) --> - <skip /> + <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Predefinição"</string> + <string name="live_caption_title" msgid="8916875614623730005">"Legenda instantânea"</string> <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> @@ -634,7 +636,7 @@ <string name="volume_panel_hint_muted" msgid="1124844870181285320">"som desativado"</string> <string name="volume_panel_hint_vibrate" msgid="4136223145435914132">"vibrar"</string> <string name="media_output_label_title" msgid="872824698593182505">"Tocando <xliff:g id="LABEL">%s</xliff:g> em"</string> - <string name="media_output_title_without_playing" msgid="3825663683169305013">"Onde o áudio vai tocar?"</string> + <string name="media_output_title_without_playing" msgid="3825663683169305013">"Áudio definido para"</string> <string name="media_output_title_ongoing_call" msgid="208426888064112006">"Ligando"</string> <string name="system_ui_tuner" msgid="1471348823289954729">"Sintonizador System UI"</string> <string name="status_bar" msgid="4357390266055077437">"Barra de status"</string> @@ -727,11 +729,6 @@ <string name="keyboard_key_button_template" msgid="8005673627272051429">"Botão <xliff:g id="NAME">%1$s</xliff:g>"</string> <string name="keyboard_key_home" msgid="3734400625170020657">"Home"</string> <string name="keyboard_key_back" msgid="4185420465469481999">"Voltar"</string> - <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"Seta para cima"</string> - <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"Seta para baixo"</string> - <string name="keyboard_key_dpad_left" msgid="144176368026538621">"Seta para a esquerda"</string> - <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"Seta para a direita"</string> - <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"Centralizar"</string> <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> @@ -789,8 +786,8 @@ <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_full_screen" msgid="336048080383640562">"Mudar da tela dividida para a tela cheia"</string> - <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Mude para o app à direita ou abaixo ao usar a tela dividida"</string> - <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Mude para o app à esquerda ou acima ao usar a tela dividida"</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> <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> @@ -1210,7 +1207,7 @@ <string name="fgs_manager_footer_label" msgid="8276763570622288231">"{count,plural, =1{# app está ativo}one{# apps está ativo}many{# de apps estão ativos}other{# apps estão ativos}}"</string> <string name="fgs_dot_content_description" msgid="2865071539464777240">"Nova informação"</string> <string name="fgs_manager_dialog_title" msgid="5879184257257718677">"Apps ativos"</string> - <string name="fgs_manager_dialog_message" msgid="2670045017200730076">"Esses apps ficam ativos e em execução mesmo quando não estão em uso. Isso melhora a funcionalidade deles, mas também pode afetar a duração da bateria."</string> + <string name="fgs_manager_dialog_message" msgid="2670045017200730076">"Esses apps ficam ativos e em execução mesmo quando não estão em uso. Isso melhora a funcionalidade deles, mas também afeta a duração da bateria."</string> <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Parar"</string> <string name="fgs_manager_app_item_stop_button_stopped_label" msgid="6950382004441263922">"Parado"</string> <string name="clipboard_edit_text_done" msgid="4551887727694022409">"Concluído"</string> @@ -1275,7 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"fechado"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"aberto"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string> - <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"Bateria restante: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Bateria da stylus em <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Conecte sua stylus a um carregador"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"Bateria da stylus fraca"</string> <string name="video_camera" msgid="7654002575156149298">"Filmadora"</string> @@ -1318,6 +1315,16 @@ <string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"Usado recentemente pelo app <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string> <string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"Em uso pelo app <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>)"</string> <string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"Usado recentemente pelo app <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>)"</string> + <string name="shortcut_helper_category_system" msgid="462110876978937359">"Sistema"</string> + <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Multitarefas"</string> + <string name="shortcut_helper_category_input" msgid="8674018654124839566">"Entrada"</string> + <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Atalhos de apps"</string> + <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Acessibilidade"</string> + <string name="shortcut_helper_title" msgid="8567500639300970049">"Atalhos do teclado"</string> + <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Atalhos de pesquisa"</string> + <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ícone \"Fechar\""</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="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Luz de fundo do teclado"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"Nível %1$d de %2$d"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"Automação residencial"</string> diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml index 1912b7ef44d3..fb0bbfa88850 100644 --- a/packages/SystemUI/res/values-pt-rPT/strings.xml +++ b/packages/SystemUI/res/values-pt-rPT/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Reativar amanhã automaticamente"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Funcionalidades como a Partilha rápida e o serviço Localizar o meu dispositivo usam o Bluetooth"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"O Bluetooth vai ser ativado amanhã de manhã"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Partilha de áudio"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"A partilhar áudio"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> de bateria"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Áudio"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Ausc. c/ mic. integ."</string> @@ -361,19 +363,19 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Que parte da experiência do disposit. foi afetada?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Selecione o tipo de problema"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Gravação de ecrã"</string> - <string-array name="qs_record_issue_types"> - <item msgid="2947988124014085798">"Desempenho"</item> - <item msgid="1627504621139124393">"Interface do utilizador"</item> - <item msgid="8309220355268900335">"Bateria"</item> - </string-array> + <string name="performance" msgid="6552785217174378320">"Desempenho"</string> + <string name="user_interface" msgid="3712869377953950887">"Interface do utilizador"</string> + <string name="thermal" msgid="6758074791325414831">"Térmico"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Modo para uma mão"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Dispositivos auditivos"</string> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Ativos"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Desligados"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Dispositivos auditivos"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Sincronizar novo dispositivo"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Clique para sincronizar um novo dispositivo"</string> <string name="hearing_devices_presets_error" msgid="350363093458408536">"Não foi possível atualizar a predefinição"</string> - <!-- no translation found for hearing_devices_preset_label (7878267405046232358) --> - <skip /> + <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Predefinição"</string> + <string name="live_caption_title" msgid="8916875614623730005">"Legendas instantâneas"</string> <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> @@ -727,11 +729,6 @@ <string name="keyboard_key_button_template" msgid="8005673627272051429">"Botão <xliff:g id="NAME">%1$s</xliff:g>"</string> <string name="keyboard_key_home" msgid="3734400625170020657">"Início"</string> <string name="keyboard_key_back" msgid="4185420465469481999">"Anterior"</string> - <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"Seta para cima"</string> - <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"Seta para baixo"</string> - <string name="keyboard_key_dpad_left" msgid="144176368026538621">"Seta para a esquerda"</string> - <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"Seta para a direita"</string> - <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"Ao centro"</string> <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string> <string name="keyboard_key_space" msgid="6980847564173394012">"Espaço"</string> <string name="keyboard_key_enter" msgid="8633362970109751646">"Enter"</string> @@ -933,7 +930,7 @@ <string name="running_foreground_services_title" msgid="5137313173431186685">"Apps em execução em segundo plano"</string> <string name="running_foreground_services_msg" msgid="3009459259222695385">"Toque para obter detalhes acerca da utilização da bateria e dos dados"</string> <string name="mobile_data_disable_title" msgid="5366476131671617790">"Desativar os dados móveis?"</string> - <string name="mobile_data_disable_message" msgid="8604966027899770415">"Não terá acesso a dados ou à Internet através do operador <xliff:g id="CARRIER">%s</xliff:g>. A Internet estará disponível apenas por Wi-Fi."</string> + <string name="mobile_data_disable_message" msgid="8604966027899770415">"Não vai ter acesso aos dados nem à Internet através do operador <xliff:g id="CARRIER">%s</xliff:g>. A Internet vai estar disponível só por Wi-Fi."</string> <string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"o seu operador"</string> <string name="auto_data_switch_disable_title" msgid="5146527155665190652">"Mudar de novo para <xliff:g id="CARRIER">%s</xliff:g>?"</string> <string name="auto_data_switch_disable_message" msgid="5885533647399535852">"Os dados móveis não vão mudar automaticamente com base na disponibilidade"</string> @@ -988,7 +985,7 @@ <string name="magnification_close_settings_click_label" msgid="4642477260651704517">"Fechar definições de ampliação"</string> <string name="magnification_exit_edit_mode_click_label" msgid="1664818325144887117">"Sair do modo de edição"</string> <string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"Arrastar o canto para redimensionar"</string> - <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"Permitir deslocamento da página na diagonal"</string> + <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"Permitir deslocamento diagonal"</string> <string name="accessibility_resize" msgid="5733759136600611551">"Redimensionar"</string> <string name="accessibility_change_magnification_type" msgid="666000085077432421">"Alterar tipo de ampliação"</string> <string name="accessibility_magnification_end_resizing" msgid="4881690585800302628">"Terminar redimensionamento"</string> @@ -1275,7 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"fechado"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"aberto"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s/%2$s"</string> - <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"<xliff:g id="PERCENTAGE">%s</xliff:g> de bateria restante"</string> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Bateria da caneta stylus a <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Ligue a caneta stylus a um carregador"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"Bateria da caneta stylus fraca"</string> <string name="video_camera" msgid="7654002575156149298">"Câmara de vídeo"</string> @@ -1318,6 +1315,16 @@ <string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"Usado recentemente pela app <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string> <string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"Em utilização pela app <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>)"</string> <string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"Usado recentemente pela app <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>)"</string> + <string name="shortcut_helper_category_system" msgid="462110876978937359">"Sistema"</string> + <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Execução de várias tarefas em simultâneo"</string> + <string name="shortcut_helper_category_input" msgid="8674018654124839566">"Entrada"</string> + <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Atalhos de apps"</string> + <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Acessibilidade"</string> + <string name="shortcut_helper_title" msgid="8567500639300970049">"Atalhos de teclado"</string> + <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Atalhos de pesquisa"</string> + <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ícone de reduzir"</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="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Luz do teclado"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"Nível %1$d de %2$d"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"Controlos domésticos"</string> diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml index e72b45a4742d..992db40d04a6 100644 --- a/packages/SystemUI/res/values-pt/strings.xml +++ b/packages/SystemUI/res/values-pt/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Ativar automaticamente de novo amanhã"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Recursos como o Quick Share e o Encontre Meu Dispositivo usam Bluetooth"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"O Bluetooth será ativado amanhã de manhã"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Compartilhamento de áudio"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Compartilhando áudio"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Bateria: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Áudio"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Fone de ouvido"</string> @@ -361,19 +363,19 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Que parte da sua experiência no dispositivo foi afetada?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Selecionar tipo de problema"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Gravação de tela"</string> - <string-array name="qs_record_issue_types"> - <item msgid="2947988124014085798">"Desempenho"</item> - <item msgid="1627504621139124393">"Interface do usuário"</item> - <item msgid="8309220355268900335">"Bateria"</item> - </string-array> + <string name="performance" msgid="6552785217174378320">"Desempenho"</string> + <string name="user_interface" msgid="3712869377953950887">"Interface do usuário"</string> + <string name="thermal" msgid="6758074791325414831">"Térmico"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Modo 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> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Aparelhos auditivos"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Parear novo dispositivo"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Clique para parear o novo dispositivo"</string> <string name="hearing_devices_presets_error" msgid="350363093458408536">"Não foi possível atualizar a predefinição"</string> - <!-- no translation found for hearing_devices_preset_label (7878267405046232358) --> - <skip /> + <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Predefinição"</string> + <string name="live_caption_title" msgid="8916875614623730005">"Legenda instantânea"</string> <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> @@ -634,7 +636,7 @@ <string name="volume_panel_hint_muted" msgid="1124844870181285320">"som desativado"</string> <string name="volume_panel_hint_vibrate" msgid="4136223145435914132">"vibrar"</string> <string name="media_output_label_title" msgid="872824698593182505">"Tocando <xliff:g id="LABEL">%s</xliff:g> em"</string> - <string name="media_output_title_without_playing" msgid="3825663683169305013">"Onde o áudio vai tocar?"</string> + <string name="media_output_title_without_playing" msgid="3825663683169305013">"Áudio definido para"</string> <string name="media_output_title_ongoing_call" msgid="208426888064112006">"Ligando"</string> <string name="system_ui_tuner" msgid="1471348823289954729">"Sintonizador System UI"</string> <string name="status_bar" msgid="4357390266055077437">"Barra de status"</string> @@ -727,11 +729,6 @@ <string name="keyboard_key_button_template" msgid="8005673627272051429">"Botão <xliff:g id="NAME">%1$s</xliff:g>"</string> <string name="keyboard_key_home" msgid="3734400625170020657">"Home"</string> <string name="keyboard_key_back" msgid="4185420465469481999">"Voltar"</string> - <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"Seta para cima"</string> - <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"Seta para baixo"</string> - <string name="keyboard_key_dpad_left" msgid="144176368026538621">"Seta para a esquerda"</string> - <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"Seta para a direita"</string> - <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"Centralizar"</string> <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> @@ -789,8 +786,8 @@ <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_full_screen" msgid="336048080383640562">"Mudar da tela dividida para a tela cheia"</string> - <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Mude para o app à direita ou abaixo ao usar a tela dividida"</string> - <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Mude para o app à esquerda ou acima ao usar a tela dividida"</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> <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> @@ -1210,7 +1207,7 @@ <string name="fgs_manager_footer_label" msgid="8276763570622288231">"{count,plural, =1{# app está ativo}one{# apps está ativo}many{# de apps estão ativos}other{# apps estão ativos}}"</string> <string name="fgs_dot_content_description" msgid="2865071539464777240">"Nova informação"</string> <string name="fgs_manager_dialog_title" msgid="5879184257257718677">"Apps ativos"</string> - <string name="fgs_manager_dialog_message" msgid="2670045017200730076">"Esses apps ficam ativos e em execução mesmo quando não estão em uso. Isso melhora a funcionalidade deles, mas também pode afetar a duração da bateria."</string> + <string name="fgs_manager_dialog_message" msgid="2670045017200730076">"Esses apps ficam ativos e em execução mesmo quando não estão em uso. Isso melhora a funcionalidade deles, mas também afeta a duração da bateria."</string> <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Parar"</string> <string name="fgs_manager_app_item_stop_button_stopped_label" msgid="6950382004441263922">"Parado"</string> <string name="clipboard_edit_text_done" msgid="4551887727694022409">"Concluído"</string> @@ -1275,7 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"fechado"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"aberto"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string> - <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"Bateria restante: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Bateria da stylus em <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Conecte sua stylus a um carregador"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"Bateria da stylus fraca"</string> <string name="video_camera" msgid="7654002575156149298">"Filmadora"</string> @@ -1318,6 +1315,16 @@ <string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"Usado recentemente pelo app <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string> <string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"Em uso pelo app <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>)"</string> <string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"Usado recentemente pelo app <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>)"</string> + <string name="shortcut_helper_category_system" msgid="462110876978937359">"Sistema"</string> + <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Multitarefas"</string> + <string name="shortcut_helper_category_input" msgid="8674018654124839566">"Entrada"</string> + <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Atalhos de apps"</string> + <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Acessibilidade"</string> + <string name="shortcut_helper_title" msgid="8567500639300970049">"Atalhos do teclado"</string> + <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Atalhos de pesquisa"</string> + <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ícone \"Fechar\""</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="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Luz de fundo do teclado"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"Nível %1$d de %2$d"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"Automação residencial"</string> diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml index b6614c671126..ff07fe9efedb 100644 --- a/packages/SystemUI/res/values-ro/strings.xml +++ b/packages/SystemUI/res/values-ro/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Activează din nou automat mâine"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Funcții precum Quick Share și Găsește-mi dispozitivul folosesc Bluetooth"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth se va activa mâine dimineață"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Permiterea accesului la audio"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Se permite accesul la conținutul audio"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Nivelul bateriei: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Căști"</string> @@ -361,19 +363,19 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Ce parte a experienței pe dispozitiv a fost afectată?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Selectează tipul problemei"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Înregistrarea ecranului"</string> - <string-array name="qs_record_issue_types"> - <item msgid="2947988124014085798">"Performanță"</item> - <item msgid="1627504621139124393">"Interfața de utilizare"</item> - <item msgid="8309220355268900335">"Baterie"</item> - </string-array> + <string name="performance" msgid="6552785217174378320">"Performanță"</string> + <string name="user_interface" msgid="3712869377953950887">"Interfața de utilizare"</string> + <string name="thermal" msgid="6758074791325414831">"Termal"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Modul cu o mână"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Aparate auditive"</string> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Activ"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Deconectat"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Aparate auditive"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Asociază un nou dispozitiv"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Dă clic pentru a asocia un nou dispozitiv"</string> <string name="hearing_devices_presets_error" msgid="350363093458408536">"Nu s-a putut actualiza presetarea"</string> - <!-- no translation found for hearing_devices_preset_label (7878267405046232358) --> - <skip /> + <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Presetare"</string> + <string name="live_caption_title" msgid="8916875614623730005">"Subtitrări live"</string> <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> @@ -727,11 +729,6 @@ <string name="keyboard_key_button_template" msgid="8005673627272051429">"Butonul <xliff:g id="NAME">%1$s</xliff:g>"</string> <string name="keyboard_key_home" msgid="3734400625170020657">"La început"</string> <string name="keyboard_key_back" msgid="4185420465469481999">"Înapoi"</string> - <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"Săgeată în sus"</string> - <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"Săgeată în jos"</string> - <string name="keyboard_key_dpad_left" msgid="144176368026538621">"Săgeată spre stânga"</string> - <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"Săgeată spre dreapta"</string> - <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"În centru"</string> <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string> <string name="keyboard_key_space" msgid="6980847564173394012">"Spațiu"</string> <string name="keyboard_key_enter" msgid="8633362970109751646">"Enter"</string> @@ -1275,7 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"închis"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"deschis"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string> - <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"<xliff:g id="PERCENTAGE">%s</xliff:g> baterie rămasă"</string> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Bateria creionului: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Conectează-ți creionul la un încărcător"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"Nivelul bateriei creionului este scăzut"</string> <string name="video_camera" msgid="7654002575156149298">"Cameră video"</string> @@ -1318,6 +1315,16 @@ <string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"Folosit recent de <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string> <string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"Se folosește pentru <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>)"</string> <string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"Folosit recent de <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>)"</string> + <string name="shortcut_helper_category_system" msgid="462110876978937359">"Sistem"</string> + <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Multitasking"</string> + <string name="shortcut_helper_category_input" msgid="8674018654124839566">"Intrare"</string> + <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Comenzi rapide pentru aplicații"</string> + <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Accesibilitate"</string> + <string name="shortcut_helper_title" msgid="8567500639300970049">"Comenzi rapide de la tastatură"</string> + <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Comenzi directe de căutare"</string> + <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Pictograma de restrângere"</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="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Iluminarea din spate a tastaturii"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"Nivelul %1$d din %2$d"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"Comenzi pentru locuință"</string> diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml index d6fa6abcbdc6..1900a8eb9f67 100644 --- a/packages/SystemUI/res/values-ru/strings.xml +++ b/packages/SystemUI/res/values-ru/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Включить завтра автоматически"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Bluetooth используется в таких функциях и сервисах, как \"Быстрая отправка\" и \"Найти устройство\""</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth включится завтра утром"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Отправка аудио"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Включена отправка аудио"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Заряд: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Аудиоустройство"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Гарнитура"</string> @@ -361,24 +363,24 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"С чем связана проблема, с которой вы столкнулись?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Выберите тип проблемы"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Запись экрана"</string> - <string-array name="qs_record_issue_types"> - <item msgid="2947988124014085798">"Производительность"</item> - <item msgid="1627504621139124393">"Интерфейс"</item> - <item msgid="8309220355268900335">"Батарея"</item> - </string-array> + <string name="performance" msgid="6552785217174378320">"Производительность"</string> + <string name="user_interface" msgid="3712869377953950887">"Интерфейс"</string> + <string name="thermal" msgid="6758074791325414831">"Тепловизор"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Режим управления одной рукой"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Слуховые аппараты"</string> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Активно"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Не подключено"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Слуховые аппараты"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Подключить новое устройство"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Нажмите, чтобы подключить новое устройство"</string> <string name="hearing_devices_presets_error" msgid="350363093458408536">"Не удалось обновить набор настроек."</string> - <!-- no translation found for hearing_devices_preset_label (7878267405046232358) --> - <skip /> + <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Набор настроек"</string> + <string name="live_caption_title" msgid="8916875614623730005">"Автоматические субтитры"</string> <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> - <string name="sensor_privacy_start_use_mic_dialog_content" msgid="1624701280680913717">"Будет снята блокировка доступа для всех приложений и сервисов с разрешением на использование микрофона."</string> - <string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"Будет снята блокировка доступа для всех приложений и сервисов с разрешением на использование камеры."</string> + <string name="sensor_privacy_start_use_mic_dialog_content" msgid="1624701280680913717">"Все приложения и сервисы, у которых есть разрешение на использование микрофона, получат к нему доступ."</string> + <string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"Все приложения и сервисы, у которых есть разрешение на использование камеры, получат к ней доступ."</string> <string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"Будет снята блокировка доступа для всех приложений и сервисов с разрешением на использование камеры или микрофона."</string> <string name="sensor_privacy_start_use_mic_blocked_dialog_title" msgid="2640140287496469689">"Микрофон заблокирован"</string> <string name="sensor_privacy_start_use_camera_blocked_dialog_title" msgid="7398084286822440384">"Камера заблокирована"</string> @@ -727,11 +729,6 @@ <string name="keyboard_key_button_template" msgid="8005673627272051429">"Кнопка <xliff:g id="NAME">%1$s</xliff:g>"</string> <string name="keyboard_key_home" msgid="3734400625170020657">"Главный экран"</string> <string name="keyboard_key_back" msgid="4185420465469481999">"Назад"</string> - <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"Стрелка вверх"</string> - <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"Стрелка вниз"</string> - <string name="keyboard_key_dpad_left" msgid="144176368026538621">"Стрелка влево"</string> - <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"Стрелка вправо"</string> - <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"Центральная стрелка"</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> @@ -932,8 +929,8 @@ <string name="qs_dnd_prompt_auto_rule_app" msgid="1841469944118486580">"Режим \"Не беспокоить\" был включен специальным правилом или приложением."</string> <string name="running_foreground_services_title" msgid="5137313173431186685">"Приложения, работающие в фоновом режиме"</string> <string name="running_foreground_services_msg" msgid="3009459259222695385">"Нажмите, чтобы проверить энергопотребление и трафик"</string> - <string name="mobile_data_disable_title" msgid="5366476131671617790">"Отключить мобильный Интернет?"</string> - <string name="mobile_data_disable_message" msgid="8604966027899770415">"Вы не сможете передавать данные или выходить в Интернет через оператора \"<xliff:g id="CARRIER">%s</xliff:g>\". Интернет будет доступен только по сети Wi-Fi."</string> + <string name="mobile_data_disable_title" msgid="5366476131671617790">"Отключить мобильный интернет?"</string> + <string name="mobile_data_disable_message" msgid="8604966027899770415">"Вы не сможете передавать данные или выходить в интернет через оператора \"<xliff:g id="CARRIER">%s</xliff:g>\". Интернет будет доступен только по сети Wi-Fi."</string> <string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"ваш оператор"</string> <string name="auto_data_switch_disable_title" msgid="5146527155665190652">"Переключиться на сеть \"<xliff:g id="CARRIER">%s</xliff:g>\"?"</string> <string name="auto_data_switch_disable_message" msgid="5885533647399535852">"Мобильный интернет не будет переключаться автоматически."</string> @@ -1275,7 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"устройство сложено"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"устройство разложено"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s/%2$s"</string> - <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"Уровень заряда батареи: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Батарея стилуса: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Поставьте стилус на зарядку."</string> <string name="stylus_battery_low" msgid="7134370101603167096">"Низкий заряд батареи стилуса"</string> <string name="video_camera" msgid="7654002575156149298">"Видеокамера"</string> @@ -1318,6 +1315,16 @@ <string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"Недавно использовалось приложением \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string> <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>)"</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>)"</string> + <string name="shortcut_helper_category_system" msgid="462110876978937359">"Система"</string> + <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Многозадачность"</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_a11y" msgid="6314444792641773464">"Специальные возможности"</string> + <string name="shortcut_helper_title" msgid="8567500639300970049">"Быстрые клавиши"</string> + <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Найти быстрые клавиши"</string> + <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Значок \"Свернуть\""</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="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> diff --git a/packages/SystemUI/res/values-si/strings.xml b/packages/SystemUI/res/values-si/strings.xml index 3de42252dfee..5d41eed4cbdd 100644 --- a/packages/SystemUI/res/values-si/strings.xml +++ b/packages/SystemUI/res/values-si/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"හෙට ස්වයංක්රීයව නැවත ක්රියාත්මක කරන්න"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"ඉක්මන් බෙදා ගැනීම සහ මගේ උපාංගය සෙවීම වැනි විශේෂාංග බ්ලූටූත් භාවිත කරයි"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"බ්ලූටූත් හෙට උදේ සක්රීය වෙයි"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"ශ්රව්ය බෙදා ගැනීම"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"ශ්රව්යය බෙදා ගැනීම"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"බැටරිය <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"ශ්රව්ය"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"හෙඩ්සෙටය"</string> @@ -361,19 +363,19 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"ඔබේ උපාංග අත්දැකීමේ කුමන කොටසට බලපෑවේ ද?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"ගැටලු වර්ගය තෝරන්න"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"තිර පටිගත කිරීම"</string> - <string-array name="qs_record_issue_types"> - <item msgid="2947988124014085798">"කාර්ය සාධනය"</item> - <item msgid="1627504621139124393">"පරිශීලක අතුරු මුහුණත"</item> - <item msgid="8309220355268900335">"බැටරිය"</item> - </string-array> + <string name="performance" msgid="6552785217174378320">"කාර්ය සාධනය"</string> + <string name="user_interface" msgid="3712869377953950887">"පරිශීලක අතුරු මුහුණත"</string> + <string name="thermal" msgid="6758074791325414831">"තාප"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"තනි අත් ප්රකාරය"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"ශ්රවණ උපාංග"</string> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"ක්රියාකාරී"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"විසන්ධි විය"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"ශ්රවණ උපාංග"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"නව උපාංගය යුගල කරන්න"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"නව උපාංගය යුගල කිරීමට ක්ලික් කරන්න"</string> <string name="hearing_devices_presets_error" msgid="350363093458408536">"පෙර සැකසීම යාවත්කාලීන කළ නොහැකි විය"</string> - <!-- no translation found for hearing_devices_preset_label (7878267405046232358) --> - <skip /> + <string name="hearing_devices_preset_label" msgid="7878267405046232358">"පෙරසැකසුම"</string> + <string name="live_caption_title" msgid="8916875614623730005">"සජීවී සිරස්තල"</string> <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> @@ -727,11 +729,6 @@ <string name="keyboard_key_button_template" msgid="8005673627272051429">"<xliff:g id="NAME">%1$s</xliff:g> බොත්තම"</string> <string name="keyboard_key_home" msgid="3734400625170020657">"Home යතුර"</string> <string name="keyboard_key_back" msgid="4185420465469481999">"ආපසු"</string> - <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"ඉහළ ඊතලය"</string> - <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"පහළ ඊතලය"</string> - <string name="keyboard_key_dpad_left" msgid="144176368026538621">"වම් ඊතලය"</string> - <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"දකුණු ඊතලය"</string> - <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"මැද"</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">"Enter යතුර"</string> @@ -1275,7 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"නැවූ"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"නොනැවූ"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string> - <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"<xliff:g id="PERCENTAGE">%s</xliff:g> බැටරිය ඉතිරිව ඇත"</string> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"පන්හිඳ බැටරිය <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"ඔබේ පන්හිඳ චාජරයකට සම්බන්ධ කරන්න"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"පන්හිඳ බැටරිය අඩුයි"</string> <string name="video_camera" msgid="7654002575156149298">"වීඩියෝ කැමරාව"</string> @@ -1318,6 +1315,16 @@ <string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"<xliff:g id="APP_NAME">%1$s</xliff:g> විසින් මෑතකදී භාවිත කරන ලදි (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string> <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>)"</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>)"</string> + <string name="shortcut_helper_category_system" msgid="462110876978937359">"පද්ධතිය"</string> + <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"බහුකාර්ය"</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_a11y" msgid="6314444792641773464">"ප්රවේශ්යතාව"</string> + <string name="shortcut_helper_title" msgid="8567500639300970049">"යතුරු පුවරු කෙටි මං"</string> + <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"කෙටි මං සොයන්න"</string> + <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"හැකුළුම් නිරූපකය"</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="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> diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml index cb8cf5beb172..eef2a01aa55c 100644 --- a/packages/SystemUI/res/values-sk/strings.xml +++ b/packages/SystemUI/res/values-sk/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Automaticky zajtra znova zapnúť"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Funkcie ako Quick Share a Nájdi moje zariadenie používajú Bluetooth"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth sa zapne zajtra ráno"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Zdieľanie zvuku"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Zdieľa sa zvuk"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Batéria: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Zvuk"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Náhlavná súprava"</string> @@ -361,19 +363,19 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Čo v zariadení bolo ovplyvnené?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Vyberte typ problému"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Rekordér obrazovky"</string> - <string-array name="qs_record_issue_types"> - <item msgid="2947988124014085798">"Výkon"</item> - <item msgid="1627504621139124393">"Používateľské rozhranie"</item> - <item msgid="8309220355268900335">"Batéria"</item> - </string-array> + <string name="performance" msgid="6552785217174378320">"Výkon"</string> + <string name="user_interface" msgid="3712869377953950887">"Používateľské rozhranie"</string> + <string name="thermal" msgid="6758074791325414831">"Termálne"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Režim jednej ruky"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Načúvacie zariadenia"</string> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Aktívne"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Odpojené"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Načúvacie zariadenia"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Spárovať nové zariadenie"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Kliknutím spárujete nové zariadenie"</string> <string name="hearing_devices_presets_error" msgid="350363093458408536">"Predvoľbu sa nepodarilo aktualizovať"</string> - <!-- no translation found for hearing_devices_preset_label (7878267405046232358) --> - <skip /> + <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Predvoľba"</string> + <string name="live_caption_title" msgid="8916875614623730005">"Živý prepis"</string> <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> @@ -727,11 +729,6 @@ <string name="keyboard_key_button_template" msgid="8005673627272051429">"Tlačidlo <xliff:g id="NAME">%1$s</xliff:g>"</string> <string name="keyboard_key_home" msgid="3734400625170020657">"Domov"</string> <string name="keyboard_key_back" msgid="4185420465469481999">"Späť"</string> - <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"Šípka nahor"</string> - <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"Šípka nadol"</string> - <string name="keyboard_key_dpad_left" msgid="144176368026538621">"Šípka doľava"</string> - <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"Šípka doprava"</string> - <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"Do stredu"</string> <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string> <string name="keyboard_key_space" msgid="6980847564173394012">"Medzerník"</string> <string name="keyboard_key_enter" msgid="8633362970109751646">"Enter"</string> @@ -1275,7 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"zložené"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"rozložené"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s/%2$s"</string> - <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"Zostáva <xliff:g id="PERCENTAGE">%s</xliff:g> batérie"</string> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Batéria dotykového pera: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Pripojte dotykové pero k nabíjačke"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"Stav batérie dotykového pera je nízky"</string> <string name="video_camera" msgid="7654002575156149298">"Videokamera"</string> @@ -1318,8 +1315,18 @@ <string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"Nedávno využila aplikácia <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string> <string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"Využíva <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>)"</string> <string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"Nedávno využila aplikácia <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>)"</string> + <string name="shortcut_helper_category_system" msgid="462110876978937359">"Systém"</string> + <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Multitasking"</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_a11y" msgid="6314444792641773464">"Dostupnosť"</string> + <string name="shortcut_helper_title" msgid="8567500639300970049">"Klávesové skratky"</string> + <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Vyhľadávacie odkazy"</string> + <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ikona zbalenia"</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="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Podsvietenie klávesnice"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"%1$d. úroveň z %2$d"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"Ovládanie domácnosti"</string> - <string name="home_controls_dream_description" msgid="4644150952104035789">"Rýchlejšie ovládanie domácnosti v šetriči obrazov."</string> + <string name="home_controls_dream_description" msgid="4644150952104035789">"Rýchly prístup k ovládaniu domácnosti z šetriča obrazovky"</string> </resources> diff --git a/packages/SystemUI/res/values-sl/strings.xml b/packages/SystemUI/res/values-sl/strings.xml index a3e4487898fd..facf72bfcda9 100644 --- a/packages/SystemUI/res/values-sl/strings.xml +++ b/packages/SystemUI/res/values-sl/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Samodejno znova vklopi jutri"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Funkcije, kot sta Hitro deljenje in Poišči mojo napravo, uporabljajo Bluetooth"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth se bo vklopil jutri zjutraj"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Deljenje zvoka"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Poteka deljenje zvoka"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Baterija na <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Zvok"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Slušalke z mikrofonom"</string> @@ -361,19 +363,19 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Na kateri del izkušnje z napravo je to vplivalo?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Izberite vrsto težave"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Snemanje zaslona"</string> - <string-array name="qs_record_issue_types"> - <item msgid="2947988124014085798">"Učinkovitost delovanja"</item> - <item msgid="1627504621139124393">"Uporabniški vmesnik"</item> - <item msgid="8309220355268900335">"Baterija"</item> - </string-array> + <string name="performance" msgid="6552785217174378320">"Učinkovitost delovanja"</string> + <string name="user_interface" msgid="3712869377953950887">"Uporabniški vmesnik"</string> + <string name="thermal" msgid="6758074791325414831">"Toplotno"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Enoročni način"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Slušni pripomočki"</string> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Aktivno"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Brez povezave"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Slušni pripomočki"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Seznanitev nove naprave"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Kliknite za seznanitev nove naprave"</string> <string name="hearing_devices_presets_error" msgid="350363093458408536">"Prednastavljenih vrednosti ni bilo mogoče posodobiti"</string> - <!-- no translation found for hearing_devices_preset_label (7878267405046232358) --> - <skip /> + <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Prednastavljeno"</string> + <string name="live_caption_title" msgid="8916875614623730005">"Samodejni podnapisi"</string> <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> @@ -727,11 +729,6 @@ <string name="keyboard_key_button_template" msgid="8005673627272051429">"Gumb <xliff:g id="NAME">%1$s</xliff:g>"</string> <string name="keyboard_key_home" msgid="3734400625170020657">"Začetek"</string> <string name="keyboard_key_back" msgid="4185420465469481999">"Nazaj"</string> - <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"Puščica gor"</string> - <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"Puščica dol"</string> - <string name="keyboard_key_dpad_left" msgid="144176368026538621">"Puščica levo"</string> - <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"Puščica desno"</string> - <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"Sredina"</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> @@ -1275,7 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"zaprto"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"razprto"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s/%2$s"</string> - <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"Preostanek energije baterije: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Napolnjenost baterije pisala: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Povežite pisalo s polnilnikom."</string> <string name="stylus_battery_low" msgid="7134370101603167096">"Skoraj prazna baterija pisala"</string> <string name="video_camera" msgid="7654002575156149298">"Videokamera"</string> @@ -1318,6 +1315,16 @@ <string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"Nedavno uporabljala aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string> <string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"Uporablja aplikacija <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>)"</string> <string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"Nedavno uporabljala aplikacija <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>)"</string> + <string name="shortcut_helper_category_system" msgid="462110876978937359">"Sistem"</string> + <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Večopravilnost"</string> + <string name="shortcut_helper_category_input" msgid="8674018654124839566">"Vnos"</string> + <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Bližnjice do aplikacij"</string> + <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Dostopnost"</string> + <string name="shortcut_helper_title" msgid="8567500639300970049">"Bližnjične tipke"</string> + <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Bližnjice za iskanje"</string> + <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ikona za strnitev"</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="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Osvetlitev tipkovnice"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"Stopnja %1$d od %2$d"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"Kontrolniki za dom"</string> diff --git a/packages/SystemUI/res/values-sq/strings.xml b/packages/SystemUI/res/values-sq/strings.xml index b06890d92e3d..533363240fa5 100644 --- a/packages/SystemUI/res/values-sq/strings.xml +++ b/packages/SystemUI/res/values-sq/strings.xml @@ -76,8 +76,7 @@ <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"dërgoi një imazh"</string> <string name="screenshot_saving_title" msgid="2298349784913287333">"Po ruan pamjen e ekranit…"</string> <string name="screenshot_saving_work_profile_title" msgid="5332829607308450880">"Pamja e ekranit po ruhet te profili i punës…"</string> - <!-- no translation found for screenshot_saving_private_profile (8934706048497093297) --> - <skip /> + <string name="screenshot_saving_private_profile" msgid="8934706048497093297">"Pamja e ekranit po ruhet te profili privat"</string> <string name="screenshot_saved_title" msgid="8893267638659083153">"Pamja e ekranit u ruajt"</string> <string name="screenshot_failed_title" msgid="3259148215671936891">"Pamja e ekranit nuk mund të ruhej"</string> <string name="screenshot_failed_external_display_indication" msgid="6555673132061101936">"Ekrani i jashtëm"</string> @@ -282,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Aktivizoje automatikisht sërish nesër"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Veçoritë e tilla si \"Ndarja e shpejtë\" dhe \"Gjej pajisjen time\" përdorin Bluetooth-in"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth-i do të aktivizohet nesër në mëngjes"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Ndarja e audios"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Audioja po ndahet"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> bateri"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Kufje me mikrofon"</string> @@ -362,19 +363,21 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Cila pjesë e përvojës me pajisjen është prekur?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Zgjidh llojin e problemit"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Regjistrim i ekranit"</string> - <string-array name="qs_record_issue_types"> - <item msgid="2947988124014085798">"Performanca"</item> - <item msgid="1627504621139124393">"Ndërfaqja e përdoruesit"</item> - <item msgid="8309220355268900335">"Bateria"</item> - </string-array> + <string name="performance" msgid="6552785217174378320">"Performanca"</string> + <string name="user_interface" msgid="3712869377953950887">"Ndërfaqja e përdoruesit"</string> + <string name="thermal" msgid="6758074791325414831">"Termike"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Modaliteti i përdorimit me një dorë"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Pajisje ndihmëse për dëgjimin"</string> + <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) --> + <skip /> + <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) --> + <skip /> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Pajisjet e dëgjimit"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Çifto pajisje të re"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Kliko për të çiftuar një pajisje të re"</string> <string name="hearing_devices_presets_error" msgid="350363093458408536">"Paravendosja nuk mund të përditësohej"</string> - <!-- no translation found for hearing_devices_preset_label (7878267405046232358) --> - <skip /> + <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Paravendosja"</string> + <string name="live_caption_title" msgid="8916875614623730005">"Titra në çast"</string> <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> @@ -595,9 +598,9 @@ <string name="screen_pinning_negative" msgid="6882816864569211666">"Jo, faleminderit!"</string> <string name="screen_pinning_start" msgid="7483998671383371313">"Aplikacioni u gozhdua"</string> <string name="screen_pinning_exit" msgid="4553787518387346893">"Aplikacioni u zhgozhdua"</string> - <string name="stream_voice_call" msgid="7468348170702375660">"Telefono"</string> + <string name="stream_voice_call" msgid="7468348170702375660">"Telefonata"</string> <string name="stream_system" msgid="7663148785370565134">"Sistemi"</string> - <string name="stream_ring" msgid="7550670036738697526">"Bjeri ziles"</string> + <string name="stream_ring" msgid="7550670036738697526">"Zilja"</string> <string name="stream_music" msgid="2188224742361847580">"Media"</string> <string name="stream_alarm" msgid="16058075093011694">"Alarmi"</string> <string name="stream_notification" msgid="7930294049046243939">"Njoftimi"</string> @@ -728,11 +731,6 @@ <string name="keyboard_key_button_template" msgid="8005673627272051429">"Butoni <xliff:g id="NAME">%1$s</xliff:g>"</string> <string name="keyboard_key_home" msgid="3734400625170020657">"Kreu"</string> <string name="keyboard_key_back" msgid="4185420465469481999">"Prapa"</string> - <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"Shigjeta lart"</string> - <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"Shigjeta poshtë"</string> - <string name="keyboard_key_dpad_left" msgid="144176368026538621">"Shigjeta majtas"</string> - <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"Shigjeta djathtas"</string> - <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"Qendror"</string> <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string> <string name="keyboard_key_space" msgid="6980847564173394012">"Hapësirë"</string> <string name="keyboard_key_enter" msgid="8633362970109751646">"Enter"</string> @@ -1276,7 +1274,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"palosur"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"shpalosur"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string> - <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"Përqindja e mbetur e baterisë: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Bateria e stilolapsit: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Lidhe stilolapsin me një karikues"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"Bateria e stilolapsit në nivel të ulët"</string> <string name="video_camera" msgid="7654002575156149298">"Videokamera"</string> @@ -1319,6 +1317,16 @@ <string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"Përdorur së fundi nga <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string> <string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"Në përdorim nga <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>)"</string> <string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"Përdorur së fundi nga <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>)"</string> + <string name="shortcut_helper_category_system" msgid="462110876978937359">"Sistemi"</string> + <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Kryerja e shumë detyrave"</string> + <string name="shortcut_helper_category_input" msgid="8674018654124839566">"Hyrja"</string> + <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Shkurtoret e aplikacionit"</string> + <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Qasshmëria"</string> + <string name="shortcut_helper_title" msgid="8567500639300970049">"Shkurtoret e tastierës"</string> + <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Kërko për shkurtoret"</string> + <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ikona e palosjes"</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="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Drita e sfondit e tastierës"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"Niveli: %1$d nga %2$d"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"Kontrollet e shtëpisë"</string> diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml index 2b851b4b522b..f35d7d86085c 100644 --- a/packages/SystemUI/res/values-sr/strings.xml +++ b/packages/SystemUI/res/values-sr/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Аутоматски поново укључи сутра"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Функције као што су Quick Share и Пронађи мој уређај користе Bluetooth"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth ће се укључити сутра ујутру"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Дељење звука"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Дели се звук"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Ниво батерије је <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Аудио"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Слушалице"</string> @@ -361,19 +363,19 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"На који део доживљаја на уређају је ово утицало?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Изаберите тип проблема"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Снимање екрана"</string> - <string-array name="qs_record_issue_types"> - <item msgid="2947988124014085798">"Учинак"</item> - <item msgid="1627504621139124393">"Кориснички интерфејс"</item> - <item msgid="8309220355268900335">"Батерија"</item> - </string-array> + <string name="performance" msgid="6552785217174378320">"Перформансе"</string> + <string name="user_interface" msgid="3712869377953950887">"Кориснички интерфејс"</string> + <string name="thermal" msgid="6758074791325414831">"Термална камера"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Режим једном руком"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Слушни апарати"</string> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Активно"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Веза је прекинута"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Слушни апарати"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Упари нови уређај"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Кликните да бисте упарили нов уређај"</string> <string name="hearing_devices_presets_error" msgid="350363093458408536">"Ажурирање задатих подешавања није успело"</string> - <!-- no translation found for hearing_devices_preset_label (7878267405046232358) --> - <skip /> + <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Унапред одређена подешавања"</string> + <string name="live_caption_title" msgid="8916875614623730005">"Титл уживо"</string> <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> @@ -727,11 +729,6 @@ <string name="keyboard_key_button_template" msgid="8005673627272051429">"Дугме <xliff:g id="NAME">%1$s</xliff:g>"</string> <string name="keyboard_key_home" msgid="3734400625170020657">"Тастер Почетна"</string> <string name="keyboard_key_back" msgid="4185420465469481999">"Тастер Назад"</string> - <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"Стрелица нагоре"</string> - <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"Стрелица надоле"</string> - <string name="keyboard_key_dpad_left" msgid="144176368026538621">"Стрелица налево"</string> - <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"Стрелица надесно"</string> - <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"Тастер са централном стрелицом"</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">"Enter"</string> @@ -1275,7 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"затворено"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"отворено"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s/%2$s"</string> - <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"Преостало је још<xliff:g id="PERCENTAGE">%s</xliff:g> батерије"</string> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Батерија писаљке је на <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Повежите писаљку са пуњачем"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"Низак ниво батерије писаљке"</string> <string name="video_camera" msgid="7654002575156149298">"Видео камера"</string> @@ -1318,6 +1315,16 @@ <string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"Недавно користила апликација <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string> <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>)"</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>)"</string> + <string name="shortcut_helper_category_system" msgid="462110876978937359">"Систем"</string> + <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Обављање више задатака истовремено"</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_a11y" msgid="6314444792641773464">"Приступачност"</string> + <string name="shortcut_helper_title" msgid="8567500639300970049">"Тастерске пречице"</string> + <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Пречице претраге"</string> + <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Икона за скупљање"</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="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> diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml index 0d1614fa66bd..535a4aa6c162 100644 --- a/packages/SystemUI/res/values-sv/strings.xml +++ b/packages/SystemUI/res/values-sv/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Aktivera automatiskt igen i morgon"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Funktioner som Snabbdelning och Hitta min enhet använder Bluetooth"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth aktiveras i morgon bitti"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Ljuddelning"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Delar ljud"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> batteri"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Ljud"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Headset"</string> @@ -361,19 +363,19 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Vilken enhetsupplevelse påverkades?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Välj problemtyp"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Skärminspelning"</string> - <string-array name="qs_record_issue_types"> - <item msgid="2947988124014085798">"Prestanda"</item> - <item msgid="1627504621139124393">"Användargränssnitt"</item> - <item msgid="8309220355268900335">"Batteri"</item> - </string-array> + <string name="performance" msgid="6552785217174378320">"Prestanda"</string> + <string name="user_interface" msgid="3712869377953950887">"Användargränssnitt"</string> + <string name="thermal" msgid="6758074791325414831">"Termisk"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Enhandsläge"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Hörhjälpmedel"</string> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Aktiva"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Frånkopplade"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Hörhjälpmedel"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Parkoppla en ny enhet"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Klicka för att parkoppla en ny enhet"</string> <string name="hearing_devices_presets_error" msgid="350363093458408536">"Det gick inte att uppdatera förinställningen"</string> - <!-- no translation found for hearing_devices_preset_label (7878267405046232358) --> - <skip /> + <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Förinställning"</string> + <string name="live_caption_title" msgid="8916875614623730005">"Live Caption"</string> <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> @@ -727,11 +729,6 @@ <string name="keyboard_key_button_template" msgid="8005673627272051429">"Knappen <xliff:g id="NAME">%1$s</xliff:g>"</string> <string name="keyboard_key_home" msgid="3734400625170020657">"Start"</string> <string name="keyboard_key_back" msgid="4185420465469481999">"Tillbaka"</string> - <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"Uppåtpil"</string> - <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"Nedåtpil"</string> - <string name="keyboard_key_dpad_left" msgid="144176368026538621">"Vänsterpil"</string> - <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"Högerpil"</string> - <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"Centrera"</string> <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string> <string name="keyboard_key_space" msgid="6980847564173394012">"Blanksteg"</string> <string name="keyboard_key_enter" msgid="8633362970109751646">"Retur"</string> @@ -1275,7 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"hopvikt"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"uppvikt"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s/%2$s"</string> - <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"<xliff:g id="PERCENTAGE">%s</xliff:g> av batteriet återstår"</string> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"E-pennans batteri: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Anslut e-pennan till en laddare"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"E-pennans batterinivå är låg"</string> <string name="video_camera" msgid="7654002575156149298">"Videokamera"</string> @@ -1318,6 +1315,16 @@ <string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"Användes nyligen av <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string> <string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"Används av <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>)"</string> <string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"Användes nyligen av <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>)"</string> + <string name="shortcut_helper_category_system" msgid="462110876978937359">"System"</string> + <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Multikörning"</string> + <string name="shortcut_helper_category_input" msgid="8674018654124839566">"Ingång"</string> + <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Genvägar till appar"</string> + <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Tillgänglighet"</string> + <string name="shortcut_helper_title" msgid="8567500639300970049">"Kortkommandon"</string> + <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Sökgenvägar"</string> + <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ikonen Komprimera"</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="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Bakgrundsbelysning för tangentbord"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"Nivå %1$d av %2$d"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"Hemstyrning"</string> diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml index 670a85c181f5..98a7e03873ba 100644 --- a/packages/SystemUI/res/values-sw/strings.xml +++ b/packages/SystemUI/res/values-sw/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Iwashe tena kesho kiotomatiki"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Vipengele kama vile Kutuma Haraka na Tafuta Kifaa Changu hutumia Bluetooth"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth itawaka kesho asubuhi"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Kusikiliza Pamoja"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Mnasikiliza Pamoja"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Chaji ya betri ni <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Sauti"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Vifaa vya sauti"</string> @@ -361,19 +363,19 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Ni sehemu gani ya matumizi ya kifaa iliathiriwa?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Chagua aina ya tatizo"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Rekodi ya skrini"</string> - <string-array name="qs_record_issue_types"> - <item msgid="2947988124014085798">"Utendaji"</item> - <item msgid="1627504621139124393">"Kiolesura cha Mtumiaji"</item> - <item msgid="8309220355268900335">"Betri"</item> - </string-array> + <string name="performance" msgid="6552785217174378320">"Utendaji"</string> + <string name="user_interface" msgid="3712869377953950887">"Kiolesura"</string> + <string name="thermal" msgid="6758074791325414831">"Joto"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Hali ya kutumia kwa mkono mmoja"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Vifaa vya kusikilizia"</string> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Vimeunganishwa"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Havijaunganishwa"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Vifaa vya kusikilizia"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Unganisha kifaa kipya"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Bofya ili uunganishe kifaa kipya"</string> <string name="hearing_devices_presets_error" msgid="350363093458408536">"Imeshindwa kusasisha mipangilio iliyowekwa mapema"</string> - <!-- no translation found for hearing_devices_preset_label (7878267405046232358) --> - <skip /> + <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Mipangilio iliyowekwa mapema"</string> + <string name="live_caption_title" msgid="8916875614623730005">"Manukuu Papo Hapo"</string> <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> @@ -727,11 +729,6 @@ <string name="keyboard_key_button_template" msgid="8005673627272051429">"Kitufe cha <xliff:g id="NAME">%1$s</xliff:g>"</string> <string name="keyboard_key_home" msgid="3734400625170020657">"Mwanzo"</string> <string name="keyboard_key_back" msgid="4185420465469481999">"Nyuma"</string> - <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"Kishale cha juu"</string> - <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"Kishale cha chini"</string> - <string name="keyboard_key_dpad_left" msgid="144176368026538621">"Kishale cha kushoto"</string> - <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"Kishale cha kulia"</string> - <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"Katikati"</string> <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> @@ -1275,7 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"kimekunjwa"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"kimefunguliwa"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string> - <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"Chaji ya betri imesalia <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Betri ya stylus <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Unganisha stylus yako kwenye chaja"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"Chaji ya betri ya Stylus imepungua"</string> <string name="video_camera" msgid="7654002575156149298">"Kamera ya video"</string> @@ -1318,6 +1315,16 @@ <string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"Ilitumiwa hivi majuzi na <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string> <string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"Inatumiwa na <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>)"</string> <string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"Ilitumiwa hivi majuzi na <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>)"</string> + <string name="shortcut_helper_category_system" msgid="462110876978937359">"Mfumo"</string> + <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Majukumu mengi"</string> + <string name="shortcut_helper_category_input" msgid="8674018654124839566">"Kifaa cha kuingiza data"</string> + <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Njia za mikato za programu"</string> + <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Ufikivu"</string> + <string name="shortcut_helper_title" msgid="8567500639300970049">"Mikato ya kibodi"</string> + <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Njia mkato za kutafutia"</string> + <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Kunja aikoni"</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="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Mwanga chini ya kibodi"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"Kiwango cha %1$d kati ya %2$d"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"Dhibiti Vifaa Nyumbani"</string> diff --git a/packages/SystemUI/res/values-sw600dp/dimens.xml b/packages/SystemUI/res/values-sw600dp/dimens.xml index 29e0dbea24f2..27af33438c84 100644 --- a/packages/SystemUI/res/values-sw600dp/dimens.xml +++ b/packages/SystemUI/res/values-sw600dp/dimens.xml @@ -56,7 +56,7 @@ <dimen name="navigation_key_padding">25dp</dimen> <!-- width of ImmersiveModeConfirmation (-1 for match_parent) --> - <dimen name="immersive_mode_cling_width">380dp</dimen> + <dimen name="immersive_mode_cling_width">600dp</dimen> <!-- Keyboard shortcuts helper --> <dimen name="ksh_layout_width">488dp</dimen> diff --git a/packages/SystemUI/res/values-ta/strings.xml b/packages/SystemUI/res/values-ta/strings.xml index 224ed3989819..896b55f3ac41 100644 --- a/packages/SystemUI/res/values-ta/strings.xml +++ b/packages/SystemUI/res/values-ta/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"நாளைக்குத் தானாகவே மீண்டும் இயக்கப்படுதல்"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"விரைவுப் பகிர்தல், Find My Device போன்ற அம்சங்கள் புளூடூத்தைப் பயன்படுத்துகின்றன"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"நாளை காலை புளூடூத் இயக்கப்படும்"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"ஆடியோ பகிர்வு"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"ஆடியோ பகிரப்படுகிறது"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> பேட்டரி"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"ஆடியோ"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"ஹெட்செட்"</string> @@ -305,7 +307,7 @@ <string name="quick_settings_networks_unavailable" msgid="1167847013337940082">"நெட்வொர்க்குகள் கிடைக்கவில்லை"</string> <string name="quick_settings_wifi_detail_empty_text" msgid="483130889414601732">"வைஃபை நெட்வொர்க்குகள் இல்லை"</string> <string name="quick_settings_wifi_secondary_label_transient" msgid="7501659015509357887">"ஆன் செய்கிறது…"</string> - <string name="quick_settings_cast_title" msgid="3033553249449938182">"Cast"</string> + <string name="quick_settings_cast_title" msgid="3033553249449938182">"அலைபரப்பு"</string> <string name="quick_settings_casting" msgid="1435880708719268055">"அனுப்புகிறது"</string> <string name="quick_settings_cast_device_default_name" msgid="6988469571141331700">"பெயரிடப்படாத சாதனம்"</string> <string name="quick_settings_cast_detail_empty_text" msgid="2846282280014617785">"சாதனங்கள் இல்லை"</string> @@ -361,19 +363,19 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"சாதன அனுபவத்தின் எந்தப் பகுதி பாதிக்கப்பட்டது?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"சிக்கல் வகையைத் தேர்வுசெய்க"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"ஸ்கிரீன் ரெக்கார்டு"</string> - <string-array name="qs_record_issue_types"> - <item msgid="2947988124014085798">"செயல்திறன்"</item> - <item msgid="1627504621139124393">"பயனர் இடைமுகம்"</item> - <item msgid="8309220355268900335">"பேட்டரி"</item> - </string-array> + <string name="performance" msgid="6552785217174378320">"செயல்திறன்"</string> + <string name="user_interface" msgid="3712869377953950887">"பயனர் இடைமுகம்"</string> + <string name="thermal" msgid="6758074791325414831">"தெர்மல்"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"ஒற்றைக் கைப் பயன்முறை"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"செவித்துணைக் கருவிகள்"</string> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"செயலில் உள்ளது"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"இணைக்கப்படவில்லை"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"செவித்துணைக் கருவிகள்"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"புதிய சாதனத்தை இணை"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"புதிய சாதனத்தை இணைக்க கிளிக் செய்யலாம்"</string> <string name="hearing_devices_presets_error" msgid="350363093458408536">"முன்னமைவைப் புதுப்பிக்க முடியவில்லை"</string> - <!-- no translation found for hearing_devices_preset_label (7878267405046232358) --> - <skip /> + <string name="hearing_devices_preset_label" msgid="7878267405046232358">"முன்னமைவு"</string> + <string name="live_caption_title" msgid="8916875614623730005">"உடனடி வசன உரை"</string> <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> @@ -727,11 +729,6 @@ <string name="keyboard_key_button_template" msgid="8005673627272051429">"<xliff:g id="NAME">%1$s</xliff:g> பட்டன்"</string> <string name="keyboard_key_home" msgid="3734400625170020657">"ஹோம்"</string> <string name="keyboard_key_back" msgid="4185420465469481999">"பேக்"</string> - <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"மேல்நோக்கிய அம்புக்குறி"</string> - <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"கீழ்நோக்கிய அம்புக்குறி"</string> - <string name="keyboard_key_dpad_left" msgid="144176368026538621">"இடது அம்புக்குறி"</string> - <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"வலது அம்புக்குறி"</string> - <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"நடு"</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> @@ -1275,7 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"மடக்கப்பட்டது"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"விரிக்கப்பட்டது"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string> - <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"<xliff:g id="PERCENTAGE">%s</xliff:g> பேட்டரி மீதமுள்ளது"</string> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"ஸ்டைலஸ் பேட்டரி <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"உங்கள் ஸ்டைலஸைச் சார்ஜருடன் இணையுங்கள்"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"ஸ்டைலஸின் பேட்டரி குறைவாக உள்ளது"</string> <string name="video_camera" msgid="7654002575156149298">"வீடியோ கேமரா"</string> @@ -1318,6 +1315,16 @@ <string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"<xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>) ஆப்ஸால் சமீபத்தில் பயன்படுத்தப்பட்டது"</string> <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>) ஆப்ஸால் பயன்படுத்தப்படுகிறது"</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>) ஆப்ஸால் சமீபத்தில் பயன்படுத்தப்பட்டது"</string> + <string name="shortcut_helper_category_system" msgid="462110876978937359">"சிஸ்டம்"</string> + <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"பல வேலைகளைச் செய்தல்"</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_a11y" msgid="6314444792641773464">"அணுகல்தன்மை"</string> + <string name="shortcut_helper_title" msgid="8567500639300970049">"கீபோர்டு ஷார்ட்கட்கள்"</string> + <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"தேடல் ஷார்ட்கட்கள்"</string> + <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"சுருக்குவதற்கான ஐகான்"</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="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> diff --git a/packages/SystemUI/res/values-te/strings.xml b/packages/SystemUI/res/values-te/strings.xml index 26bb871c7deb..b88386ff05c4 100644 --- a/packages/SystemUI/res/values-te/strings.xml +++ b/packages/SystemUI/res/values-te/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"రేపు మళ్లీ ఆటోమేటిక్గా ఆన్ చేస్తుంది"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"క్విక్ షేర్, Find My Device వంటి ఫీచర్లు బ్లూటూత్ను ఉపయోగిస్తాయి"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"బ్లూటూత్ రేపు ఉదయం ఆన్ అవుతుంది"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"ఆడియో షేరింగ్"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"ఆడియోను షేర్ చేస్తున్నారు"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> బ్యాటరీ"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"ఆడియో"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"హెడ్సెట్"</string> @@ -361,19 +363,19 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"మీ పరికర అనుభవంలో ఏ భాగం ప్రభావితమైంది?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"సమస్య రకాన్ని ఎంచుకోండి"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"స్క్రీన్ రికార్డ్"</string> - <string-array name="qs_record_issue_types"> - <item msgid="2947988124014085798">"పనితీరు"</item> - <item msgid="1627504621139124393">"యూజర్ ఇంటర్ఫేస్"</item> - <item msgid="8309220355268900335">"బ్యాటరీ"</item> - </string-array> + <string name="performance" msgid="6552785217174378320">"పనితీరు"</string> + <string name="user_interface" msgid="3712869377953950887">"యూజర్ ఇంటర్ఫేస్"</string> + <string name="thermal" msgid="6758074791325414831">"థర్మల్"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"వన్-హ్యాండెడ్ మోడ్"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"వినికిడి పరికరాలు"</string> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"యాక్టివ్"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"డిస్కనెక్ట్ అయ్యింది"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"వినికిడి పరికరాలు"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"కొత్త పరికరాన్ని పెయిర్ చేయండి"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"కొత్త పరికరాన్ని పెయిర్ చేయడానికి క్లిక్ చేయండి"</string> <string name="hearing_devices_presets_error" msgid="350363093458408536">"ప్రీసెట్ను అప్డేట్ చేయడం సాధ్యపడలేదు"</string> - <!-- no translation found for hearing_devices_preset_label (7878267405046232358) --> - <skip /> + <string name="hearing_devices_preset_label" msgid="7878267405046232358">"ప్రీసెట్"</string> + <string name="live_caption_title" msgid="8916875614623730005">"లైవ్ క్యాప్షన్"</string> <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> @@ -727,11 +729,6 @@ <string name="keyboard_key_button_template" msgid="8005673627272051429">"బటన్ <xliff:g id="NAME">%1$s</xliff:g>"</string> <string name="keyboard_key_home" msgid="3734400625170020657">"Home"</string> <string name="keyboard_key_back" msgid="4185420465469481999">"వెనుకకు"</string> - <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"పై వైపు బాణం"</string> - <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"కింది వైపు బాణం"</string> - <string name="keyboard_key_dpad_left" msgid="144176368026538621">"ఎడమ వైపు బాణం"</string> - <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"కుడి వైపు బాణం"</string> - <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"మధ్య"</string> <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> @@ -988,7 +985,7 @@ <string name="magnification_close_settings_click_label" msgid="4642477260651704517">"మాగ్నిఫికేషన్ సెట్టింగ్లను మూసివేయండి"</string> <string name="magnification_exit_edit_mode_click_label" msgid="1664818325144887117">"సవరణ మోడ్ నుండి ఎగ్జిట్ అవ్వండి"</string> <string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"సైజ్ మార్చడానికి మూలను లాగండి"</string> - <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"డయాగనల్ స్క్రోలింగ్ను అనుమతించండి"</string> + <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"డయాగోనల్ స్క్రోలింగ్ను అనుమతించండి"</string> <string name="accessibility_resize" msgid="5733759136600611551">"సైజ్ మార్చండి"</string> <string name="accessibility_change_magnification_type" msgid="666000085077432421">"మ్యాగ్నిఫికేషన్ రకాన్ని మార్చండి"</string> <string name="accessibility_magnification_end_resizing" msgid="4881690585800302628">"సైజ్ను మార్చడం ముగించండి"</string> @@ -1275,7 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"మడిచే సదుపాయం గల పరికరం"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"మడిచే సదుపాయం లేని పరికరం"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string> - <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"<xliff:g id="PERCENTAGE">%s</xliff:g> బ్యాటరీ మిగిలి ఉంది"</string> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"స్టయిలస్ బ్యాటరీ <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"మీ స్టైలస్ను ఛార్జర్కి కనెక్ట్ చేయండి"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"తక్కువ స్టైలస్ బ్యాటరీ"</string> <string name="video_camera" msgid="7654002575156149298">"వీడియో కెమెరా"</string> @@ -1318,6 +1315,16 @@ <string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"<xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>) ద్వారా ఇటీవల వినియోగించబడింది"</string> <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>) ద్వారా వినియోగంలో ఉంది"</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>) ద్వారా ఇటీవల ఉపయోగించబడింది"</string> + <string name="shortcut_helper_category_system" msgid="462110876978937359">"సిస్టమ్"</string> + <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"మల్టీ-టాస్కింగ్"</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_a11y" msgid="6314444792641773464">"యాక్సెసిబిలిటీ"</string> + <string name="shortcut_helper_title" msgid="8567500639300970049">"కీబోర్డ్ షార్ట్కట్లు"</string> + <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"సెర్చ్ షార్ట్కట్లు"</string> + <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"కుదించండి చిహ్నం"</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="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> diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml index 02ee0175f0e7..12c676ee482f 100644 --- a/packages/SystemUI/res/values-th/strings.xml +++ b/packages/SystemUI/res/values-th/strings.xml @@ -279,10 +279,12 @@ <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"ยกเลิกการเชื่อมต่อ"</string> <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"เปิดใช้งาน"</string> <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"เปิดอีกครั้งโดยอัตโนมัติในวันพรุ่งนี้"</string> - <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"ฟีเจอร์ต่างๆ เช่น Quick Share และหาอุปกรณ์ของฉัน ใช้บลูทูธ"</string> + <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"ฟีเจอร์ต่างๆ เช่น Quick Share และ \"หาอุปกรณ์ของฉัน\" ใช้บลูทูธ"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"บลูทูธจะเปิดพรุ่งนี้เช้า"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"การแชร์เสียง"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"กำลังแชร์เสียง"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"แบตเตอรี่ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"เสียง"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"ชุดหูฟัง"</string> @@ -361,19 +363,19 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"ประสบการณ์การใช้งานอุปกรณ์ส่วนใดที่ได้รับผลกระทบ"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"เลือกประเภทปัญหา"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"บันทึกหน้าจอ"</string> - <string-array name="qs_record_issue_types"> - <item msgid="2947988124014085798">"ประสิทธิภาพ"</item> - <item msgid="1627504621139124393">"อินเทอร์เฟซผู้ใช้"</item> - <item msgid="8309220355268900335">"แบตเตอรี่"</item> - </string-array> + <string name="performance" msgid="6552785217174378320">"ประสิทธิภาพ"</string> + <string name="user_interface" msgid="3712869377953950887">"อินเทอร์เฟซผู้ใช้"</string> + <string name="thermal" msgid="6758074791325414831">"ความร้อน"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"โหมดมือเดียว"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"เครื่องช่วยฟัง"</string> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"ใช้งานอยู่"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"ยกเลิกการเชื่อมต่อแล้ว"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"เครื่องช่วยฟัง"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"จับคู่อุปกรณ์ใหม่"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"คลิกเพื่อจับคู่อุปกรณ์ใหม่"</string> <string name="hearing_devices_presets_error" msgid="350363093458408536">"ไม่สามารถอัปเดตค่าที่กำหนดล่วงหน้า"</string> - <!-- no translation found for hearing_devices_preset_label (7878267405046232358) --> - <skip /> + <string name="hearing_devices_preset_label" msgid="7878267405046232358">"ค่าที่กำหนดล่วงหน้า"</string> + <string name="live_caption_title" msgid="8916875614623730005">"คำบรรยายสด"</string> <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> @@ -727,11 +729,6 @@ <string name="keyboard_key_button_template" msgid="8005673627272051429">"ปุ่ม <xliff:g id="NAME">%1$s</xliff:g>"</string> <string name="keyboard_key_home" msgid="3734400625170020657">"Home"</string> <string name="keyboard_key_back" msgid="4185420465469481999">"กลับ"</string> - <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"ลูกศรขึ้น"</string> - <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"ลูกศรลง"</string> - <string name="keyboard_key_dpad_left" msgid="144176368026538621">"ลูกศรซ้าย"</string> - <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"ลูกศรขวา"</string> - <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"กึ่งกลาง"</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">"Enter"</string> @@ -1275,7 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"พับ"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"กางออก"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string> - <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"เหลือแบตเตอรี่ <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"แบตเตอรี่สไตลัส <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"เชื่อมต่อสไตลัสกับที่ชาร์จ"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"แบตเตอรี่สไตลัสเหลือน้อย"</string> <string name="video_camera" msgid="7654002575156149298">"กล้องวิดีโอ"</string> @@ -1318,6 +1315,16 @@ <string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"ใช้ล่าสุดโดย <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string> <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>)"</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>)"</string> + <string name="shortcut_helper_category_system" msgid="462110876978937359">"ระบบ"</string> + <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"การทํางานหลายอย่างพร้อมกัน"</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_a11y" msgid="6314444792641773464">"การช่วยเหลือพิเศษ"</string> + <string name="shortcut_helper_title" msgid="8567500639300970049">"แป้นพิมพ์ลัด"</string> + <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"ค้นหาแป้นพิมพ์ลัด"</string> + <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"ไอคอนยุบ"</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="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> diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml index aabba456d49c..84a4541f3694 100644 --- a/packages/SystemUI/res/values-tl/strings.xml +++ b/packages/SystemUI/res/values-tl/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Awtomatikong i-on ulit bukas"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Gumagamit ng Bluetooth ang mga feature tulad ng Quick Share at Hanapin ang Aking Device"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Mag-o-on ang Bluetooth bukas ng umaga"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Pag-share ng Audio"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Ibinabahagi ang Audio"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> na baterya"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Headset"</string> @@ -361,19 +363,19 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Ano\'ng naapektuhang parte ng experience sa device?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Piliin ang uri ng isyu"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Pag-record ng screen"</string> - <string-array name="qs_record_issue_types"> - <item msgid="2947988124014085798">"Performance"</item> - <item msgid="1627504621139124393">"User Interface"</item> - <item msgid="8309220355268900335">"Baterya"</item> - </string-array> + <string name="performance" msgid="6552785217174378320">"Performance"</string> + <string name="user_interface" msgid="3712869377953950887">"User Interface"</string> + <string name="thermal" msgid="6758074791325414831">"Thermal"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"One-hand mode"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Mga hearing device"</string> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Aktibo"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Nadiskonekta"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Mga hearing device"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Magpares ng bagong device"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"I-click para magpares ng bagong device"</string> <string name="hearing_devices_presets_error" msgid="350363093458408536">"Hindi ma-update ang preset"</string> - <!-- no translation found for hearing_devices_preset_label (7878267405046232358) --> - <skip /> + <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Preset"</string> + <string name="live_caption_title" msgid="8916875614623730005">"Instant Caption"</string> <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> @@ -727,11 +729,6 @@ <string name="keyboard_key_button_template" msgid="8005673627272051429">"Button na <xliff:g id="NAME">%1$s</xliff:g>"</string> <string name="keyboard_key_home" msgid="3734400625170020657">"Home"</string> <string name="keyboard_key_back" msgid="4185420465469481999">"Back"</string> - <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"Pataas na arrow"</string> - <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"Pababang arrow"</string> - <string name="keyboard_key_dpad_left" msgid="144176368026538621">"Pakaliwang arrow"</string> - <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"Pakanang arrow"</string> - <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"Center"</string> <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> @@ -1275,7 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"naka-fold"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"hindi naka-fold"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string> - <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"<xliff:g id="PERCENTAGE">%s</xliff:g> baterya na lang ang natitira"</string> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Baterya ng stylus <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Ikonekta sa charger ang iyong stylus"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"Paubos na ang baterya ng stylus"</string> <string name="video_camera" msgid="7654002575156149298">"Video camera"</string> @@ -1318,6 +1315,16 @@ <string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"Kamakailang ginamit ng <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string> <string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"Ginagamit ng <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>)"</string> <string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"Kamakailang ginamit ng <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>)"</string> + <string name="shortcut_helper_category_system" msgid="462110876978937359">"System"</string> + <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Pag-multitask"</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_a11y" msgid="6314444792641773464">"Accessibility"</string> + <string name="shortcut_helper_title" msgid="8567500639300970049">"Mga keyboard shortcut"</string> + <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Mga shortcut ng paghahanap"</string> + <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"I-collapse ang icon"</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="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Backlight ng keyboard"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"Level %1$d sa %2$d"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"Mga Home Control"</string> diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml index db84644d7a5e..c2df2c24c61d 100644 --- a/packages/SystemUI/res/values-tr/strings.xml +++ b/packages/SystemUI/res/values-tr/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Yarın otomatik olarak tekrar aç"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Quick Share ve Cihazımı Bul gibi özellikler Bluetooth\'u kullanır"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth yarın sabah açılacak"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Ses Paylaşımı"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Ses paylaşılıyor"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Pil düzeyi <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Ses"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Mikrofonlu kulaklık"</string> @@ -361,19 +363,19 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Cihaz deneyiminiz ne şekilde etkilendi?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Sorun türünü seçin"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Ekran kaydedicisi"</string> - <string-array name="qs_record_issue_types"> - <item msgid="2947988124014085798">"Performans"</item> - <item msgid="1627504621139124393">"Kullanıcı Arayüzü"</item> - <item msgid="8309220355268900335">"Pil"</item> - </string-array> + <string name="performance" msgid="6552785217174378320">"Performans"</string> + <string name="user_interface" msgid="3712869377953950887">"Kullanıcı Arayüzü"</string> + <string name="thermal" msgid="6758074791325414831">"Termal"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Tek el modu"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"İşitme cihazları"</string> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Etkin"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Bağlı değil"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"İşitme cihazları"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Yeni cihaz eşle"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Yeni cihaz eşlemek için tıklayın"</string> <string name="hearing_devices_presets_error" msgid="350363093458408536">"Hazır ayar güncellenemedi"</string> - <!-- no translation found for hearing_devices_preset_label (7878267405046232358) --> - <skip /> + <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Hazır Ayar"</string> + <string name="live_caption_title" msgid="8916875614623730005">"Canlı Altyazı"</string> <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> @@ -727,11 +729,6 @@ <string name="keyboard_key_button_template" msgid="8005673627272051429">"<xliff:g id="NAME">%1$s</xliff:g> düğmesi"</string> <string name="keyboard_key_home" msgid="3734400625170020657">"Home"</string> <string name="keyboard_key_back" msgid="4185420465469481999">"Geri"</string> - <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"Yukarı ok"</string> - <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"Aşağı ok"</string> - <string name="keyboard_key_dpad_left" msgid="144176368026538621">"Sol ok"</string> - <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"Sağ ok"</string> - <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"Orta"</string> <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string> <string name="keyboard_key_space" msgid="6980847564173394012">"Boşluk"</string> <string name="keyboard_key_enter" msgid="8633362970109751646">"Enter"</string> @@ -1275,7 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"katlanmış"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"katlanmamış"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s/%2$s"</string> - <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"<xliff:g id="PERCENTAGE">%s</xliff:g> pil kaldı"</string> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Ekran kalemi pili: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Ekran kaleminizi bir şarj cihazına bağlayın"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"Ekran kaleminin pil seviyesi düşük"</string> <string name="video_camera" msgid="7654002575156149298">"Video kamera"</string> @@ -1318,6 +1315,16 @@ <string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"En son <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>) tarafından kullanıldı"</string> <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>) tarafından kullanılıyor"</string> <string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"En son <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>) tarafından kullanıldı"</string> + <string name="shortcut_helper_category_system" msgid="462110876978937359">"Sistem"</string> + <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Çoklu görev becerisi"</string> + <string name="shortcut_helper_category_input" msgid="8674018654124839566">"Giriş"</string> + <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Uygulama kısayolları"</string> + <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Erişilebilirlik"</string> + <string name="shortcut_helper_title" msgid="8567500639300970049">"Klavye kısayolları"</string> + <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Arama kısayolları"</string> + <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Daralt simgesi"</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="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Klavye aydınlatması"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"Seviye %1$d / %2$d"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"Ev Kontrolleri"</string> diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml index 1afcf2ac9065..5fcfb3ea41c5 100644 --- a/packages/SystemUI/res/values-uk/strings.xml +++ b/packages/SystemUI/res/values-uk/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Автоматично ввімкнути знову завтра"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Такі функції, як швидкий обмін і \"Знайти пристрій\", використовують Bluetooth"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth увімкнеться завтра вранці"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Надсилання аудіо"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Надсилання аудіо"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> заряду акумулятора"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Аудіопристрій"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Гарнітура"</string> @@ -361,19 +363,19 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"На який аспект роботи пристрою вплинула проблема?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Виберіть тип проблеми"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Запис відео з екрана"</string> - <string-array name="qs_record_issue_types"> - <item msgid="2947988124014085798">"Продуктивність"</item> - <item msgid="1627504621139124393">"Інтерфейс користувача"</item> - <item msgid="8309220355268900335">"Акумулятор"</item> - </string-array> + <string name="performance" msgid="6552785217174378320">"Продуктивність"</string> + <string name="user_interface" msgid="3712869377953950887">"Інтерфейс користувача"</string> + <string name="thermal" msgid="6758074791325414831">"Нагрівання"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Режим керування однією рукою"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Слухові апарати"</string> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Під’єднано"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Від’єднано"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Слухові апарати"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Підключити новий пристрій"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Натисніть, щоб підключити новий пристрій"</string> <string name="hearing_devices_presets_error" msgid="350363093458408536">"Не вдалось оновити набір налаштувань"</string> - <!-- no translation found for hearing_devices_preset_label (7878267405046232358) --> - <skip /> + <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Набір налаштувань"</string> + <string name="live_caption_title" msgid="8916875614623730005">"Живі субтитри"</string> <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> @@ -727,11 +729,6 @@ <string name="keyboard_key_button_template" msgid="8005673627272051429">"Кнопка <xliff:g id="NAME">%1$s</xliff:g>"</string> <string name="keyboard_key_home" msgid="3734400625170020657">"Home"</string> <string name="keyboard_key_back" msgid="4185420465469481999">"Назад"</string> - <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"Стрілка вгору"</string> - <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"Стрілка вниз"</string> - <string name="keyboard_key_dpad_left" msgid="144176368026538621">"Стрілка вліво"</string> - <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"Стрілка вправо"</string> - <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"Центр"</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">"Enter"</string> @@ -1275,7 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"складений"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"розкладений"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string> - <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"Заряд акумулятора: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Заряд акумулятора стилуса: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Підключіть стилус до зарядного пристрою"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"Низький заряд акумулятора стилуса"</string> <string name="video_camera" msgid="7654002575156149298">"Відеокамера"</string> @@ -1318,6 +1315,16 @@ <string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"Нещодавно використано (<xliff:g id="APP_NAME">%1$s</xliff:g>, <xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string> <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>)"</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>)"</string> + <string name="shortcut_helper_category_system" msgid="462110876978937359">"Система"</string> + <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Багатозадачність"</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_a11y" msgid="6314444792641773464">"Доступність"</string> + <string name="shortcut_helper_title" msgid="8567500639300970049">"Комбінації клавіш"</string> + <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Комбінації клавіш для пошуку"</string> + <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Значок згортання"</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="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> diff --git a/packages/SystemUI/res/values-ur/strings.xml b/packages/SystemUI/res/values-ur/strings.xml index d7c4c88cd802..f08dd7eb13b7 100644 --- a/packages/SystemUI/res/values-ur/strings.xml +++ b/packages/SystemUI/res/values-ur/strings.xml @@ -278,11 +278,13 @@ <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"محفوظ ہے"</string> <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"غیر منسلک کریں"</string> <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"فعال کریں"</string> - <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"کل دوبارہ خودکار طور پر آن ہوگا"</string> + <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"کل دوبارہ خودکار طور پر آن کریں"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"فوری اشتراک اور \'میرا آلہ ڈھونڈیں\' جیسی خصوصیات بلوٹوتھ کا استعمال کرتی ہیں"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"بلوٹوتھ کل صبح آن ہو جائے گا"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"آڈیو کا اشتراک"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"آڈیو کا اشتراک ہو رہا ہے"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> بیٹری"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"آڈیو"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"ہیڈ سیٹ"</string> @@ -361,19 +363,19 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"آپ کے آلے کے تجربے کا کون سا حصہ متاثر ہوا؟"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"مسئلہ کی قسم منتخب کریں"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"اسکرین ریکارڈ"</string> - <string-array name="qs_record_issue_types"> - <item msgid="2947988124014085798">"کارکردگی"</item> - <item msgid="1627504621139124393">"یوزر انٹرفیس"</item> - <item msgid="8309220355268900335">"بیٹری"</item> - </string-array> + <string name="performance" msgid="6552785217174378320">"کارکردگی"</string> + <string name="user_interface" msgid="3712869377953950887">"یوزر انٹرفیس"</string> + <string name="thermal" msgid="6758074791325414831">"تھرمل"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"ایک ہاتھ کی وضع"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"سماعت کے آلات"</string> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"فعال"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"غیر منسلک ہے"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"سماعت کے آلات"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"نئے آلے کا جوڑا بنائیں"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"نئے آلے کا جوڑا بنانے کے لیے کلک کریں"</string> <string name="hearing_devices_presets_error" msgid="350363093458408536">"پہلے سے ترتیب شدہ کو اپ ڈیٹ نہیں کیا جا سکا"</string> - <!-- no translation found for hearing_devices_preset_label (7878267405046232358) --> - <skip /> + <string name="hearing_devices_preset_label" msgid="7878267405046232358">"پہلے سے ترتیب شدہ"</string> + <string name="live_caption_title" msgid="8916875614623730005">"لائیو کیپشن"</string> <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> @@ -731,11 +733,6 @@ <string name="keyboard_key_button_template" msgid="8005673627272051429">"بٹن <xliff:g id="NAME">%1$s</xliff:g>"</string> <string name="keyboard_key_home" msgid="3734400625170020657">"Home"</string> <string name="keyboard_key_back" msgid="4185420465469481999">"پیچھے"</string> - <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"اوپر تیر کا نشان"</string> - <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"نیچے تیر کا نشان"</string> - <string name="keyboard_key_dpad_left" msgid="144176368026538621">"بایاں تیر"</string> - <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"دایاں تیر"</string> - <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"سینٹر"</string> <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> @@ -1279,7 +1276,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"فولڈ کردہ"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"اَن فولڈ کردہ"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string> - <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"<xliff:g id="PERCENTAGE">%s</xliff:g> بیٹری باقی ہے"</string> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"<xliff:g id="PERCENTAGE">%s</xliff:g> اسٹائلس بیٹری"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"اپنے اسٹائلس کو چارجر منسلک کریں"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"اسٹائلس بیٹری کم ہے"</string> <string name="video_camera" msgid="7654002575156149298">"ویڈیو کیمرا"</string> @@ -1322,6 +1319,16 @@ <string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"<xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>) کے ذریعے حال ہی میں استعمال کیا گیا"</string> <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>) کے زیر استعمال"</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>) کے ذریعے حال ہی میں استعمال کیا گیا"</string> + <string name="shortcut_helper_category_system" msgid="462110876978937359">"سسٹم"</string> + <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"ملٹی ٹاسکنگ"</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_a11y" msgid="6314444792641773464">"ایکسیسبیلٹی"</string> + <string name="shortcut_helper_title" msgid="8567500639300970049">"کی بورڈ شارٹ کٹس"</string> + <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"تلاش کے شارٹ کٹس"</string> + <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"آئیکن سکیڑیں"</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="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> diff --git a/packages/SystemUI/res/values-uz/strings.xml b/packages/SystemUI/res/values-uz/strings.xml index 55bdd8490342..4a04a337a6cd 100644 --- a/packages/SystemUI/res/values-uz/strings.xml +++ b/packages/SystemUI/res/values-uz/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Ertaga yana avtomatik yoqilsin"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Tezkor ulashuv va Qurilmamni top kabi funksiyalar Bluetooth ishlatadi"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth ertaga ertalab yoqiladi"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Audio ulashuvi"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Audio ulashuvi yoniq"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Batareya quvvati: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Garnitura"</string> @@ -361,19 +363,19 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Qurilma ishlashining qaysi qismiga taʼsir qildi?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Muammo turini tanlang"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Ekran yozuvi"</string> - <string-array name="qs_record_issue_types"> - <item msgid="2947988124014085798">"Unumdorlik"</item> - <item msgid="1627504621139124393">"Foydalanuvchi interfeysi"</item> - <item msgid="8309220355268900335">"Batareya"</item> - </string-array> + <string name="performance" msgid="6552785217174378320">"Unumdorlik"</string> + <string name="user_interface" msgid="3712869377953950887">"Foydalanuvchi interfeysi"</string> + <string name="thermal" msgid="6758074791325414831">"Termal"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Ixcham rejim"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Eshitish qurilmalari"</string> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Faol"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Uzildi"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Eshitish qurilmalari"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Yangi qurilmani ulash"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Yangi qurilmani ulash uchun bosing"</string> <string name="hearing_devices_presets_error" msgid="350363093458408536">"Andoza yangilanmadi"</string> - <!-- no translation found for hearing_devices_preset_label (7878267405046232358) --> - <skip /> + <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Andoza"</string> + <string name="live_caption_title" msgid="8916875614623730005">"Jonli izoh"</string> <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> @@ -727,11 +729,6 @@ <string name="keyboard_key_button_template" msgid="8005673627272051429">"<xliff:g id="NAME">%1$s</xliff:g> tugmasi"</string> <string name="keyboard_key_home" msgid="3734400625170020657">"Bosh ekran"</string> <string name="keyboard_key_back" msgid="4185420465469481999">"Orqaga"</string> - <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"Tepaga strelka"</string> - <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"Pastga strelka"</string> - <string name="keyboard_key_dpad_left" msgid="144176368026538621">"Chapga strelka"</string> - <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"Oʻngga strelka"</string> - <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"Markaziy ko‘rsatkichli chiziq"</string> <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string> <string name="keyboard_key_space" msgid="6980847564173394012">"Probel"</string> <string name="keyboard_key_enter" msgid="8633362970109751646">"Enter"</string> @@ -1275,7 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"buklangan"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"buklanmagan"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string> - <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"Batareya quvvati: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Stilus batareyasi: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Stilusni quvvat manbaiga ulang"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"Stilus batareyasi kam"</string> <string name="video_camera" msgid="7654002575156149298">"Videokamera"</string> @@ -1318,6 +1315,16 @@ <string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"Yaqinda <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>) ishlatgan"</string> <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>) ishlatmoqda"</string> <string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"Yaqinda <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>) ishlatgan"</string> + <string name="shortcut_helper_category_system" msgid="462110876978937359">"Tizim"</string> + <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Multi-vazifalilik"</string> + <string name="shortcut_helper_category_input" msgid="8674018654124839566">"Kiritish"</string> + <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Ilova yorliqlari"</string> + <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Qulayliklar"</string> + <string name="shortcut_helper_title" msgid="8567500639300970049">"Tezkor tugmalar"</string> + <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Tezkor tugmalar qidiruvi"</string> + <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Yigʻish belgisi"</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="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Klaviatura orqa yoritkichi"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"Daraja: %1$d / %2$d"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"Uy boshqaruvi"</string> diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml index fb1f28cd679a..45d9165472cc 100644 --- a/packages/SystemUI/res/values-vi/strings.xml +++ b/packages/SystemUI/res/values-vi/strings.xml @@ -247,7 +247,7 @@ <string name="accessibility_quick_settings_dnd" msgid="2415967452264206047">"Không làm phiền."</string> <string name="accessibility_quick_settings_bluetooth" msgid="8250942386687551283">"Bluetooth."</string> <string name="accessibility_quick_settings_bluetooth_on" msgid="3819082137684078013">"Bluetooth bật."</string> - <string name="accessibility_quick_settings_alarm" msgid="558094529584082090">"Báo thức được đặt cho <xliff:g id="TIME">%s</xliff:g>."</string> + <string name="accessibility_quick_settings_alarm" msgid="558094529584082090">"Chuông báo được đặt cho <xliff:g id="TIME">%s</xliff:g>."</string> <string name="accessibility_quick_settings_more_time" msgid="7646479831704665284">"Nhiều thời gian hơn."</string> <string name="accessibility_quick_settings_less_time" msgid="9110364286464977870">"Ít thời gian hơn."</string> <string name="accessibility_casting_turned_off" msgid="1387906158563374962">"Đã ngừng truyền màn hình."</string> @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Tự động bật lại vào ngày mai"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Các tính năng như Chia sẻ nhanh và Tìm thiết bị của tôi đều sử dụng Bluetooth"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth sẽ bật vào sáng mai"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Chia sẻ âm thanh"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Đang chia sẻ âm thanh"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> pin"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Âm thanh"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Tai nghe"</string> @@ -361,19 +363,19 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Bạn gặp loại vấn đề gì khi dùng thiết bị?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Chọn loại vấn đề"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Ghi màn hình"</string> - <string-array name="qs_record_issue_types"> - <item msgid="2947988124014085798">"Hiệu suất"</item> - <item msgid="1627504621139124393">"Giao diện người dùng"</item> - <item msgid="8309220355268900335">"Pin"</item> - </string-array> + <string name="performance" msgid="6552785217174378320">"Hiệu suất"</string> + <string name="user_interface" msgid="3712869377953950887">"Giao diện người dùng"</string> + <string name="thermal" msgid="6758074791325414831">"Nhiệt"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Chế độ một tay"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Thiết bị trợ thính"</string> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Đang hoạt động"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Đã ngắt kết nối"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Thiết bị trợ thính"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Ghép nối thiết bị mới"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Nhấp để ghép nối thiết bị mới"</string> <string name="hearing_devices_presets_error" msgid="350363093458408536">"Không cập nhật được giá trị đặt trước"</string> - <!-- no translation found for hearing_devices_preset_label (7878267405046232358) --> - <skip /> + <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Chế độ đặt sẵn"</string> + <string name="live_caption_title" msgid="8916875614623730005">"Phụ đề trực tiếp"</string> <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> @@ -594,11 +596,11 @@ <string name="screen_pinning_negative" msgid="6882816864569211666">"Không, cảm ơn"</string> <string name="screen_pinning_start" msgid="7483998671383371313">"Đã ghim ứng dụng"</string> <string name="screen_pinning_exit" msgid="4553787518387346893">"Đã bỏ ghim ứng dụng"</string> - <string name="stream_voice_call" msgid="7468348170702375660">"Gọi"</string> + <string name="stream_voice_call" msgid="7468348170702375660">"Cuộc gọi"</string> <string name="stream_system" msgid="7663148785370565134">"Hệ thống"</string> <string name="stream_ring" msgid="7550670036738697526">"Chuông"</string> <string name="stream_music" msgid="2188224742361847580">"Nội dung nghe nhìn"</string> - <string name="stream_alarm" msgid="16058075093011694">"Báo thức"</string> + <string name="stream_alarm" msgid="16058075093011694">"Chuông báo"</string> <string name="stream_notification" msgid="7930294049046243939">"Thông báo"</string> <string name="stream_bluetooth_sco" msgid="6234562365528664331">"Bluetooth"</string> <string name="stream_dtmf" msgid="7322536356554673067">"Tần số đa chuông kép"</string> @@ -642,7 +644,7 @@ <string name="enable_demo_mode" msgid="3180345364745966431">"Bật chế độ trình diễn"</string> <string name="show_demo_mode" msgid="3677956462273059726">"Hiển thị chế độ trình diễn"</string> <string name="status_bar_ethernet" msgid="5690979758988647484">"Ethernet"</string> - <string name="status_bar_alarm" msgid="87160847643623352">"Báo thức"</string> + <string name="status_bar_alarm" msgid="87160847643623352">"Chuông báo"</string> <string name="wallet_title" msgid="5369767670735827105">"Ví"</string> <string name="wallet_empty_state_label" msgid="7776761245237530394">"Thiết lập để mua hàng nhanh hơn và an toàn hơn bằng điện thoại"</string> <string name="wallet_app_button_label" msgid="7123784239111190992">"Hiện tất cả"</string> @@ -655,7 +657,7 @@ <string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"Đang cập nhật"</string> <string name="status_bar_work" msgid="5238641949837091056">"Hồ sơ công việc"</string> <string name="status_bar_airplane" msgid="4848702508684541009">"Chế độ trên máy bay"</string> - <string name="zen_alarm_warning" msgid="7844303238486849503">"Bạn sẽ không nghe thấy báo thức tiếp theo lúc <xliff:g id="WHEN">%1$s</xliff:g> của mình"</string> + <string name="zen_alarm_warning" msgid="7844303238486849503">"Bạn sẽ không nghe thấy chuông báo tiếp theo lúc <xliff:g id="WHEN">%1$s</xliff:g> của mình"</string> <string name="alarm_template" msgid="2234991538018805736">"lúc <xliff:g id="WHEN">%1$s</xliff:g>"</string> <string name="alarm_template_far" msgid="3561752195856839456">"vào <xliff:g id="WHEN">%1$s</xliff:g>"</string> <string name="accessibility_status_bar_hotspot" msgid="2888479317489131669">"Điểm phát sóng"</string> @@ -727,11 +729,6 @@ <string name="keyboard_key_button_template" msgid="8005673627272051429">"Nút <xliff:g id="NAME">%1$s</xliff:g>"</string> <string name="keyboard_key_home" msgid="3734400625170020657">"Home"</string> <string name="keyboard_key_back" msgid="4185420465469481999">"Quay lại"</string> - <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"Mũi tên lên"</string> - <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"Mũi tên xuống"</string> - <string name="keyboard_key_dpad_left" msgid="144176368026538621">"Mũi tên trái"</string> - <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"Mũi tên phải"</string> - <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"Giữa"</string> <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string> <string name="keyboard_key_space" msgid="6980847564173394012">"Dấu cách"</string> <string name="keyboard_key_enter" msgid="8633362970109751646">"Enter"</string> @@ -933,7 +930,7 @@ <string name="running_foreground_services_title" msgid="5137313173431186685">"Ứng dụng đang chạy trong nền"</string> <string name="running_foreground_services_msg" msgid="3009459259222695385">"Nhấn để biết chi tiết về mức sử dụng dữ liệu và pin"</string> <string name="mobile_data_disable_title" msgid="5366476131671617790">"Tắt dữ liệu di động?"</string> - <string name="mobile_data_disable_message" msgid="8604966027899770415">"Bạn sẽ không có quyền sử dụng dữ liệu hoặc truy cập Internet thông qua chế độ <xliff:g id="CARRIER">%s</xliff:g>. Bạn chỉ có thể truy cập Internet thông qua Wi-Fi."</string> + <string name="mobile_data_disable_message" msgid="8604966027899770415">"Bạn sẽ không có quyền sử dụng dữ liệu hoặc truy cập Internet thông qua mạng <xliff:g id="CARRIER">%s</xliff:g>. Bạn chỉ có thể truy cập Internet thông qua Wi-Fi."</string> <string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"nhà mạng của bạn"</string> <string name="auto_data_switch_disable_title" msgid="5146527155665190652">"Chuyển về <xliff:g id="CARRIER">%s</xliff:g>?"</string> <string name="auto_data_switch_disable_message" msgid="5885533647399535852">"Dữ liệu di động sẽ không tự động chuyển dựa trên tình trạng phủ sóng"</string> @@ -1275,7 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"gập"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"mở"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s/%2$s"</string> - <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"Còn <xliff:g id="PERCENTAGE">%s</xliff:g> pin"</string> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Mức pin bút cảm ứng <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Hãy kết nối bút cảm ứng với bộ sạc"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"Bút cảm ứng bị yếu pin"</string> <string name="video_camera" msgid="7654002575156149298">"Máy quay video"</string> @@ -1318,6 +1315,16 @@ <string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"<xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>) đã dùng gần đây"</string> <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_multitasking" msgid="7413381961404090136">"Đa nhiệm"</string> + <string name="shortcut_helper_category_input" msgid="8674018654124839566">"Phương thức nhập"</string> + <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Lối tắt ứng dụng"</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> + <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Lối tắt tìm kiếm"</string> + <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Biểu tượng Thu gọn"</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="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Đèn nền bàn phím"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"Độ sáng %1$d/%2$d"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"Điều khiển nhà"</string> diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml index 1a3055c3f226..98a1187bd661 100644 --- a/packages/SystemUI/res/values-zh-rCN/strings.xml +++ b/packages/SystemUI/res/values-zh-rCN/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"明天自动重新开启"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"快速分享、查找我的设备等功能会使用蓝牙"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"蓝牙将在明天早上开启"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"音频分享"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"正在分享音频"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> 的电量"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"音频"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"耳机"</string> @@ -361,24 +363,24 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"设备体验的哪个方面受到影响?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"选择问题类型"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"屏幕录制"</string> - <string-array name="qs_record_issue_types"> - <item msgid="2947988124014085798">"性能"</item> - <item msgid="1627504621139124393">"界面"</item> - <item msgid="8309220355268900335">"电池"</item> - </string-array> + <string name="performance" msgid="6552785217174378320">"性能"</string> + <string name="user_interface" msgid="3712869377953950887">"界面"</string> + <string name="thermal" msgid="6758074791325414831">"散热"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"单手模式"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"助听装置"</string> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"已连接"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"已断开连接"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"助听装置"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"与新设备配对"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"点击即可与新设备配对"</string> <string name="hearing_devices_presets_error" msgid="350363093458408536">"无法更新预设"</string> - <!-- no translation found for hearing_devices_preset_label (7878267405046232358) --> - <skip /> + <string name="hearing_devices_preset_label" msgid="7878267405046232358">"预设"</string> + <string name="live_caption_title" msgid="8916875614623730005">"实时字幕"</string> <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> - <string name="sensor_privacy_start_use_mic_dialog_content" msgid="1624701280680913717">"这将会为所有获准使用您麦克风的应用和服务启用这项权限。"</string> - <string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"这将会为所有获准使用您摄像头的应用和服务启用这项权限。"</string> + <string name="sensor_privacy_start_use_mic_dialog_content" msgid="1624701280680913717">"解锁后,所有具有麦克风使用权的应用和服务都可使用您的麦克风。"</string> + <string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"解锁后,所有具有摄像头使用权的应用和服务都可使用您的摄像头。"</string> <string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"这将会为所有获准使用您的摄像头或麦克风的应用和服务启用这项权限。"</string> <string name="sensor_privacy_start_use_mic_blocked_dialog_title" msgid="2640140287496469689">"麦克风已被屏蔽"</string> <string name="sensor_privacy_start_use_camera_blocked_dialog_title" msgid="7398084286822440384">"摄像头已被屏蔽"</string> @@ -727,11 +729,6 @@ <string name="keyboard_key_button_template" msgid="8005673627272051429">"<xliff:g id="NAME">%1$s</xliff:g>按钮"</string> <string name="keyboard_key_home" msgid="3734400625170020657">"Home"</string> <string name="keyboard_key_back" msgid="4185420465469481999">"返回"</string> - <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"向上键"</string> - <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"向下键"</string> - <string name="keyboard_key_dpad_left" msgid="144176368026538621">"向左键"</string> - <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"向右键"</string> - <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"中心"</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">"Enter"</string> @@ -933,7 +930,7 @@ <string name="running_foreground_services_title" msgid="5137313173431186685">"在后台运行的应用"</string> <string name="running_foreground_services_msg" msgid="3009459259222695385">"点按即可详细了解电量和流量消耗情况"</string> <string name="mobile_data_disable_title" msgid="5366476131671617790">"要关闭移动数据网络吗?"</string> - <string name="mobile_data_disable_message" msgid="8604966027899770415">"您将无法通过<xliff:g id="CARRIER">%s</xliff:g>使用移动数据或互联网,只能通过 WLAN 连接到互联网。"</string> + <string name="mobile_data_disable_message" msgid="8604966027899770415">"关闭后,您将无法通过<xliff:g id="CARRIER">%s</xliff:g>使用移动数据或互联网,只能通过 WLAN 连接到互联网。"</string> <string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"您的运营商"</string> <string name="auto_data_switch_disable_title" msgid="5146527155665190652">"是否要切换回 <xliff:g id="CARRIER">%s</xliff:g>?"</string> <string name="auto_data_switch_disable_message" msgid="5885533647399535852">"移动流量不会根据网络可用情况自动切换"</string> @@ -1207,7 +1204,7 @@ <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"添加功能块"</string> <string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"不添加功能块"</string> <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"选择用户"</string> - <string name="fgs_manager_footer_label" msgid="8276763570622288231">"{count,plural, =1{# 个应用处于活动状态}other{# 个应用处于活动状态}}"</string> + <string name="fgs_manager_footer_label" msgid="8276763570622288231">"{count,plural, =1{# 个应用处于运行状态}other{# 个应用处于运行状态}}"</string> <string name="fgs_dot_content_description" msgid="2865071539464777240">"新信息"</string> <string name="fgs_manager_dialog_title" msgid="5879184257257718677">"已开启的应用"</string> <string name="fgs_manager_dialog_message" msgid="2670045017200730076">"这些应用即使在闲置时仍处于活跃运行状态。应用的功能会因此提升,但可能会影响电池续航时间。"</string> @@ -1275,7 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"折叠状态"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"展开状态"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s/%2$s"</string> - <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"电池还剩 <xliff:g id="PERCENTAGE">%s</xliff:g> 的电量"</string> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"触控笔电量为 <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"请将触控笔连接充电器"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"触控笔电池电量低"</string> <string name="video_camera" msgid="7654002575156149298">"摄像机"</string> @@ -1318,6 +1315,16 @@ <string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"“<xliff:g id="APP_NAME">%1$s</xliff:g>”最近使用过(<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string> <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>)"</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>)"</string> + <string name="shortcut_helper_category_system" msgid="462110876978937359">"系统"</string> + <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"多任务处理"</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_a11y" msgid="6314444792641773464">"无障碍功能"</string> + <string name="shortcut_helper_title" msgid="8567500639300970049">"键盘快捷键"</string> + <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"搜索快捷键"</string> + <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"收起图标"</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="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> diff --git a/packages/SystemUI/res/values-zh-rHK/strings.xml b/packages/SystemUI/res/values-zh-rHK/strings.xml index 8523972ef9ab..edc624ff9c26 100644 --- a/packages/SystemUI/res/values-zh-rHK/strings.xml +++ b/packages/SystemUI/res/values-zh-rHK/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"明天自動重新開啟"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"「快速共享」和「尋找我的裝置」等功能都會使用藍牙"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"藍牙將於明天上午開啟"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"音訊分享功能"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"正在分享音訊"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"電量:<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"音訊"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"耳機"</string> @@ -361,19 +363,19 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"哪些裝置使用體驗受影響?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"選取問題類型"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"螢幕錄影"</string> - <string-array name="qs_record_issue_types"> - <item msgid="2947988124014085798">"效能"</item> - <item msgid="1627504621139124393">"使用者介面"</item> - <item msgid="8309220355268900335">"電池"</item> - </string-array> + <string name="performance" msgid="6552785217174378320">"效能"</string> + <string name="user_interface" msgid="3712869377953950887">"使用者介面"</string> + <string name="thermal" msgid="6758074791325414831">"熱能"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"單手模式"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"助聽器"</string> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"已連線"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"已中斷連線"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"助聽器"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"配對新裝置"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"㩒一下就可以配對新裝置"</string> <string name="hearing_devices_presets_error" msgid="350363093458408536">"無法更新預設"</string> - <!-- no translation found for hearing_devices_preset_label (7878267405046232358) --> - <skip /> + <string name="hearing_devices_preset_label" msgid="7878267405046232358">"預設"</string> + <string name="live_caption_title" msgid="8916875614623730005">"即時字幕"</string> <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> @@ -727,11 +729,6 @@ <string name="keyboard_key_button_template" msgid="8005673627272051429">"<xliff:g id="NAME">%1$s</xliff:g> 鍵"</string> <string name="keyboard_key_home" msgid="3734400625170020657">"Home"</string> <string name="keyboard_key_back" msgid="4185420465469481999">"返回"</string> - <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"向上箭咀"</string> - <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"向下箭咀"</string> - <string name="keyboard_key_dpad_left" msgid="144176368026538621">"向左箭咀"</string> - <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"向右箭咀"</string> - <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"箭咀中央"</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">"輸入 (Enter)"</string> @@ -1275,7 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"已摺疊"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"已打開"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string> - <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"剩餘電量:<xliff:g id="PERCENTAGE">%s</xliff:g>"</string> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"觸控筆電量:<xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"將觸控筆連接充電器"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"觸控筆電量不足"</string> <string name="video_camera" msgid="7654002575156149298">"攝影機"</string> @@ -1318,6 +1315,16 @@ <string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"「<xliff:g id="APP_NAME">%1$s</xliff:g>」最近使用過此權限 (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string> <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>)"</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>)"</string> + <string name="shortcut_helper_category_system" msgid="462110876978937359">"系統"</string> + <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"多工處理"</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_a11y" msgid="6314444792641773464">"無障礙功能"</string> + <string name="shortcut_helper_title" msgid="8567500639300970049">"鍵盤快速鍵"</string> + <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"搜尋快速鍵"</string> + <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"收合圖示"</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="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> diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml index 8d5cad40b295..567928276f46 100644 --- a/packages/SystemUI/res/values-zh-rTW/strings.xml +++ b/packages/SystemUI/res/values-zh-rTW/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"明天自動重新開啟"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"「快速分享」和「尋找我的裝置」等功能都需要使用藍牙技術"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"藍牙會在明天早上開啟"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"音訊分享"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"正在分享音訊"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"電量:<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"音訊"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"耳機"</string> @@ -361,21 +363,21 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"哪些裝置使用體驗受到影響?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"選取問題類型"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"螢幕錄影"</string> - <string-array name="qs_record_issue_types"> - <item msgid="2947988124014085798">"效能"</item> - <item msgid="1627504621139124393">"使用者介面"</item> - <item msgid="8309220355268900335">"電池"</item> - </string-array> + <string name="performance" msgid="6552785217174378320">"效能"</string> + <string name="user_interface" msgid="3712869377953950887">"使用者介面"</string> + <string name="thermal" msgid="6758074791325414831">"熱成像"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"單手模式"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"助聽器"</string> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"已連線"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"連線中斷"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"助聽器"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"配對新裝置"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"按一下即可配對新裝置"</string> <string name="hearing_devices_presets_error" msgid="350363093458408536">"無法更新預設設定"</string> - <!-- no translation found for hearing_devices_preset_label (7878267405046232358) --> - <skip /> + <string name="hearing_devices_preset_label" msgid="7878267405046232358">"預設"</string> + <string name="live_caption_title" msgid="8916875614623730005">"即時字幕"</string> <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_camera_dialog_title" msgid="8807639852654305227">"解除封鎖裝置相機?"</string> <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"要將裝置的相機和麥克風解除封鎖嗎?"</string> <string name="sensor_privacy_start_use_mic_dialog_content" msgid="1624701280680913717">"執行後,具備麥克風存取權的所有應用程式和服務,都將可使用麥克風。"</string> <string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"執行後,具備相機存取權的所有應用程式和服務,都將可使用相機。"</string> @@ -727,11 +729,6 @@ <string name="keyboard_key_button_template" msgid="8005673627272051429">"<xliff:g id="NAME">%1$s</xliff:g> 按鈕"</string> <string name="keyboard_key_home" msgid="3734400625170020657">"Home 鍵"</string> <string name="keyboard_key_back" msgid="4185420465469481999">"返回"</string> - <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"向上箭頭"</string> - <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"向下箭頭"</string> - <string name="keyboard_key_dpad_left" msgid="144176368026538621">"向左箭頭"</string> - <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"向右箭頭"</string> - <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"中央鍵"</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">"Enter 鍵"</string> @@ -933,7 +930,7 @@ <string name="running_foreground_services_title" msgid="5137313173431186685">"在背景執行的應用程式"</string> <string name="running_foreground_services_msg" msgid="3009459259222695385">"輕觸即可查看電池和數據用量詳情"</string> <string name="mobile_data_disable_title" msgid="5366476131671617790">"要關閉行動數據嗎?"</string> - <string name="mobile_data_disable_message" msgid="8604966027899770415">"你將無法透過「<xliff:g id="CARRIER">%s</xliff:g>」使用行動數據或網際網路。你只能透過 Wi-Fi 使用網際網路。"</string> + <string name="mobile_data_disable_message" msgid="8604966027899770415">"你將無法透過「<xliff:g id="CARRIER">%s</xliff:g>」使用行動數據或網際網路,只能利用 Wi-Fi 上網。"</string> <string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"你的電信業者"</string> <string name="auto_data_switch_disable_title" msgid="5146527155665190652">"要切換回「<xliff:g id="CARRIER">%s</xliff:g>」嗎?"</string> <string name="auto_data_switch_disable_message" msgid="5885533647399535852">"行動數據不會依據可用性自動切換"</string> @@ -1210,7 +1207,7 @@ <string name="fgs_manager_footer_label" msgid="8276763570622288231">"{count,plural, =1{# 個應用程式正在運作}other{# 個應用程式正在運作}}"</string> <string name="fgs_dot_content_description" msgid="2865071539464777240">"新資訊"</string> <string name="fgs_manager_dialog_title" msgid="5879184257257718677">"運作中的應用程式"</string> - <string name="fgs_manager_dialog_message" msgid="2670045017200730076">"這些應用程式會在沒有使用的情況下持續運作。應用程式的實用度會因此提升,但也可能影響電池續航力。"</string> + <string name="fgs_manager_dialog_message" msgid="2670045017200730076">"這些應用程式即使是在閒置狀態下,也會持續運作。應用程式的功能會因而提升,但也可能影響電池續航力。"</string> <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"停止"</string> <string name="fgs_manager_app_item_stop_button_stopped_label" msgid="6950382004441263922">"已停止"</string> <string name="clipboard_edit_text_done" msgid="4551887727694022409">"完成"</string> @@ -1275,7 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"已摺疊"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"已展開"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string> - <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"剩餘電量:<xliff:g id="PERCENTAGE">%s</xliff:g>"</string> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"觸控筆電量:<xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"將觸控筆接上充電器"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"觸控筆電力不足"</string> <string name="video_camera" msgid="7654002575156149298">"攝影機"</string> @@ -1318,6 +1315,16 @@ <string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"「<xliff:g id="APP_NAME">%1$s</xliff:g>」最近用過這項權限 (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string> <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>)"</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>)"</string> + <string name="shortcut_helper_category_system" msgid="462110876978937359">"系統"</string> + <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"多工處理"</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_a11y" msgid="6314444792641773464">"無障礙"</string> + <string name="shortcut_helper_title" msgid="8567500639300970049">"鍵盤快速鍵"</string> + <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"搜尋快速鍵"</string> + <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"收合圖示"</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="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> diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml index 6644b53591a8..a0a57acaa362 100644 --- a/packages/SystemUI/res/values-zu/strings.xml +++ b/packages/SystemUI/res/values-zu/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Vula ngokuzenzekela futhi kusasa"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Izakhi ezifana nokuthi Ukwabelana Ngokushesha kanye nokuthi Thola Idivayisi Yami zisebenzisa i-Bluetooth"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"IBluetooth izovuleka kusasa ekuseni"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Ukwabelana Ngokuqoshiwe"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Ukwabelana Ngomsindo"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> ibhethri"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Umsindo"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Ihedisethi"</string> @@ -361,19 +363,19 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Kuthinteke yiphi ingxenye yokusebenzisa idivayisi?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Khetha uhlobo lwenkinga"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Irekhodi lesikrini"</string> - <string-array name="qs_record_issue_types"> - <item msgid="2947988124014085798">"Ukusebenza"</item> - <item msgid="1627504621139124393">"Okusetshenziswa Kubonwa"</item> - <item msgid="8309220355268900335">"Ibhethri"</item> - </string-array> + <string name="performance" msgid="6552785217174378320">"Ukusebenza"</string> + <string name="user_interface" msgid="3712869377953950887">"Okusetshenziswa Kubonwa"</string> + <string name="thermal" msgid="6758074791325414831">"Ithermal"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Imodi yesandla esisodwa"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Izinsizakuzwa"</string> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Kuyasebenza"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Inqamukile"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Izinsizakuzwa"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Bhangqa idivayisi entsha"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Chofoza ukuze ubhangqe idivayisi entsha"</string> <string name="hearing_devices_presets_error" msgid="350363093458408536">"Ayikwazanga ukubuyekeza ukusetha ngaphambilini"</string> - <!-- no translation found for hearing_devices_preset_label (7878267405046232358) --> - <skip /> + <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Ukusetha ngaphambilini"</string> + <string name="live_caption_title" msgid="8916875614623730005">"Amagama-ncazo abukhoma"</string> <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> @@ -727,11 +729,6 @@ <string name="keyboard_key_button_template" msgid="8005673627272051429">"Inkinobho <xliff:g id="NAME">%1$s</xliff:g>"</string> <string name="keyboard_key_home" msgid="3734400625170020657">"Ekhaya"</string> <string name="keyboard_key_back" msgid="4185420465469481999">"Emuva"</string> - <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"Umcibisholo waphezulu"</string> - <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"Umcibisholo waphansi"</string> - <string name="keyboard_key_dpad_left" msgid="144176368026538621">"Umcibisholo wangokwesokunxele"</string> - <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"Umcibisholo wangokwesokudla"</string> - <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"Maphakathi"</string> <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string> <string name="keyboard_key_space" msgid="6980847564173394012">"Isikhala"</string> <string name="keyboard_key_enter" msgid="8633362970109751646">"Faka"</string> @@ -1275,7 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"kugoqiwe"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"kuvuliwe"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string> - <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"<xliff:g id="PERCENTAGE">%s</xliff:g> ibhethri elisele"</string> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Ibhethri lestylus <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Xhuma i-stylus yakho kushaja"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"Ibhethri le-stylus liphansi"</string> <string name="video_camera" msgid="7654002575156149298">"Ikhamera yevidiyo"</string> @@ -1318,6 +1315,16 @@ <string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"Kusetshenziswe kamuva yi-<xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string> <string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"Isetshenziswa yi-<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>)"</string> <string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"Kusetshenziswe kamuva yi-<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>)"</string> + <string name="shortcut_helper_category_system" msgid="462110876978937359">"Isistimu"</string> + <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Ukwenza imisebenzi eminingi"</string> + <string name="shortcut_helper_category_input" msgid="8674018654124839566">"Okokufaka"</string> + <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Izinqamuleli Zohlelo lokusebenza"</string> + <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Ukufinyeleleka"</string> + <string name="shortcut_helper_title" msgid="8567500639300970049">"Izinqamuleli zekhibhodi"</string> + <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Sesha izinqamuleli"</string> + <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Goqa isithonjana"</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="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Ilambu lekhibhodi"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"Ileveli %1$d ka-%2$d"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"Izilawuli Zasekhaya"</string> diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml index fb883640c9a9..f8762f0a98d5 100644 --- a/packages/SystemUI/res/values/config.xml +++ b/packages/SystemUI/res/values/config.xml @@ -37,6 +37,9 @@ <item>400</item> </integer-array> + <!-- Whether to use deadzone with nav bar --> + <bool name="config_useDeadZone">true</bool> + <!-- decay duration (from size_max -> size), in ms --> <integer name="navigation_bar_deadzone_hold">333</integer> <integer name="navigation_bar_deadzone_decay">333</integer> diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index 8ce20684d892..e825fc5c5b9a 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -912,6 +912,10 @@ obvious when corner radii differ.--> <dimen name="communal_enforced_rounded_corner_max_radius">28dp</dimen> + <!-- Width and height used to filter widgets displayed in the communal widget picker --> + <dimen name="communal_widget_picker_desired_width">360dp</dimen> + <dimen name="communal_widget_picker_desired_height">240dp</dimen> + <!-- The width/height of the unlock icon view on keyguard. --> <dimen name="keyguard_lock_height">42dp</dimen> <dimen name="keyguard_lock_padding">20dp</dimen> @@ -1101,6 +1105,7 @@ <dimen name="biometric_dialog_button_negative_max_width">160dp</dimen> <dimen name="biometric_dialog_button_positive_max_width">136dp</dimen> <dimen name="biometric_dialog_corner_size">28dp</dimen> + <dimen name="biometric_dialog_indicator_max_width">280dp</dimen> <!-- Y translation when showing/dismissing the dialog--> <dimen name="biometric_dialog_animation_translation_offset">350dp</dimen> <dimen name="biometric_dialog_border_padding">4dp</dimen> @@ -1117,9 +1122,9 @@ <dimen name="biometric_prompt_panel_max_width">640dp</dimen> <dimen name="biometric_prompt_land_small_horizontal_guideline_padding">344dp</dimen> <dimen name="biometric_prompt_two_pane_udfps_horizontal_guideline_padding">114dp</dimen> - <dimen name="biometric_prompt_two_pane_udfps_mid_guideline_padding">409dp</dimen> + <dimen name="biometric_prompt_two_pane_udfps_shorter_content_width">216dp</dimen> + <dimen name="biometric_prompt_two_pane_udfps_shorter_horizontal_guideline_padding">661dp</dimen> <dimen name="biometric_prompt_two_pane_medium_horizontal_guideline_padding">640dp</dimen> - <dimen name="biometric_prompt_two_pane_medium_mid_guideline_padding">330dp</dimen> <dimen name="biometric_prompt_one_pane_medium_top_guideline_padding">119dp</dimen> <dimen name="biometric_prompt_one_pane_medium_horizontal_guideline_padding">0dp</dimen> @@ -1674,6 +1679,7 @@ <dimen name="rounded_slider_background_padding">8dp</dimen> <!-- rounded_slider_corner_radius + rounded_slider_background_padding --> <dimen name="rounded_slider_background_rounded_corner">32dp</dimen> + <dimen name="rounded_slider_boundary_offset">16dp</dimen> <!-- Location on the screen of the center of the physical power button. This is a reasonable default that should be overridden by device-specific overlays. --> diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index cac2df5b45d9..0d23a6da0973 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -319,6 +319,29 @@ <string name="screenrecord_save_error">Error saving screen recording</string> <!-- A toast message shown when the screen recording cannot be started due to a generic error [CHAR LIMIT=NONE] --> <string name="screenrecord_start_error">Error starting screen recording</string> + <!-- Title for a dialog shown to the user that will let them stop recording their screen [CHAR LIMIT=50] --> + <string name="screenrecord_stop_dialog_title">Stop recording screen?</string> + <!-- Text telling a user that they will stop recording their screen if they click the "Stop recording" button [CHAR LIMIT=100] --> + <string name="screenrecord_stop_dialog_message">You will stop recording your screen</string> + <!-- Button to stop a screen recording [CHAR LIMIT=35] --> + <string name="screenrecord_stop_dialog_button">Stop recording</string> + + <!-- Title for a dialog shown to the user that will let them stop sharing their screen to another app on the device [CHAR LIMIT=50] --> + <string name="share_to_app_stop_dialog_title">Stop sharing screen?</string> + <!-- Text telling a user that they will stop sharing their screen if they click the "Stop sharing" button [CHAR LIMIT=100] --> + <string name="share_to_app_stop_dialog_message">You will stop sharing your screen</string> + <!-- Button to stop screen sharing [CHAR LIMIT=35] --> + <string name="share_to_app_stop_dialog_button">Stop sharing</string> + + <!-- Title for a dialog shown to the user that will let them stop casting their screen to a different device [CHAR LIMIT=50] --> + <string name="cast_to_other_device_stop_dialog_title">Stop casting screen?</string> + <!-- Text telling a user that they will stop casting their screen to a different device if they click the "Stop casting" button [CHAR LIMIT=100] --> + <string name="cast_to_other_device_stop_dialog_message">You will stop casting your screen</string> + <!-- Button to stop screen casting to a different device [CHAR LIMIT=35] --> + <string name="cast_to_other_device_stop_dialog_button">Stop casting</string> + + <!-- Button to close a dialog without doing any action [CHAR LIMIT=20] --> + <string name="close_dialog_button">Close</string> <!-- Notification title displayed for issue recording [CHAR LIMIT=50]--> <string name="issuerecord_title">Issue Recorder</string> @@ -344,7 +367,7 @@ <!-- Cling help message title when hiding the navigation bar entering immersive mode [CHAR LIMIT=none] --> <string name="immersive_cling_title">Viewing full screen</string> <!-- Cling help message description when hiding the navigation bar entering immersive mode [CHAR LIMIT=none] --> - <string name="immersive_cling_description">To exit, swipe down from the top.</string> + <string name="immersive_cling_description">To exit, swipe down from the top of your screen</string> <!-- Cling help message confirmation button when hiding the navigation bar entering immersive mode [CHAR LIMIT=30] --> <string name="immersive_cling_positive">Got it</string> @@ -693,15 +716,15 @@ <!-- QuickSettings: Accessibility label to activate a device [CHAR LIMIT=NONE]--> <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate">activate</string> <!-- QuickSettings: Bluetooth auto on tomorrow [CHAR LIMIT=NONE]--> - <string name="turn_on_bluetooth_auto_tomorrow">Automatically turn on again tomorrow</string> + <string name="turn_on_bluetooth_auto_tomorrow">Automatically turn on tomorrow</string> <!-- QuickSettings: Bluetooth auto on info text when disabled [CHAR LIMIT=NONE]--> <string name="turn_on_bluetooth_auto_info_disabled">Features like Quick Share and Find My Device use Bluetooth</string> <!-- QuickSettings: Bluetooth auto on info text when enabled [CHAR LIMIT=NONE]--> <string name="turn_on_bluetooth_auto_info_enabled">Bluetooth will turn on tomorrow morning</string> <!-- QuickSettings: Bluetooth dialog audio sharing button text [CHAR LIMIT=50]--> - <string name="quick_settings_bluetooth_audio_sharing_button">Audio Sharing</string> + <string name="quick_settings_bluetooth_audio_sharing_button">Share audio</string> <!-- QuickSettings: Bluetooth dialog audio sharing button text when sharing audio [CHAR LIMIT=50]--> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing">Sharing Audio</string> + <string name="quick_settings_bluetooth_audio_sharing_button_sharing">Sharing audio</string> <!-- QuickSettings: Bluetooth secondary label for the battery level of a connected device [CHAR LIMIT=20]--> <string name="quick_settings_bluetooth_secondary_label_battery_level"><xliff:g id="battery_level_as_percentage">%s</xliff:g> battery</string> @@ -905,6 +928,10 @@ <!-- Hearing devices --> <!-- QuickSettings: Hearing devices [CHAR LIMIT=NONE] --> <string name="quick_settings_hearing_devices_label">Hearing devices</string> + <!-- QuickSettings: Hearing Devices' secondary label shown when the hearing aids are currently connected. [CHAR LIMIT=NONE] --> + <string name="quick_settings_hearing_devices_connected">Active</string> + <!-- QuickSettings: Hearing devices' secondary label shown when there is no connected hearing aids. [CHAR LIMIT=NONE] --> + <string name="quick_settings_hearing_devices_disconnected">Disconnected</string> <!-- QuickSettings: Quick Settings Hearing devices dialog title [CHAR LIMIT=30] --> <string name="quick_settings_hearing_devices_dialog_title">Hearing devices</string> <!-- QuickSettings: Hearing devices dialog pair new device [CHAR LIMIT=NONE]--> @@ -3525,6 +3552,23 @@ use. The helper shows shortcuts in categories, which can be collapsed or expanded. [CHAR LIMIT=NONE] --> <string name="shortcut_helper_content_description_expand_icon">Expand icon</string> + <!-- Word that separates different possible key combinations of a shortcut. For example the + "Go to home screen" shortcut could be triggered using "home button" OR "ctrl + h". + This is that "or" separator. + The keyboard shortcut helper is a component that shows the user which keyboard shortcuts + they can use. + [CHAR LIMIT=NONE] + --> + <string name="shortcut_helper_key_combinations_or_separator">or</string> + + <!-- Label for button opening tutorial for back gesture on touchpad [CHAR LIMIT=NONE] --> + <string name="touchpad_tutorial_back_gesture_button">Back gesture</string> + <!-- Label for button opening tutorial for back gesture on touchpad [CHAR LIMIT=NONE] --> + <string name="touchpad_tutorial_home_gesture_button">Home gesture</string> + <!-- Label for button opening tutorial on using Action key from keyboard [CHAR LIMIT=NONE] --> + <string name="touchpad_tutorial_action_key_button">Action key</string> + <!-- Label for button finishing touchpad tutorial [CHAR LIMIT=NONE] --> + <string name="touchpad_tutorial_done_button">Done</string> <!-- Content description for keyboard backlight brightness dialog [CHAR LIMIT=NONE] --> <string name="keyboard_backlight_dialog_title">Keyboard backlight</string> diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml index 1e0adec4e84f..73b7586f1210 100644 --- a/packages/SystemUI/res/values/styles.xml +++ b/packages/SystemUI/res/values/styles.xml @@ -338,8 +338,11 @@ <item name="android:textSize">16sp</item> </style> - <style name="AuthCredentialPanelStyle"> + <style name="AuthNonCredentialPanelStyle"> <item name="android:background">?androidprv:attr/materialColorSurfaceBright</item> + </style> + + <style name="AuthCredentialPanelStyle" parent="AuthNonCredentialPanelStyle"> <item name="android:clickable">true</item> <item name="android:clipToOutline">true</item> <item name="android:importantForAccessibility">no</item> diff --git a/packages/SystemUI/shared/biometrics/src/com/android/systemui/biometrics/Utils.kt b/packages/SystemUI/shared/biometrics/src/com/android/systemui/biometrics/Utils.kt index 8979ef1aa160..12d881b20ca1 100644 --- a/packages/SystemUI/shared/biometrics/src/com/android/systemui/biometrics/Utils.kt +++ b/packages/SystemUI/shared/biometrics/src/com/android/systemui/biometrics/Utils.kt @@ -25,7 +25,11 @@ import android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX import android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_SOMETHING import android.content.Context import android.content.pm.PackageManager +import android.graphics.Bitmap +import android.graphics.Canvas import android.graphics.Insets +import android.graphics.drawable.BitmapDrawable +import android.graphics.drawable.Drawable import android.hardware.biometrics.BiometricManager.Authenticators import android.hardware.biometrics.PromptInfo import android.hardware.biometrics.SensorPropertiesInternal @@ -122,4 +126,26 @@ object Utils { return windowMetrics?.windowInsets?.getInsets(WindowInsets.Type.navigationBars()) ?: Insets.NONE } + + /** Converts `drawable` to a [Bitmap]. */ + @JvmStatic + fun Drawable?.toBitmap(): Bitmap? { + if (this == null) { + return null + } + if (this is BitmapDrawable) { + return bitmap + } + val bitmap: Bitmap = + if (intrinsicWidth <= 0 || intrinsicHeight <= 0) { + Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888) + // Single color bitmap will be created of 1x1 pixel + } else { + Bitmap.createBitmap(intrinsicWidth, intrinsicHeight, Bitmap.Config.ARGB_8888) + } + val canvas = Canvas(bitmap) + setBounds(0, 0, canvas.width, canvas.height) + draw(canvas) + return bitmap + } } diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/animation/UnfoldConstantTranslateAnimator.kt b/packages/SystemUI/shared/src/com/android/systemui/shared/animation/UnfoldConstantTranslateAnimator.kt index 7a8c82cee32a..4fd54566bfb6 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/animation/UnfoldConstantTranslateAnimator.kt +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/animation/UnfoldConstantTranslateAnimator.kt @@ -37,10 +37,17 @@ class UnfoldConstantTranslateAnimator( private lateinit var rootView: ViewGroup private var translationMax = 0f + /** + * Initializes the animator, it is allowed to call this method multiple times, for example + * to update the rootView or maximum translation + */ fun init(rootView: ViewGroup, translationMax: Float) { + if (!::rootView.isInitialized) { + progressProvider.addCallback(this) + } + this.rootView = rootView this.translationMax = translationMax - progressProvider.addCallback(this) } override fun onTransitionStarted() { diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/pip/PipSurfaceTransactionHelper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/pip/PipSurfaceTransactionHelper.java index c33b7ce1d002..c225cbcc6e81 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/pip/PipSurfaceTransactionHelper.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/pip/PipSurfaceTransactionHelper.java @@ -87,10 +87,15 @@ public class PipSurfaceTransactionHelper { mTmpDestinationRect.inset(insets); // Scale to the bounds no smaller than the destination and offset such that the top/left // of the scaled inset source rect aligns with the top/left of the destination bounds - final float scale; + final float scale, left, top; if (sourceRectHint.isEmpty() || sourceRectHint.width() == sourceBounds.width()) { scale = Math.max((float) destinationBounds.width() / sourceBounds.width(), (float) destinationBounds.height() / sourceBounds.height()); + // Work around the rounding error by fix the position at very beginning. + left = scale == 1 + ? 0 : destinationBounds.left - (insets.left + sourceBounds.left) * scale; + top = scale == 1 + ? 0 : destinationBounds.top - (insets.top + sourceBounds.top) * scale; } else { // scale by sourceRectHint if it's not edge-to-edge final float endScale = sourceRectHint.width() <= sourceRectHint.height() @@ -100,9 +105,9 @@ public class PipSurfaceTransactionHelper { ? (float) destinationBounds.width() / sourceBounds.width() : (float) destinationBounds.height() / sourceBounds.height(); scale = Math.min((1 - progress) * startScale + progress * endScale, 1.0f); + left = destinationBounds.left - (insets.left + sourceBounds.left) * scale; + top = destinationBounds.top - (insets.top + sourceBounds.top) * scale; } - final float left = destinationBounds.left - (insets.left + sourceBounds.left) * scale; - final float top = destinationBounds.top - (insets.top + sourceBounds.top) * scale; mTmpTransform.setScale(scale, scale); final float cornerRadius = getScaledCornerRadius(mTmpDestinationRect, destinationBounds); tx.setMatrix(leash, mTmpTransform, mTmpFloat9) diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/ThumbnailData.kt b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/ThumbnailData.kt index dcf7754221bb..757760f63c0f 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/ThumbnailData.kt +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/ThumbnailData.kt @@ -70,10 +70,10 @@ data class ThumbnailData( @JvmStatic fun wrap(taskIds: IntArray?, snapshots: Array<TaskSnapshot>?): HashMap<Int, ThumbnailData> { - return if (taskIds == null || snapshots == null || taskIds.size != snapshots.size) { - HashMap() - } else { - HashMap(taskIds.associateWith { taskId -> fromSnapshot(snapshots[taskId]) }) + return hashMapOf<Int, ThumbnailData>().apply { + if (taskIds != null && snapshots != null && taskIds.size == snapshots.size) { + repeat(snapshots.size) { put(taskIds[it], fromSnapshot(snapshots[it])) } + } } } diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java index b4377eae4d1f..c0c8b755108c 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java @@ -122,6 +122,8 @@ public class QuickStepContract { public static final long SYSUI_STATE_STATUS_BAR_KEYGUARD_GOING_AWAY = 1L << 31; // Physical keyboard shortcuts helper is showing public static final long SYSUI_STATE_SHORTCUT_HELPER_SHOWING = 1L << 32; + // Touchpad gestures are disabled + public static final long SYSUI_STATE_TOUCHPAD_GESTURES_DISABLED = 1L << 33; // Mask for SystemUiStateFlags to isolate SYSUI_STATE_AWAKE and // SYSUI_STATE_WAKEFULNESS_TRANSITION, to match WAKEFULNESS_* constants @@ -170,6 +172,7 @@ public class QuickStepContract { SYSUI_STATE_NOTIFICATION_PANEL_VISIBLE, SYSUI_STATE_STATUS_BAR_KEYGUARD_GOING_AWAY, SYSUI_STATE_SHORTCUT_HELPER_SHOWING, + SYSUI_STATE_TOUCHPAD_GESTURES_DISABLED, }) public @interface SystemUiStateFlags {} @@ -271,6 +274,9 @@ public class QuickStepContract { if ((flags & SYSUI_STATE_SHORTCUT_HELPER_SHOWING) != 0) { str.add("shortcut_helper_showing"); } + if ((flags & SYSUI_STATE_TOUCHPAD_GESTURES_DISABLED) != 0) { + str.add("touchpad_gestures_disabled"); + } return str.toString(); } diff --git a/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt index 86c807bf9d07..5dcf1618ed6b 100644 --- a/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt +++ b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt @@ -178,6 +178,7 @@ constructor( smallClockOnAttachStateChangeListener = object : OnAttachStateChangeListener { var pastVisibility: Int? = null + override fun onViewAttachedToWindow(view: View) { clock.events.onTimeFormatChanged(DateFormat.is24HourFormat(context)) // Match the asing for view.parent's layout classes. @@ -213,6 +214,7 @@ constructor( override fun onViewAttachedToWindow(p0: View) { clock.events.onTimeFormatChanged(DateFormat.is24HourFormat(context)) } + override fun onViewDetachedFromWindow(p0: View) {} } clock.largeClock.view.addOnAttachStateChangeListener(largeClockOnAttachStateChangeListener) @@ -284,8 +286,10 @@ constructor( var smallRegionSampler: RegionSampler? = null private set + var largeRegionSampler: RegionSampler? = null private set + var smallTimeListener: TimeListener? = null var largeTimeListener: TimeListener? = null val shouldTimeListenerRun: Boolean @@ -560,7 +564,7 @@ constructor( internal fun listenForAnyStateToAodTransition(scope: CoroutineScope): Job { return scope.launch { keyguardTransitionInteractor - .transitionStepsToState(AOD) + .transition(Edge.create(to = AOD)) .filter { it.transitionState == TransitionState.STARTED } .filter { it.from != LOCKSCREEN } .collect { handleDoze(1f) } @@ -571,7 +575,7 @@ constructor( internal fun listenForAnyStateToLockscreenTransition(scope: CoroutineScope): Job { return scope.launch { keyguardTransitionInteractor - .transitionStepsToState(LOCKSCREEN) + .transition(Edge.create(to = LOCKSCREEN)) .filter { it.transitionState == TransitionState.STARTED } .filter { it.from != AOD } .collect { handleDoze(0f) } @@ -586,7 +590,7 @@ constructor( internal fun listenForAnyStateToDozingTransition(scope: CoroutineScope): Job { return scope.launch { keyguardTransitionInteractor - .transitionStepsToState(DOZING) + .transition(Edge.create(to = DOZING)) .filter { it.transitionState == TransitionState.FINISHED } .collect { handleDoze(1f) } } diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUnfoldTransition.kt b/packages/SystemUI/src/com/android/keyguard/KeyguardUnfoldTransition.kt index 7170be6124d2..19d918f5c556 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardUnfoldTransition.kt +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUnfoldTransition.kt @@ -17,16 +17,21 @@ package com.android.keyguard import android.content.Context -import android.view.ViewGroup -import com.android.systemui.res.R +import android.view.View +import com.android.systemui.keyguard.MigrateClocksToBlueprint +import com.android.systemui.keyguard.ui.view.KeyguardRootView import com.android.systemui.plugins.statusbar.StatusBarStateController -import com.android.systemui.statusbar.StatusBarState.KEYGUARD +import com.android.systemui.res.R +import com.android.systemui.shared.R as sharedR +import com.android.systemui.shade.NotificationShadeWindowView 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.KEYGUARD import com.android.systemui.unfold.SysUIUnfoldScope -import com.android.systemui.unfold.util.NaturalRotationUnfoldProgressProvider +import com.android.systemui.unfold.UnfoldTransitionProgressProvider +import com.android.systemui.unfold.dagger.NaturalRotation import javax.inject.Inject /** @@ -38,8 +43,10 @@ class KeyguardUnfoldTransition @Inject constructor( private val context: Context, + private val keyguardRootView: KeyguardRootView, + private val shadeWindowView: NotificationShadeWindowView, statusBarStateController: StatusBarStateController, - unfoldProgressProvider: NaturalRotationUnfoldProgressProvider, + @NaturalRotation unfoldProgressProvider: UnfoldTransitionProgressProvider, ) { /** Certain views only need to move if they are not currently centered */ @@ -50,27 +57,94 @@ constructor( private val filterKeyguard: () -> Boolean = { statusBarStateController.getState() == KEYGUARD } private val translateAnimator by lazy { + val smartSpaceViews = if (MigrateClocksToBlueprint.isEnabled) { + // Use scrollX instead of translationX as translation is already set by [AodBurnInLayer] + val scrollXTranslation = { view: View, translation: Float -> + view.scrollX = -translation.toInt() + } + + setOf( + ViewIdToTranslate( + viewId = sharedR.id.date_smartspace_view, + direction = START, + shouldBeAnimated = filterKeyguard, + translateFunc = scrollXTranslation, + ), + ViewIdToTranslate( + viewId = sharedR.id.bc_smartspace_view, + direction = START, + shouldBeAnimated = filterKeyguard, + translateFunc = scrollXTranslation, + ), + ViewIdToTranslate( + viewId = sharedR.id.weather_smartspace_view, + direction = START, + shouldBeAnimated = filterKeyguard, + translateFunc = scrollXTranslation, + ) + ) + } else { + setOf(ViewIdToTranslate( + viewId = R.id.keyguard_status_area, + direction = START, + shouldBeAnimated = filterKeyguard, + translateFunc = { view, value -> + (view as? KeyguardStatusAreaView)?.translateXFromUnfold = value + } + )) + } + UnfoldConstantTranslateAnimator( viewsIdToTranslate = setOf( - ViewIdToTranslate(R.id.keyguard_status_area, START, filterKeyguard, - { view, value -> - (view as? KeyguardStatusAreaView)?.translateXFromUnfold = value - }), ViewIdToTranslate( - R.id.lockscreen_clock_view_large, START, filterKeyguardAndSplitShadeOnly), - ViewIdToTranslate(R.id.lockscreen_clock_view, START, filterKeyguard), + viewId = R.id.lockscreen_clock_view_large, + direction = START, + shouldBeAnimated = filterKeyguardAndSplitShadeOnly + ), + ViewIdToTranslate( + viewId = R.id.lockscreen_clock_view, + direction = START, + shouldBeAnimated = filterKeyguard + ), ViewIdToTranslate( - R.id.notification_stack_scroller, END, filterKeyguardAndSplitShadeOnly), - ViewIdToTranslate(R.id.start_button, START, filterKeyguard), - ViewIdToTranslate(R.id.end_button, END, filterKeyguard)), - progressProvider = unfoldProgressProvider) + viewId = R.id.notification_stack_scroller, + direction = END, + shouldBeAnimated = filterKeyguardAndSplitShadeOnly + ) + ) + smartSpaceViews, + progressProvider = unfoldProgressProvider + ) } - /** Relies on the [parent] to locate views to translate. */ - fun setup(parent: ViewGroup) { + private val shortcutButtonsAnimator by lazy { + UnfoldConstantTranslateAnimator( + viewsIdToTranslate = + setOf( + ViewIdToTranslate( + viewId = R.id.start_button, + direction = START, + shouldBeAnimated = filterKeyguard + ), + ViewIdToTranslate( + viewId = R.id.end_button, + direction = END, + shouldBeAnimated = filterKeyguard + ) + ), + progressProvider = unfoldProgressProvider + ) + } + + /** Initializes the keyguard fold/unfold transition */ + fun setup() { val translationMax = context.resources.getDimensionPixelSize(R.dimen.keyguard_unfold_translation_x).toFloat() - translateAnimator.init(parent, translationMax) + + translateAnimator.init(shadeWindowView, translationMax) + + // Use keyguard root view as there is another instance of start/end buttons with the same ID + // outside of the keyguard root view + shortcutButtonsAnimator.init(keyguardRootView, translationMax) } } diff --git a/packages/SystemUI/src/com/android/keyguard/LegacyLockIconViewController.java b/packages/SystemUI/src/com/android/keyguard/LegacyLockIconViewController.java index a9fd34015e73..03b13fe47c10 100644 --- a/packages/SystemUI/src/com/android/keyguard/LegacyLockIconViewController.java +++ b/packages/SystemUI/src/com/android/keyguard/LegacyLockIconViewController.java @@ -70,7 +70,7 @@ import com.android.systemui.keyguard.KeyguardBottomAreaRefactor; import com.android.systemui.keyguard.MigrateClocksToBlueprint; import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor; import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor; -import com.android.systemui.keyguard.shared.model.TransitionStep; +import com.android.systemui.keyguard.shared.model.KeyguardState; import com.android.systemui.plugins.FalsingManager; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.res.R; @@ -167,9 +167,9 @@ public class LegacyLockIconViewController implements Dumpable, LockIconViewContr private LockIconView mView; @VisibleForTesting - final Consumer<TransitionStep> mDozeTransitionCallback = (TransitionStep step) -> { - mInterpolatedDarkAmount = step.getValue(); - mView.setDozeAmount(step.getValue()); + final Consumer<Float> mDozeTransitionCallback = (Float value) -> { + mInterpolatedDarkAmount = value; + mView.setDozeAmount(value); updateBurnInOffsets(); }; @@ -265,7 +265,7 @@ public class LegacyLockIconViewController implements Dumpable, LockIconViewContr mView.setAccessibilityDelegate(mAccessibilityDelegate); if (mFeatureFlags.isEnabled(DOZING_MIGRATION_1)) { - collectFlow(mView, mTransitionInteractor.getDozeAmountTransition(), + collectFlow(mView, mTransitionInteractor.transitionValue(KeyguardState.AOD), mDozeTransitionCallback); collectFlow(mView, mKeyguardInteractor.isDozing(), mIsDozingCallback); } diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/FullscreenMagnificationController.java b/packages/SystemUI/src/com/android/systemui/accessibility/FullscreenMagnificationController.java index 0bd6d6ede9ef..3c4c0034b7b9 100644 --- a/packages/SystemUI/src/com/android/systemui/accessibility/FullscreenMagnificationController.java +++ b/packages/SystemUI/src/com/android/systemui/accessibility/FullscreenMagnificationController.java @@ -30,7 +30,12 @@ import android.content.res.Configuration; import android.graphics.PixelFormat; import android.graphics.Rect; import android.graphics.Region; +import android.os.Handler; +import android.util.Log; import android.view.AttachedSurfaceControl; +import android.view.Display; +import android.view.IRotationWatcher; +import android.view.IWindowManager; import android.view.LayoutInflater; import android.view.SurfaceControl; import android.view.SurfaceControlViewHost; @@ -46,15 +51,18 @@ import androidx.annotation.UiThread; import com.android.internal.annotations.VisibleForTesting; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.res.R; +import com.android.systemui.util.leak.RotationUtils; import java.util.concurrent.Executor; import java.util.function.Supplier; class FullscreenMagnificationController implements ComponentCallbacks { + private static final String TAG = "FullscreenMagnificationController"; private final Context mContext; private final AccessibilityManager mAccessibilityManager; private final WindowManager mWindowManager; + private final IWindowManager mIWindowManager; private Supplier<SurfaceControlViewHost> mScvhSupplier; private SurfaceControlViewHost mSurfaceControlViewHost = null; private SurfaceControl mBorderSurfaceControl = null; @@ -65,33 +73,50 @@ class FullscreenMagnificationController implements ComponentCallbacks { private final int mDisplayId; private static final Region sEmptyRegion = new Region(); private ValueAnimator mShowHideBorderAnimator; + private Handler mHandler; private Executor mExecutor; private boolean mFullscreenMagnificationActivated = false; private final Configuration mConfiguration; + private final Runnable mShowBorderRunnable = this::showBorderWithNullCheck; + private int mRotation; + private final IRotationWatcher mRotationWatcher = new IRotationWatcher.Stub() { + @Override + public void onRotationChanged(final int rotation) { + handleScreenRotation(); + } + }; + private final long mLongAnimationTimeMs; FullscreenMagnificationController( @UiContext Context context, - Executor executor, + @Main Handler handler, + @Main Executor executor, AccessibilityManager accessibilityManager, WindowManager windowManager, + IWindowManager iWindowManager, Supplier<SurfaceControlViewHost> scvhSupplier) { - this(context, executor, accessibilityManager, windowManager, scvhSupplier, - new SurfaceControl.Transaction(), createNullTargetObjectAnimator(context)); + this(context, handler, executor, accessibilityManager, + windowManager, iWindowManager, scvhSupplier, + new SurfaceControl.Transaction(), null); } @VisibleForTesting FullscreenMagnificationController( @UiContext Context context, + @Main Handler handler, @Main Executor executor, AccessibilityManager accessibilityManager, WindowManager windowManager, + IWindowManager iWindowManager, Supplier<SurfaceControlViewHost> scvhSupplier, SurfaceControl.Transaction transaction, ValueAnimator valueAnimator) { mContext = context; + mHandler = handler; mExecutor = executor; mAccessibilityManager = accessibilityManager; mWindowManager = windowManager; + mIWindowManager = iWindowManager; mWindowBounds = mWindowManager.getCurrentWindowMetrics().getBounds(); mTransaction = transaction; mScvhSupplier = scvhSupplier; @@ -101,7 +126,10 @@ class FullscreenMagnificationController implements ComponentCallbacks { R.dimen.magnifier_border_width_fullscreen); mDisplayId = mContext.getDisplayId(); mConfiguration = new Configuration(context.getResources().getConfiguration()); - mShowHideBorderAnimator = valueAnimator; + mLongAnimationTimeMs = mContext.getResources().getInteger( + com.android.internal.R.integer.config_longAnimTime); + mShowHideBorderAnimator = (valueAnimator == null) + ? createNullTargetObjectAnimator() : valueAnimator; mShowHideBorderAnimator.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(@NonNull Animator animation, boolean isReverse) { @@ -114,15 +142,13 @@ class FullscreenMagnificationController implements ComponentCallbacks { }); } - private static ValueAnimator createNullTargetObjectAnimator(Context context) { + private ValueAnimator createNullTargetObjectAnimator() { final ValueAnimator valueAnimator = ObjectAnimator.ofFloat(/* target= */ null, View.ALPHA, 0f, 1f); Interpolator interpolator = new AccelerateDecelerateInterpolator(); - final long longAnimationDuration = context.getResources().getInteger( - com.android.internal.R.integer.config_longAnimTime); valueAnimator.setInterpolator(interpolator); - valueAnimator.setDuration(longAnimationDuration); + valueAnimator.setDuration(mLongAnimationTimeMs); return valueAnimator; } @@ -149,7 +175,11 @@ class FullscreenMagnificationController implements ComponentCallbacks { */ @UiThread private void removeFullscreenMagnificationBorder() { + if (mHandler.hasCallbacks(mShowBorderRunnable)) { + mHandler.removeCallbacks(mShowBorderRunnable); + } mContext.unregisterComponentCallbacks(this); + mShowHideBorderAnimator.reverse(); } @@ -161,6 +191,11 @@ class FullscreenMagnificationController implements ComponentCallbacks { if (mFullscreenBorder != null) { mFullscreenBorder = null; + try { + mIWindowManager.removeRotationWatcher(mRotationWatcher); + } catch (Exception e) { + Log.w(TAG, "Failed to remove rotation watcher", e); + } } } @@ -186,6 +221,11 @@ class FullscreenMagnificationController implements ComponentCallbacks { mSurfaceControlViewHost = mScvhSupplier.get(); mSurfaceControlViewHost.setView(mFullscreenBorder, getBorderLayoutParams()); mBorderSurfaceControl = mSurfaceControlViewHost.getSurfacePackage().getSurfaceControl(); + try { + mIWindowManager.watchRotation(mRotationWatcher, Display.DEFAULT_DISPLAY); + } catch (Exception e) { + Log.w(TAG, "Failed to register rotation watcher", e); + } } mTransaction @@ -256,11 +296,55 @@ class FullscreenMagnificationController implements ComponentCallbacks { reCreateWindow = true; } - if (mFullscreenBorder != null && reCreateWindow) { + if (mFullscreenBorder == null) { + return; + } + + if (reCreateWindow) { final int newWidth = mWindowBounds.width() + 2 * mBorderOffset; final int newHeight = mWindowBounds.height() + 2 * mBorderOffset; mSurfaceControlViewHost.relayout(newWidth, newHeight); } + + // Rotating from Landscape to ReverseLandscape will not trigger the config changes in + // CONFIG_SCREEN_SIZE and CONFIG_ORIENTATION. Therefore, we would like to check the device + // rotation separately. + // Since there's a possibility that {@link onConfigurationChanged} comes before + // {@link onRotationChanged}, we would like to handle screen rotation in either case that + // happens earlier. + int newRotation = RotationUtils.getRotation(mContext); + if (newRotation != mRotation) { + mRotation = newRotation; + handleScreenRotation(); + } + } + + private boolean isActivated() { + return mFullscreenBorder != null; + } + + private void handleScreenRotation() { + if (!isActivated()) { + return; + } + + if (mHandler.hasCallbacks(mShowBorderRunnable)) { + mHandler.removeCallbacks(mShowBorderRunnable); + } + + // We hide the border immediately as early as possible to beat the redrawing of window + // in response to the orientation change so users won't see a weird shape border. + mHandler.postAtFrontOfQueue(() -> { + mFullscreenBorder.setAlpha(0f); + }); + + mHandler.postDelayed(mShowBorderRunnable, mLongAnimationTimeMs); + } + + private void showBorderWithNullCheck() { + if (mShowHideBorderAnimator != null) { + mShowHideBorderAnimator.start(); + } } private void updateDimensions() { diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/Magnification.java b/packages/SystemUI/src/com/android/systemui/accessibility/Magnification.java index 35c202437ed7..e22a4e46b513 100644 --- a/packages/SystemUI/src/com/android/systemui/accessibility/Magnification.java +++ b/packages/SystemUI/src/com/android/systemui/accessibility/Magnification.java @@ -34,6 +34,7 @@ import android.os.Looper; import android.os.Message; import android.util.SparseArray; import android.view.Display; +import android.view.IWindowManager; import android.view.SurfaceControl; import android.view.SurfaceControlViewHost; import android.view.WindowManager; @@ -148,13 +149,19 @@ public class Magnification implements CoreStartable, CommandQueue.Callbacks { DisplayIdIndexSupplier<FullscreenMagnificationController> { private final Context mContext; + private final Handler mHandler; private final Executor mExecutor; + private final IWindowManager mIWindowManager; - FullscreenMagnificationControllerSupplier(Context context, DisplayManager displayManager, - Executor executor) { + FullscreenMagnificationControllerSupplier(Context context, + DisplayManager displayManager, + Handler handler, + Executor executor, IWindowManager iWindowManager) { super(displayManager); mContext = context; + mHandler = handler; mExecutor = executor; + mIWindowManager = iWindowManager; } @Override @@ -166,9 +173,11 @@ public class Magnification implements CoreStartable, CommandQueue.Callbacks { windowContext.setTheme(com.android.systemui.res.R.style.Theme_SystemUI); return new FullscreenMagnificationController( windowContext, + mHandler, mExecutor, windowContext.getSystemService(AccessibilityManager.class), windowContext.getSystemService(WindowManager.class), + mIWindowManager, scvhSupplier); } } @@ -211,14 +220,16 @@ public class Magnification implements CoreStartable, CommandQueue.Callbacks { DisplayIdIndexSupplier<MagnificationSettingsController> mMagnificationSettingsSupplier; @Inject - public Magnification(Context context, @Main Handler mainHandler, @Main Executor executor, + public Magnification(Context context, + @Main Handler mainHandler, @Main Executor executor, CommandQueue commandQueue, ModeSwitchesController modeSwitchesController, SysUiState sysUiState, OverviewProxyService overviewProxyService, SecureSettings secureSettings, DisplayTracker displayTracker, - DisplayManager displayManager, AccessibilityLogger a11yLogger) { + DisplayManager displayManager, AccessibilityLogger a11yLogger, + IWindowManager iWindowManager) { this(context, mainHandler.getLooper(), executor, commandQueue, modeSwitchesController, sysUiState, overviewProxyService, secureSettings, - displayTracker, displayManager, a11yLogger); + displayTracker, displayManager, a11yLogger, iWindowManager); } @VisibleForTesting @@ -226,7 +237,8 @@ public class Magnification implements CoreStartable, CommandQueue.Callbacks { CommandQueue commandQueue, ModeSwitchesController modeSwitchesController, SysUiState sysUiState, OverviewProxyService overviewProxyService, SecureSettings secureSettings, DisplayTracker displayTracker, - DisplayManager displayManager, AccessibilityLogger a11yLogger) { + DisplayManager displayManager, AccessibilityLogger a11yLogger, + IWindowManager iWindowManager) { mContext = context; mHandler = new Handler(looper) { @Override @@ -248,7 +260,7 @@ public class Magnification implements CoreStartable, CommandQueue.Callbacks { mHandler, mWindowMagnifierCallback, displayManager, sysUiState, secureSettings); mFullscreenMagnificationControllerSupplier = new FullscreenMagnificationControllerSupplier( - context, displayManager, mExecutor); + context, displayManager, mHandler, mExecutor, iWindowManager); mMagnificationSettingsSupplier = new SettingsSupplier(context, mMagnificationSettingsControllerCallback, displayManager, secureSettings); diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuAnimationController.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuAnimationController.java index d5e911efe570..dafd5f89f417 100644 --- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuAnimationController.java +++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuAnimationController.java @@ -311,6 +311,7 @@ class MenuAnimationController { constrainPositionAndUpdate( new PointF(mMenuView.getTranslationX(), mMenuView.getTranslationY()), /* writeToPosition = */ true); + mMenuView.onPositionChanged(true); moveToEdgeAndHide(); return true; } diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuListViewTouchHandler.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuListViewTouchHandler.java index 75191685b119..9511e3769a8d 100644 --- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuListViewTouchHandler.java +++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuListViewTouchHandler.java @@ -107,6 +107,7 @@ class MenuListViewTouchHandler implements RecyclerView.OnItemTouchListener { mVelocityTracker.computeCurrentVelocity(VELOCITY_UNIT_SECONDS); mMenuAnimationController.flingMenuThenSpringToEdge(endX, mVelocityTracker.getXVelocity(), mVelocityTracker.getYVelocity()); + mMenuAnimationController.fadeOutIfEnabled(); } // Avoid triggering the listener of the item. return true; diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesChecker.java b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesChecker.java new file mode 100644 index 000000000000..2d1cd03aea4d --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesChecker.java @@ -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.accessibility.hearingaid; + +import android.bluetooth.BluetoothDevice; +import android.content.Context; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.annotation.WorkerThread; + +import com.android.settingslib.bluetooth.BluetoothUtils; +import com.android.settingslib.bluetooth.CachedBluetoothDevice; +import com.android.settingslib.bluetooth.LocalBluetoothManager; +import com.android.systemui.dagger.SysUISingleton; + +import javax.inject.Inject; + +/** + * HearingDevicesChecker provides utility methods to determine the presence and status of + * connected hearing aid devices. + * + * <p>It also filters out devices that are exclusively managed by other applications to avoid + * interfering with their operation. + */ +@SysUISingleton +public class HearingDevicesChecker { + + private final Context mContext; + private final LocalBluetoothManager mLocalBluetoothManager; + + @Inject + public HearingDevicesChecker( + Context context, + @Nullable LocalBluetoothManager localBluetoothManager) { + mContext = context; + mLocalBluetoothManager = localBluetoothManager; + } + + /** + * Checks if any hearing device is already paired. + * + * <p>It includes {@link BluetoothDevice.BOND_BONDING} and {@link BluetoothDevice.BOND_BONDED}). + * + * <p>A bonded device means it has been paired, but may not connected now. + * + * @return {@code true} if any bonded hearing device is found, {@code false} otherwise. + */ + @WorkerThread + public boolean isAnyPairedHearingDevice() { + if (mLocalBluetoothManager == null) { + return false; + } + if (!mLocalBluetoothManager.getBluetoothAdapter().isEnabled()) { + return false; + } + + return mLocalBluetoothManager.getCachedDeviceManager().getCachedDevicesCopy().stream() + .anyMatch(device -> device.isHearingAidDevice() + && device.getBondState() != BluetoothDevice.BOND_NONE + && !isExclusivelyManagedBluetoothDevice(device)); + } + + /** + * Checks if there are any active hearing device. + * + * <p>An active device means it is currently connected and streaming media. + * + * @return {@code true} if any active hearing device is found, {@code false} otherwise. + */ + @WorkerThread + public boolean isAnyActiveHearingDevice() { + if (mLocalBluetoothManager == null) { + return false; + } + if (!mLocalBluetoothManager.getBluetoothAdapter().isEnabled()) { + return false; + } + + return mLocalBluetoothManager.getCachedDeviceManager().getCachedDevicesCopy().stream() + .anyMatch(device -> BluetoothUtils.isActiveMediaDevice(device) + && BluetoothUtils.isAvailableHearingDevice(device) + && !isExclusivelyManagedBluetoothDevice(device)); + } + + private boolean isExclusivelyManagedBluetoothDevice( + @NonNull CachedBluetoothDevice cachedDevice) { + if (com.android.settingslib.flags.Flags.enableHideExclusivelyManagedBluetoothDevice()) { + return BluetoothUtils.isExclusivelyManagedBluetoothDevice(mContext, + cachedDevice.getDevice()); + } + return false; + } +} diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogManager.java b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogManager.java index 14e5f3422a27..bc4cb45582ff 100644 --- a/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogManager.java +++ b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogManager.java @@ -16,19 +16,24 @@ package com.android.systemui.accessibility.hearingaid; -import android.bluetooth.BluetoothDevice; import android.util.Log; -import androidx.annotation.Nullable; +import androidx.concurrent.futures.CallbackToFutureAdapter; import com.android.internal.jank.InteractionJankMonitor; -import com.android.settingslib.bluetooth.LocalBluetoothManager; import com.android.systemui.animation.DialogCuj; import com.android.systemui.animation.DialogTransitionAnimator; import com.android.systemui.animation.Expandable; import com.android.systemui.dagger.SysUISingleton; +import com.android.systemui.dagger.qualifiers.Background; +import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.statusbar.phone.SystemUIDialog; +import com.google.common.util.concurrent.ListenableFuture; + +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executor; + import javax.inject.Inject; /** @@ -43,16 +48,22 @@ public class HearingDevicesDialogManager { private SystemUIDialog mDialog; private final DialogTransitionAnimator mDialogTransitionAnimator; private final HearingDevicesDialogDelegate.Factory mDialogFactory; - private final LocalBluetoothManager mLocalBluetoothManager; + private final HearingDevicesChecker mDevicesChecker; + private final Executor mBackgroundExecutor; + private final Executor mMainExecutor; @Inject public HearingDevicesDialogManager( DialogTransitionAnimator dialogTransitionAnimator, HearingDevicesDialogDelegate.Factory dialogFactory, - @Nullable LocalBluetoothManager localBluetoothManager) { + HearingDevicesChecker devicesChecker, + @Background Executor backgroundExecutor, + @Main Executor mainExecutor) { mDialogTransitionAnimator = dialogTransitionAnimator; mDialogFactory = dialogFactory; - mLocalBluetoothManager = localBluetoothManager; + mDevicesChecker = devicesChecker; + mBackgroundExecutor = backgroundExecutor; + mMainExecutor = mainExecutor; } /** @@ -68,36 +79,41 @@ public class HearingDevicesDialogManager { destroyDialog(); } - mDialog = mDialogFactory.create(!isAnyBondedHearingDevice()).createDialog(); + final ListenableFuture<Boolean> pairedHearingDeviceCheckTask = + CallbackToFutureAdapter.getFuture(completer -> { + mBackgroundExecutor.execute( + () -> { + completer.set(mDevicesChecker.isAnyPairedHearingDevice()); + }); + // This value is used only for debug purposes: it will be used in toString() + // of returned future or error cases. + return "isAnyPairedHearingDevice check"; + }); + pairedHearingDeviceCheckTask.addListener(() -> { + try { + mDialog = mDialogFactory.create(!pairedHearingDeviceCheckTask.get()).createDialog(); + + if (expandable != null) { + DialogTransitionAnimator.Controller controller = + expandable.dialogTransitionController( + new DialogCuj(InteractionJankMonitor.CUJ_SHADE_DIALOG_OPEN, + INTERACTION_JANK_TAG)); + if (controller != null) { + mDialogTransitionAnimator.show(mDialog, + controller, /* animateBackgroundBoundsChange= */ true); + return; + } + } + mDialog.show(); - if (expandable != null) { - DialogTransitionAnimator.Controller controller = expandable.dialogTransitionController( - new DialogCuj(InteractionJankMonitor.CUJ_SHADE_DIALOG_OPEN, - INTERACTION_JANK_TAG)); - if (controller != null) { - mDialogTransitionAnimator.show(mDialog, - controller, /* animateBackgroundBoundsChange= */ true); - return; + } catch (InterruptedException | ExecutionException e) { + Log.e(TAG, "Exception occurs while running pairedHearingDeviceCheckTask", e); } - } - mDialog.show(); + }, mMainExecutor); } private void destroyDialog() { mDialog.dismiss(); mDialog = null; } - - private boolean isAnyBondedHearingDevice() { - if (mLocalBluetoothManager == null) { - return false; - } - if (!mLocalBluetoothManager.getBluetoothAdapter().isEnabled()) { - return false; - } - - return mLocalBluetoothManager.getCachedDeviceManager().getCachedDevicesCopy().stream() - .anyMatch(device -> device.isHearingAidDevice() - && device.getBondState() != BluetoothDevice.BOND_NONE); - } } diff --git a/packages/SystemUI/src/com/android/systemui/ambient/dagger/AmbientModule.kt b/packages/SystemUI/src/com/android/systemui/ambient/dagger/AmbientModule.kt index ea0039858551..b0314d8fab84 100644 --- a/packages/SystemUI/src/com/android/systemui/ambient/dagger/AmbientModule.kt +++ b/packages/SystemUI/src/com/android/systemui/ambient/dagger/AmbientModule.kt @@ -16,11 +16,19 @@ package com.android.systemui.ambient.dagger +import com.android.systemui.ambient.statusbar.dagger.AmbientStatusBarComponent import com.android.systemui.ambient.touch.dagger.AmbientTouchComponent import com.android.systemui.ambient.touch.dagger.InputSessionComponent import dagger.Module -@Module(subcomponents = [AmbientTouchComponent::class, InputSessionComponent::class]) +@Module( + subcomponents = + [ + AmbientStatusBarComponent::class, + AmbientTouchComponent::class, + InputSessionComponent::class, + ] +) interface AmbientModule { companion object { const val TOUCH_HANDLERS = "touch_handlers" diff --git a/packages/SystemUI/src/com/android/systemui/ambient/statusbar/dagger/AmbientStatusBarComponent.kt b/packages/SystemUI/src/com/android/systemui/ambient/statusbar/dagger/AmbientStatusBarComponent.kt new file mode 100644 index 000000000000..8ad4d000015a --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/ambient/statusbar/dagger/AmbientStatusBarComponent.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.ambient.statusbar.dagger + +import com.android.systemui.ambient.statusbar.ui.AmbientStatusBarView +import com.android.systemui.ambient.statusbar.ui.AmbientStatusBarViewController +import dagger.BindsInstance +import dagger.Subcomponent + +/** + * [AmbientStatusBarComponent] can be used for displaying a status bar over ambient surfaces like + * the dream or communal hub. + */ +@Subcomponent +interface AmbientStatusBarComponent { + @Subcomponent.Factory + interface Factory { + fun create( + @BindsInstance view: AmbientStatusBarView, + ): AmbientStatusBarComponent + } + + /** Builds a [AmbientStatusBarViewController] */ + fun getController(): AmbientStatusBarViewController +} diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarView.java b/packages/SystemUI/src/com/android/systemui/ambient/statusbar/ui/AmbientStatusBarView.java index 8e77079e0b5d..aa9623127d17 100644 --- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarView.java +++ b/packages/SystemUI/src/com/android/systemui/ambient/statusbar/ui/AmbientStatusBarView.java @@ -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,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.dreams; +package com.android.systemui.ambient.statusbar.ui; import android.annotation.IntDef; import android.annotation.Nullable; @@ -39,10 +39,10 @@ import java.util.Map; import java.util.Objects; /** - * {@link DreamOverlayStatusBarView} is the view responsible for displaying the status bar in a + * {@link AmbientStatusBarView} is the view responsible for displaying the status bar in a * dream. The status bar displays conditional status icons such as "priority mode" and "no wifi". */ -public class DreamOverlayStatusBarView extends ConstraintLayout { +public class AmbientStatusBarView extends ConstraintLayout { @Retention(RetentionPolicy.SOURCE) @IntDef(prefix = { "STATUS_ICON_" }, value = { @@ -76,20 +76,20 @@ public class DreamOverlayStatusBarView extends ConstraintLayout { private static final float KEY_SHADOW_ALPHA = 0.35f; private static final float AMBIENT_SHADOW_ALPHA = 0.4f; - public DreamOverlayStatusBarView(Context context) { + public AmbientStatusBarView(Context context) { this(context, null); } - public DreamOverlayStatusBarView(Context context, AttributeSet attrs) { + public AmbientStatusBarView(Context context, AttributeSet attrs) { this(context, attrs, 0); } - public DreamOverlayStatusBarView(Context context, AttributeSet attrs, int defStyleAttr) { + public AmbientStatusBarView(Context context, AttributeSet attrs, int defStyleAttr) { this(context, attrs, defStyleAttr, 0); mContext = context; } - public DreamOverlayStatusBarView( + public AmbientStatusBarView( Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); } diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarViewController.java b/packages/SystemUI/src/com/android/systemui/ambient/statusbar/ui/AmbientStatusBarViewController.java index da72a569e854..abdc3338b7f2 100644 --- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarViewController.java +++ b/packages/SystemUI/src/com/android/systemui/ambient/statusbar/ui/AmbientStatusBarViewController.java @@ -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,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.dreams; +package com.android.systemui.ambient.statusbar.ui; import static com.android.systemui.util.kotlin.JavaAdapterKt.collectFlow; @@ -30,21 +30,24 @@ import android.view.View; import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; +import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor; import com.android.systemui.dagger.qualifiers.Main; +import com.android.systemui.dreams.DreamLogger; +import com.android.systemui.dreams.DreamOverlayNotificationCountProvider; +import com.android.systemui.dreams.DreamOverlayStateController; +import com.android.systemui.dreams.DreamOverlayStatusBarItemsProvider; import com.android.systemui.dreams.DreamOverlayStatusBarItemsProvider.StatusBarItem; -import com.android.systemui.dreams.dagger.DreamOverlayComponent; import com.android.systemui.log.LogBuffer; import com.android.systemui.log.dagger.DreamLog; import com.android.systemui.res.R; import com.android.systemui.settings.UserTracker; import com.android.systemui.statusbar.CrossFadeHelper; -import com.android.systemui.statusbar.pipeline.wifi.data.repository.WifiRepository; +import com.android.systemui.statusbar.pipeline.wifi.domain.interactor.WifiInteractor; import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel; import com.android.systemui.statusbar.policy.IndividualSensorPrivacyController; import com.android.systemui.statusbar.policy.NextAlarmController; import com.android.systemui.statusbar.policy.ZenModeController; import com.android.systemui.statusbar.window.StatusBarWindowStateController; -import com.android.systemui.touch.TouchInsetManager; import com.android.systemui.util.ViewController; import com.android.systemui.util.time.DateFormatUtil; @@ -59,13 +62,11 @@ import java.util.stream.Collectors; import javax.inject.Inject; /** - * View controller for {@link DreamOverlayStatusBarView}. + * View controller for {@link AmbientStatusBarView}. */ -@DreamOverlayComponent.DreamOverlayScope -public class DreamOverlayStatusBarViewController extends ViewController<DreamOverlayStatusBarView> { +public class AmbientStatusBarViewController extends ViewController<AmbientStatusBarView> { private static final String TAG = "DreamStatusBarCtrl"; - private final TouchInsetManager.TouchInsetSession mTouchInsetSession; private final NextAlarmController mNextAlarmController; private final AlarmManager mAlarmManager; private final Resources mResources; @@ -76,15 +77,17 @@ public class DreamOverlayStatusBarViewController extends ViewController<DreamOve private final ZenModeController mZenModeController; private final DreamOverlayStateController mDreamOverlayStateController; private final UserTracker mUserTracker; - private final WifiRepository mWifiRepository; + private final WifiInteractor mWifiInteractor; private final StatusBarWindowStateController mStatusBarWindowStateController; private final DreamOverlayStatusBarItemsProvider mStatusBarItemsProvider; private final Executor mMainExecutor; private final List<DreamOverlayStatusBarItemsProvider.StatusBarItem> mExtraStatusBarItems = new ArrayList<>(); + private final CommunalSceneInteractor mCommunalSceneInteractor; private final DreamLogger mLogger; private boolean mIsAttached; + private boolean mCommunalVisible; // Whether dream entry animations are finished. private boolean mEntryAnimationsFinished = false; @@ -115,7 +118,7 @@ public class DreamOverlayStatusBarViewController extends ViewController<DreamOve private final DreamOverlayNotificationCountProvider.Callback mNotificationCountCallback = notificationCount -> showIcon( - DreamOverlayStatusBarView.STATUS_ICON_NOTIFICATIONS, + AmbientStatusBarView.STATUS_ICON_NOTIFICATIONS, notificationCount > 0, notificationCount > 0 ? buildNotificationsContentDescription(notificationCount) @@ -125,11 +128,10 @@ public class DreamOverlayStatusBarViewController extends ViewController<DreamOve this::onStatusBarItemsChanged; @Inject - public DreamOverlayStatusBarViewController( - DreamOverlayStatusBarView view, + public AmbientStatusBarViewController( + AmbientStatusBarView view, @Main Resources resources, @Main Executor mainExecutor, - TouchInsetManager.TouchInsetSession touchInsetSession, AlarmManager alarmManager, NextAlarmController nextAlarmController, DateFormatUtil dateFormatUtil, @@ -140,12 +142,12 @@ public class DreamOverlayStatusBarViewController extends ViewController<DreamOve DreamOverlayStatusBarItemsProvider statusBarItemsProvider, DreamOverlayStateController dreamOverlayStateController, UserTracker userTracker, - WifiRepository wifiRepository, + WifiInteractor wifiInteractor, + CommunalSceneInteractor communalSceneInteractor, @DreamLog LogBuffer logBuffer) { super(view); mResources = resources; mMainExecutor = mainExecutor; - mTouchInsetSession = touchInsetSession; mAlarmManager = alarmManager; mNextAlarmController = nextAlarmController; mDateFormatUtil = dateFormatUtil; @@ -156,7 +158,8 @@ public class DreamOverlayStatusBarViewController extends ViewController<DreamOve mZenModeController = zenModeController; mDreamOverlayStateController = dreamOverlayStateController; mUserTracker = userTracker; - mWifiRepository = wifiRepository; + mWifiInteractor = wifiInteractor; + mCommunalSceneInteractor = communalSceneInteractor; mLogger = new DreamLogger(logBuffer, TAG); // Register to receive show/hide updates for the system status bar. Our custom status bar @@ -170,10 +173,16 @@ public class DreamOverlayStatusBarViewController extends ViewController<DreamOve collectFlow( mView, - mWifiRepository.getWifiNetwork(), + mWifiInteractor.getWifiNetwork(), network -> updateWifiUnavailableStatusIcon( network instanceof WifiNetworkModel.Active)); + collectFlow( + mView, + mCommunalSceneInteractor.isCommunalVisible(), + this::onCommunalVisibleChanged + ); + mNextAlarmController.addCallback(mNextAlarmCallback); updateAlarmStatusIcon(); @@ -202,7 +211,6 @@ public class DreamOverlayStatusBarViewController extends ViewController<DreamOve mView.removeAllExtraStatusBarItemViews(); mDreamOverlayStateController.setDreamOverlayStatusBarVisible(false); mDreamOverlayStateController.removeCallback(mDreamOverlayStateCallback); - mTouchInsetSession.clear(); mIsAttached = false; } @@ -212,7 +220,7 @@ public class DreamOverlayStatusBarViewController extends ViewController<DreamOve * * No-op if the dream overlay status bar should not be shown. */ - protected void setFadeAmount(float fadeAmount, boolean fadingOut) { + public void setFadeAmount(float fadeAmount, boolean fadingOut) { updateVisibility(); if (mView.getVisibility() != View.VISIBLE) { @@ -233,14 +241,20 @@ public class DreamOverlayStatusBarViewController extends ViewController<DreamOve mView.setTranslationY(translationY); } + private void onCommunalVisibleChanged(boolean visible) { + mCommunalVisible = visible; + updateVisibility(); + } + private boolean shouldShowStatusBar() { - return !mDreamOverlayStateController.isLowLightActive() - && !mStatusBarWindowStateController.windowIsShowing(); + return (!mDreamOverlayStateController.isLowLightActive() + && !mStatusBarWindowStateController.windowIsShowing()) + || mCommunalVisible; } @VisibleForTesting void updateWifiUnavailableStatusIcon(boolean available) { - showIcon(DreamOverlayStatusBarView.STATUS_ICON_WIFI_UNAVAILABLE, !available, + showIcon(AmbientStatusBarView.STATUS_ICON_WIFI_UNAVAILABLE, !available, R.string.wifi_unavailable_dream_overlay_content_description); } @@ -249,13 +263,13 @@ public class DreamOverlayStatusBarViewController extends ViewController<DreamOve mAlarmManager.getNextAlarmClock(mUserTracker.getUserId()); final boolean hasAlarm = alarm != null && alarm.getTriggerTime() > 0; showIcon( - DreamOverlayStatusBarView.STATUS_ICON_ALARM_SET, + AmbientStatusBarView.STATUS_ICON_ALARM_SET, hasAlarm, hasAlarm ? buildAlarmContentDescription(alarm) : null); } private void updateAssistantAttentionIcon() { - showIcon(DreamOverlayStatusBarView.STATUS_ICON_ASSISTANT_ATTENTION_ACTIVE, + showIcon(AmbientStatusBarView.STATUS_ICON_ASSISTANT_ATTENTION_ACTIVE, mDreamOverlayStateController.hasAssistantAttention(), R.string.assistant_attention_content_description); } @@ -284,17 +298,17 @@ public class DreamOverlayStatusBarViewController extends ViewController<DreamOve .isSensorBlocked(SensorPrivacyManager.Sensors.MICROPHONE); final boolean cameraBlocked = mSensorPrivacyController .isSensorBlocked(SensorPrivacyManager.Sensors.CAMERA); - @DreamOverlayStatusBarView.StatusIconType int iconType = Resources.ID_NULL; + @AmbientStatusBarView.StatusIconType int iconType = Resources.ID_NULL; showIcon( - DreamOverlayStatusBarView.STATUS_ICON_CAMERA_DISABLED, + AmbientStatusBarView.STATUS_ICON_CAMERA_DISABLED, !micBlocked && cameraBlocked, R.string.camera_blocked_dream_overlay_content_description); showIcon( - DreamOverlayStatusBarView.STATUS_ICON_MIC_DISABLED, + AmbientStatusBarView.STATUS_ICON_MIC_DISABLED, micBlocked && !cameraBlocked, R.string.microphone_blocked_dream_overlay_content_description); showIcon( - DreamOverlayStatusBarView.STATUS_ICON_MIC_CAMERA_DISABLED, + AmbientStatusBarView.STATUS_ICON_MIC_CAMERA_DISABLED, micBlocked && cameraBlocked, R.string.camera_and_microphone_blocked_dream_overlay_content_description); } @@ -308,24 +322,24 @@ public class DreamOverlayStatusBarViewController extends ViewController<DreamOve private void updatePriorityModeStatusIcon() { showIcon( - DreamOverlayStatusBarView.STATUS_ICON_PRIORITY_MODE_ON, + AmbientStatusBarView.STATUS_ICON_PRIORITY_MODE_ON, mZenModeController.getZen() != Settings.Global.ZEN_MODE_OFF, R.string.priority_mode_dream_overlay_content_description); } - private void showIcon(@DreamOverlayStatusBarView.StatusIconType int iconType, boolean show, + private void showIcon(@AmbientStatusBarView.StatusIconType int iconType, boolean show, int contentDescriptionResId) { showIcon(iconType, show, mResources.getString(contentDescriptionResId)); } private void showIcon( - @DreamOverlayStatusBarView.StatusIconType int iconType, + @AmbientStatusBarView.StatusIconType int iconType, boolean show, @Nullable String contentDescription) { mMainExecutor.execute(() -> { if (mIsAttached) { mLogger.logShowOrHideStatusBarItem( - show, DreamOverlayStatusBarView.getLoggableStatusIconType(iconType)); + show, AmbientStatusBarView.getLoggableStatusIconType(iconType)); mView.showIcon(iconType, show, contentDescription); } }); diff --git a/packages/SystemUI/src/com/android/systemui/ambient/touch/ShadeTouchHandler.java b/packages/SystemUI/src/com/android/systemui/ambient/touch/ShadeTouchHandler.java index 9ef9938ab8ad..fcd7ef53d42a 100644 --- a/packages/SystemUI/src/com/android/systemui/ambient/touch/ShadeTouchHandler.java +++ b/packages/SystemUI/src/com/android/systemui/ambient/touch/ShadeTouchHandler.java @@ -18,11 +18,15 @@ package com.android.systemui.ambient.touch; import static com.android.systemui.ambient.touch.dagger.ShadeModule.NOTIFICATION_SHADE_GESTURE_INITIATION_HEIGHT; +import android.app.DreamManager; import android.graphics.Rect; import android.graphics.Region; import android.view.GestureDetector; import android.view.MotionEvent; +import androidx.annotation.NonNull; + +import com.android.systemui.Flags; import com.android.systemui.shade.ShadeViewController; import com.android.systemui.statusbar.phone.CentralSurfaces; @@ -38,28 +42,39 @@ import javax.inject.Named; public class ShadeTouchHandler implements TouchHandler { private final Optional<CentralSurfaces> mSurfaces; private final ShadeViewController mShadeViewController; + private final DreamManager mDreamManager; private final int mInitiationHeight; + /** + * Tracks whether or not we are capturing a given touch. Will be null before and after a touch. + */ + private Boolean mCapture; + @Inject ShadeTouchHandler(Optional<CentralSurfaces> centralSurfaces, ShadeViewController shadeViewController, + DreamManager dreamManager, @Named(NOTIFICATION_SHADE_GESTURE_INITIATION_HEIGHT) int initiationHeight) { mSurfaces = centralSurfaces; mShadeViewController = shadeViewController; + mDreamManager = dreamManager; mInitiationHeight = initiationHeight; } @Override public void onSessionStart(TouchSession session) { - if (mSurfaces.map(CentralSurfaces::isBouncerShowing).orElse(false)) { + if (mSurfaces.isEmpty()) { session.pop(); return; } - session.registerInputListener(ev -> { - mShadeViewController.handleExternalTouch((MotionEvent) ev); + session.registerCallback(() -> mCapture = null); + session.registerInputListener(ev -> { if (ev instanceof MotionEvent) { + if (mCapture != null && mCapture) { + sendTouchEvent((MotionEvent) ev); + } if (((MotionEvent) ev).getAction() == MotionEvent.ACTION_UP) { session.pop(); } @@ -68,19 +83,41 @@ public class ShadeTouchHandler implements TouchHandler { session.registerGestureListener(new GestureDetector.SimpleOnGestureListener() { @Override - public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, + public boolean onScroll(MotionEvent e1, @NonNull MotionEvent e2, float distanceX, float distanceY) { - return true; + if (mCapture == null) { + // Only capture swipes that are going downwards. + mCapture = Math.abs(distanceY) > Math.abs(distanceX) && distanceY < 0; + if (mCapture) { + // Send the initial touches over, as the input listener has already + // processed these touches. + sendTouchEvent(e1); + sendTouchEvent(e2); + } + } + return mCapture; } @Override - public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, + public boolean onFling(MotionEvent e1, @NonNull MotionEvent e2, float velocityX, float velocityY) { - return true; + return mCapture; } }); } + private void sendTouchEvent(MotionEvent event) { + if (Flags.communalHub() && !mDreamManager.isDreaming()) { + // Send touches to central surfaces only when on the glanceable hub while not dreaming. + // While sending touches where while dreaming will open the shade, the shade + // while closing if opened then closed in the same gesture. + mSurfaces.get().handleExternalShadeWindowTouch(event); + } else { + // Send touches to the shade view when dreaming. + mShadeViewController.handleExternalTouch(event); + } + } + @Override public void getTouchInitiationRegion(Rect bounds, Region region, Rect exclusionRect) { final Rect outBounds = new Rect(bounds); diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java index 1ee4908437a6..430ff0716ee0 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java @@ -515,7 +515,9 @@ public class AuthContainerView extends LinearLayout } else { throw new IllegalStateException("Unknown credential type: " + credentialType); } - mCredentialView = factory.inflate(layoutResourceId, null, false); + // TODO(b/288175645): Once AuthContainerView is removed, set 0dp in credential view xml + // files with the corresponding left/right or top/bottom constraints being set to "parent". + mCredentialView = factory.inflate(layoutResourceId, mLayout, false); // The background is used for detecting taps / cancelling authentication. Since the // credential view is full-screen and should not be canceled from background taps, @@ -552,8 +554,6 @@ public class AuthContainerView extends LinearLayout } mWakefulnessLifecycle.addObserver(this); - mPanelInteractionDetector.enable( - () -> animateAway(AuthDialogCallback.DISMISSED_USER_CANCELED)); if (constraintBp()) { // Do nothing on attachment with constraintLayout } else if (mPromptViewModel.getPromptKind().getValue().isBiometric()) { @@ -566,6 +566,8 @@ public class AuthContainerView extends LinearLayout } if (!constraintBp()) { + mPanelInteractionDetector.enable( + () -> animateAway(AuthDialogCallback.DISMISSED_USER_CANCELED)); updatePositionByCapability(false /* invalidate */); } @@ -977,7 +979,7 @@ public class AuthContainerView extends LinearLayout final WindowManager.LayoutParams lp = new WindowManager.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT, - WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG, + WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY, windowFlags, PixelFormat.TRANSLUCENT); lp.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS; diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java index fb718d3412e3..85b5faf2d556 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java @@ -34,6 +34,7 @@ import android.content.Intent; import android.content.IntentFilter; import android.graphics.Rect; import android.hardware.biometrics.BiometricFingerprintConstants; +import android.hardware.biometrics.BiometricPrompt; import android.hardware.biometrics.SensorProperties; import android.hardware.display.DisplayManager; import android.hardware.fingerprint.FingerprintManager; @@ -43,7 +44,9 @@ import android.hardware.fingerprint.IUdfpsOverlayController; import android.hardware.fingerprint.IUdfpsOverlayControllerCallback; import android.hardware.input.InputManager; import android.os.Build; +import android.os.CancellationSignal; import android.os.Handler; +import android.os.Looper; import android.os.PowerManager; import android.os.Trace; import android.os.VibrationAttributes; @@ -382,6 +385,26 @@ public class UdfpsController implements DozeReceiver, Dumpable { UdfpsController.this.mFingerprintManager.onUdfpsUiEvent( FingerprintManager.UDFPS_UI_READY, requestId, sensorId); } + + /** + * Debug to show biometric prompt + */ + public void debugBiometricPrompt() { + final BiometricPrompt.AuthenticationCallback authenticationCallback = + new BiometricPrompt.AuthenticationCallback() { + }; + + final BiometricPrompt biometricPrompt = new BiometricPrompt.Builder(mContext) + .setTitle("Test") + .setDeviceCredentialAllowed(true) + .setAllowBackgroundAuthentication(true) + .build(); + final Handler handler = new Handler(Looper.getMainLooper()); + biometricPrompt.authenticate( + new CancellationSignal(), + handler::post, + authenticationCallback); + } } /** diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerLegacy.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerLegacy.kt index 298b87d05f39..c3d9240c40a1 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerLegacy.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerLegacy.kt @@ -39,7 +39,6 @@ import com.android.systemui.keyguard.shared.model.KeyguardState.DREAMING import com.android.systemui.keyguard.shared.model.KeyguardState.GONE import com.android.systemui.keyguard.shared.model.KeyguardState.OCCLUDED import com.android.systemui.keyguard.shared.model.KeyguardState.PRIMARY_BOUNCER -import com.android.systemui.keyguard.shared.model.TransitionState import com.android.systemui.lifecycle.repeatWhenAttached import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.res.R @@ -59,7 +58,6 @@ import java.io.PrintWriter import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.Job -import kotlinx.coroutines.flow.first import kotlinx.coroutines.launch /** Class that coordinates non-HBM animations during keyguard authentication. */ @@ -307,27 +305,12 @@ open class UdfpsKeyguardViewControllerLegacy( @VisibleForTesting suspend fun listenForLockscreenAodTransitions(scope: CoroutineScope): Job { return scope.launch { - transitionInteractor.dozeAmountTransition.collect { transitionStep -> - if ( - transitionStep.from == AOD && - transitionStep.transitionState == TransitionState.CANCELED - ) { - if (transitionInteractor.startedKeyguardTransitionStep.first().to != AOD) { - // If the next started transition isn't transitioning back to AOD, force - // doze amount to be 0f (as if the transition to the lockscreen completed). - view.onDozeAmountChanged( - 0f, - 0f, - UdfpsKeyguardViewLegacy.ANIMATION_NONE, - ) - } - } else { - view.onDozeAmountChanged( - transitionStep.value, - transitionStep.value, - UdfpsKeyguardViewLegacy.ANIMATION_BETWEEN_AOD_AND_LOCKSCREEN, - ) - } + transitionInteractor.transitionValue(AOD).collect { + view.onDozeAmountChanged( + it, + it, + UdfpsKeyguardViewLegacy.ANIMATION_BETWEEN_AOD_AND_LOCKSCREEN, + ) } } } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsShell.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsShell.kt index 88b9e1bdfd97..f5e3d29cb878 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsShell.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsShell.kt @@ -45,16 +45,13 @@ private const val SENSOR_ID = 0 private const val MINOR = 10F private const val MAJOR = 10F -/** - * Used to show and hide the UDFPS overlay with statusbar commands. - */ +/** Used to show and hide the UDFPS overlay with statusbar commands. */ @SysUISingleton -class UdfpsShell @Inject constructor( - commandRegistry: CommandRegistry -) : Command { +class UdfpsShell @Inject constructor(commandRegistry: CommandRegistry) : Command { /** * Set in [UdfpsController.java] constructor, used to show and hide the UDFPS overlay. + * * TODO: inject after b/229290039 is resolved */ var udfpsOverlayController: UdfpsController.UdfpsOverlayController? = null @@ -76,6 +73,8 @@ class UdfpsShell @Inject constructor( simFingerDown() } else if (args.size == 1 && args[0] == "simFingerUp") { simFingerUp() + } else if (args.size == 1 && args[0] == "biometricPrompt") { + launchBiometricPrompt() } else { invalidCommand(pw) } @@ -85,8 +84,10 @@ class UdfpsShell @Inject constructor( pw.println("Usage: adb shell cmd statusbar udfps <cmd>") pw.println("Supported commands:") pw.println(" - show <reason>") - pw.println(" -> supported reasons: [enroll-find-sensor, enroll-enrolling, auth-bp, " + - "auth-keyguard, auth-other, auth-settings]") + pw.println( + " -> supported reasons: [enroll-find-sensor, enroll-enrolling, auth-bp, " + + "auth-keyguard, auth-other, auth-settings]" + ) pw.println(" -> reason otherwise defaults to unknown") pw.println(" - hide") pw.println(" - onUiReady") @@ -94,6 +95,8 @@ class UdfpsShell @Inject constructor( pw.println(" -> Simulates onFingerDown on sensor") pw.println(" - simFingerUp") pw.println(" -> Simulates onFingerUp on sensor") + pw.println(" - biometricPrompt") + pw.println(" -> Shows Biometric Prompt") } private fun invalidCommand(pw: PrintWriter) { @@ -115,14 +118,14 @@ class UdfpsShell @Inject constructor( private fun showOverlay(reason: Int) { udfpsOverlayController?.showUdfpsOverlay( - REQUEST_ID, - SENSOR_ID, - reason, - object : IUdfpsOverlayControllerCallback.Stub() { - override fun onUserCanceled() { - Log.e(TAG, "User cancelled") - } + REQUEST_ID, + SENSOR_ID, + reason, + object : IUdfpsOverlayControllerCallback.Stub() { + override fun onUserCanceled() { + Log.e(TAG, "User cancelled") } + } ) } @@ -130,6 +133,9 @@ class UdfpsShell @Inject constructor( udfpsOverlayController?.hideUdfpsOverlay(SENSOR_ID) } + private fun launchBiometricPrompt() { + udfpsOverlayController?.debugBiometricPrompt() + } @VisibleForTesting fun onUiReady() { @@ -140,12 +146,24 @@ class UdfpsShell @Inject constructor( fun simFingerDown() { val sensorBounds: Rect = udfpsOverlayController!!.sensorBounds - val downEvent: MotionEvent? = obtainMotionEvent(ACTION_DOWN, sensorBounds.exactCenterX(), - sensorBounds.exactCenterY(), MINOR, MAJOR) + val downEvent: MotionEvent? = + obtainMotionEvent( + ACTION_DOWN, + sensorBounds.exactCenterX(), + sensorBounds.exactCenterY(), + MINOR, + MAJOR + ) udfpsOverlayController?.debugOnTouch(downEvent) - val moveEvent: MotionEvent? = obtainMotionEvent(ACTION_MOVE, sensorBounds.exactCenterX(), - sensorBounds.exactCenterY(), MINOR, MAJOR) + val moveEvent: MotionEvent? = + obtainMotionEvent( + ACTION_MOVE, + sensorBounds.exactCenterX(), + sensorBounds.exactCenterY(), + MINOR, + MAJOR + ) udfpsOverlayController?.debugOnTouch(moveEvent) downEvent?.recycle() @@ -156,18 +174,24 @@ class UdfpsShell @Inject constructor( fun simFingerUp() { val sensorBounds: Rect = udfpsOverlayController!!.sensorBounds - val upEvent: MotionEvent? = obtainMotionEvent(ACTION_UP, sensorBounds.exactCenterX(), - sensorBounds.exactCenterY(), MINOR, MAJOR) + val upEvent: MotionEvent? = + obtainMotionEvent( + ACTION_UP, + sensorBounds.exactCenterX(), + sensorBounds.exactCenterY(), + MINOR, + MAJOR + ) udfpsOverlayController?.debugOnTouch(upEvent) upEvent?.recycle() } private fun obtainMotionEvent( - action: Int, - x: Float, - y: Float, - minor: Float, - major: Float + action: Int, + x: Float, + y: Float, + minor: Float, + major: Float ): MotionEvent? { val pp = MotionEvent.PointerProperties() pp.id = 1 @@ -176,7 +200,21 @@ class UdfpsShell @Inject constructor( pc.y = y pc.touchMinor = minor pc.touchMajor = major - return MotionEvent.obtain(0, 0, action, 1, arrayOf(pp), arrayOf(pc), - 0, 0, 1f, 1f, 0, 0, 0, 0) + return MotionEvent.obtain( + 0, + 0, + action, + 1, + arrayOf(pp), + arrayOf(pc), + 0, + 0, + 1f, + 1f, + 0, + 0, + 0, + 0 + ) } } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/DisplayStateRepository.kt b/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/DisplayStateRepository.kt index 9b14d6f68e35..2fa4a89b3fd3 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/DisplayStateRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/DisplayStateRepository.kt @@ -139,9 +139,10 @@ constructor( } .stateIn( backgroundScope, - started = SharingStarted.WhileSubscribed(), + started = SharingStarted.Eagerly, initialValue = false, ) + private fun dpiFromPx(size: Float, densityDpi: Int): Float { val densityRatio = densityDpi.toFloat() / DisplayMetrics.DENSITY_DEFAULT return size / densityRatio diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/domain/model/BiometricPromptRequest.kt b/packages/SystemUI/src/com/android/systemui/biometrics/domain/model/BiometricPromptRequest.kt index 4f96c1e03d11..348b4234a430 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/domain/model/BiometricPromptRequest.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/domain/model/BiometricPromptRequest.kt @@ -40,8 +40,7 @@ sealed class BiometricPromptRequest( operationInfo = operationInfo, showEmergencyCallButton = info.isShowEmergencyCallButton ) { - val logoRes: Int = info.logoRes - val logoBitmap: Bitmap? = info.logoBitmap + val logoBitmap: Bitmap? = info.logo val logoDescription: String? = info.logoDescription val negativeButtonText: String = info.negativeButtonText?.toString() ?: "" val componentNameForConfirmDeviceCredentialActivity: ComponentName? = diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewSizeBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewSizeBinder.kt index c836f89a8ff4..a87ee24da2f7 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewSizeBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewSizeBinder.kt @@ -51,6 +51,7 @@ import com.android.systemui.biometrics.ui.viewmodel.isLeft import com.android.systemui.biometrics.ui.viewmodel.isMedium import com.android.systemui.biometrics.ui.viewmodel.isNullOrNotSmall import com.android.systemui.biometrics.ui.viewmodel.isSmall +import com.android.systemui.biometrics.ui.viewmodel.isTop import com.android.systemui.lifecycle.repeatWhenAttached import com.android.systemui.res.R import kotlin.math.abs @@ -100,13 +101,13 @@ object BiometricViewSizeBinder { val iconHolderView = view.requireViewById<View>(R.id.biometric_icon) val panelView = view.requireViewById<View>(R.id.panel) val cornerRadius = view.resources.getDimension(R.dimen.biometric_dialog_corner_size) - val cornerRadiusPx = + val pxToDp = TypedValue.applyDimension( - TypedValue.COMPLEX_UNIT_DIP, - cornerRadius, - view.resources.displayMetrics - ) - .toInt() + TypedValue.COMPLEX_UNIT_DIP, + 1f, + view.resources.displayMetrics + ) + val cornerRadiusPx = (pxToDp * cornerRadius).toInt() var currentSize: PromptSize? = null var currentPosition: PromptPosition = PromptPosition.Bottom @@ -132,18 +133,10 @@ object BiometricViewSizeBinder { cornerRadiusPx.toFloat() ) } + PromptPosition.Bottom, PromptPosition.Top -> { outline.setRoundRect( 0, - -cornerRadiusPx, - view.width, - view.height, - cornerRadiusPx.toFloat() - ) - } - PromptPosition.Bottom -> { - outline.setRoundRect( - 0, 0, view.width, view.height + cornerRadiusPx, @@ -308,6 +301,7 @@ object BiometricViewSizeBinder { } } } + lifecycleScope.launch { viewModel.iconSize.collect { iconSize -> iconHolderView.layoutParams.width = iconSize.first @@ -367,24 +361,25 @@ object BiometricViewSizeBinder { smallConstraintSet.setGuidelineEnd(topGuideline.id, abs(bounds.top)) } - // Use rect bottom to set mid guideline of two-pane. if (midGuideline != null) { - if (bounds.bottom >= 0) { - midGuideline.setGuidelineEnd(bounds.bottom) - mediumConstraintSet.setGuidelineEnd( - midGuideline.id, - bounds.bottom - ) - } else if (bounds.bottom < 0) { - midGuideline.setGuidelineBegin(abs(bounds.bottom)) - mediumConstraintSet.setGuidelineBegin( - midGuideline.id, - abs(bounds.bottom) - ) - } + val left = + if (bounds.left >= 0) { + abs(bounds.left) + } else { + view.width - abs(bounds.left) + } + val right = + if (bounds.right >= 0) { + view.width - abs(bounds.right) + } else { + abs(bounds.right) + } + val mid = (left + right) / 2 + mediumConstraintSet.setGuidelineBegin(midGuideline.id, mid) } } } + lifecycleScope.launch { combine(viewModel.hideSensorIcon, viewModel.size, ::Pair).collect { (hideSensorIcon, size) -> @@ -415,6 +410,33 @@ object BiometricViewSizeBinder { R.id.rightGuideline, ConstraintSet.RIGHT ) + } else if (position.isTop) { + // Top position is only used for 180 rotation Udfps + // Requires repositioning due to sensor location at top of screen + mediumConstraintSet.connect( + R.id.scrollView, + ConstraintSet.TOP, + R.id.indicator, + ConstraintSet.BOTTOM + ) + mediumConstraintSet.connect( + R.id.scrollView, + ConstraintSet.BOTTOM, + R.id.button_bar, + ConstraintSet.TOP + ) + mediumConstraintSet.connect( + R.id.panel, + ConstraintSet.TOP, + R.id.biometric_icon, + ConstraintSet.TOP + ) + mediumConstraintSet.setMargin( + R.id.panel, + ConstraintSet.TOP, + (-24 * pxToDp).toInt() + ) + mediumConstraintSet.setVerticalBias(R.id.scrollView, 0f) } when { diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/PromptIconViewBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/PromptIconViewBinder.kt index adf5b7480cb9..900d7cc791f2 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/PromptIconViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/PromptIconViewBinder.kt @@ -17,9 +17,7 @@ package com.android.systemui.biometrics.ui.binder -import android.graphics.drawable.Animatable2 import android.graphics.drawable.AnimatedVectorDrawable -import android.graphics.drawable.Drawable import android.util.Log import androidx.constraintlayout.widget.ConstraintLayout import androidx.constraintlayout.widget.ConstraintSet @@ -36,7 +34,6 @@ import com.android.systemui.biometrics.ui.viewmodel.PromptViewModel import com.android.systemui.lifecycle.repeatWhenAttached import com.android.systemui.res.R import com.android.systemui.util.kotlin.Utils.Companion.toQuad -import com.android.systemui.util.kotlin.Utils.Companion.toQuint import com.android.systemui.util.kotlin.Utils.Companion.toTriple import com.android.systemui.util.kotlin.sample import kotlinx.coroutines.flow.combine @@ -70,16 +67,37 @@ object PromptIconViewBinder { } var faceIcon: AnimatedVectorDrawable? = null - val faceIconCallback = - object : Animatable2.AnimationCallback() { - override fun onAnimationStart(drawable: Drawable) { - viewModel.onAnimationStart() - } - override fun onAnimationEnd(drawable: Drawable) { - viewModel.onAnimationEnd() + fun updateXmlIconAsset( + iconAsset: Int, + shouldAnimateIconView: Boolean, + activeAuthType: AuthType + ) { + faceIcon?.stop() + faceIcon = iconView.context.getDrawable(iconAsset) as AnimatedVectorDrawable + faceIcon?.apply { + iconView.setIconFailureListener(iconAsset, activeAuthType) + iconView.setImageDrawable(this) + if (shouldAnimateIconView) { + forceAnimationOnUI() + start() } } + } + + fun updateJsonIconAsset( + iconAsset: Int, + shouldAnimateIconView: Boolean, + activeAuthType: AuthType + ) { + iconView.setIconFailureListener(iconAsset, activeAuthType) + iconView.setAnimation(iconAsset) + iconView.frame = 0 + + if (shouldAnimateIconView) { + iconView.playAnimation() + } + } if (!constraintBp()) { launch { @@ -145,52 +163,55 @@ object PromptIconViewBinder { combine( viewModel.activeAuthType, viewModel.shouldAnimateIconView, - viewModel.shouldRepeatAnimation, viewModel.showingError, - ::toQuad + ::Triple ), - ::toQuint + ::toQuad ) - .collect { - ( - iconAsset, - activeAuthType, - shouldAnimateIconView, - shouldRepeatAnimation, - showingError) -> + .collect { (iconAsset, activeAuthType, shouldAnimateIconView, showingError) + -> if (iconAsset != -1) { when (activeAuthType) { AuthType.Fingerprint, AuthType.Coex -> { - iconView.setIconFailureListener(iconAsset, activeAuthType) - iconView.setAnimation(iconAsset) - iconView.frame = 0 - - if (shouldAnimateIconView) { - iconView.playAnimation() + // TODO(b/318569643): Until assets unified to one type, this + // check + // is needed in face-auth-error-triggered implicit -> + // explicit + // coex auth transition, in case iconAsset updates to + // face_dialog_dark_to_error (XML) after activeAuthType + // updates + // from AuthType.Face (which expects XML) + // to AuthType.Coex (which expects JSON) + if (iconAsset == R.drawable.face_dialog_dark_to_error) { + updateXmlIconAsset( + iconAsset, + shouldAnimateIconView, + activeAuthType + ) + } else { + updateJsonIconAsset( + iconAsset, + shouldAnimateIconView, + activeAuthType + ) } } AuthType.Face -> { - faceIcon?.apply { - unregisterAnimationCallback(faceIconCallback) - stop() - } - faceIcon = - iconView.context.getDrawable(iconAsset) - as AnimatedVectorDrawable - faceIcon?.apply { - iconView.setIconFailureListener( + // TODO(b/318569643): Consolidate logic once all face auth + // assets are migrated from drawable to json + if (iconAsset == R.raw.face_dialog_authenticating) { + updateJsonIconAsset( + iconAsset, + shouldAnimateIconView, + activeAuthType + ) + } else { + updateXmlIconAsset( iconAsset, + shouldAnimateIconView, activeAuthType ) - iconView.setImageDrawable(this) - if (shouldAnimateIconView) { - forceAnimationOnUI() - if (shouldRepeatAnimation) { - registerAnimationCallback(faceIconCallback) - } - start() - } } } } @@ -294,11 +315,10 @@ private val assetIdToString: Map<Int, String> = // Face assets R.drawable.face_dialog_wink_from_dark to "face_dialog_wink_from_dark", R.drawable.face_dialog_dark_to_checkmark to "face_dialog_dark_to_checkmark", - R.drawable.face_dialog_pulse_light_to_dark to "face_dialog_pulse_light_to_dark", - R.drawable.face_dialog_pulse_dark_to_light to "face_dialog_pulse_dark_to_light", R.drawable.face_dialog_dark_to_error to "face_dialog_dark_to_error", R.drawable.face_dialog_error_to_idle to "face_dialog_error_to_idle", R.drawable.face_dialog_idle_static to "face_dialog_idle_static", + R.raw.face_dialog_authenticating to "face_dialog_authenticating", // Co-ex assets R.raw.fingerprint_dialogue_unlocked_to_checkmark_success_lottie to "fingerprint_dialogue_unlocked_to_checkmark_success_lottie", diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptIconViewModel.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptIconViewModel.kt index bde3e992a295..7081661708de 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptIconViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptIconViewModel.kt @@ -32,12 +32,10 @@ import com.android.systemui.res.R import com.android.systemui.util.kotlin.combine import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.flowOf -import kotlinx.coroutines.flow.map /** * Models UI of [BiometricPromptLayout.iconView] and [BiometricPromptLayout.biometric_icon_overlay] @@ -58,11 +56,8 @@ constructor( } /** - * Indicates what auth type the UI currently displays. - * Fingerprint-only auth -> Fingerprint - * Face-only auth -> Face - * Co-ex auth, implicit flow -> Face - * Co-ex auth, explicit flow -> Coex + * Indicates what auth type the UI currently displays. Fingerprint-only auth -> Fingerprint + * Face-only auth -> Face Co-ex auth, implicit flow -> Face Co-ex auth, explicit flow -> Coex */ val activeAuthType: Flow<AuthType> = combine( @@ -119,35 +114,6 @@ constructor( _previousIconOverlayWasError.value = previousIconOverlayWasError } - /** Called when iconView begins animating. */ - fun onAnimationStart() { - _animationEnded.value = false - } - - /** Called when iconView ends animating. */ - fun onAnimationEnd() { - _animationEnded.value = true - } - - private val _animationEnded: MutableStateFlow<Boolean> = MutableStateFlow(false) - - /** - * Whether a face iconView should pulse (i.e. while isAuthenticating and previous animation - * ended). - */ - val shouldPulseAnimation: Flow<Boolean> = - combine(_animationEnded, promptViewModel.isAuthenticating) { - animationEnded, - isAuthenticating -> - animationEnded && isAuthenticating - } - .distinctUntilChanged() - - private val _lastPulseLightToDark: MutableStateFlow<Boolean> = MutableStateFlow(false) - - /** Tracks whether a face iconView last pulsed light to dark (vs. dark to light) */ - val lastPulseLightToDark: Flow<Boolean> = _lastPulseLightToDark.asStateFlow() - val iconSize: Flow<Pair<Int, Int>> = combine( promptViewModel.position, @@ -195,35 +161,22 @@ constructor( } } AuthType.Face -> - shouldPulseAnimation.flatMapLatest { shouldPulseAnimation: Boolean -> - if (shouldPulseAnimation) { - val iconAsset = - if (_lastPulseLightToDark.value) { - R.drawable.face_dialog_pulse_dark_to_light - } else { - R.drawable.face_dialog_pulse_light_to_dark - } - _lastPulseLightToDark.value = !_lastPulseLightToDark.value - flowOf(iconAsset) - } else { - combine( - promptViewModel.isAuthenticated.distinctUntilChanged(), - promptViewModel.isAuthenticating.distinctUntilChanged(), - promptViewModel.isPendingConfirmation.distinctUntilChanged(), - promptViewModel.showingError.distinctUntilChanged() - ) { - authState: PromptAuthState, - isAuthenticating: Boolean, - isPendingConfirmation: Boolean, - showingError: Boolean -> - getFaceIconViewAsset( - authState, - isAuthenticating, - isPendingConfirmation, - showingError - ) - } - } + combine( + promptViewModel.isAuthenticated.distinctUntilChanged(), + promptViewModel.isAuthenticating.distinctUntilChanged(), + promptViewModel.isPendingConfirmation.distinctUntilChanged(), + promptViewModel.showingError.distinctUntilChanged() + ) { + authState: PromptAuthState, + isAuthenticating: Boolean, + isPendingConfirmation: Boolean, + showingError: Boolean -> + getFaceIconViewAsset( + authState, + isAuthenticating, + isPendingConfirmation, + showingError + ) } AuthType.Coex -> combine( @@ -327,8 +280,7 @@ constructor( } else if (authState.isAuthenticated) { R.drawable.face_dialog_dark_to_checkmark } else if (isAuthenticating) { - _lastPulseLightToDark.value = false - R.drawable.face_dialog_pulse_dark_to_light + R.raw.face_dialog_authenticating } else if (showingError) { R.drawable.face_dialog_dark_to_error } else if (_previousIconWasError.value) { @@ -703,16 +655,6 @@ constructor( } } - /** Whether the current BiometricPromptLayout.iconView asset animation should be repeated. */ - val shouldRepeatAnimation: Flow<Boolean> = - activeAuthType.flatMapLatest { activeAuthType: AuthType -> - when (activeAuthType) { - AuthType.Fingerprint, - AuthType.Coex -> flowOf(false) - AuthType.Face -> promptViewModel.isAuthenticating.map { it } - } - } - /** Called on configuration changes */ fun onConfigurationChanged(newConfig: Configuration) { displayStateInteractor.onConfigurationChanged(newConfig) 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 c17b83dd4fbe..7d494a5cf229 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 @@ -30,6 +30,7 @@ import android.hardware.biometrics.BiometricPrompt import android.hardware.biometrics.Flags.customBiometricPrompt import android.hardware.biometrics.PromptContentView import android.os.UserHandle +import android.text.TextPaint import android.util.Log import android.util.RotationUtils import android.view.HapticFeedbackConstants @@ -52,6 +53,7 @@ import com.android.systemui.biometrics.shared.model.PromptKind import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.keyguard.shared.model.AcquiredFingerprintAuthenticationStatus import com.android.systemui.res.R +import com.android.systemui.util.kotlin.combine import javax.inject.Inject import kotlinx.coroutines.Job import kotlinx.coroutines.coroutineScope @@ -260,17 +262,19 @@ constructor( val position: Flow<PromptPosition> = combine( _forceLargeSize, + promptKind, displayStateInteractor.isLargeScreen, displayStateInteractor.currentRotation, - ) { forceLarge, isLargeScreen, rotation -> + modalities + ) { forceLarge, promptKind, isLargeScreen, rotation, modalities -> when { forceLarge || isLargeScreen || - promptKind.value.isOnePaneNoSensorLandscapeBiometric() -> - PromptPosition.Bottom + promptKind.isOnePaneNoSensorLandscapeBiometric() -> PromptPosition.Bottom rotation == DisplayRotation.ROTATION_90 -> PromptPosition.Right rotation == DisplayRotation.ROTATION_270 -> PromptPosition.Left - rotation == DisplayRotation.ROTATION_180 -> PromptPosition.Top + rotation == DisplayRotation.ROTATION_180 && modalities.hasUdfps -> + PromptPosition.Top else -> PromptPosition.Bottom } } @@ -306,9 +310,9 @@ constructor( context.resources.getDimensionPixelSize( R.dimen.biometric_prompt_two_pane_udfps_horizontal_guideline_padding ) - private val udfpsMidGuidelinePadding = + private val udfpsHorizontalShorterGuidelinePadding = context.resources.getDimensionPixelSize( - R.dimen.biometric_prompt_two_pane_udfps_mid_guideline_padding + R.dimen.biometric_prompt_two_pane_udfps_shorter_horizontal_guideline_padding ) private val mediumTopGuidelinePadding = context.resources.getDimensionPixelSize( @@ -318,10 +322,6 @@ constructor( context.resources.getDimensionPixelSize( R.dimen.biometric_prompt_two_pane_medium_horizontal_guideline_padding ) - private val mediumMidGuidelinePadding = - context.resources.getDimensionPixelSize( - R.dimen.biometric_prompt_two_pane_medium_mid_guideline_padding - ) /** Rect for positioning biometric icon */ val iconPosition: Flow<Rect> = @@ -370,7 +370,14 @@ constructor( landscapeMediumBottomPadding ) } - PromptPosition.Top -> Rect() + PromptPosition.Top -> + if (size.isSmall) { + Rect(0, 0, 0, portraitSmallBottomPadding) + } else if (size.isMedium && modalities.hasUdfps) { + Rect(0, 0, 0, sensorBounds.bottom) + } else { + Rect(0, 0, 0, portraitMediumBottomPadding) + } } } .distinctUntilChanged() @@ -448,52 +455,6 @@ constructor( } } - /** - * Rect for positioning prompt guidelines (left, top, right, mid) - * - * Negative values are used to signify that guideline measuring should be flipped, measuring - * from opposite side of the screen - */ - val guidelineBounds: Flow<Rect> = - combine(iconPosition, promptKind, size, position, modalities) { - _, - promptKind, - size, - position, - modalities -> - when (position) { - PromptPosition.Bottom -> - if (promptKind.isOnePaneNoSensorLandscapeBiometric()) { - Rect(0, 0, 0, 0) - } else { - Rect(0, mediumTopGuidelinePadding, 0, 0) - } - PromptPosition.Right -> - if (size.isSmall) { - Rect(-smallHorizontalGuidelinePadding, 0, 0, 0) - } else if (modalities.hasUdfps) { - Rect(udfpsHorizontalGuidelinePadding, 0, 0, udfpsMidGuidelinePadding) - } else { - Rect(-mediumHorizontalGuidelinePadding, 0, 0, mediumMidGuidelinePadding) - } - PromptPosition.Left -> - if (size.isSmall) { - Rect(0, 0, -smallHorizontalGuidelinePadding, 0) - } else if (modalities.hasUdfps) { - Rect(0, 0, udfpsHorizontalGuidelinePadding, -udfpsMidGuidelinePadding) - } else { - Rect( - 0, - 0, - -mediumHorizontalGuidelinePadding, - -mediumMidGuidelinePadding - ) - } - PromptPosition.Top -> Rect() - } - } - .distinctUntilChanged() - /** Padding for prompt UI elements */ val promptPadding: Flow<Rect> = combine(size, displayStateInteractor.currentRotation) { size, rotation -> @@ -517,7 +478,6 @@ constructor( .map { when { !(customBiometricPrompt() && constraintBp()) || it == null -> null - it.logoRes != -1 -> context.resources.getDrawable(it.logoRes, context.theme) it.logoBitmap != null -> BitmapDrawable(context.resources, it.logoBitmap) else -> context.getUserBadgedIcon(it, iconProvider, activityTaskManager) } @@ -561,6 +521,81 @@ constructor( if (contentView == null) description else "" } + private val hasOnlyOneLineTitle: Flow<Boolean> = + combine(title, subtitle, contentView, description) { + title, + subtitle, + contentView, + description -> + if (subtitle.isNotEmpty() || contentView != null || description.isNotEmpty()) { + false + } else { + val maxWidth = + context.resources.getDimensionPixelSize( + R.dimen.biometric_prompt_two_pane_udfps_shorter_content_width + ) + val attributes = + context.obtainStyledAttributes( + R.style.TextAppearance_AuthCredential_Title, + intArrayOf(android.R.attr.textSize) + ) + val paint = TextPaint() + paint.textSize = attributes.getDimensionPixelSize(0, 0).toFloat() + val textWidth = paint.measureText(title) + attributes.recycle() + textWidth / maxWidth <= 1 + } + } + + /** + * Rect for positioning prompt guidelines (left, top, right, unused) + * + * Negative values are used to signify that guideline measuring should be flipped, measuring + * from opposite side of the screen + */ + val guidelineBounds: Flow<Rect> = + combine(iconPosition, promptKind, size, position, modalities, hasOnlyOneLineTitle) { + _, + promptKind, + size, + position, + modalities, + hasOnlyOneLineTitle -> + var left = 0 + var top = 0 + var right = 0 + when (position) { + PromptPosition.Bottom -> { + val noSensorLandscape = promptKind.isOnePaneNoSensorLandscapeBiometric() + top = if (noSensorLandscape) 0 else mediumTopGuidelinePadding + } + PromptPosition.Right -> + left = getHorizontalPadding(size, modalities, hasOnlyOneLineTitle) + PromptPosition.Left -> + right = getHorizontalPadding(size, modalities, hasOnlyOneLineTitle) + PromptPosition.Top -> {} + } + Rect(left, top, right, 0) + } + .distinctUntilChanged() + + private fun getHorizontalPadding( + size: PromptSize, + modalities: BiometricModalities, + hasOnlyOneLineTitle: Boolean + ) = + if (size.isSmall) { + -smallHorizontalGuidelinePadding + } else if (modalities.hasUdfps) { + if (hasOnlyOneLineTitle) { + -udfpsHorizontalShorterGuidelinePadding + } else { + udfpsHorizontalGuidelinePadding + } + } else { + -mediumHorizontalGuidelinePadding + } + /** If the indicator (help, error) message should be shown. */ val isIndicatorMessageVisible: Flow<Boolean> = combine( diff --git a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogLogger.kt b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogLogger.kt index c30aea07e959..72312b87dc57 100644 --- a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogLogger.kt @@ -16,6 +16,7 @@ package com.android.systemui.bluetooth.qsdialog +import android.bluetooth.BluetoothDevice import com.android.systemui.log.LogBuffer import com.android.systemui.log.core.LogLevel.DEBUG import com.android.systemui.log.dagger.BluetoothTileDialogLog @@ -103,4 +104,29 @@ constructor(@BluetoothTileDialogLog private val logBuffer: LogBuffer) { fun logDeviceUiUpdate(duration: Long) = logBuffer.log(TAG, DEBUG, { long1 = duration }, { "DeviceUiUpdate. duration=$long1" }) + + fun logDeviceClickInAudioSharingWhenEnabled(inAudioSharing: Boolean) { + logBuffer.log( + TAG, + DEBUG, + { str1 = inAudioSharing.toString() }, + { "DeviceClick. in audio sharing=$str1" } + ) + } + + fun logConnectedLeByGroupId(map: Map<Int, List<BluetoothDevice>>) { + logBuffer.log(TAG, DEBUG, { str1 = map.toString() }, { "ConnectedLeByGroupId. map=$str1" }) + } + + fun logLaunchSettingsCriteriaMatched(criteria: String, deviceItem: DeviceItem) { + logBuffer.log( + TAG, + DEBUG, + { + str1 = criteria + str2 = deviceItem.toString() + }, + { "$str1. deviceItem=$str2" } + ) + } } diff --git a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogUiEvent.kt b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogUiEvent.kt index b592b8ed4332..4a358c0b1292 100644 --- a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogUiEvent.kt +++ b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogUiEvent.kt @@ -35,7 +35,17 @@ enum class BluetoothTileDialogUiEvent(val metricId: Int) : UiEventLogger.UiEvent CONNECTED_OTHER_DEVICE_DISCONNECT(1508), @UiEvent(doc = "The auto on toggle is clicked") BLUETOOTH_AUTO_ON_TOGGLE_CLICKED(1617), @UiEvent(doc = "The audio sharing button is clicked") - BLUETOOTH_AUDIO_SHARING_BUTTON_CLICKED(1700); + BLUETOOTH_AUDIO_SHARING_BUTTON_CLICKED(1700), + @UiEvent(doc = "Currently broadcasting and a LE audio supported device is clicked") + LAUNCH_SETTINGS_IN_SHARING_LE_DEVICE_CLICKED(1717), + @UiEvent(doc = "Currently broadcasting and a non-LE audio supported device is clicked") + LAUNCH_SETTINGS_IN_SHARING_NON_LE_DEVICE_CLICKED(1718), + @UiEvent( + doc = "Not broadcasting, having one connected, another saved LE audio device is clicked" + ) + LAUNCH_SETTINGS_NOT_SHARING_SAVED_LE_DEVICE_CLICKED(1719), + @UiEvent(doc = "Not broadcasting, one of the two connected LE audio devices is clicked") + LAUNCH_SETTINGS_NOT_SHARING_CONNECTED_LE_DEVICE_CLICKED(1720); override fun getId() = metricId } diff --git a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/DeviceItemActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/DeviceItemActionInteractor.kt index 931176003b1b..4dafa93ab5c2 100644 --- a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/DeviceItemActionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/DeviceItemActionInteractor.kt @@ -16,32 +16,87 @@ package com.android.systemui.bluetooth.qsdialog +import android.bluetooth.BluetoothDevice +import android.bluetooth.BluetoothProfile +import android.content.Intent +import android.os.Bundle +import android.provider.Settings import com.android.internal.logging.UiEventLogger +import com.android.settingslib.bluetooth.A2dpProfile +import com.android.settingslib.bluetooth.BluetoothUtils +import com.android.settingslib.bluetooth.HeadsetProfile +import com.android.settingslib.bluetooth.HearingAidProfile +import com.android.settingslib.bluetooth.LeAudioProfile +import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcast +import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastAssistant +import com.android.settingslib.bluetooth.LocalBluetoothManager +import com.android.systemui.animation.DialogTransitionAnimator +import com.android.systemui.bluetooth.qsdialog.DeviceItemActionInteractor.LaunchSettingsCriteria.Companion.getCurrentConnectedLeByGroupId import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Background +import com.android.systemui.plugins.ActivityStarter import com.android.systemui.statusbar.phone.SystemUIDialog import javax.inject.Inject import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.withContext -/** Defines interface for click handling of a DeviceItem. */ -interface DeviceItemActionInteractor { - suspend fun onClick(deviceItem: DeviceItem, dialog: SystemUIDialog) -} - @SysUISingleton -open class DeviceItemActionInteractorImpl +class DeviceItemActionInteractor @Inject constructor( + private val activityStarter: ActivityStarter, + private val dialogTransitionAnimator: DialogTransitionAnimator, + private val localBluetoothManager: LocalBluetoothManager?, @Background private val backgroundDispatcher: CoroutineDispatcher, private val logger: BluetoothTileDialogLogger, private val uiEventLogger: UiEventLogger, -) : DeviceItemActionInteractor { +) { + private val leAudioProfile: LeAudioProfile? + get() = localBluetoothManager?.profileManager?.leAudioProfile + + private val assistantProfile: LocalBluetoothLeBroadcastAssistant? + get() = localBluetoothManager?.profileManager?.leAudioBroadcastAssistantProfile + + private val launchSettingsCriteriaList: List<LaunchSettingsCriteria> + get() = + listOf( + InSharingClickedNoSource(localBluetoothManager, backgroundDispatcher, logger), + NotSharingClickedNonConnect( + leAudioProfile, + assistantProfile, + backgroundDispatcher, + logger + ), + NotSharingClickedConnected( + leAudioProfile, + assistantProfile, + backgroundDispatcher, + logger + ) + ) - override suspend fun onClick(deviceItem: DeviceItem, dialog: SystemUIDialog) { + suspend fun onClick(deviceItem: DeviceItem, dialog: SystemUIDialog) { withContext(backgroundDispatcher) { logger.logDeviceClick(deviceItem.cachedBluetoothDevice.address, deviceItem.type) + if ( + BluetoothUtils.isAudioSharingEnabled() && + localBluetoothManager != null && + leAudioProfile != null && + assistantProfile != null + ) { + val inAudioSharing = BluetoothUtils.isBroadcasting(localBluetoothManager) + logger.logDeviceClickInAudioSharingWhenEnabled(inAudioSharing) + val criteriaMatched = + launchSettingsCriteriaList.firstOrNull { + it.matched(inAudioSharing, deviceItem) + } + if (criteriaMatched != null) { + uiEventLogger.log(criteriaMatched.getClickUiEvent(deviceItem)) + launchSettings(deviceItem.cachedBluetoothDevice.device, dialog) + return@withContext + } + } deviceItem.cachedBluetoothDevice.apply { when (deviceItem.type) { DeviceItemType.ACTIVE_MEDIA_BLUETOOTH_DEVICE -> { @@ -69,4 +124,184 @@ constructor( } } } + + private fun launchSettings(device: BluetoothDevice, dialog: SystemUIDialog) { + val intent = + Intent(Settings.ACTION_BLUETOOTH_SETTINGS).apply { + putExtra( + EXTRA_SHOW_FRAGMENT_ARGUMENTS, + Bundle().apply { + putParcelable(LocalBluetoothLeBroadcast.EXTRA_BLUETOOTH_DEVICE, device) + } + ) + } + intent.flags = Intent.FLAG_ACTIVITY_CLEAR_TASK + activityStarter.postStartActivityDismissingKeyguard( + intent, + 0, + dialogTransitionAnimator.createActivityTransitionController(dialog) + ) + } + + private interface LaunchSettingsCriteria { + suspend fun matched(inAudioSharing: Boolean, deviceItem: DeviceItem): Boolean + + suspend fun getClickUiEvent(deviceItem: DeviceItem): BluetoothTileDialogUiEvent + + companion object { + suspend fun getCurrentConnectedLeByGroupId( + leAudioProfile: LeAudioProfile, + assistantProfile: LocalBluetoothLeBroadcastAssistant, + @Background backgroundDispatcher: CoroutineDispatcher, + logger: BluetoothTileDialogLogger, + ): Map<Int, List<BluetoothDevice>> { + return withContext(backgroundDispatcher) { + assistantProfile + .getDevicesMatchingConnectionStates( + intArrayOf(BluetoothProfile.STATE_CONNECTED) + ) + ?.filterNotNull() + ?.groupBy { leAudioProfile.getGroupId(it) } + ?.also { logger.logConnectedLeByGroupId(it) } ?: emptyMap() + } + } + } + } + + private class InSharingClickedNoSource( + private val localBluetoothManager: LocalBluetoothManager?, + @Background private val backgroundDispatcher: CoroutineDispatcher, + private val logger: BluetoothTileDialogLogger, + ) : LaunchSettingsCriteria { + // If currently broadcasting and the clicked device is not connected to the source + override suspend fun matched(inAudioSharing: Boolean, deviceItem: DeviceItem): Boolean { + return withContext(backgroundDispatcher) { + val matched = + inAudioSharing && + deviceItem.isMediaDevice && + !BluetoothUtils.hasConnectedBroadcastSource( + deviceItem.cachedBluetoothDevice, + localBluetoothManager + ) + + if (matched) { + logger.logLaunchSettingsCriteriaMatched("InSharingClickedNoSource", deviceItem) + } + + matched + } + } + + override suspend fun getClickUiEvent(deviceItem: DeviceItem) = + if (deviceItem.isLeAudioSupported) + BluetoothTileDialogUiEvent.LAUNCH_SETTINGS_IN_SHARING_LE_DEVICE_CLICKED + else BluetoothTileDialogUiEvent.LAUNCH_SETTINGS_IN_SHARING_NON_LE_DEVICE_CLICKED + } + + private class NotSharingClickedNonConnect( + private val leAudioProfile: LeAudioProfile?, + private val assistantProfile: LocalBluetoothLeBroadcastAssistant?, + @Background private val backgroundDispatcher: CoroutineDispatcher, + private val logger: BluetoothTileDialogLogger, + ) : LaunchSettingsCriteria { + // If not broadcasting, having one device connected, and clicked on a not yet connected LE + // audio device + override suspend fun matched(inAudioSharing: Boolean, deviceItem: DeviceItem): Boolean { + return withContext(backgroundDispatcher) { + val matched = + leAudioProfile?.let { leAudio -> + assistantProfile?.let { assistant -> + !inAudioSharing && + getCurrentConnectedLeByGroupId( + leAudio, + assistant, + backgroundDispatcher, + logger + ) + .size == 1 && + deviceItem.isNotConnectedLeAudioSupported + } + } ?: false + + if (matched) { + logger.logLaunchSettingsCriteriaMatched( + "NotSharingClickedNonConnect", + deviceItem + ) + } + + matched + } + } + + override suspend fun getClickUiEvent(deviceItem: DeviceItem) = + BluetoothTileDialogUiEvent.LAUNCH_SETTINGS_NOT_SHARING_SAVED_LE_DEVICE_CLICKED + } + + private class NotSharingClickedConnected( + private val leAudioProfile: LeAudioProfile?, + private val assistantProfile: LocalBluetoothLeBroadcastAssistant?, + @Background private val backgroundDispatcher: CoroutineDispatcher, + private val logger: BluetoothTileDialogLogger, + ) : LaunchSettingsCriteria { + // If not broadcasting, having two device connected, clicked on any connected LE audio + // devices + override suspend fun matched(inAudioSharing: Boolean, deviceItem: DeviceItem): Boolean { + return withContext(backgroundDispatcher) { + val matched = + leAudioProfile?.let { leAudio -> + assistantProfile?.let { assistant -> + !inAudioSharing && + getCurrentConnectedLeByGroupId( + leAudio, + assistant, + backgroundDispatcher, + logger + ) + .size == 2 && + deviceItem.isActiveOrConnectedLeAudioSupported + } + } ?: false + + if (matched) { + logger.logLaunchSettingsCriteriaMatched( + "NotSharingClickedConnected", + deviceItem + ) + } + + matched + } + } + + override suspend fun getClickUiEvent(deviceItem: DeviceItem) = + BluetoothTileDialogUiEvent.LAUNCH_SETTINGS_NOT_SHARING_CONNECTED_LE_DEVICE_CLICKED + } + + private companion object { + const val EXTRA_SHOW_FRAGMENT_ARGUMENTS = ":settings:show_fragment_args" + + val DeviceItem.isLeAudioSupported: Boolean + get() = + cachedBluetoothDevice.profiles.any { profile -> + profile is LeAudioProfile && profile.isEnabled(cachedBluetoothDevice.device) + } + + val DeviceItem.isNotConnectedLeAudioSupported: Boolean + get() = type == DeviceItemType.SAVED_BLUETOOTH_DEVICE && isLeAudioSupported + + val DeviceItem.isActiveOrConnectedLeAudioSupported: Boolean + get() = + (type == DeviceItemType.ACTIVE_MEDIA_BLUETOOTH_DEVICE || + type == DeviceItemType.AVAILABLE_MEDIA_BLUETOOTH_DEVICE) && isLeAudioSupported + + val DeviceItem.isMediaDevice: Boolean + get() = + cachedBluetoothDevice.connectableProfiles.any { + it is A2dpProfile || + it is HearingAidProfile || + it is LeAudioProfile || + it is HeadsetProfile + } + } } diff --git a/packages/SystemUI/src/com/android/systemui/communal/CommunalDreamStartable.kt b/packages/SystemUI/src/com/android/systemui/communal/CommunalDreamStartable.kt index 8993a3be058b..38f51daca2a4 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/CommunalDreamStartable.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/CommunalDreamStartable.kt @@ -19,23 +19,25 @@ package com.android.systemui.communal import android.annotation.SuppressLint import android.app.DreamManager import com.android.systemui.CoreStartable -import com.android.systemui.Flags.glanceableHubAllowKeyguardWhenDreaming import com.android.systemui.Flags.communalHub +import com.android.systemui.Flags.glanceableHubAllowKeyguardWhenDreaming import com.android.systemui.Flags.restartDreamOnUnocclude -import com.android.systemui.communal.domain.interactor.CommunalInteractor import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor +import com.android.systemui.keyguard.shared.model.Edge import com.android.systemui.keyguard.shared.model.KeyguardState +import com.android.systemui.keyguard.shared.model.TransitionState +import com.android.systemui.keyguard.shared.model.filterState import com.android.systemui.power.domain.interactor.PowerInteractor -import com.android.systemui.util.kotlin.Utils.Companion.sample -import com.android.systemui.util.kotlin.sample +import com.android.systemui.scene.shared.model.Scenes +import com.android.systemui.util.kotlin.Utils.Companion.sampleFilter +import javax.inject.Inject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach -import javax.inject.Inject /** * A [CoreStartable] responsible for automatically starting the dream when the communal hub is @@ -48,7 +50,6 @@ constructor( private val powerInteractor: PowerInteractor, private val keyguardInteractor: KeyguardInteractor, private val keyguardTransitionInteractor: KeyguardTransitionInteractor, - private val communalInteractor: CommunalInteractor, private val dreamManager: DreamManager, @Background private val bgScope: CoroutineScope, ) : CoreStartable { @@ -60,31 +61,28 @@ constructor( // Return to dream from occluded when not already dreaming. if (restartDreamOnUnocclude()) { - keyguardTransitionInteractor.startedKeyguardTransitionStep - .sample(keyguardInteractor.isDreaming, ::Pair) - .filter { - it.first.from == KeyguardState.OCCLUDED && - it.first.to == KeyguardState.DREAMING && - !it.second - } + keyguardTransitionInteractor + .transition(Edge.create(from = KeyguardState.OCCLUDED, to = KeyguardState.DREAMING)) + .filterState(TransitionState.STARTED) + .sampleFilter(keyguardInteractor.isDreaming) { isDreaming -> !isDreaming } .onEach { dreamManager.startDream() } .launchIn(bgScope) } // Restart the dream underneath the hub in order to support the ability to swipe // away the hub to enter the dream. - keyguardTransitionInteractor.finishedKeyguardState - .sample(powerInteractor.isAwake, keyguardInteractor.isDreaming) - .onEach { (finishedState, isAwake, dreaming) -> - if ( - finishedState == KeyguardState.GLANCEABLE_HUB && - !dreaming && - !glanceableHubAllowKeyguardWhenDreaming() && - dreamManager.canStartDreaming(isAwake) - ) { - dreamManager.startDream() - } + keyguardTransitionInteractor + .transition( + edge = Edge.create(to = Scenes.Communal), + edgeWithoutSceneContainer = Edge.create(to = KeyguardState.GLANCEABLE_HUB) + ) + .filterState(TransitionState.FINISHED) + .sampleFilter(powerInteractor.isAwake) { isAwake -> + dreamManager.canStartDreaming(isAwake) } + .sampleFilter(keyguardInteractor.isDreaming) { isDreaming -> !isDreaming } + .filter { !glanceableHubAllowKeyguardWhenDreaming() } + .onEach { dreamManager.startDream() } .launchIn(bgScope) } } diff --git a/packages/SystemUI/src/com/android/systemui/communal/CommunalSceneStartable.kt b/packages/SystemUI/src/com/android/systemui/communal/CommunalSceneStartable.kt index d522c7e16603..88c3f9f6af2e 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/CommunalSceneStartable.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/CommunalSceneStartable.kt @@ -208,7 +208,10 @@ constructor( // doing any translation. CommunalScenes.Communal } - to == KeyguardState.GONE -> CommunalScenes.Blank + // Transitioning to Blank scene when entering the edit mode will be handled separately + // with custom animations. + to == KeyguardState.GONE && !communalInteractor.editModeOpen.value -> + CommunalScenes.Blank !docked && !KeyguardState.deviceIsAwakeInState(to) -> { // If the user taps the screen and wakes the device within this timeout, we don't // want to dismiss the hub diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalSceneRepository.kt b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalSceneRepository.kt index 260dcbad6201..7a4006d515f7 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalSceneRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalSceneRepository.kt @@ -28,6 +28,7 @@ import com.android.systemui.scene.shared.model.SceneDataSource 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.MutableStateFlow import kotlinx.coroutines.flow.SharingStarted @@ -51,7 +52,7 @@ interface CommunalSceneRepository { fun changeScene(toScene: SceneKey, transitionKey: TransitionKey? = null) /** Immediately snaps to the desired scene. */ - fun snapToScene(toScene: SceneKey) + fun snapToScene(toScene: SceneKey, delayMillis: Long = 0) /** * Updates the transition state of the hub [SceneTransitionLayout]. @@ -92,10 +93,11 @@ constructor( } } - override fun snapToScene(toScene: SceneKey) { + override fun snapToScene(toScene: SceneKey, delayMillis: Long) { applicationScope.launch { // SceneTransitionLayout state updates must be triggered on the thread the STL was // created on. + delay(delayMillis) sceneDataSource.snapToScene(toScene) } } diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt index 2be28caa71a9..00678a81b572 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt @@ -38,6 +38,7 @@ import com.android.systemui.communal.shared.model.CommunalContentSize.HALF import com.android.systemui.communal.shared.model.CommunalContentSize.THIRD import com.android.systemui.communal.shared.model.CommunalScenes import com.android.systemui.communal.shared.model.CommunalWidgetContentModel +import com.android.systemui.communal.shared.model.EditModeState import com.android.systemui.communal.widgets.CommunalAppWidgetHost import com.android.systemui.communal.widgets.EditWidgetsActivityStarter import com.android.systemui.communal.widgets.WidgetConfigurator @@ -46,6 +47,7 @@ import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor +import com.android.systemui.keyguard.shared.model.Edge import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.log.LogBuffer import com.android.systemui.log.core.Logger @@ -98,7 +100,7 @@ constructor( broadcastDispatcher: BroadcastDispatcher, private val widgetRepository: CommunalWidgetRepository, private val communalPrefsRepository: CommunalPrefsRepository, - mediaRepository: CommunalMediaRepository, + private val mediaRepository: CommunalMediaRepository, smartspaceRepository: SmartspaceRepository, keyguardInteractor: KeyguardInteractor, keyguardTransitionInteractor: KeyguardTransitionInteractor, @@ -164,7 +166,7 @@ constructor( /** Whether to start dreaming when returning from occluded */ val dreamFromOccluded: Flow<Boolean> = keyguardTransitionInteractor - .transitionStepsToState(KeyguardState.OCCLUDED) + .transition(Edge.create(to = KeyguardState.OCCLUDED)) .map { it.from == KeyguardState.DREAMING } .stateIn(scope = applicationScope, SharingStarted.Eagerly, false) @@ -306,6 +308,7 @@ constructor( preselectedKey: String? = null, shouldOpenWidgetPickerOnStart: Boolean = false, ) { + communalSceneInteractor.setEditModeState(EditModeState.STARTING) editWidgetsActivityStarter.startActivity(preselectedKey, shouldOpenWidgetPickerOnStart) } @@ -479,40 +482,41 @@ constructor( * A flow of ongoing content, including smartspace timers and umo, ordered by creation time and * sized dynamically. */ - val ongoingContent: Flow<List<CommunalContentModel.Ongoing>> = + fun getOngoingContent(mediaHostVisible: Boolean): Flow<List<CommunalContentModel.Ongoing>> = combine(smartspaceTargets, mediaRepository.mediaModel) { smartspace, media -> - val ongoingContent = mutableListOf<CommunalContentModel.Ongoing>() - - // Add smartspace - ongoingContent.addAll( - smartspace.map { target -> - CommunalContentModel.Smartspace( - smartspaceTargetId = target.smartspaceTargetId, - remoteViews = target.remoteViews!!, - createdTimestampMillis = target.creationTimeMillis, + val ongoingContent = mutableListOf<CommunalContentModel.Ongoing>() + + // Add smartspace + ongoingContent.addAll( + smartspace.map { target -> + CommunalContentModel.Smartspace( + smartspaceTargetId = target.smartspaceTargetId, + remoteViews = target.remoteViews!!, + createdTimestampMillis = target.creationTimeMillis, + ) + } + ) + + // Add UMO + if (mediaHostVisible && media.hasActiveMediaOrRecommendation) { + ongoingContent.add( + CommunalContentModel.Umo( + createdTimestampMillis = media.createdTimestampMillis, + ) ) } - ) - // Add UMO - if (media.hasActiveMediaOrRecommendation) { - ongoingContent.add( - CommunalContentModel.Umo( - createdTimestampMillis = media.createdTimestampMillis, - ) - ) - } + // Order by creation time descending + ongoingContent.sortByDescending { it.createdTimestampMillis } - // Order by creation time descending - ongoingContent.sortByDescending { it.createdTimestampMillis } + // Dynamic sizing + ongoingContent.forEachIndexed { index, model -> + model.size = dynamicContentSize(ongoingContent.size, index) + } - // Dynamic sizing - ongoingContent.forEachIndexed { index, model -> - model.size = dynamicContentSize(ongoingContent.size, index) + ongoingContent } - - return@combine ongoingContent - } + .flowOn(bgDispatcher) /** * Filter and retain widgets associated with an existing user, safeguarding against displaying diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSceneInteractor.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSceneInteractor.kt index 5cfe9798420d..20d8a2a191cf 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSceneInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSceneInteractor.kt @@ -22,14 +22,17 @@ import com.android.compose.animation.scene.TransitionKey import com.android.systemui.communal.data.repository.CommunalSceneRepository import com.android.systemui.communal.domain.model.CommunalTransitionProgressModel import com.android.systemui.communal.shared.model.CommunalScenes +import com.android.systemui.communal.shared.model.EditModeState import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import javax.inject.Inject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.flowOf @@ -53,8 +56,18 @@ constructor( } /** Immediately snaps to the new scene. */ - fun snapToScene(newScene: SceneKey) { - communalSceneRepository.snapToScene(newScene) + fun snapToScene(newScene: SceneKey, delayMillis: Long = 0) { + communalSceneRepository.snapToScene(newScene, delayMillis) + } + + /** Immediately snaps to the new scene when activity is started. */ + fun snapToSceneForActivityStart(newScene: SceneKey, delayMillis: Long = 0) { + // skip if we're starting edit mode activity, as it will be handled later by changeScene + // with transition key [CommunalTransitionKeys.ToEditMode]. + if (_editModeState.value == EditModeState.STARTING) { + return + } + snapToScene(newScene, delayMillis) } /** @@ -62,6 +75,17 @@ constructor( */ val currentScene: Flow<SceneKey> = communalSceneRepository.currentScene + private val _editModeState = MutableStateFlow<EditModeState?>(null) + /** + * Current state for glanceable hub edit mode, used to chain the animations when transitioning + * between communal scene and the edit mode activity. + */ + val editModeState = _editModeState.asStateFlow() + + fun setEditModeState(value: EditModeState?) { + _editModeState.value = value + } + /** Transition state of the hub mode. */ val transitionState: StateFlow<ObservableTransitionState> = communalSceneRepository.transitionState diff --git a/packages/SystemUI/src/com/android/systemui/communal/shared/model/CommunalBackgroundType.kt b/packages/SystemUI/src/com/android/systemui/communal/shared/model/CommunalBackgroundType.kt index 8b816db53970..4eaba065e078 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/shared/model/CommunalBackgroundType.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/shared/model/CommunalBackgroundType.kt @@ -21,4 +21,5 @@ enum class CommunalBackgroundType(val value: Int) { DEFAULT(0), STATIC_GRADIENT(1), ANIMATED(2), + NONE(3), } diff --git a/packages/SystemUI/src/com/android/systemui/communal/shared/model/CommunalTransitionKeys.kt b/packages/SystemUI/src/com/android/systemui/communal/shared/model/CommunalTransitionKeys.kt index 73cfb5286e1c..11fb2332dc5f 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/shared/model/CommunalTransitionKeys.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/shared/model/CommunalTransitionKeys.kt @@ -26,6 +26,10 @@ import com.android.compose.animation.scene.TransitionKey object CommunalTransitionKeys { /** Fades the glanceable hub without any translation */ val SimpleFade = TransitionKey("SimpleFade") + /** Transition from the glanceable hub before entering edit mode */ + val ToEditMode = TransitionKey("ToEditMode") + /** Transition to the glanceable hub after exiting edit mode */ + val FromEditMode = TransitionKey("FromEditMode") /** Immediately transitions without any delay */ val Immediately = TransitionKey("Immediately") } diff --git a/packages/SystemUI/src/com/android/systemui/communal/shared/model/EditModeState.kt b/packages/SystemUI/src/com/android/systemui/communal/shared/model/EditModeState.kt new file mode 100644 index 000000000000..ace9c2e171e0 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/communal/shared/model/EditModeState.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.communal.shared.model + +/** + * Models the state of the edit mode activity. Used to chain the animation during the transition + * between the hub on communal scene, and the edit mode activity after unlocking the keyguard. + */ +enum class EditModeState(val value: Int) { + // starting activity after dismissing keyguard + STARTING(0), + // activity content is showing + SHOWING(1), +} diff --git a/packages/SystemUI/src/com/android/systemui/communal/smartspace/SmartspaceInteractionHandler.kt b/packages/SystemUI/src/com/android/systemui/communal/smartspace/SmartspaceInteractionHandler.kt new file mode 100644 index 000000000000..a88b777be785 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/communal/smartspace/SmartspaceInteractionHandler.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.communal.smartspace + +import android.app.ActivityOptions +import android.app.PendingIntent +import android.content.Intent +import android.view.View +import android.widget.RemoteViews +import com.android.systemui.animation.ActivityTransitionAnimator +import com.android.systemui.communal.util.InteractionHandlerDelegate +import com.android.systemui.communal.widgets.SmartspaceAppWidgetHostView +import com.android.systemui.plugins.ActivityStarter +import javax.inject.Inject + +/** + * Handles interactions on smartspace elements on the hub. + */ +class SmartspaceInteractionHandler @Inject constructor( + private val activityStarter: ActivityStarter, +) : RemoteViews.InteractionHandler { + private val delegate = InteractionHandlerDelegate( + findViewToAnimate = { view -> view is SmartspaceAppWidgetHostView }, + intentStarter = this::startIntent, + ) + + override fun onInteraction( + view: View, + pendingIntent: PendingIntent, + response: RemoteViews.RemoteResponse + ): Boolean = delegate.onInteraction(view, pendingIntent, response) + + private fun startIntent( + pendingIntent: PendingIntent, + fillInIntent: Intent, + extraOptions: ActivityOptions, + animationController: ActivityTransitionAnimator.Controller? + ): Boolean { + activityStarter.startPendingIntentWithoutDismissing( + pendingIntent, + /* dismissShade = */ false, + /* intentSentUiThreadCallback = */ null, + animationController, + fillInIntent, + extraOptions.toBundle() + ) + return true + } +} 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 8cd5603bdc7f..cc9073040c96 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 @@ -25,6 +25,7 @@ import com.android.compose.animation.scene.TransitionKey import com.android.systemui.communal.domain.interactor.CommunalInteractor import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor import com.android.systemui.communal.domain.model.CommunalContentModel +import com.android.systemui.communal.shared.model.EditModeState import com.android.systemui.communal.widgets.WidgetConfigurator import com.android.systemui.media.controls.ui.view.MediaHost import kotlinx.coroutines.flow.Flow @@ -34,12 +35,15 @@ import kotlinx.coroutines.flow.flowOf /** The base view model for the communal hub. */ abstract class BaseCommunalViewModel( - private val communalSceneInteractor: CommunalSceneInteractor, + val communalSceneInteractor: CommunalSceneInteractor, private val communalInteractor: CommunalInteractor, val mediaHost: MediaHost, ) { val currentScene: Flow<SceneKey> = communalSceneInteractor.currentScene + /** Used to animate showing or hiding the communal content. */ + open val isCommunalContentVisible: Flow<Boolean> = MutableStateFlow(false) + /** Whether communal hub should be focused by accessibility tools. */ open val isFocusable: Flow<Boolean> = MutableStateFlow(false) @@ -63,6 +67,8 @@ abstract class BaseCommunalViewModel( communalSceneInteractor.changeScene(scene, transitionKey) } + fun setEditModeState(state: EditModeState?) = communalSceneInteractor.setEditModeState(state) + /** * Updates the transition state of the hub [SceneTransitionLayout]. * 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 bc65ccb9ed5a..c0c586191834 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 @@ -24,25 +24,33 @@ import android.content.res.Resources import android.util.Log import androidx.activity.result.ActivityResultLauncher import com.android.internal.logging.UiEventLogger +import com.android.systemui.Flags.enableWidgetPickerSizeFilter import com.android.systemui.communal.domain.interactor.CommunalInteractor import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor import com.android.systemui.communal.domain.interactor.CommunalSettingsInteractor import com.android.systemui.communal.domain.model.CommunalContentModel import com.android.systemui.communal.shared.log.CommunalUiEvent +import com.android.systemui.communal.shared.model.EditModeState import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Background +import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor +import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.log.LogBuffer import com.android.systemui.log.core.Logger import com.android.systemui.log.dagger.CommunalLog import com.android.systemui.media.controls.ui.view.MediaHost import com.android.systemui.media.dagger.MediaModule +import com.android.systemui.res.R +import com.android.systemui.util.kotlin.BooleanFlowOperators.allOf import javax.inject.Inject import javax.inject.Named import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.first +import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.withContext @@ -54,6 +62,7 @@ constructor( communalSceneInteractor: CommunalSceneInteractor, private val communalInteractor: CommunalInteractor, private val communalSettingsInteractor: CommunalSettingsInteractor, + keyguardTransitionInteractor: KeyguardTransitionInteractor, @Named(MediaModule.COMMUNAL_HUB) mediaHost: MediaHost, private val uiEventLogger: UiEventLogger, @CommunalLog logBuffer: LogBuffer, @@ -64,6 +73,20 @@ constructor( override val isEditMode = true + override val isCommunalContentVisible: Flow<Boolean> = + communalSceneInteractor.editModeState.map { it == EditModeState.SHOWING } + + /** + * Emits when edit mode activity can show, after we've transitioned to [KeyguardState.GONE] + * and edit mode is open. + */ + val canShowEditMode = + allOf( + keyguardTransitionInteractor.isFinishedInState(KeyguardState.GONE), + communalInteractor.editModeOpen + ) + .filter { it } + // Only widgets are editable. override val communalContent: Flow<List<CommunalContentModel>> = communalInteractor.widgetContent.onEach { models -> @@ -138,6 +161,16 @@ constructor( return Intent(Intent.ACTION_PICK).apply { setPackage(packageName) + if (enableWidgetPickerSizeFilter()) { + putExtra( + EXTRA_DESIRED_WIDGET_WIDTH, + resources.getDimensionPixelSize(R.dimen.communal_widget_picker_desired_width) + ) + putExtra( + EXTRA_DESIRED_WIDGET_HEIGHT, + resources.getDimensionPixelSize(R.dimen.communal_widget_picker_desired_height) + ) + } putExtra( AppWidgetManager.EXTRA_CATEGORY_FILTER, communalSettingsInteractor.communalWidgetCategories.value @@ -160,9 +193,16 @@ constructor( /** Sets whether edit mode is currently open */ fun setEditModeOpen(isOpen: Boolean) = communalInteractor.setEditModeOpen(isOpen) + /** Called when exiting the edit mode, before transitioning back to the communal scene. */ + fun cleanupEditModeState() { + communalSceneInteractor.setEditModeState(null) + } + companion object { private const val TAG = "CommunalEditModeViewModel" + private const val EXTRA_DESIRED_WIDGET_WIDTH = "desired_widget_width" + private const val EXTRA_DESIRED_WIDGET_HEIGHT = "desired_widget_height" private const val EXTRA_UI_SURFACE_KEY = "ui_surface" private const val EXTRA_UI_SURFACE_VALUE = "widgets_hub" const val EXTRA_ADDED_APP_WIDGETS_KEY = "added_app_widgets" diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalTransitionViewModel.kt index 9114aabae2e9..fce18a263902 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalTransitionViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalTransitionViewModel.kt @@ -18,20 +18,23 @@ package com.android.systemui.communal.ui.viewmodel import android.graphics.Color import com.android.systemui.communal.domain.interactor.CommunalInteractor +import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor import com.android.systemui.communal.util.CommunalColors import com.android.systemui.dagger.SysUISingleton import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor +import com.android.systemui.keyguard.shared.model.Edge import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.shared.model.TransitionState import com.android.systemui.keyguard.ui.viewmodel.DreamingToGlanceableHubTransitionViewModel import com.android.systemui.keyguard.ui.viewmodel.GlanceableHubToDreamingTransitionViewModel import com.android.systemui.keyguard.ui.viewmodel.GlanceableHubToLockscreenTransitionViewModel import com.android.systemui.keyguard.ui.viewmodel.LockscreenToGlanceableHubTransitionViewModel +import com.android.systemui.scene.shared.model.Scenes +import com.android.systemui.util.kotlin.BooleanFlowOperators.allOf import javax.inject.Inject import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.combine -import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.merge @@ -48,22 +51,27 @@ constructor( dreamToGlanceableHubTransitionViewModel: DreamingToGlanceableHubTransitionViewModel, glanceableHubToDreamTransitionViewModel: GlanceableHubToDreamingTransitionViewModel, communalInteractor: CommunalInteractor, - keyguardTransitionInteractor: KeyguardTransitionInteractor, + communalSceneInteractor: CommunalSceneInteractor, + keyguardTransitionInteractor: KeyguardTransitionInteractor ) { // Show UMO on glanceable hub immediately on transition into glanceable hub private val showUmoFromOccludedToGlanceableHub: Flow<Boolean> = keyguardTransitionInteractor - .transitionStepsFromState(KeyguardState.OCCLUDED) + .transition( + Edge.create(from = KeyguardState.OCCLUDED, to = KeyguardState.GLANCEABLE_HUB) + ) .filter { - it.to == KeyguardState.GLANCEABLE_HUB && - (it.transitionState == TransitionState.STARTED || - it.transitionState == TransitionState.CANCELED) + (it.transitionState == TransitionState.STARTED || + it.transitionState == TransitionState.CANCELED) } .map { it.transitionState == TransitionState.STARTED } private val showUmoFromGlanceableHubToOccluded: Flow<Boolean> = keyguardTransitionInteractor - .transitionStepsFromState(KeyguardState.GLANCEABLE_HUB) + .transition( + edge = Edge.create(from = Scenes.Communal), + edgeWithoutSceneContainer = Edge.create(from = KeyguardState.GLANCEABLE_HUB) + ) .filter { it.to == KeyguardState.OCCLUDED && (it.transitionState == TransitionState.FINISHED || @@ -77,7 +85,13 @@ constructor( * of UMO should be updated. */ val isUmoOnCommunal: Flow<Boolean> = - merge( + allOf( + // Only show UMO on the hub if the hub is at least partially visible. This prevents + // the UMO from being missing on the lock screen when going from the hub to lock + // screen in some way other than through a direct transition, such as unlocking from + // the hub, then pressing power twice to go back to the lock screen. + communalSceneInteractor.isCommunalVisible, + merge( lockscreenToGlanceableHubTransitionViewModel.showUmo, glanceableHubToLockscreenTransitionViewModel.showUmo, dreamToGlanceableHubTransitionViewModel.showUmo, @@ -85,17 +99,18 @@ constructor( showUmoFromOccludedToGlanceableHub, showUmoFromGlanceableHubToOccluded, ) - .distinctUntilChanged() + ) /** Whether to show communal when exiting the occluded state. */ val showCommunalFromOccluded: Flow<Boolean> = communalInteractor.showCommunalFromOccluded val transitionFromOccludedEnded = - keyguardTransitionInteractor.transitionStepsFromState(KeyguardState.OCCLUDED).filter { step - -> - step.transitionState == TransitionState.FINISHED || - step.transitionState == TransitionState.CANCELED - } + keyguardTransitionInteractor + .transition(Edge.create(from = KeyguardState.OCCLUDED)) + .filter { step -> + step.transitionState == TransitionState.FINISHED || + step.transitionState == TransitionState.CANCELED + } val recentsBackgroundColor: Flow<Color?> = combine(showCommunalFromOccluded, communalColors.backgroundColor) { diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt index 3e00b0455575..11247df5e2fd 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt @@ -42,12 +42,15 @@ import com.android.systemui.res.R import com.android.systemui.shade.domain.interactor.ShadeInteractor import com.android.systemui.util.kotlin.BooleanFlowOperators.allOf import com.android.systemui.util.kotlin.BooleanFlowOperators.not +import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow import com.android.systemui.utils.coroutines.flow.flatMapLatestConflated import javax.inject.Inject import javax.inject.Named +import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.Job +import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.delay import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow @@ -56,8 +59,10 @@ import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.flatMapLatest 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.launch /** The default view model used for showing the communal hub. */ @@ -66,6 +71,7 @@ import kotlinx.coroutines.launch class CommunalViewModel @Inject constructor( + @Main val mainDispatcher: CoroutineDispatcher, @Application private val scope: CoroutineScope, @Main private val resources: Resources, keyguardTransitionInteractor: KeyguardTransitionInteractor, @@ -79,6 +85,18 @@ constructor( @CommunalLog logBuffer: LogBuffer, ) : BaseCommunalViewModel(communalSceneInteractor, communalInteractor, mediaHost) { + private val _isMediaHostVisible = + conflatedCallbackFlow<Boolean> { + val callback = { visible: Boolean -> + trySend(visible) + Unit + } + mediaHost.addVisibilityChangeListener(callback) + awaitClose { mediaHost.removeVisibilityChangeListener(callback) } + } + .onStart { emit(mediaHost.visible) } + .flowOn(mainDispatcher) + private val logger = Logger(logBuffer, "CommunalViewModel") /** Communal content saved from the previous emission when the flow is active (not "frozen"). */ @@ -91,8 +109,10 @@ constructor( if (isTutorialMode) { return@flatMapLatest flowOf(communalInteractor.tutorialContent) } + val ongoingContent = + _isMediaHostVisible.flatMapLatest { communalInteractor.getOngoingContent(it) } combine( - communalInteractor.ongoingContent, + ongoingContent, communalInteractor.widgetContent, communalInteractor.ctaTileContent, ) { ongoing, widgets, ctaTile, @@ -105,6 +125,8 @@ constructor( logger.d({ "Content updated: $str1" }) { str1 = models.joinToString { it.key } } } + override val isCommunalContentVisible: Flow<Boolean> = MutableStateFlow(true) + /** * Freeze the content flow, when an activity is about to show, like starting a timer via voice: * 1) in handheld mode, use the keyguard occluded state; diff --git a/packages/SystemUI/src/com/android/systemui/communal/util/InteractionHandlerDelegate.kt b/packages/SystemUI/src/com/android/systemui/communal/util/InteractionHandlerDelegate.kt new file mode 100644 index 000000000000..40b182dba817 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/communal/util/InteractionHandlerDelegate.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.systemui.communal.util + +import android.app.ActivityOptions +import android.app.PendingIntent +import android.content.Intent +import android.view.View +import android.widget.RemoteViews +import androidx.core.util.component1 +import androidx.core.util.component2 +import com.android.systemui.animation.ActivityTransitionAnimator + + +/** A delegate that can be used to launch activities from [RemoteViews] */ +class InteractionHandlerDelegate( + private val findViewToAnimate: (View) -> Boolean, + private val intentStarter: IntentStarter, +) : RemoteViews.InteractionHandler { + + /** + * Responsible for starting the pending intent for launching activities. + */ + fun interface IntentStarter { + fun startPendingIntent( + intent: PendingIntent, + fillInIntent: Intent, + activityOptions: ActivityOptions, + controller: ActivityTransitionAnimator.Controller?, + ): Boolean + } + + override fun onInteraction( + view: View, + pendingIntent: PendingIntent, + response: RemoteViews.RemoteResponse + ): Boolean { + val launchOptions = response.getLaunchOptions(view) + return when { + pendingIntent.isActivity -> { + // Forward the fill-in intent and activity options retrieved from the response + // to populate the pending intent, so that list items can launch respective + // activities. + val hostView = getNearestParent(view) + val animationController = + hostView?.let(ActivityTransitionAnimator.Controller::fromView) + val (fillInIntent, activityOptions) = launchOptions + intentStarter.startPendingIntent( + pendingIntent, + fillInIntent, + activityOptions, + animationController + ) + } + + else -> RemoteViews.startPendingIntent(view, pendingIntent, launchOptions) + } + } + + private fun getNearestParent(child: View): View? { + var view: Any? = child + while (view is View) { + if (findViewToAnimate(view)) return view + view = view.parent + } + return null + } +} 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 426f484e4d02..40df6cecaeb8 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt @@ -37,6 +37,7 @@ import com.android.internal.logging.UiEventLogger import com.android.systemui.communal.shared.log.CommunalUiEvent import com.android.systemui.communal.shared.model.CommunalScenes import com.android.systemui.communal.shared.model.CommunalTransitionKeys +import com.android.systemui.communal.shared.model.EditModeState import com.android.systemui.communal.ui.compose.CommunalHub import com.android.systemui.communal.ui.viewmodel.CommunalEditModeViewModel import com.android.systemui.communal.util.WidgetPickerIntentUtils.getWidgetExtraFromIntent @@ -70,8 +71,6 @@ constructor( private var shouldOpenWidgetPickerOnStart = false - private var lockOnDestroy = false - private val addWidgetActivityLauncher: ActivityResultLauncher<Intent> = registerForActivityResult(StartActivityForResult()) { result -> when (result.resultCode) { @@ -97,8 +96,7 @@ constructor( run { Log.w(TAG, "No AppWidgetProviderInfo found in result.") } } } - } - ?: run { Log.w(TAG, "No data in result.") } + } ?: run { Log.w(TAG, "No data in result.") } } else -> Log.w( @@ -110,6 +108,7 @@ constructor( override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) + listenForTransitionAndChangeScene() communalViewModel.setEditModeOpen(true) @@ -141,6 +140,22 @@ constructor( } } + // Handle scene change to show the activity and animate in its content + private fun listenForTransitionAndChangeScene() { + lifecycleScope.launch { + communalViewModel.canShowEditMode.collect { + communalViewModel.changeScene( + CommunalScenes.Blank, + CommunalTransitionKeys.ToEditMode + ) + // wait till transitioned to Blank scene, then animate in communal content in + // edit mode + communalViewModel.currentScene.first { it == CommunalScenes.Blank } + communalViewModel.setEditModeState(EditModeState.SHOWING) + } + } + } + private fun onOpenWidgetPicker() { lifecycleScope.launch { communalViewModel.onOpenWidgetPicker( @@ -153,16 +168,18 @@ constructor( private fun onEditDone() { lifecycleScope.launch { + communalViewModel.cleanupEditModeState() + communalViewModel.changeScene( CommunalScenes.Communal, - CommunalTransitionKeys.SimpleFade + CommunalTransitionKeys.FromEditMode ) // Wait for the current scene to be idle on communal. communalViewModel.isIdleOnCommunal.first { it } - // Then finish the activity (this helps to avoid a flash of lockscreen when locking - // in onDestroy()). - lockOnDestroy = true + + // Lock to go back to the hub after exiting. + lockNow() finish() } } @@ -195,9 +212,8 @@ constructor( override fun onDestroy() { super.onDestroy() + communalViewModel.cleanupEditModeState() communalViewModel.setEditModeOpen(false) - - if (lockOnDestroy) lockNow() } private fun lockNow() { diff --git a/packages/SystemUI/src/com/android/systemui/communal/widgets/SmartspaceAppWidgetHostView.kt b/packages/SystemUI/src/com/android/systemui/communal/widgets/SmartspaceAppWidgetHostView.kt new file mode 100644 index 000000000000..7f1146399f4e --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/communal/widgets/SmartspaceAppWidgetHostView.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.communal.widgets + +import android.appwidget.AppWidgetHostView +import android.appwidget.AppWidgetProviderInfo +import android.content.Context +import com.android.systemui.animation.LaunchableView +import com.android.systemui.animation.LaunchableViewDelegate + +/** AppWidgetHostView that displays in communal hub to show smartspace content. */ +class SmartspaceAppWidgetHostView(context: Context) : AppWidgetHostView(context), LaunchableView { + private val launchableViewDelegate = + LaunchableViewDelegate( + this, + superSetVisibility = { super.setVisibility(it) }, + ) + + override fun setAppWidget(appWidgetId: Int, info: AppWidgetProviderInfo?) { + super.setAppWidget(appWidgetId, info) + setPadding(0, 0, 0, 0) + } + + override fun getRemoteContextEnsuringCorrectCachedApkPath(): Context? { + // Silence errors + return null + } + + override fun setShouldBlockVisibilityChanges(block: Boolean) = + launchableViewDelegate.setShouldBlockVisibilityChanges(block) + + override fun setVisibility(visibility: Int) = launchableViewDelegate.setVisibility(visibility) +} diff --git a/packages/SystemUI/src/com/android/systemui/communal/widgets/WidgetInteractionHandler.kt b/packages/SystemUI/src/com/android/systemui/communal/widgets/WidgetInteractionHandler.kt index 51a3a6d827dd..cbc6c977e3e6 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/widgets/WidgetInteractionHandler.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/widgets/WidgetInteractionHandler.kt @@ -19,53 +19,46 @@ package com.android.systemui.communal.widgets import android.app.ActivityOptions import android.app.PendingIntent import android.content.Intent -import android.util.Pair import android.view.View import android.widget.RemoteViews -import androidx.core.util.component1 -import androidx.core.util.component2 import com.android.systemui.animation.ActivityTransitionAnimator -import com.android.systemui.common.ui.view.getNearestParent +import com.android.systemui.communal.util.InteractionHandlerDelegate +import com.android.systemui.dagger.SysUISingleton import com.android.systemui.plugins.ActivityStarter import javax.inject.Inject +@SysUISingleton class WidgetInteractionHandler @Inject constructor( private val activityStarter: ActivityStarter, ) : RemoteViews.InteractionHandler { + + private val delegate = InteractionHandlerDelegate( + findViewToAnimate = { view -> view is CommunalAppWidgetHostView }, + intentStarter = this::startIntent, + ) + override fun onInteraction( view: View, pendingIntent: PendingIntent, response: RemoteViews.RemoteResponse - ): Boolean { - val launchOptions = response.getLaunchOptions(view) - return when { - pendingIntent.isActivity -> - // Forward the fill-in intent and activity options retrieved from the response - // to populate the pending intent, so that list items can launch respective - // activities. - startActivity(view, pendingIntent, launchOptions) - else -> RemoteViews.startPendingIntent(view, pendingIntent, launchOptions) - } - } + ): Boolean = delegate.onInteraction(view, pendingIntent, response) - private fun startActivity( - view: View, + + private fun startIntent( pendingIntent: PendingIntent, - launchOptions: Pair<Intent, ActivityOptions>, + fillInIntent: Intent, + extraOptions: ActivityOptions, + controller: ActivityTransitionAnimator.Controller? ): Boolean { - val hostView = view.getNearestParent<CommunalAppWidgetHostView>() - val animationController = hostView?.let(ActivityTransitionAnimator.Controller::fromView) - val (fillInIntent, activityOptions) = launchOptions - activityStarter.startPendingIntentMaybeDismissingKeyguard( pendingIntent, /* dismissShade = */ false, /* intentSentUiThreadCallback = */ null, - animationController, + controller, fillInIntent, - activityOptions.toBundle(), + extraOptions.toBundle(), ) return true } diff --git a/packages/SystemUI/src/com/android/systemui/complication/DreamHomeControlsComplication.java b/packages/SystemUI/src/com/android/systemui/complication/DreamHomeControlsComplication.java index e284bc7752cf..92108e9e7103 100644 --- a/packages/SystemUI/src/com/android/systemui/complication/DreamHomeControlsComplication.java +++ b/packages/SystemUI/src/com/android/systemui/complication/DreamHomeControlsComplication.java @@ -36,7 +36,6 @@ import androidx.annotation.Nullable; import com.android.internal.logging.UiEventLogger; import com.android.settingslib.Utils; import com.android.systemui.CoreStartable; -import com.android.systemui.Flags; import com.android.systemui.animation.ActivityTransitionAnimator; import com.android.systemui.complication.dagger.DreamHomeControlsComplicationComponent; import com.android.systemui.controls.ControlsServiceInfo; @@ -137,9 +136,7 @@ public class DreamHomeControlsComplication implements Complication { private void updateHomeControlsComplication() { mControlsComponent.getControlsListingController().ifPresent(c -> { - final boolean replacedWithOpenHub = - Flags.glanceableHubShortcutButton() && mReplacedByOpenHub; - if (isHomeControlsAvailable(c.getCurrentServices()) && !replacedWithOpenHub) { + if (isHomeControlsAvailable(c.getCurrentServices())) { mDreamOverlayStateController.addComplication(mComplication); } else { mDreamOverlayStateController.removeComplication(mComplication); diff --git a/packages/SystemUI/src/com/android/systemui/complication/OpenHubComplication.java b/packages/SystemUI/src/com/android/systemui/complication/OpenHubComplication.java index a679bfb15476..05df2bb4aa59 100644 --- a/packages/SystemUI/src/com/android/systemui/complication/OpenHubComplication.java +++ b/packages/SystemUI/src/com/android/systemui/complication/OpenHubComplication.java @@ -28,7 +28,6 @@ import android.widget.ImageView; import com.android.settingslib.Utils; import com.android.systemui.CoreStartable; -import com.android.systemui.Flags; import com.android.systemui.communal.domain.interactor.CommunalInteractor; import com.android.systemui.communal.shared.model.CommunalScenes; import com.android.systemui.complication.dagger.OpenHubComplicationComponent; @@ -111,11 +110,11 @@ public class OpenHubComplication implements Complication { private void updateOpenHubComplication() { // TODO(b/339667383): don't show the complication if glanceable hub is disabled - if (Flags.glanceableHubShortcutButton()) { - mDreamOverlayStateController.addComplication(mComplication); - } else { - mDreamOverlayStateController.removeComplication(mComplication); - } +// if (Flags.glanceableHubShortcutButton()) { +// mDreamOverlayStateController.addComplication(mComplication); +// } else { +// mDreamOverlayStateController.removeComplication(mComplication); +// } } } diff --git a/packages/SystemUI/src/com/android/systemui/dagger/CommonSystemUIUnfoldModule.kt b/packages/SystemUI/src/com/android/systemui/dagger/CommonSystemUIUnfoldModule.kt new file mode 100644 index 000000000000..a91ce164ae7f --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/dagger/CommonSystemUIUnfoldModule.kt @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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.dagger + +import com.android.systemui.unfold.SysUIUnfoldComponent +import com.android.systemui.unfold.SysUIUnfoldModule.BoundFromSysUiUnfoldModule +import dagger.BindsOptionalOf +import dagger.Module +import dagger.Provides +import java.util.Optional +import kotlin.jvm.optionals.getOrElse + + +/** + * Module for foldable-related classes that is available in all SystemUI variants. + * Provides `Optional<SysUIUnfoldComponent>` which is present when the device is a foldable + * device that has fold/unfold animation enabled. + */ +@Module +abstract class CommonSystemUIUnfoldModule { + + /* Note this will be injected as @BoundFromSysUiUnfoldModule Optional<Optional<...>> */ + @BindsOptionalOf + @BoundFromSysUiUnfoldModule + abstract fun optionalSysUiUnfoldComponent(): Optional<SysUIUnfoldComponent> + + companion object { + @Provides + @SysUISingleton + fun sysUiUnfoldComponent( + /** + * This will be empty when [com.android.systemui.unfold.SysUIUnfoldModule] is not part + * of the graph, and contain the optional when it is. + */ + @BoundFromSysUiUnfoldModule + optionalOfOptional: Optional<Optional<SysUIUnfoldComponent>> + ): Optional<SysUIUnfoldComponent> = optionalOfOptional.getOrElse { Optional.empty() } + } +}
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java b/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java index c2e1e33f5318..2fbb75ec720a 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java @@ -29,6 +29,7 @@ import com.android.systemui.screenshot.scroll.LongScreenshotActivity; import com.android.systemui.sensorprivacy.SensorUseStartedActivity; import com.android.systemui.settings.brightness.BrightnessDialog; import com.android.systemui.telephony.ui.activity.SwitchToManagedProfileForCallActivity; +import com.android.systemui.touchpad.tutorial.ui.view.TouchpadTutorialActivity; import com.android.systemui.tuner.TunerActivity; import com.android.systemui.usb.UsbAccessoryUriActivity; import com.android.systemui.usb.UsbConfirmActivity; @@ -156,4 +157,10 @@ public abstract class DefaultActivityBinder { @ClassKey(SwitchToManagedProfileForCallActivity.class) public abstract Activity bindSwitchToManagedProfileForCallActivity( SwitchToManagedProfileForCallActivity activity); + + /** Inject into TouchpadTutorialActivity. */ + @Binds + @IntoMap + @ClassKey(TouchpadTutorialActivity.class) + public abstract Activity bindTouchpadTutorialActivity(TouchpadTutorialActivity activity); } diff --git a/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSysUIComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSysUIComponent.java index a431a59fcef6..b71af69b13cb 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSysUIComponent.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSysUIComponent.java @@ -19,6 +19,7 @@ package com.android.systemui.dagger; import com.android.systemui.keyguard.CustomizationProvider; import com.android.systemui.statusbar.NotificationInsetsModule; import com.android.systemui.statusbar.QsFrameTranslateModule; +import com.android.systemui.unfold.SysUIUnfoldModule; import dagger.Subcomponent; @@ -34,6 +35,7 @@ import dagger.Subcomponent; SystemUIBinder.class, SystemUIModule.class, SystemUICoreStartableModule.class, + SysUIUnfoldModule.class, ReferenceSystemUIModule.class}) public interface ReferenceSysUIComponent extends SysUIComponent { @@ -51,3 +53,4 @@ public interface ReferenceSysUIComponent extends SysUIComponent { */ void inject(CustomizationProvider customizationProvider); } + diff --git a/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java index 7aab37e12b8c..9f0fc51abdd2 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java @@ -31,6 +31,7 @@ import com.android.systemui.display.ui.viewmodel.ConnectingDisplayViewModel; import com.android.systemui.dock.DockManager; import com.android.systemui.dock.DockManagerImpl; import com.android.systemui.doze.DozeHost; +import com.android.systemui.keyboard.shortcut.ShortcutHelperModule; import com.android.systemui.keyguard.ui.composable.blueprint.DefaultBlueprintModule; import com.android.systemui.keyguard.ui.view.layout.blueprints.KeyguardBlueprintModule; import com.android.systemui.keyguard.ui.view.layout.sections.KeyguardSectionsModule; @@ -137,7 +138,8 @@ import javax.inject.Named; UnfoldTransitionModule.Startables.class, ToastModule.class, VolumeModule.class, - WallpaperModule.class + WallpaperModule.class, + ShortcutHelperModule.class, }) public abstract class ReferenceSystemUIModule { diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java index 2ebb94f8bcf4..a7ff3c36a641 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java @@ -143,7 +143,6 @@ import com.android.systemui.statusbar.window.StatusBarWindowModule; import com.android.systemui.telephony.data.repository.TelephonyRepositoryModule; import com.android.systemui.temporarydisplay.dagger.TemporaryDisplayModule; import com.android.systemui.tuner.dagger.TunerModule; -import com.android.systemui.unfold.SysUIUnfoldModule; import com.android.systemui.user.UserModule; import com.android.systemui.user.domain.UserDomainLayerModule; import com.android.systemui.util.EventLogModule; @@ -254,7 +253,7 @@ import javax.inject.Named; SystemPropertiesFlagsModule.class, SysUIConcurrencyModule.class, SysUICoroutinesModule.class, - SysUIUnfoldModule.class, + CommonSystemUIUnfoldModule.class, TelephonyRepositoryModule.class, TemporaryDisplayModule.class, TunerModule.class, diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt index 9d110e665ebe..10d6881e4f93 100644 --- a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt @@ -29,6 +29,7 @@ import com.android.systemui.keyguard.domain.interactor.TrustInteractor import com.android.systemui.scene.domain.interactor.SceneInteractor import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.util.kotlin.Quad +import com.android.systemui.utils.coroutines.flow.mapLatestConflated import javax.inject.Inject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.ExperimentalCoroutinesApi @@ -37,6 +38,7 @@ import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.filter +import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.map @@ -96,7 +98,16 @@ constructor( .filter { currentScene -> currentScene == Scenes.Gone || currentScene == Scenes.Lockscreen } - .map { it == Scenes.Gone } + .mapLatestConflated { scene -> + if (scene == Scenes.Gone) { + // Make sure device unlock status is definitely unlocked before we consider the + // device "entered". + deviceUnlockedInteractor.deviceUnlockStatus.first { it.isUnlocked } + true + } else { + false + } + } .stateIn( scope = applicationScope, started = SharingStarted.Eagerly, 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 4ac0c5683d06..9d6c2bf4c1d1 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 @@ -16,6 +16,7 @@ package com.android.systemui.display.data.repository +import android.annotation.SuppressLint import android.hardware.display.DisplayManager import android.hardware.display.DisplayManager.DISPLAY_CATEGORY_ALL_INCLUDING_DISABLED import android.hardware.display.DisplayManager.DisplayListener @@ -27,6 +28,7 @@ import android.util.Log import android.view.Display import com.android.app.tracing.FlowTracing.traceEach import com.android.app.tracing.traceSection +import com.android.systemui.Flags import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Background @@ -42,11 +44,12 @@ import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.distinctUntilChanged -import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.filterIsInstance import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onStart +import kotlinx.coroutines.flow.scan import kotlinx.coroutines.flow.shareIn import kotlinx.coroutines.flow.stateIn @@ -91,6 +94,7 @@ interface DisplayRepository { } @SysUISingleton +@SuppressLint("SharedFlowCreation") class DisplayRepositoryImpl @Inject constructor( @@ -132,44 +136,77 @@ constructor( allDisplayEvents.filterIsInstance<DisplayEvent.Changed>().map { event -> event.displayId } override val displayAdditionEvent: Flow<Display?> = - allDisplayEvents.filterIsInstance<DisplayEvent.Added>().map { - displayManager.getDisplay(it.displayId) - } + allDisplayEvents.filterIsInstance<DisplayEvent.Added>().map { getDisplay(it.displayId) } + + private val oldEnabledDisplays: Flow<Set<Display>> = + allDisplayEvents + .map { getDisplays() } + .shareIn(bgApplicationScope, started = SharingStarted.WhileSubscribed(), replay = 1) + + /** Propagate to the listeners only enabled displays */ + private val enabledDisplayIds: Flow<Set<Int>> = + if (Flags.enableEfficientDisplayRepository()) { + allDisplayEvents + .scan(initial = emptySet()) { previousIds: Set<Int>, event: DisplayEvent -> + val id = event.displayId + when (event) { + is DisplayEvent.Removed -> previousIds - id + is DisplayEvent.Added, + is DisplayEvent.Changed -> previousIds + id + } + } + .shareIn( + bgApplicationScope, + started = SharingStarted.WhileSubscribed(), + replay = 1 + ) + } else { + oldEnabledDisplays.map { enabledDisplaysSet -> + enabledDisplaysSet.map { it.displayId }.toSet() + } + } + .debugLog("enabledDisplayIds") /** * Represents displays that went though the [DisplayListener.onDisplayAdded] callback. * * Those are commonly the ones provided by [DisplayManager.getDisplays] by default. */ - private val enabledDisplays = - allDisplayEvents - .map { getDisplays() } - .shareIn(bgApplicationScope, started = SharingStarted.WhileSubscribed(), replay = 1) + private val enabledDisplays: Flow<Set<Display>> = + if (Flags.enableEfficientDisplayRepository()) { + enabledDisplayIds + .mapElementsLazily { displayId -> getDisplay(displayId) } + .flowOn(backgroundCoroutineDispatcher) + .debugLog("enabledDisplayIds") + } else { + oldEnabledDisplays + } + /** + * Represents displays that went though the [DisplayListener.onDisplayAdded] callback. + * + * Those are commonly the ones provided by [DisplayManager.getDisplays] by default. + */ override val displays: Flow<Set<Display>> = enabledDisplays private fun getDisplays(): Set<Display> = traceSection("$TAG#getDisplays()") { displayManager.displays?.toSet() ?: emptySet() } - /** Propagate to the listeners only enabled displays */ - private val enabledDisplayIds: Flow<Set<Int>> = - enabledDisplays - .map { enabledDisplaysSet -> enabledDisplaysSet.map { it.displayId }.toSet() } - .debugLog("enabledDisplayIds") - private val _ignoredDisplayIds = MutableStateFlow<Set<Int>>(emptySet()) private val ignoredDisplayIds: Flow<Set<Int>> = _ignoredDisplayIds.debugLog("ignoredDisplayIds") private fun getInitialConnectedDisplays(): Set<Int> = - displayManager - .getDisplays(DISPLAY_CATEGORY_ALL_INCLUDING_DISABLED) - .map { it.displayId } - .toSet() - .also { - if (DEBUG) { - Log.d(TAG, "getInitialConnectedDisplays: $it") + traceSection("$TAG#getInitialConnectedDisplays") { + displayManager + .getDisplays(DISPLAY_CATEGORY_ALL_INCLUDING_DISABLED) + .map { it.displayId } + .toSet() + .also { + if (DEBUG) { + Log.d(TAG, "getInitialConnectedDisplays: $it") + } } - } + } /* keeps connected displays until they are disconnected. */ private val connectedDisplayIds: StateFlow<Set<Int>> = @@ -230,6 +267,9 @@ constructor( private fun getDisplayType(displayId: Int): Int? = traceSection("$TAG#getDisplayType") { displayManager.getDisplay(displayId)?.type } + private fun getDisplay(displayId: Int): Display? = + traceSection("$TAG#getDisplay") { displayManager.getDisplay(displayId) } + /** * Pending displays are the ones connected, but not enabled and not ignored. * @@ -307,6 +347,30 @@ constructor( } } + /** + * Maps a set of T to a set of V, minimizing the number of `createValue` calls taking into + * account the diff between each root flow emission. + * + * This is needed to minimize the number of [getDisplay] in this class. Note that if the + * [createValue] returns a null element, it will not be added in the output set. + */ + private fun <T, V> Flow<Set<T>>.mapElementsLazily(createValue: (T) -> V?): Flow<Set<V>> { + var initialSet = emptySet<T>() + val currentMap = mutableMapOf<T, V>() + var resultSet = emptySet<V>() + return onEach { currentSet -> + val removed = initialSet - currentSet + val added = currentSet - initialSet + if (added.isNotEmpty() || removed.isNotEmpty()) { + added.forEach { key: T -> createValue(key)?.let { currentMap[key] = it } } + removed.forEach { key: T -> currentMap.remove(key) } + resultSet = currentMap.values.toSet() // Creates a **copy** of values + } + initialSet = currentSet + } + .map { resultSet } + } + private companion object { const val TAG = "DisplayRepository" val DEBUG = Log.isLoggable(TAG, Log.DEBUG) || Compile.IS_DEBUG diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java index 93111874c69b..4a9f741494f4 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java @@ -40,6 +40,7 @@ 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; @@ -564,6 +565,12 @@ public class DozeTriggers implements DozeMachine.Part { return; } + // 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) { + return; + } + if (!mAllowPulseTriggers || mDozeHost.isPulsePending() || !canPulse(dozeState, performedProxCheck)) { if (!mAllowPulseTriggers) { diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java b/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java index 95012a2643a5..1a06418065a2 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java @@ -18,6 +18,7 @@ package com.android.systemui.doze; import static com.android.systemui.doze.DozeMachine.State.DOZE; import static com.android.systemui.doze.DozeMachine.State.DOZE_AOD_PAUSED; +import static com.android.systemui.Flags.dozeuiSchedulingAlarmsBackgroundExecution; import android.app.AlarmManager; import android.content.Context; @@ -70,6 +71,7 @@ public class DozeUi implements DozeMachine.Part { @Inject public DozeUi(Context context, AlarmManager alarmManager, WakeLock wakeLock, DozeHost host, @Main Handler handler, + @Background Handler bgHandler, DozeParameters params, @Background DelayableExecutor bgExecutor, DozeLog dozeLog) { @@ -80,7 +82,13 @@ public class DozeUi implements DozeMachine.Part { mBgExecutor = bgExecutor; mCanAnimateTransition = !params.getDisplayNeedsBlanking(); mDozeParameters = params; - mTimeTicker = new AlarmTimeout(alarmManager, this::onTimeTick, "doze_time_tick", handler); + if (dozeuiSchedulingAlarmsBackgroundExecution()) { + mTimeTicker = new AlarmTimeout(alarmManager, this::onTimeTick, "doze_time_tick", + bgHandler); + } else { + mTimeTicker = new AlarmTimeout(alarmManager, this::onTimeTick, "doze_time_tick", + handler); + } mDozeLog = dozeLog; } diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayAnimationsController.kt b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayAnimationsController.kt index f860893f800b..3294c816d67c 100644 --- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayAnimationsController.kt +++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayAnimationsController.kt @@ -27,11 +27,13 @@ import androidx.lifecycle.Lifecycle import androidx.lifecycle.repeatOnLifecycle import com.android.app.animation.Interpolators import com.android.dream.lowlight.util.TruncatedInterpolator +import com.android.systemui.ambient.statusbar.ui.AmbientStatusBarViewController import com.android.systemui.complication.ComplicationHostViewController import com.android.systemui.complication.ComplicationLayoutParams import com.android.systemui.complication.ComplicationLayoutParams.POSITION_BOTTOM import com.android.systemui.complication.ComplicationLayoutParams.POSITION_TOP import com.android.systemui.complication.ComplicationLayoutParams.Position +import com.android.systemui.dreams.dagger.DreamOverlayComponent.DreamOverlayScope import com.android.systemui.dreams.dagger.DreamOverlayModule import com.android.systemui.dreams.ui.viewmodel.DreamViewModel import com.android.systemui.lifecycle.repeatWhenAttached @@ -45,12 +47,13 @@ import javax.inject.Named import kotlinx.coroutines.launch /** Controller for dream overlay animations. */ +@DreamOverlayScope class DreamOverlayAnimationsController @Inject constructor( private val mBlurUtils: BlurUtils, private val mComplicationHostViewController: ComplicationHostViewController, - private val mStatusBarViewController: DreamOverlayStatusBarViewController, + private val mStatusBarViewController: AmbientStatusBarViewController, private val mOverlayStateController: DreamOverlayStateController, @Named(DreamOverlayModule.DREAM_BLUR_RADIUS) private val mDreamBlurRadius: Int, private val dreamViewModel: DreamViewModel, diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java index 1e725eb71dde..245def8fd27b 100644 --- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java +++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java @@ -40,6 +40,7 @@ import android.view.ViewGroup; import com.android.app.animation.Interpolators; import com.android.dream.lowlight.LowLightTransitionCoordinator; +import com.android.systemui.ambient.statusbar.ui.AmbientStatusBarViewController; import com.android.systemui.ambient.touch.scrim.BouncerlessScrimController; import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerCallbackInteractor; import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerCallbackInteractor.PrimaryBouncerExpansionCallback; @@ -55,6 +56,7 @@ import com.android.systemui.res.R; import com.android.systemui.shade.ShadeExpansionChangeEvent; import com.android.systemui.shade.domain.interactor.ShadeInteractor; import com.android.systemui.statusbar.BlurUtils; +import com.android.systemui.touch.TouchInsetManager; import com.android.systemui.util.ViewController; import kotlinx.coroutines.CoroutineDispatcher; @@ -72,10 +74,12 @@ import javax.inject.Named; public class DreamOverlayContainerViewController extends ViewController<DreamOverlayContainerView> implements LowLightTransitionCoordinator.LowLightEnterListener { - private final DreamOverlayStatusBarViewController mStatusBarViewController; + private final AmbientStatusBarViewController mStatusBarViewController; + private final TouchInsetManager.TouchInsetSession mTouchInsetSession; private final BlurUtils mBlurUtils; private final DreamOverlayAnimationsController mDreamOverlayAnimationsController; private final DreamOverlayStateController mStateController; + private final LowLightTransitionCoordinator mLowLightTransitionCoordinator; private final KeyguardTransitionInteractor mKeyguardTransitionInteractor; private final ShadeInteractor mShadeInteractor; @@ -188,8 +192,9 @@ public class DreamOverlayContainerViewController extends ComplicationHostViewController complicationHostViewController, @Named(DreamOverlayModule.DREAM_OVERLAY_CONTENT_VIEW) ViewGroup contentView, @Named(DreamOverlayModule.HUB_GESTURE_INDICATOR_VIEW) View hubGestureIndicatorView, - DreamOverlayStatusBarViewController statusBarViewController, + AmbientStatusBarViewController statusBarViewController, LowLightTransitionCoordinator lowLightTransitionCoordinator, + TouchInsetManager.TouchInsetSession touchInsetSession, BlurUtils blurUtils, @Main Handler handler, @Background CoroutineDispatcher backgroundDispatcher, @@ -209,6 +214,7 @@ public class DreamOverlayContainerViewController extends super(containerView); mDreamOverlayContentView = contentView; mStatusBarViewController = statusBarViewController; + mTouchInsetSession = touchInsetSession; mBlurUtils = blurUtils; mDreamOverlayAnimationsController = animationsController; mStateController = stateController; @@ -294,6 +300,7 @@ public class DreamOverlayContainerViewController extends mHandler.removeCallbacksAndMessages(null); mPrimaryBouncerCallbackInteractor.removeBouncerExpansionCallback(mBouncerExpansionCallback); mBouncerlessScrimController.removeCallback(mBouncerlessExpansionCallback); + mTouchInsetSession.clear(); mDreamOverlayAnimationsController.cancelAnimations(); } diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayDotImageView.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayDotImageView.java index 409b196fe525..08335180db87 100644 --- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayDotImageView.java +++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayDotImageView.java @@ -31,12 +31,13 @@ import android.util.AttributeSet; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import com.android.systemui.ambient.statusbar.ui.AmbientStatusBarView; import com.android.systemui.res.R; import com.android.systemui.statusbar.AlphaOptimizedImageView; /** * An {@link AlphaOptimizedImageView} that is responsible for rendering a dot. Used by - * {@link DreamOverlayStatusBarView}. + * {@link AmbientStatusBarView}. */ public class DreamOverlayDotImageView extends AlphaOptimizedImageView { private final @ColorInt int mDotColor; diff --git a/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamOverlayModule.java b/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamOverlayModule.java index 789b7f8550d7..76fcabd635d8 100644 --- a/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamOverlayModule.java +++ b/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamOverlayModule.java @@ -25,9 +25,11 @@ import androidx.lifecycle.Lifecycle; import androidx.lifecycle.LifecycleOwner; import com.android.internal.util.Preconditions; +import com.android.systemui.ambient.statusbar.dagger.AmbientStatusBarComponent; +import com.android.systemui.ambient.statusbar.ui.AmbientStatusBarView; +import com.android.systemui.ambient.statusbar.ui.AmbientStatusBarViewController; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.dreams.DreamOverlayContainerView; -import com.android.systemui.dreams.DreamOverlayStatusBarView; import com.android.systemui.res.R; import com.android.systemui.touch.TouchInsetManager; @@ -60,7 +62,7 @@ public abstract class DreamOverlayModule { public static DreamOverlayContainerView providesDreamOverlayContainerView( LayoutInflater layoutInflater) { return Preconditions.checkNotNull((DreamOverlayContainerView) - layoutInflater.inflate(R.layout.dream_overlay_container, null), + layoutInflater.inflate(R.layout.dream_overlay_container, null), "R.layout.dream_layout_container could not be properly inflated"); } @@ -95,13 +97,23 @@ public abstract class DreamOverlayModule { /** */ @Provides @DreamOverlayComponent.DreamOverlayScope - public static DreamOverlayStatusBarView providesDreamOverlayStatusBarView( + public static AmbientStatusBarView providesDreamOverlayStatusBarView( DreamOverlayContainerView view) { return Preconditions.checkNotNull(view.findViewById(R.id.dream_overlay_status_bar), "R.id.status_bar must not be null"); } - /** */ + /** + * Provides the view controller for the {@link AmbientStatusBarView} + */ + @Provides + @DreamOverlayComponent.DreamOverlayScope + public static AmbientStatusBarViewController providesStatusBarViewController( + AmbientStatusBarView view, AmbientStatusBarComponent.Factory factory) { + return factory.create(view).getController(); + } + + /** */ @Provides @DreamOverlayComponent.DreamOverlayScope @Named(MAX_BURN_IN_OFFSET) diff --git a/packages/SystemUI/src/com/android/systemui/haptics/qs/QSLongPressEffect.kt b/packages/SystemUI/src/com/android/systemui/haptics/qs/QSLongPressEffect.kt index 30b958393b60..be4c90356a3d 100644 --- a/packages/SystemUI/src/com/android/systemui/haptics/qs/QSLongPressEffect.kt +++ b/packages/SystemUI/src/com/android/systemui/haptics/qs/QSLongPressEffect.kt @@ -17,23 +17,19 @@ package com.android.systemui.haptics.qs import android.os.VibrationEffect -import android.view.View import androidx.annotation.VisibleForTesting import com.android.systemui.animation.Expandable -import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor import com.android.systemui.plugins.qs.QSTile import com.android.systemui.statusbar.VibratorHelper +import com.android.systemui.statusbar.policy.KeyguardStateController import javax.inject.Inject -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.combine /** * A class that handles the long press visuo-haptic effect for a QS tile. * - * The class is also a [View.OnTouchListener] to handle the touch events, clicks and long-press - * gestures of the tile. The class also provides a [State] tha can be used to determine the current - * state of the long press effect. + * The class can contain references to a [QSTile] and an [Expandable] to perform clicks and + * long-clicks on the tile. The class also provides a [State] tha can be used to determine the + * current state of the long press effect. * * @property[vibratorHelper] The [VibratorHelper] to deliver haptic effects. * @property[effectDuration] The duration of the effect in ms. @@ -43,7 +39,7 @@ class QSLongPressEffect @Inject constructor( private val vibratorHelper: VibratorHelper?, - keyguardInteractor: KeyguardInteractor, + private val keyguardStateController: KeyguardStateController, ) { var effectDuration = 0 @@ -53,24 +49,13 @@ constructor( var state = State.IDLE private set - /** The QSTile and Expandable used to perform a long-click and click actions */ + /** Callback object for effect actions */ + var callback: Callback? = null + + /** The [QSTile] and [Expandable] used to perform a long-click and click actions */ var qsTile: QSTile? = null var expandable: Expandable? = null - /** Flow for view control and action */ - private val _postedActionType = MutableStateFlow<ActionType?>(null) - val actionType: Flow<ActionType?> = - combine( - _postedActionType, - keyguardInteractor.isKeyguardDismissible, - ) { action, isDismissible -> - if (!isDismissible && action == ActionType.LONG_PRESS) { - ActionType.RESET_AND_LONG_PRESS - } else { - action - } - } - /** Haptic effects */ private val durations = vibratorHelper?.getPrimitiveDurations( @@ -111,29 +96,25 @@ constructor( when (state) { State.IDLE -> { setState(State.TIMEOUT_WAIT) - _postedActionType.value = ActionType.WAIT_TAP_TIMEOUT } - State.RUNNING_BACKWARDS -> _postedActionType.value = ActionType.CANCEL_ANIMATOR + State.RUNNING_BACKWARDS -> callback?.onCancelAnimator() else -> {} } } fun handleActionUp() { if (state == State.RUNNING_FORWARD) { - _postedActionType.value = ActionType.REVERSE_ANIMATOR setState(State.RUNNING_BACKWARDS) + callback?.onReverseAnimator() } } fun handleActionCancel() { when (state) { - State.TIMEOUT_WAIT -> { - setState(State.IDLE) - clearActionType() - } + State.TIMEOUT_WAIT -> setState(State.IDLE) State.RUNNING_FORWARD -> { - _postedActionType.value = ActionType.REVERSE_ANIMATOR setState(State.RUNNING_BACKWARDS) + callback?.onReverseAnimator() } else -> {} } @@ -146,33 +127,42 @@ constructor( /** This function is called both when an animator completes or gets cancelled */ fun handleAnimationComplete() { - when (state) { - State.RUNNING_FORWARD -> { - setState(State.IDLE) - vibrate(snapEffect) - _postedActionType.value = ActionType.LONG_PRESS - } - State.RUNNING_BACKWARDS -> { - setState(State.IDLE) - clearActionType() + if (state == State.RUNNING_FORWARD) { + setState(State.IDLE) + vibrate(snapEffect) + if (keyguardStateController.isUnlocked) { + callback?.onPrepareForLaunch() + qsTile?.longClick(expandable) + } else { + callback?.onResetProperties() + qsTile?.longClick(expandable) } - else -> {} + } + if (state != State.TIMEOUT_WAIT) { + // This will happen if the animator did not finish by being cancelled + setState(State.IDLE) } } fun handleAnimationCancel() { setState(State.TIMEOUT_WAIT) - _postedActionType.value = ActionType.WAIT_TAP_TIMEOUT } fun handleTimeoutComplete() { if (state == State.TIMEOUT_WAIT) { - _postedActionType.value = ActionType.START_ANIMATOR + callback?.onStartAnimator() } } - fun clearActionType() { - _postedActionType.value = null + fun onTileClick(): Boolean { + if (state == State.TIMEOUT_WAIT) { + setState(State.IDLE) + qsTile?.let { + it.click(expandable) + return true + } + } + return false } /** @@ -196,22 +186,9 @@ constructor( effectDuration ) setState(State.IDLE) - clearActionType() return true } - fun onTileClick(): Boolean { - if (state == State.TIMEOUT_WAIT) { - setState(State.IDLE) - clearActionType() - qsTile?.let { - it.click(expandable) - return true - } - } - return false - } - enum class State { IDLE, /* The effect is idle waiting for touch input */ TIMEOUT_WAIT, /* The effect is waiting for a [PRESSED_TIMEOUT] period */ @@ -219,13 +196,22 @@ constructor( RUNNING_BACKWARDS, /* The effect was interrupted and is now running backwards */ } - /* A type of action to perform on the view depending on the effect's state and logic */ - enum class ActionType { - WAIT_TAP_TIMEOUT, - LONG_PRESS, - RESET_AND_LONG_PRESS, - START_ANIMATOR, - REVERSE_ANIMATOR, - CANCEL_ANIMATOR, + /** Callbacks to notify view and animator actions */ + interface Callback { + + /** Prepare for an activity launch */ + fun onPrepareForLaunch() + + /** Reset the tile visual properties */ + fun onResetProperties() + + /** Start the effect animator */ + fun onStartAnimator() + + /** Reverse the effect animator */ + fun onReverseAnimator() + + /** Cancel the effect animator */ + fun onCancelAnimator() } } diff --git a/packages/SystemUI/src/com/android/systemui/haptics/qs/QSLongPressEffectViewBinder.kt b/packages/SystemUI/src/com/android/systemui/haptics/qs/QSLongPressEffectViewBinder.kt deleted file mode 100644 index 92a55ef0e74f..000000000000 --- a/packages/SystemUI/src/com/android/systemui/haptics/qs/QSLongPressEffectViewBinder.kt +++ /dev/null @@ -1,105 +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.haptics.qs - -import android.animation.ValueAnimator -import android.view.ViewConfiguration -import android.view.animation.AccelerateDecelerateInterpolator -import androidx.core.animation.doOnCancel -import androidx.core.animation.doOnEnd -import androidx.core.animation.doOnStart -import androidx.lifecycle.Lifecycle -import androidx.lifecycle.repeatOnLifecycle -import com.android.app.tracing.coroutines.launch -import com.android.systemui.lifecycle.repeatWhenAttached -import com.android.systemui.qs.tileimpl.QSTileViewImpl -import kotlinx.coroutines.DisposableHandle -import kotlinx.coroutines.delay -import kotlinx.coroutines.flow.filterNotNull - -object QSLongPressEffectViewBinder { - - fun bind( - tile: QSTileViewImpl, - qsLongPressEffect: QSLongPressEffect?, - tileSpec: String?, - ): DisposableHandle? { - if (qsLongPressEffect == null) return null - - return tile.repeatWhenAttached { - repeatOnLifecycle(Lifecycle.State.CREATED) { - // Action to perform - launch({ "${tileSpec ?: "unknownTileSpec"}#LongPressEffect#action" }) { - var effectAnimator: ValueAnimator? = null - - qsLongPressEffect.actionType.filterNotNull().collect { action -> - when (action) { - QSLongPressEffect.ActionType.WAIT_TAP_TIMEOUT -> { - delay(ViewConfiguration.getTapTimeout().toLong()) - qsLongPressEffect.handleTimeoutComplete() - } - QSLongPressEffect.ActionType.LONG_PRESS -> { - tile.prepareForLaunch() - qsLongPressEffect.qsTile?.longClick(qsLongPressEffect.expandable) - qsLongPressEffect.clearActionType() - } - QSLongPressEffect.ActionType.RESET_AND_LONG_PRESS -> { - tile.resetLongPressEffectProperties() - qsLongPressEffect.qsTile?.longClick(qsLongPressEffect.expandable) - qsLongPressEffect.clearActionType() - } - QSLongPressEffect.ActionType.START_ANIMATOR -> { - if (effectAnimator?.isRunning != true) { - effectAnimator = - ValueAnimator.ofFloat(0f, 1f).apply { - this.duration = - qsLongPressEffect.effectDuration.toLong() - interpolator = AccelerateDecelerateInterpolator() - - doOnStart { qsLongPressEffect.handleAnimationStart() } - addUpdateListener { - val value = animatedValue as Float - if (value == 0f) { - tile.bringToFront() - } else { - tile.updateLongPressEffectProperties(value) - } - } - doOnEnd { qsLongPressEffect.handleAnimationComplete() } - doOnCancel { qsLongPressEffect.handleAnimationCancel() } - start() - } - } - } - QSLongPressEffect.ActionType.REVERSE_ANIMATOR -> { - effectAnimator?.let { - val pausedProgress = it.animatedFraction - qsLongPressEffect.playReverseHaptics(pausedProgress) - it.reverse() - } - } - QSLongPressEffect.ActionType.CANCEL_ANIMATOR -> { - tile.resetLongPressEffectProperties() - effectAnimator?.cancel() - } - } - } - } - } - } - } -} diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/KeyboardModule.kt b/packages/SystemUI/src/com/android/systemui/keyboard/KeyboardModule.kt index fc9406bd27d8..c6fb4f9d6956 100644 --- a/packages/SystemUI/src/com/android/systemui/keyboard/KeyboardModule.kt +++ b/packages/SystemUI/src/com/android/systemui/keyboard/KeyboardModule.kt @@ -19,13 +19,12 @@ package com.android.systemui.keyboard import com.android.systemui.keyboard.data.repository.KeyboardRepository import com.android.systemui.keyboard.data.repository.KeyboardRepositoryImpl -import com.android.systemui.keyboard.shortcut.ShortcutHelperModule import com.android.systemui.keyboard.stickykeys.data.repository.StickyKeysRepository import com.android.systemui.keyboard.stickykeys.data.repository.StickyKeysRepositoryImpl import dagger.Binds import dagger.Module -@Module(includes = [ShortcutHelperModule::class]) +@Module abstract class KeyboardModule { @Binds 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 52ccc219353e..271e79b8d060 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 @@ -16,13 +16,16 @@ package com.android.systemui.keyboard.shortcut.ui.composable -import androidx.annotation.StringRes import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.core.animateFloatAsState import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.BoxScope import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.ExperimentalLayoutApi +import androidx.compose.foundation.layout.FlowRow +import androidx.compose.foundation.layout.FlowRowScope import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize @@ -32,21 +35,19 @@ import androidx.compose.foundation.layout.heightIn import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.items import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.verticalScroll import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.filled.OpenInNew -import androidx.compose.material.icons.filled.Accessibility -import androidx.compose.material.icons.filled.Apps import androidx.compose.material.icons.filled.ExpandMore -import androidx.compose.material.icons.filled.Keyboard import androidx.compose.material.icons.filled.Search -import androidx.compose.material.icons.filled.Tv -import androidx.compose.material.icons.filled.VerticalSplit import androidx.compose.material3.CenterAlignedTopAppBar import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.material3.NavigationDrawerItemColors @@ -56,6 +57,7 @@ import androidx.compose.material3.SearchBarDefaults import androidx.compose.material3.Surface import androidx.compose.material3.Text import androidx.compose.material3.TopAppBarDefaults +import androidx.compose.material3.windowsizeclass.WindowHeightSizeClass import androidx.compose.material3.windowsizeclass.WindowWidthSizeClass import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue @@ -69,10 +71,13 @@ import androidx.compose.ui.graphics.RectangleShape import androidx.compose.ui.graphics.Shape import androidx.compose.ui.graphics.graphicsLayer import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.input.nestedscroll.nestedScroll +import androidx.compose.ui.platform.rememberNestedScrollInteropConnection import androidx.compose.ui.res.stringResource import androidx.compose.ui.semantics.Role import androidx.compose.ui.semantics.role import androidx.compose.ui.semantics.semantics +import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.compose.ui.util.fastForEach @@ -81,8 +86,13 @@ import com.android.compose.windowsizeclass.LocalWindowSizeClass import com.android.systemui.res.R @Composable -fun ShortcutHelper(modifier: Modifier = Modifier, onKeyboardSettingsClicked: () -> Unit) { - if (shouldUseSinglePane()) { +fun ShortcutHelper( + onKeyboardSettingsClicked: () -> Unit, + modifier: Modifier = Modifier, + categories: List<ShortcutHelperCategory> = ShortcutHelperTemporaryData.categories, + useSinglePane: @Composable () -> Boolean = { shouldUseSinglePane() }, +) { + if (useSinglePane()) { ShortcutHelperSinglePane(modifier, categories, onKeyboardSettingsClicked) } else { ShortcutHelperTwoPane(modifier, categories, onKeyboardSettingsClicked) @@ -91,7 +101,8 @@ fun ShortcutHelper(modifier: Modifier = Modifier, onKeyboardSettingsClicked: () @Composable private fun shouldUseSinglePane() = - LocalWindowSizeClass.current.widthSizeClass == WindowWidthSizeClass.Compact + LocalWindowSizeClass.current.widthSizeClass == WindowWidthSizeClass.Compact || + LocalWindowSizeClass.current.heightSizeClass == WindowHeightSizeClass.Compact @Composable private fun ShortcutHelperSinglePane( @@ -209,11 +220,31 @@ private fun RotatingExpandCollapseIcon(isExpanded: Boolean) { @Composable private fun ShortcutCategoryDetailsSinglePane(category: ShortcutHelperCategory) { - Box(modifier = Modifier.fillMaxWidth().heightIn(min = 300.dp)) { - Text( - modifier = Modifier.align(Alignment.Center), - text = stringResource(category.labelResId), - ) + Column(Modifier.padding(horizontal = 16.dp)) { + category.subCategories.fastForEach { subCategory -> + ShortcutSubCategorySinglePane(subCategory) + } + } +} + +@Composable +private fun ShortcutSubCategorySinglePane(subCategory: SubCategory) { + // This @Composable is expected to be in a Column. + SubCategoryTitle(subCategory.label) + subCategory.shortcuts.fastForEachIndexed { index, shortcut -> + if (index > 0) { + HorizontalDivider() + } + ShortcutSinglePane(shortcut) + } +} + +@Composable +private fun ShortcutSinglePane(shortcut: Shortcut) { + Column(Modifier.padding(vertical = 24.dp)) { + ShortcutDescriptionText(shortcut = shortcut) + Spacer(modifier = Modifier.height(12.dp)) + ShortcutKeyCombinations(shortcut = shortcut) } } @@ -223,6 +254,7 @@ private fun ShortcutHelperTwoPane( categories: List<ShortcutHelperCategory>, onKeyboardSettingsClicked: () -> Unit, ) { + var selectedCategory by remember { mutableStateOf(categories.first()) } Column(modifier = modifier.fillMaxSize().padding(start = 24.dp, end = 24.dp, top = 26.dp)) { TitleBar() Spacer(modifier = Modifier.height(12.dp)) @@ -230,39 +262,189 @@ private fun ShortcutHelperTwoPane( StartSidePanel( modifier = Modifier.fillMaxWidth(fraction = 0.32f), categories = categories, + selectedCategory = selectedCategory, + onCategoryClicked = { selectedCategory = it }, onKeyboardSettingsClicked = onKeyboardSettingsClicked, ) Spacer(modifier = Modifier.width(24.dp)) - EndSidePanel(Modifier.fillMaxSize()) + EndSidePanel(Modifier.fillMaxSize(), selectedCategory) + } + } +} + +@Composable +private fun EndSidePanel(modifier: Modifier, category: ShortcutHelperCategory) { + LazyColumn(modifier.nestedScroll(rememberNestedScrollInteropConnection())) { + items(items = category.subCategories, key = { item -> item.label }) { + SubCategoryContainerDualPane(it) + Spacer(modifier = Modifier.height(8.dp)) } } } @Composable +private fun SubCategoryContainerDualPane(subCategory: SubCategory) { + Surface( + modifier = Modifier.fillMaxWidth(), + shape = RoundedCornerShape(28.dp), + color = MaterialTheme.colorScheme.surfaceBright + ) { + Column(Modifier.padding(horizontal = 32.dp, vertical = 24.dp)) { + SubCategoryTitle(subCategory.label) + Spacer(Modifier.height(24.dp)) + subCategory.shortcuts.fastForEachIndexed { index, shortcut -> + if (index > 0) { + HorizontalDivider() + } + ShortcutViewDualPane(shortcut) + } + } + } +} + +@Composable +private fun SubCategoryTitle(title: String) { + Text( + title, + style = MaterialTheme.typography.titleSmall, + color = MaterialTheme.colorScheme.primary, + ) +} + +@Composable +private fun ShortcutViewDualPane(shortcut: Shortcut) { + Row(Modifier.padding(vertical = 16.dp)) { + ShortcutDescriptionText( + modifier = Modifier.weight(0.25f).align(Alignment.CenterVertically), + shortcut = shortcut, + ) + ShortcutKeyCombinations( + modifier = Modifier.weight(0.75f), + shortcut = shortcut, + ) + } +} + +@OptIn(ExperimentalLayoutApi::class) +@Composable +private fun ShortcutKeyCombinations( + modifier: Modifier = Modifier, + shortcut: Shortcut, +) { + FlowRow(modifier = modifier, verticalArrangement = Arrangement.spacedBy(8.dp)) { + shortcut.commands.forEachIndexed { index, command -> + if (index > 0) { + ShortcutOrSeparator(spacing = 16.dp) + } + ShortcutCommand(command) + } + } +} + +@Composable +private fun ShortcutCommand(command: ShortcutCommand) { + // This @Composable is expected to be in a Row or FlowRow. + command.keys.forEachIndexed { keyIndex, key -> + if (keyIndex > 0) { + Spacer(Modifier.width(4.dp)) + } + ShortcutKeyContainer { + if (key is ShortcutKey.Text) { + ShortcutTextKey(key) + } else if (key is ShortcutKey.Icon) { + ShortcutIconKey(key) + } + } + } +} + +@Composable +private fun ShortcutKeyContainer(shortcutKeyContent: @Composable BoxScope.() -> Unit) { + Box( + modifier = + Modifier.height(36.dp) + .background( + color = MaterialTheme.colorScheme.surfaceContainer, + shape = RoundedCornerShape(12.dp) + ), + ) { + shortcutKeyContent() + } +} + +@Composable +private fun BoxScope.ShortcutTextKey(key: ShortcutKey.Text) { + Text( + text = key.value, + modifier = Modifier.align(Alignment.Center).padding(horizontal = 12.dp), + style = MaterialTheme.typography.titleSmall, + ) +} + +@Composable +private fun BoxScope.ShortcutIconKey(key: ShortcutKey.Icon) { + Icon( + imageVector = key.value, + contentDescription = null, + modifier = Modifier.align(Alignment.Center).padding(6.dp) + ) +} + +@OptIn(ExperimentalLayoutApi::class) +@Composable +private fun FlowRowScope.ShortcutOrSeparator(spacing: Dp) { + Spacer(Modifier.width(spacing)) + Text( + text = stringResource(R.string.shortcut_helper_key_combinations_or_separator), + modifier = Modifier.align(Alignment.CenterVertically), + style = MaterialTheme.typography.titleSmall, + ) + Spacer(Modifier.width(spacing)) +} + +@Composable +private fun ShortcutDescriptionText( + shortcut: Shortcut, + modifier: Modifier = Modifier, +) { + Text( + modifier = modifier, + text = shortcut.label, + style = MaterialTheme.typography.bodyMedium, + color = MaterialTheme.colorScheme.onSurface, + ) +} + +@Composable private fun StartSidePanel( modifier: Modifier, categories: List<ShortcutHelperCategory>, onKeyboardSettingsClicked: () -> Unit, + selectedCategory: ShortcutHelperCategory, + onCategoryClicked: (ShortcutHelperCategory) -> Unit, ) { Column(modifier) { ShortcutsSearchBar() Spacer(modifier = Modifier.heightIn(16.dp)) - CategoriesPanelTwoPane(categories) + CategoriesPanelTwoPane(categories, selectedCategory, onCategoryClicked) Spacer(modifier = Modifier.weight(1f)) KeyboardSettings(onKeyboardSettingsClicked) } } @Composable -private fun CategoriesPanelTwoPane(categories: List<ShortcutHelperCategory>) { - var selected by remember { mutableStateOf(categories.first()) } +private fun CategoriesPanelTwoPane( + categories: List<ShortcutHelperCategory>, + selectedCategory: ShortcutHelperCategory, + onCategoryClicked: (ShortcutHelperCategory) -> Unit +) { Column { categories.fastForEach { CategoryItemTwoPane( label = stringResource(it.labelResId), icon = it.icon, - selected = selected == it, - onClick = { selected = it } + selected = selectedCategory == it, + onClick = { onCategoryClicked(it) } ) } } @@ -305,15 +487,6 @@ private fun CategoryItemTwoPane( } @Composable -fun EndSidePanel(modifier: Modifier) { - Surface( - modifier = modifier, - shape = RoundedCornerShape(28.dp), - color = MaterialTheme.colorScheme.surfaceBright - ) {} -} - -@Composable @OptIn(ExperimentalMaterial3Api::class) private fun TitleBar() { CenterAlignedTopAppBar( @@ -333,6 +506,7 @@ private fun TitleBar() { private fun ShortcutsSearchBar() { var query by remember { mutableStateOf("") } SearchBar( + modifier = Modifier.fillMaxWidth(), colors = SearchBarDefaults.colors(containerColor = MaterialTheme.colorScheme.surfaceBright), query = query, active = false, @@ -372,25 +546,6 @@ private fun KeyboardSettings(onClick: () -> Unit) { } } -/** Temporary data class just to populate the UI. */ -private data class ShortcutHelperCategory( - @StringRes val labelResId: Int, - val icon: ImageVector, -) - -// Temporarily populating the categories directly in the UI. -private val categories = - listOf( - ShortcutHelperCategory(R.string.shortcut_helper_category_system, Icons.Default.Tv), - ShortcutHelperCategory( - R.string.shortcut_helper_category_multitasking, - Icons.Default.VerticalSplit - ), - ShortcutHelperCategory(R.string.shortcut_helper_category_input, Icons.Default.Keyboard), - ShortcutHelperCategory(R.string.shortcut_helper_category_app_shortcuts, Icons.Default.Apps), - ShortcutHelperCategory(R.string.shortcut_helper_category_a11y, Icons.Default.Accessibility), - ) - object ShortcutHelper { object Shapes { diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/ShortcutHelperTemporaryData.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/ShortcutHelperTemporaryData.kt new file mode 100644 index 000000000000..fa2388f6287d --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/ShortcutHelperTemporaryData.kt @@ -0,0 +1,251 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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.annotation.StringRes +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Accessibility +import androidx.compose.material.icons.filled.Apps +import androidx.compose.material.icons.filled.ArrowBackIosNew +import androidx.compose.material.icons.filled.Keyboard +import androidx.compose.material.icons.filled.KeyboardCommandKey +import androidx.compose.material.icons.filled.RadioButtonUnchecked +import androidx.compose.material.icons.filled.Tv +import androidx.compose.material.icons.filled.VerticalSplit +import androidx.compose.ui.graphics.vector.ImageVector +import com.android.systemui.res.R + +/** Temporary data classes and data below just to populate the UI. */ +data class ShortcutHelperCategory( + @StringRes val labelResId: Int, + val icon: ImageVector, + val subCategories: List<SubCategory>, +) + +data class SubCategory( + val label: String, + val shortcuts: List<Shortcut>, +) + +data class Shortcut(val label: String, val commands: List<ShortcutCommand>) + +data class ShortcutCommand(val keys: List<ShortcutKey>) + +sealed interface ShortcutKey { + data class Text(val value: String) : ShortcutKey + + data class Icon(val value: ImageVector) : ShortcutKey +} + +// DSL Builder Functions +private fun shortcutHelperCategory( + labelResId: Int, + icon: ImageVector, + block: ShortcutHelperCategoryBuilder.() -> Unit +): ShortcutHelperCategory = ShortcutHelperCategoryBuilder(labelResId, icon).apply(block).build() + +private fun ShortcutHelperCategoryBuilder.subCategory( + label: String, + block: SubCategoryBuilder.() -> Unit +) { + subCategories.add(SubCategoryBuilder(label).apply(block).build()) +} + +private fun SubCategoryBuilder.shortcut(label: String, block: ShortcutBuilder.() -> Unit) { + shortcuts.add(ShortcutBuilder(label).apply(block).build()) +} + +private fun ShortcutBuilder.command(block: ShortcutCommandBuilder.() -> Unit) { + commands.add(ShortcutCommandBuilder().apply(block).build()) +} + +private fun ShortcutCommandBuilder.key(value: String) { + keys.add(ShortcutKey.Text(value)) +} + +private fun ShortcutCommandBuilder.key(value: ImageVector) { + keys.add(ShortcutKey.Icon(value)) +} + +private class ShortcutHelperCategoryBuilder( + private val labelResId: Int, + private val icon: ImageVector +) { + val subCategories = mutableListOf<SubCategory>() + + fun build() = ShortcutHelperCategory(labelResId, icon, subCategories) +} + +private class SubCategoryBuilder(private val label: String) { + val shortcuts = mutableListOf<Shortcut>() + + fun build() = SubCategory(label, shortcuts) +} + +private class ShortcutBuilder(private val label: String) { + val commands = mutableListOf<ShortcutCommand>() + + fun build() = Shortcut(label, commands) +} + +private class ShortcutCommandBuilder { + val keys = mutableListOf<ShortcutKey>() + + fun build() = ShortcutCommand(keys) +} + +object ShortcutHelperTemporaryData { + + // Some shortcuts and their strings below are made up just to populate the UI for now. + // For this reason they are not in translatable resources yet. + val categories = + listOf( + shortcutHelperCategory(R.string.shortcut_helper_category_system, Icons.Default.Tv) { + subCategory("System controls") { + shortcut("Go to home screen") { + command { key(Icons.Default.RadioButtonUnchecked) } + command { + key(Icons.Default.KeyboardCommandKey) + key("H") + } + command { + key(Icons.Default.KeyboardCommandKey) + key("Return") + } + } + shortcut("View recent apps") { + command { + key(Icons.Default.KeyboardCommandKey) + key("Tab") + } + } + shortcut("All apps search") { + command { key(Icons.Default.KeyboardCommandKey) } + } + } + subCategory("System apps") { + shortcut("Go back") { + command { key(Icons.Default.ArrowBackIosNew) } + command { + key(Icons.Default.KeyboardCommandKey) + key("Left arrow") + } + command { + key(Icons.Default.KeyboardCommandKey) + key("ESC") + } + command { + key(Icons.Default.KeyboardCommandKey) + key("Backspace") + } + } + shortcut("View notifications") { + command { + key(Icons.Default.KeyboardCommandKey) + key("N") + } + } + shortcut("Take a screenshot") { + command { key(Icons.Default.KeyboardCommandKey) } + command { key("CTRL") } + command { key("S") } + } + shortcut("Open Settings") { + command { + key(Icons.Default.KeyboardCommandKey) + key("I") + } + } + } + }, + shortcutHelperCategory( + R.string.shortcut_helper_category_multitasking, + Icons.Default.VerticalSplit + ) { + subCategory("Multitasking & windows") { + shortcut("Take a screenshot") { + command { key(Icons.Default.KeyboardCommandKey) } + command { key("CTRL") } + command { key("S") } + } + } + }, + shortcutHelperCategory( + R.string.shortcut_helper_category_input, + Icons.Default.Keyboard + ) { + subCategory("Input") { + shortcut("Open Settings") { + command { + key(Icons.Default.KeyboardCommandKey) + key("I") + } + } + shortcut("View notifications") { + command { + key(Icons.Default.KeyboardCommandKey) + key("N") + } + } + } + }, + shortcutHelperCategory( + R.string.shortcut_helper_category_app_shortcuts, + Icons.Default.Apps + ) { + subCategory("App shortcuts") { + shortcut("Open Settings") { + command { + key(Icons.Default.KeyboardCommandKey) + key("I") + } + } + shortcut("Go back") { + command { key(Icons.Default.ArrowBackIosNew) } + command { + key(Icons.Default.KeyboardCommandKey) + key("Left arrow") + } + command { + key(Icons.Default.KeyboardCommandKey) + key("ESC") + } + command { + key(Icons.Default.KeyboardCommandKey) + key("Backspace") + } + } + } + }, + shortcutHelperCategory( + R.string.shortcut_helper_category_a11y, + Icons.Default.Accessibility + ) { + subCategory("Accessibility shortcuts") { + shortcut("View recent apps") { + command { + key(Icons.Default.KeyboardCommandKey) + key("Tab") + } + } + shortcut("All apps search") { + command { key(Icons.Default.KeyboardCommandKey) } + } + } + } + ) +} diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java index f2a544ece2cb..209bc7adac33 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java @@ -80,6 +80,7 @@ import com.android.systemui.dagger.qualifiers.Application; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.flags.FeatureFlags; import com.android.systemui.keyguard.domain.interactor.KeyguardEnabledInteractor; +import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor; import com.android.systemui.keyguard.ui.binder.KeyguardSurfaceBehindParamsApplier; import com.android.systemui.keyguard.ui.binder.KeyguardSurfaceBehindViewBinder; import com.android.systemui.keyguard.ui.binder.WindowManagerLockscreenVisibilityViewBinder; @@ -118,6 +119,7 @@ public class KeyguardService extends Service { private final ShellTransitions mShellTransitions; private final DisplayTracker mDisplayTracker; private final PowerInteractor mPowerInteractor; + private final KeyguardInteractor mKeyguardInteractor; private final Lazy<SceneInteractor> mSceneInteractorLazy; private final Executor mMainExecutor; @@ -338,6 +340,7 @@ public class KeyguardService extends Service { WindowManagerOcclusionManager windowManagerOcclusionManager, Lazy<SceneInteractor> sceneInteractorLazy, @Main Executor mainExecutor, + KeyguardInteractor keyguardInteractor, KeyguardEnabledInteractor keyguardEnabledInteractor) { super(); mKeyguardViewMediator = keyguardViewMediator; @@ -347,6 +350,7 @@ public class KeyguardService extends Service { mDisplayTracker = displayTracker; mFlags = featureFlags; mPowerInteractor = powerInteractor; + mKeyguardInteractor = keyguardInteractor; mSceneInteractorLazy = sceneInteractorLazy; mMainExecutor = mainExecutor; @@ -474,6 +478,7 @@ public class KeyguardService extends Service { public void onDreamingStarted() { trace("onDreamingStarted"); checkPermission(); + mKeyguardInteractor.setDreaming(true); mKeyguardViewMediator.onDreamingStarted(); } @@ -481,6 +486,7 @@ public class KeyguardService extends Service { public void onDreamingStopped() { trace("onDreamingStopped"); checkPermission(); + mKeyguardInteractor.setDreaming(false); mKeyguardViewMediator.onDreamingStopped(); } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java index f3a1843b6c31..2d60fcca58cc 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java @@ -176,6 +176,8 @@ import com.android.systemui.util.time.SystemClock; import com.android.systemui.wallpapers.data.repository.WallpaperRepository; import com.android.wm.shell.keyguard.KeyguardTransitions; +import dagger.Lazy; + import java.io.PrintWriter; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -185,7 +187,6 @@ import java.util.Objects; import java.util.concurrent.Executor; import java.util.function.Consumer; -import dagger.Lazy; import kotlinx.coroutines.CoroutineDispatcher; /** @@ -3014,7 +3015,7 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, && mPM.isInteractive(), WakeAndUnlockUpdateReason.HIDE); } - if ((mShowing && !mOccluded) || mUnlockingAndWakingFromDream) { + if (mBootCompleted && ((mShowing && !mOccluded) || mUnlockingAndWakingFromDream)) { if (mUnlockingAndWakingFromDream) { Log.d(TAG, "hiding keyguard before waking from dream"); } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/WindowManagerLockscreenVisibilityManager.kt b/packages/SystemUI/src/com/android/systemui/keyguard/WindowManagerLockscreenVisibilityManager.kt index 1b342edb28fe..180afb2b259a 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/WindowManagerLockscreenVisibilityManager.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/WindowManagerLockscreenVisibilityManager.kt @@ -142,15 +142,17 @@ constructor( nonApps: Array<RemoteAnimationTarget>, finishedCallback: IRemoteAnimationFinishedCallback ) { - if (apps.isNotEmpty()) { - // 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 keyguard. In this case, this is the first and only signal we'll receive to start - // a transition to GONE. - keyguardTransitionInteractor.startDismissKeyguardTransition( - reason = "Going away remote animation started" - ) + // 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 + // keyguard. In this case, this is the first and only signal we'll receive to start + // a transition to GONE. This transition needs to start even if we're not provided an app + // animation target - it's possible the app is destroyed on creation, etc. but we'll still + // be unlocking. + keyguardTransitionInteractor.startDismissKeyguardTransition( + reason = "Going away remote animation started" + ) + if (apps.isNotEmpty()) { goingAwayRemoteAnimationFinishedCallback = finishedCallback keyguardSurfaceBehindAnimator.applyParamsToSurface(apps[0]) } else { @@ -183,25 +185,11 @@ constructor( /** * Sets the lockscreen state WM-side by calling ATMS#setLockScreenShown. * - * [lockscreenShowing] defaults to true, since it's only ever null during the boot sequence, - * when we haven't yet called ATMS#setLockScreenShown. Typically, - * setWmLockscreenState(lockscreenShowing = true) is called early in the boot sequence, before - * setWmLockscreenState(aodVisible = true), so we don't expect to need to use this default, but - * if so, true should be the right choice. + * If [lockscreenShowing] is null, it means we don't know if the lockscreen is showing yet. This + * will be decided by the [KeyguardTransitionBootInteractor] shortly. */ private fun setWmLockscreenState( - lockscreenShowing: Boolean = - this.isLockscreenShowing - ?: true.also { - Log.d( - TAG, - "Using isLockscreenShowing=true default in setWmLockscreenState, " + - "because setAodVisible was called before the first " + - "setLockscreenShown call during boot. This is not typical, but is " + - "theoretically possible. If you're investigating the lockscreen " + - "showing unexpectedly, start here." - ) - }, + lockscreenShowing: Boolean? = this.isLockscreenShowing, aodVisible: Boolean = this.isAodVisible ) { Log.d( @@ -211,10 +199,27 @@ constructor( "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." + ) + this.isAodVisible = aodVisible + return + } + if (this.isLockscreenShowing == lockscreenShowing && this.isAodVisible == aodVisible) { return } + Log.d( + TAG, + "ATMS#setLockScreenShown(" + + "isLockscreenShowing=$lockscreenShowing, " + + "aodVisible=$aodVisible)." + ) activityTaskManagerService.setLockScreenShown(lockscreenShowing, aodVisible) this.isLockscreenShowing = lockscreenShowing this.isAodVisible = aodVisible diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/GlanceableHubQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/GlanceableHubQuickAffordanceConfig.kt index d09b9f68ea60..5d541260b05f 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/GlanceableHubQuickAffordanceConfig.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/GlanceableHubQuickAffordanceConfig.kt @@ -38,6 +38,7 @@ constructor( ) : KeyguardQuickAffordanceConfig { override val key: String = BuiltInKeyguardQuickAffordanceKeys.GLANCEABLE_HUB + override fun pickerName(): String = "Glanceable hub" override val pickerIconResourceId = R.drawable.ic_widgets @@ -52,6 +53,10 @@ constructor( } } + override suspend fun getPickerScreenState(): KeyguardQuickAffordanceConfig.PickerScreenState { + return KeyguardQuickAffordanceConfig.PickerScreenState.UnavailableOnDevice + } + override fun onTriggered( expandable: Expandable? ): KeyguardQuickAffordanceConfig.OnTriggeredResult { diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt index f15896ba8dcf..d508b2bc11e8 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt @@ -148,7 +148,7 @@ interface KeyguardRepository { * Dozing/AOD is a specific type of dream, but it is also possible for other non-systemui dreams * to be active, such as screensavers. */ - val isDreaming: Flow<Boolean> + val isDreaming: MutableStateFlow<Boolean> /** Observable for whether the device is dreaming with an overlay, see [DreamOverlayService] */ val isDreamingWithOverlay: Flow<Boolean> @@ -250,6 +250,9 @@ interface KeyguardRepository { /** Sets the current amount of alpha that should be used for rendering the keyguard. */ fun setKeyguardAlpha(alpha: Float) + /** Whether the device is actively dreaming */ + fun setDreaming(isDreaming: Boolean) + /** * Returns whether the keyguard bottom area should be constrained to the top of the lock icon */ @@ -509,25 +512,7 @@ constructor( } .distinctUntilChanged() - override val isDreaming: Flow<Boolean> = - conflatedCallbackFlow { - val callback = - object : KeyguardUpdateMonitorCallback() { - override fun onDreamingStateChanged(isDreaming: Boolean) { - trySendWithFailureLogging(isDreaming, TAG, "updated isDreaming") - } - } - keyguardUpdateMonitor.registerCallback(callback) - trySendWithFailureLogging( - keyguardUpdateMonitor.isDreaming, - TAG, - "initial isDreaming", - ) - - awaitClose { keyguardUpdateMonitor.removeCallback(callback) } - } - .flowOn(mainDispatcher) - .distinctUntilChanged() + override val isDreaming: MutableStateFlow<Boolean> = MutableStateFlow(false) override val linearDozeAmount: Flow<Float> = conflatedCallbackFlow { val callback = @@ -674,6 +659,10 @@ constructor( _keyguardAlpha.value = alpha } + override fun setDreaming(isDreaming: Boolean) { + this.isDreaming.value = isDreaming + } + override fun isUdfpsSupported(): Boolean = keyguardUpdateMonitor.isUdfpsSupported override fun setQuickSettingsVisible(isVisible: Boolean) { 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 8ec460a7088f..1b201ceda310 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 @@ -20,6 +20,7 @@ import android.animation.AnimatorListenerAdapter import android.animation.ValueAnimator import android.animation.ValueAnimator.AnimatorUpdateListener import android.annotation.FloatRange +import android.annotation.SuppressLint import android.os.Trace import android.util.Log import com.android.app.tracing.coroutines.withContext @@ -117,10 +118,11 @@ class KeyguardTransitionRepositoryImpl constructor( @Main val mainDispatcher: CoroutineDispatcher, ) : KeyguardTransitionRepository { - /* - * Each transition between [KeyguardState]s will have an associated Flow. - * In order to collect these events, clients should call [transition]. + /** + * Each transition between [KeyguardState]s will have an associated Flow. In order to collect + * these events, clients should call [transition]. */ + @SuppressLint("SharedFlowCreation") private val _transitions = MutableSharedFlow<TransitionStep>( replay = 2, diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt index 756c6c20e58d..49d00af61a24 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt @@ -24,9 +24,12 @@ import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.keyguard.KeyguardWmStateRefactor import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository +import com.android.systemui.keyguard.shared.model.Edge import com.android.systemui.keyguard.shared.model.KeyguardState +import com.android.systemui.keyguard.shared.model.TransitionStep import com.android.systemui.power.domain.interactor.PowerInteractor import com.android.systemui.scene.shared.flag.SceneContainerFlag +import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.util.kotlin.Utils.Companion.sample as sampleCombine import com.android.wm.shell.animation.Interpolators import javax.inject.Inject @@ -36,7 +39,6 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.delay import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.flow.filter @@ -81,17 +83,16 @@ constructor( } val surfaceBehindVisibility: Flow<Boolean?> = - combine( - transitionInteractor.startedKeyguardTransitionStep, - transitionInteractor.transitionStepsFromState(KeyguardState.ALTERNATE_BOUNCER) - ) { startedStep, fromBouncerStep -> - if (startedStep.to != KeyguardState.GONE) { - return@combine null - } - + transitionInteractor + .transition( + edge = Edge.create(from = KeyguardState.ALTERNATE_BOUNCER, to = Scenes.Gone), + edgeWithoutSceneContainer = + Edge.create(from = KeyguardState.ALTERNATE_BOUNCER, to = KeyguardState.GONE) + ) + .map<TransitionStep, Boolean?> { // The alt bouncer is pretty fast to hide, so start the surface behind animation // around 30%. - fromBouncerStep.value > 0.3f + it.value > 0.3f } .onStart { // Default to null ("don't care, use a reasonable default"). @@ -113,13 +114,26 @@ constructor( powerInteractor.isAwake, keyguardInteractor.isAodAvailable, communalInteractor.isIdleOnCommunal, + communalInteractor.editModeOpen, keyguardInteractor.isKeyguardOccluded, ) .filterRelevantKeyguardStateAnd { (isAlternateBouncerShowing, isPrimaryBouncerShowing, _, _, _) -> !isAlternateBouncerShowing && !isPrimaryBouncerShowing } - .collect { (_, _, isAwake, isAodAvailable, isIdleOnCommunal, isOccluded) -> + .collect { + ( + _, + _, + isAwake, + isAodAvailable, + isIdleOnCommunal, + isCommunalEditMode, + isOccluded) -> + // When unlocking over glanceable hub to enter edit mode, transitioning directly + // to GONE prevents the lockscreen flash. Let listenForAlternateBouncerToGone + // handle it. + if (isCommunalEditMode) return@collect val to = if (!isAwake) { if (isAodAvailable) { diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt index a540d761c38f..f5b12a2ecfcf 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt @@ -169,6 +169,7 @@ constructor( KeyguardState.AOD -> TO_AOD_DURATION KeyguardState.DOZING -> TO_DOZING_DURATION KeyguardState.LOCKSCREEN -> TO_LOCKSCREEN_DURATION + KeyguardState.GLANCEABLE_HUB -> TO_GLANCEABLE_HUB_DURATION else -> DEFAULT_DURATION }.inWholeMilliseconds } @@ -181,5 +182,6 @@ constructor( val TO_AOD_DURATION = 1300.milliseconds val TO_DOZING_DURATION = 933.milliseconds val TO_LOCKSCREEN_DURATION = DEFAULT_DURATION + val TO_GLANCEABLE_HUB_DURATION = DEFAULT_DURATION } } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt index 8cab3cd35dcf..f30eef080ede 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt @@ -24,16 +24,18 @@ import com.android.systemui.Flags import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.dagger.qualifiers.Main -import com.android.systemui.flags.FeatureFlags import com.android.systemui.keyguard.KeyguardWmStateRefactor import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository +import com.android.systemui.keyguard.shared.model.Edge import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.shared.model.StatusBarState.KEYGUARD import com.android.systemui.keyguard.shared.model.TransitionInfo import com.android.systemui.keyguard.shared.model.TransitionModeOnCanceled 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.scene.shared.flag.SceneContainerFlag +import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.shade.data.repository.ShadeRepository import com.android.systemui.util.kotlin.Utils.Companion.sample as sampleCombine import java.util.UUID @@ -59,7 +61,6 @@ constructor( @Background bgDispatcher: CoroutineDispatcher, @Main mainDispatcher: CoroutineDispatcher, keyguardInteractor: KeyguardInteractor, - private val flags: FeatureFlags, private val shadeRepository: ShadeRepository, powerInteractor: PowerInteractor, private val glanceableHubTransitions: GlanceableHubTransitions, @@ -97,14 +98,13 @@ constructor( * LOCKSCREEN is running. */ val surfaceBehindVisibility: Flow<Boolean?> = - transitionInteractor.startedKeyguardTransitionStep - .map { startedStep -> - if (startedStep.to != KeyguardState.GONE) { - // LOCKSCREEN to anything but GONE does not require any special surface - // visibility handling. - return@map null - } - + transitionInteractor + .transition( + edge = Edge.create(from = KeyguardState.LOCKSCREEN, to = Scenes.Gone), + edgeWithoutSceneContainer = + Edge.create(from = KeyguardState.LOCKSCREEN, to = KeyguardState.GONE) + ) + .map<TransitionStep, Boolean?> { true // Make the surface visible during LS -> GONE transitions. } .onStart { diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt index 8cf4b53618b4..f8208b37ce4a 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt @@ -22,11 +22,12 @@ import com.android.systemui.communal.domain.interactor.CommunalInteractor import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.dagger.qualifiers.Main -import com.android.systemui.flags.FeatureFlags import com.android.systemui.keyguard.KeyguardWmStateRefactor import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository +import com.android.systemui.keyguard.shared.model.Edge import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.shared.model.TransitionModeOnCanceled +import com.android.systemui.keyguard.shared.model.TransitionStep import com.android.systemui.power.domain.interactor.PowerInteractor import com.android.systemui.scene.shared.flag.SceneContainerFlag import com.android.systemui.user.domain.interactor.SelectedUserInteractor @@ -38,8 +39,9 @@ import kotlin.time.Duration.Companion.milliseconds import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.flowOf +import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onStart import kotlinx.coroutines.launch @@ -54,7 +56,6 @@ constructor( @Main mainDispatcher: CoroutineDispatcher, keyguardInteractor: KeyguardInteractor, private val communalInteractor: CommunalInteractor, - private val flags: FeatureFlags, private val keyguardSecurityModel: KeyguardSecurityModel, private val selectedUserInteractor: SelectedUserInteractor, powerInteractor: PowerInteractor, @@ -79,21 +80,25 @@ constructor( } val surfaceBehindVisibility: Flow<Boolean?> = - combine( - transitionInteractor.startedKeyguardTransitionStep, - transitionInteractor.transitionStepsFromState(KeyguardState.PRIMARY_BOUNCER) - ) { startedStep, fromBouncerStep -> - if (startedStep.to != KeyguardState.GONE) { - return@combine null + if (SceneContainerFlag.isEnabled) { + // The edge Scenes.Bouncer <-> Scenes.Gone is handled by STL + flowOf(null) + } else { + transitionInteractor + .transition( + edge = Edge.INVALID, + edgeWithoutSceneContainer = + Edge.create(from = KeyguardState.PRIMARY_BOUNCER, to = KeyguardState.GONE) + ) + .map<TransitionStep, Boolean?> { + it.value > TO_GONE_SURFACE_BEHIND_VISIBLE_THRESHOLD } - - fromBouncerStep.value > TO_GONE_SURFACE_BEHIND_VISIBLE_THRESHOLD - } - .onStart { - // Default to null ("don't care, use a reasonable default"). - emit(null) - } - .distinctUntilChanged() + .onStart { + // Default to null ("don't care, use a reasonable default"). + emit(null) + } + .distinctUntilChanged() + } fun dismissPrimaryBouncer() { scope.launch { startTransitionTo(KeyguardState.GONE) } 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 73835a3c1c96..ef96be0a98fd 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 @@ -51,6 +51,7 @@ import com.android.systemui.statusbar.CommandQueue import com.android.systemui.statusbar.notification.NotificationUtils.interpolate import com.android.systemui.statusbar.notification.stack.domain.interactor.SharedNotificationContainerInteractor import com.android.systemui.util.kotlin.Utils.Companion.sample as sampleCombine +import com.android.systemui.util.kotlin.Utils.Companion.sampleFilter import com.android.systemui.util.kotlin.pairwise import com.android.systemui.util.kotlin.sample import javax.inject.Inject @@ -77,6 +78,7 @@ import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.merge import kotlinx.coroutines.flow.onStart import kotlinx.coroutines.flow.stateIn +import kotlinx.coroutines.flow.transform /** * Encapsulates business-logic related to the keyguard but not to a more specific part within it. @@ -91,7 +93,7 @@ constructor( bouncerRepository: KeyguardBouncerRepository, configurationInteractor: ConfigurationInteractor, shadeRepository: ShadeRepository, - keyguardTransitionInteractor: KeyguardTransitionInteractor, + private val keyguardTransitionInteractor: KeyguardTransitionInteractor, sceneInteractorProvider: Provider<SceneInteractor>, private val fromGoneTransitionInteractor: Provider<FromGoneTransitionInteractor>, private val fromLockscreenTransitionInteractor: Provider<FromLockscreenTransitionInteractor>, @@ -110,7 +112,7 @@ constructor( keyguardTransitionInteractor.transitionState.map { step -> val startingProgress = lastChangeStep.value val progress = step.value - if (step.to == AOD && progress >= startingProgress) { + if (step.to == AOD && progress >= startingProgress && startingProgress < 1f) { val adjustedProgress = ((progress - startingProgress) / (1F - startingProgress)).coerceIn( 0F, @@ -248,21 +250,17 @@ constructor( val isKeyguardGoingAway: Flow<Boolean> = repository.isKeyguardGoingAway /** Keyguard can be clipped at the top as the shade is dragged */ - val topClippingBounds: Flow<Int?> = - combineTransform( - configurationInteractor.onAnyConfigurationChange, + val topClippingBounds: Flow<Int?> by lazy { + repository.topClippingBounds + .sampleFilter( keyguardTransitionInteractor - .transitionValue(GONE) - .map { it == 1f } - .onStart { emit(false) } - .distinctUntilChanged(), - repository.topClippingBounds - ) { _, isGone, topClippingBounds -> - if (!isGone) { - emit(topClippingBounds) - } + .transitionValue(scene = Scenes.Gone, stateWithoutSceneContainer = GONE) + .onStart { emit(0f) } + ) { goneValue -> + goneValue != 1f } .distinctUntilChanged() + } /** Last point that [KeyguardRootView] view was tapped */ val lastRootViewTapPosition: Flow<Point?> = repository.lastRootViewTapPosition.asStateFlow() @@ -330,28 +328,35 @@ constructor( * This uses legacyShadeExpansion to process swipe up events. In the future, the touch input * signal should be sent directly to transitions. */ - val dismissAlpha: Flow<Float?> = + val dismissAlpha: Flow<Float> = shadeRepository.legacyShadeExpansion - .filter { it < 1f } .sampleCombine( statusBarState, keyguardTransitionInteractor.currentKeyguardState, + keyguardTransitionInteractor.transitionState, isKeyguardDismissible, ) - .map { - (legacyShadeExpansion, statusBarState, currentKeyguardState, isKeyguardDismissible) - -> + .filter { (_, _, _, step, _) -> !step.transitionState.isTransitioning() } + .transform { + ( + legacyShadeExpansion, + statusBarState, + currentKeyguardState, + step, + isKeyguardDismissible) -> if ( statusBarState == StatusBarState.KEYGUARD && isKeyguardDismissible && - currentKeyguardState == LOCKSCREEN + currentKeyguardState == LOCKSCREEN && + legacyShadeExpansion != 1f ) { - MathUtils.constrainedMap(0f, 1f, 0.95f, 1f, legacyShadeExpansion) - } else { - null + emit(MathUtils.constrainedMap(0f, 1f, 0.95f, 1f, legacyShadeExpansion)) + } else if (legacyShadeExpansion == 0f || legacyShadeExpansion == 1f) { + // Resets alpha state + emit(1f) } } - .onStart { emit(null) } + .onStart { emit(1f) } .distinctUntilChanged() val keyguardTranslationY: Flow<Float> = @@ -477,6 +482,10 @@ constructor( repository.topClippingBounds.value = top } + fun setDreaming(isDreaming: Boolean) { + repository.setDreaming(isDreaming) + } + /** Temporary shim, until [KeyguardWmStateRefactor] is enabled */ fun showKeyguard() { fromGoneTransitionInteractor.get().showKeyguard() 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 2766b71fae02..37272dca911f 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 @@ -223,6 +223,17 @@ constructor( } } + fun transitionValue( + scene: SceneKey, + stateWithoutSceneContainer: KeyguardState, + ): Flow<Float> { + return if (SceneContainerFlag.isEnabled) { + sceneInteractor.get().transitionProgress(scene) + } else { + transitionValue(stateWithoutSceneContainer) + } + } + /** * The amount of transition into or out of the given [KeyguardState]. * @@ -232,26 +243,13 @@ constructor( fun transitionValue( state: KeyguardState, ): Flow<Float> { + if (SceneContainerFlag.isEnabled && state != state.mapToSceneContainerState()) { + Log.e(TAG, "SceneContainer is enabled but a deprecated state $state is used.") + return transitionValue(state.mapToSceneContainerScene()!!, state) + } return getTransitionValueFlow(state) } - /** - * AOD<->* transition information, mapped to dozeAmount range of AOD (1f) <-> - * * (0f). - */ - @SuppressLint("SharedFlowCreation") - val dozeAmountTransition: Flow<TransitionStep> = - repository.transitions - .filter { step -> step.from == AOD || step.to == AOD } - .map { step -> - if (step.from == AOD) { - step.copy(value = 1 - step.value) - } else { - step - } - } - .shareIn(scope, SharingStarted.Eagerly, replay = 1) - /** The last [TransitionStep] with a [TransitionState] of STARTED */ val startedKeyguardTransitionStep: Flow<TransitionStep> = repository.transitions.filter { step -> step.transitionState == TransitionState.STARTED } @@ -267,8 +265,6 @@ constructor( .map { step -> step.to } .shareIn(scope, SharingStarted.Eagerly, replay = 1) - val currentTransitionInfo: StateFlow<TransitionInfo> = repository.currentTransitionInfoInternal - /** The from state of the last [TransitionState.STARTED] transition. */ // TODO: is it performant to have several SharedFlows side by side instead of one? @SuppressLint("SharedFlowCreation") @@ -415,14 +411,6 @@ constructor( /** Whether we've currently STARTED a transition and haven't yet FINISHED it. */ val isInTransitionToAnyState = isInTransitionWhere({ true }, { true }) - fun transitionStepsFromState(fromState: KeyguardState): Flow<TransitionStep> { - return transition(Edge.create(from = fromState, to = null)) - } - - fun transitionStepsToState(toState: KeyguardState): Flow<TransitionStep> { - return transition(Edge.create(from = null, to = toState)) - } - /** * Called to start a transition that will ultimately dismiss the keyguard from the current * state. @@ -558,10 +546,6 @@ constructor( return currentKeyguardState.replayCache.last() } - fun getStartedState(): KeyguardState { - return startedKeyguardState.replayCache.last() - } - fun getStartedFromState(): KeyguardState { return startedKeyguardFromState.replayCache.last() } 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 88e6602e56b7..069f65b4efa6 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 @@ -105,7 +105,17 @@ constructor( is ObservableTransitionState.Transition -> when { transitionState.fromScene == Scenes.Lockscreen && - transitionState.toScene == Scenes.Gone -> flowOf(true) + transitionState.toScene == Scenes.Gone -> + sceneInteractor + .get() + .isTransitionUserInputOngoing + .flatMapLatestConflated { isUserInputOngoing -> + if (isUserInputOngoing) { + isDeviceEntered + } else { + flowOf(true) + } + } transitionState.fromScene == Scenes.Bouncer && transitionState.toScene == Scenes.Gone -> transitionState.progress.map { progress -> @@ -229,11 +239,14 @@ constructor( val aodVisibility: Flow<Boolean> = combine( keyguardInteractor.isDozing, + keyguardInteractor.isAodAvailable, keyguardInteractor.biometricUnlockState, - ) { isDozing, biometricUnlockState -> + ) { isDozing, isAodAvailable, biometricUnlockState -> // AOD is visible if we're dozing, unless we are wake and unlocking (where we go // directly from AOD to unlocked while dozing). - isDozing && !BiometricUnlockMode.isWakeAndUnlock(biometricUnlockState.mode) + isDozing && + isAodAvailable && + !BiometricUnlockMode.isWakeAndUnlock(biometricUnlockState.mode) } .distinctUntilChanged() diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/scenetransition/LockscreenSceneTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/scenetransition/LockscreenSceneTransitionInteractor.kt index 3baeb7682e12..9b3ba7d8feb0 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/scenetransition/LockscreenSceneTransitionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/scenetransition/LockscreenSceneTransitionInteractor.kt @@ -131,7 +131,7 @@ constructor( val newTransition = TransitionInfo( ownerName = this::class.java.simpleName, - from = transitionInteractor.currentTransitionInfo.value.to, + from = transitionInteractor.currentTransitionInfoInternal.value.to, to = state, animator = null, modeOnCanceled = TransitionModeOnCanceled.REVERSE @@ -150,7 +150,7 @@ constructor( private suspend fun handleTransition(transition: ObservableTransitionState.Transition) { if (transition.fromScene == Scenes.Lockscreen) { if (currentTransitionId != null) { - val currentToState = transitionInteractor.currentTransitionInfo.value.to + val currentToState = transitionInteractor.currentTransitionInfoInternal.value.to if (currentToState == UNDEFINED) { transitionKtfTo(transitionInteractor.getStartedFromState()) } @@ -201,7 +201,7 @@ constructor( } private suspend fun startTransitionFromLockscreen() { - val currentState = transitionInteractor.currentTransitionInfo.value.to + val currentState = transitionInteractor.currentTransitionInfoInternal.value.to val newTransition = TransitionInfo( ownerName = this::class.java.simpleName, diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/Edge.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/Edge.kt index c1e8d2214282..1306b2690c05 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/Edge.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/Edge.kt @@ -96,10 +96,23 @@ sealed class Edge { companion object { private const val TAG = "Edge" + @JvmStatic + @JvmOverloads fun create(from: KeyguardState? = null, to: KeyguardState? = null) = StateToState(from, to) + @JvmStatic + @JvmOverloads fun create(from: KeyguardState? = null, to: SceneKey) = StateToScene(from, to) + @JvmStatic + @JvmOverloads fun create(from: SceneKey, to: KeyguardState? = null) = SceneToState(from, to) + + /** + * This edge is a placeholder for when an edge needs to be passed but there is no edge for + * this flag configuration available. Usually for Scene <-> Scene edges with scene container + * enabled where these edges are managed by STL separately. + */ + val INVALID = StateToState(UNDEFINED, UNDEFINED) } } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/TransitionStep.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/TransitionStep.kt index 2b4c4af98ccd..0a8c1909937c 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/TransitionStep.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/TransitionStep.kt @@ -15,6 +15,9 @@ */ package com.android.systemui.keyguard.shared.model +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.filter + /** This information will flow from the [KeyguardTransitionRepository] to control the UI layer */ data class TransitionStep @JvmOverloads @@ -39,3 +42,6 @@ constructor( return to == state && transitionState == TransitionState.FINISHED } } + +fun Flow<TransitionStep>.filterState(transitionState: TransitionState) = + this.filter { it.transitionState == transitionState } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSmartspaceViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSmartspaceViewBinder.kt index 6b11dc57e096..191056c5578a 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSmartspaceViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSmartspaceViewBinder.kt @@ -121,10 +121,7 @@ object KeyguardSmartspaceViewBinder { ) { val dateView = constraintLayout.requireViewById<View>(sharedR.id.date_smartspace_view) - val weatherView = - constraintLayout.requireViewById<View>(sharedR.id.weather_smartspace_view) addView(dateView) - addView(weatherView) } } } @@ -141,9 +138,6 @@ object KeyguardSmartspaceViewBinder { ) { val dateView = constraintLayout.requireViewById<View>(sharedR.id.date_smartspace_view) - val weatherView = - constraintLayout.requireViewById<View>(sharedR.id.weather_smartspace_view) - removeView(weatherView) removeView(dateView) } } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/transitions/DeviceEntryIconTransitionModule.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/transitions/DeviceEntryIconTransitionModule.kt index 1f4bc61fb003..ecdc21cf3bb5 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/transitions/DeviceEntryIconTransitionModule.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/transitions/DeviceEntryIconTransitionModule.kt @@ -35,6 +35,7 @@ import com.android.systemui.keyguard.ui.viewmodel.GlanceableHubToDreamingTransit import com.android.systemui.keyguard.ui.viewmodel.GlanceableHubToOccludedTransitionViewModel import com.android.systemui.keyguard.ui.viewmodel.GoneToAodTransitionViewModel import com.android.systemui.keyguard.ui.viewmodel.GoneToDozingTransitionViewModel +import com.android.systemui.keyguard.ui.viewmodel.GoneToGlanceableHubTransitionViewModel import com.android.systemui.keyguard.ui.viewmodel.GoneToLockscreenTransitionViewModel import com.android.systemui.keyguard.ui.viewmodel.LockscreenToAodTransitionViewModel import com.android.systemui.keyguard.ui.viewmodel.LockscreenToDozingTransitionViewModel @@ -246,4 +247,10 @@ abstract class DeviceEntryIconTransitionModule { abstract fun occludedToGlanceableHub( impl: OccludedToGlanceableHubTransitionViewModel ): DeviceEntryIconTransition + + @Binds + @IntoSet + abstract fun goneToGlanceableHub( + impl: GoneToGlanceableHubTransitionViewModel + ): DeviceEntryIconTransition } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/transitions/IntraBlueprintTransition.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/transitions/IntraBlueprintTransition.kt index 39f1ebe25299..aa0a9d9cee1f 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/transitions/IntraBlueprintTransition.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/transitions/IntraBlueprintTransition.kt @@ -62,7 +62,7 @@ class IntraBlueprintTransition( addTransition( clockViewModel.currentClock.value?.let { DefaultClockSteppingTransition(it) } ) - else -> addTransition(ClockSizeTransition(config, clockViewModel, smartspaceViewModel)) + else -> addTransition(ClockSizeTransition(config, clockViewModel)) } } } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSection.kt index 0435531bb87c..8a751f05e102 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSection.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSection.kt @@ -18,6 +18,7 @@ package com.android.systemui.keyguard.ui.view.layout.sections import android.content.Context import android.view.View +import android.view.ViewGroup import android.view.ViewTreeObserver.OnGlobalLayoutListener import androidx.constraintlayout.widget.Barrier import androidx.constraintlayout.widget.ConstraintLayout @@ -51,7 +52,7 @@ constructor( ) : KeyguardSection() { private var smartspaceView: View? = null private var weatherView: View? = null - private var dateView: View? = null + private var dateWeatherView: ViewGroup? = null private var smartspaceVisibilityListener: OnGlobalLayoutListener? = null private var pastVisibility: Int = -1 @@ -69,12 +70,15 @@ constructor( if (!keyguardSmartspaceViewModel.isSmartspaceEnabled) return smartspaceView = smartspaceController.buildAndConnectView(constraintLayout) weatherView = smartspaceController.buildAndConnectWeatherView(constraintLayout) - dateView = smartspaceController.buildAndConnectDateView(constraintLayout) + dateWeatherView = + smartspaceController.buildAndConnectDateView(constraintLayout) as ViewGroup pastVisibility = smartspaceView?.visibility ?: View.GONE constraintLayout.addView(smartspaceView) if (keyguardSmartspaceViewModel.isDateWeatherDecoupled) { - constraintLayout.addView(weatherView) - constraintLayout.addView(dateView) + constraintLayout.addView(dateWeatherView) + // Place weather right after the date, before the extras (alarm and dnd) + val index = if (dateWeatherView?.childCount == 0) 0 else 1 + dateWeatherView?.addView(weatherView, index) } keyguardUnlockAnimationController.lockscreenSmartspace = smartspaceView smartspaceVisibilityListener = OnGlobalLayoutListener { @@ -116,26 +120,6 @@ constructor( ConstraintSet.START, horizontalPaddingStart ) - constrainWidth(sharedR.id.weather_smartspace_view, ConstraintSet.WRAP_CONTENT) - connect( - sharedR.id.weather_smartspace_view, - ConstraintSet.TOP, - sharedR.id.date_smartspace_view, - ConstraintSet.TOP - ) - connect( - sharedR.id.weather_smartspace_view, - ConstraintSet.BOTTOM, - sharedR.id.date_smartspace_view, - ConstraintSet.BOTTOM - ) - connect( - sharedR.id.weather_smartspace_view, - ConstraintSet.START, - sharedR.id.date_smartspace_view, - ConstraintSet.END, - 4 - ) // migrate addSmartspaceView from KeyguardClockSwitchController constrainHeight(sharedR.id.bc_smartspace_view, ConstraintSet.WRAP_CONTENT) @@ -186,7 +170,6 @@ constructor( *intArrayOf( sharedR.id.bc_smartspace_view, sharedR.id.date_smartspace_view, - sharedR.id.weather_smartspace_view, ) ) } @@ -196,7 +179,7 @@ constructor( override fun removeViews(constraintLayout: ConstraintLayout) { if (!MigrateClocksToBlueprint.isEnabled) return if (!keyguardSmartspaceViewModel.isSmartspaceEnabled) return - listOf(smartspaceView, dateView, weatherView).forEach { + listOf(smartspaceView, dateWeatherView).forEach { it?.let { if (it.parent == constraintLayout) { constraintLayout.removeView(it) @@ -220,7 +203,7 @@ constructor( setVisibility(sharedR.id.weather_smartspace_view, weatherVisibility) setAlpha( sharedR.id.weather_smartspace_view, - if (weatherVisibility == ConstraintSet.VISIBLE) 1f else 0f + if (weatherVisibility == View.VISIBLE) 1f else 0f ) val dateVisibility = if (keyguardClockViewModel.hasCustomWeatherDataDisplay.value) ConstraintSet.GONE 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 f17dbd24cf25..4d914c721d0c 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 @@ -31,8 +31,9 @@ import android.view.ViewTreeObserver.OnPreDrawListener import com.android.app.animation.Interpolators import com.android.systemui.keyguard.ui.view.layout.blueprints.transitions.IntraBlueprintTransition import com.android.systemui.keyguard.ui.view.layout.blueprints.transitions.IntraBlueprintTransition.Type +import com.android.systemui.keyguard.ui.view.layout.sections.transitions.ClockSizeTransition.SmartspaceMoveTransition.Companion.STATUS_AREA_MOVE_DOWN_MILLIS +import com.android.systemui.keyguard.ui.view.layout.sections.transitions.ClockSizeTransition.SmartspaceMoveTransition.Companion.STATUS_AREA_MOVE_UP_MILLIS import com.android.systemui.keyguard.ui.viewmodel.KeyguardClockViewModel -import com.android.systemui.keyguard.ui.viewmodel.KeyguardSmartspaceViewModel import com.android.systemui.res.R import com.android.systemui.shared.R as sharedR import com.google.android.material.math.MathUtils @@ -46,13 +47,12 @@ internal fun View.setRect(rect: Rect) = class ClockSizeTransition( config: IntraBlueprintTransition.Config, clockViewModel: KeyguardClockViewModel, - smartspaceViewModel: KeyguardSmartspaceViewModel, ) : TransitionSet() { init { ordering = ORDERING_TOGETHER if (config.type != Type.SmartspaceVisibility) { - addTransition(ClockFaceOutTransition(config, clockViewModel, smartspaceViewModel)) - addTransition(ClockFaceInTransition(config, clockViewModel, smartspaceViewModel)) + addTransition(ClockFaceOutTransition(config, clockViewModel)) + addTransition(ClockFaceInTransition(config, clockViewModel)) } addTransition(SmartspaceMoveTransition(config, clockViewModel)) } @@ -60,8 +60,11 @@ class ClockSizeTransition( abstract class VisibilityBoundsTransition() : Transition() { abstract val captureSmartspace: Boolean protected val TAG = this::class.simpleName!! + override fun captureEndValues(transition: TransitionValues) = captureValues(transition) + override fun captureStartValues(transition: TransitionValues) = captureValues(transition) + override fun getTransitionProperties(): Array<String> = TRANSITION_PROPERTIES private fun captureValues(transition: TransitionValues) { @@ -222,21 +225,19 @@ class ClockSizeTransition( } } - class ClockFaceInTransition( + abstract class ClockFaceTransition( config: IntraBlueprintTransition.Config, val viewModel: KeyguardClockViewModel, - val smartspaceViewModel: KeyguardSmartspaceViewModel, ) : VisibilityBoundsTransition() { - override val captureSmartspace = !viewModel.isLargeClockVisible.value + protected abstract val isLargeClock: Boolean + protected abstract val smallClockMoveScale: Float + override val captureSmartspace + get() = !isLargeClock - init { - duration = CLOCK_IN_MILLIS - startDelay = CLOCK_IN_START_DELAY_MILLIS - interpolator = CLOCK_IN_INTERPOLATOR - - if (viewModel.isLargeClockVisible.value) { + protected fun addTargets() { + if (isLargeClock) { viewModel.currentClock.value?.let { - if (DEBUG) Log.i(TAG, "Large Clock In: ${it.largeClock.layout.views}") + if (DEBUG) Log.i(TAG, "Adding large clock views: ${it.largeClock.layout.views}") it.largeClock.layout.views.forEach { addTarget(it) } } ?: run { @@ -244,7 +245,7 @@ class ClockSizeTransition( addTarget(R.id.lockscreen_clock_view_large) } } else { - if (DEBUG) Log.i(TAG, "Small Clock In") + if (DEBUG) Log.i(TAG, "Adding small clock") addTarget(R.id.lockscreen_clock_view) } } @@ -262,89 +263,59 @@ class ClockSizeTransition( if (fromIsVis == toIsVis) return fromBounds.set(toBounds) - if (viewModel.isLargeClockVisible.value) { + if (isLargeClock) { // Large clock shouldn't move; fromBounds already set } else if (toSSBounds != null && fromSSBounds != null) { // Instead of moving the small clock the full distance, we compute the distance // smartspace will move. We then scale this to match the duration of this animation // so that the small clock moves at the same speed as smartspace. val ssTranslation = - abs((toSSBounds.top - fromSSBounds.top) * SMALL_CLOCK_IN_MOVE_SCALE).toInt() + abs((toSSBounds.top - fromSSBounds.top) * smallClockMoveScale).toInt() fromBounds.top = toBounds.top - ssTranslation fromBounds.bottom = toBounds.bottom - ssTranslation } else { Log.e(TAG, "mutateBounds: smallClock received no smartspace bounds") } } + } + + class ClockFaceInTransition( + config: IntraBlueprintTransition.Config, + viewModel: KeyguardClockViewModel, + ) : ClockFaceTransition(config, viewModel) { + override val isLargeClock = viewModel.isLargeClockVisible.value + override val smallClockMoveScale = CLOCK_IN_MILLIS / STATUS_AREA_MOVE_DOWN_MILLIS.toFloat() + + init { + duration = CLOCK_IN_MILLIS + startDelay = CLOCK_IN_START_DELAY_MILLIS + interpolator = CLOCK_IN_INTERPOLATOR + addTargets() + } companion object { const val CLOCK_IN_MILLIS = 167L const val CLOCK_IN_START_DELAY_MILLIS = 133L val CLOCK_IN_INTERPOLATOR = Interpolators.LINEAR_OUT_SLOW_IN - const val SMALL_CLOCK_IN_MOVE_SCALE = - CLOCK_IN_MILLIS / SmartspaceMoveTransition.STATUS_AREA_MOVE_DOWN_MILLIS.toFloat() } } class ClockFaceOutTransition( config: IntraBlueprintTransition.Config, - val viewModel: KeyguardClockViewModel, - val smartspaceViewModel: KeyguardSmartspaceViewModel, - ) : VisibilityBoundsTransition() { - override val captureSmartspace = viewModel.isLargeClockVisible.value + viewModel: KeyguardClockViewModel, + ) : ClockFaceTransition(config, viewModel) { + override val isLargeClock = !viewModel.isLargeClockVisible.value + override val smallClockMoveScale = CLOCK_OUT_MILLIS / STATUS_AREA_MOVE_UP_MILLIS.toFloat() init { duration = CLOCK_OUT_MILLIS interpolator = CLOCK_OUT_INTERPOLATOR - - if (viewModel.isLargeClockVisible.value) { - if (DEBUG) Log.i(TAG, "Small Clock Out") - addTarget(R.id.lockscreen_clock_view) - } else { - viewModel.currentClock.value?.let { - if (DEBUG) Log.i(TAG, "Large Clock Out: ${it.largeClock.layout.views}") - it.largeClock.layout.views.forEach { addTarget(it) } - } - ?: run { - Log.e(TAG, "No large clock set, falling back") - addTarget(R.id.lockscreen_clock_view_large) - } - } - } - - override fun mutateBounds( - view: View, - fromIsVis: Boolean, - toIsVis: Boolean, - fromBounds: Rect, - toBounds: Rect, - fromSSBounds: Rect?, - toSSBounds: Rect? - ) { - // Move normally if clock is not changing visibility - if (fromIsVis == toIsVis) return - - toBounds.set(fromBounds) - if (!viewModel.isLargeClockVisible.value) { - // Large clock shouldn't move; toBounds already set - } else if (toSSBounds != null && fromSSBounds != null) { - // Instead of moving the small clock the full distance, we compute the distance - // smartspace will move. We then scale this to match the duration of this animation - // so that the small clock moves at the same speed as smartspace. - val ssTranslation = - abs((toSSBounds.top - fromSSBounds.top) * SMALL_CLOCK_OUT_MOVE_SCALE).toInt() - toBounds.top = fromBounds.top - ssTranslation - toBounds.bottom = fromBounds.bottom - ssTranslation - } else { - Log.e(TAG, "mutateBounds: smallClock received no smartspace bounds") - } + addTargets() } companion object { const val CLOCK_OUT_MILLIS = 133L val CLOCK_OUT_INTERPOLATOR = Interpolators.LINEAR - const val SMALL_CLOCK_OUT_MOVE_SCALE = - CLOCK_OUT_MILLIS / SmartspaceMoveTransition.STATUS_AREA_MOVE_UP_MILLIS.toFloat() } } @@ -353,15 +324,14 @@ class ClockSizeTransition( val config: IntraBlueprintTransition.Config, viewModel: KeyguardClockViewModel, ) : VisibilityBoundsTransition() { + private val isLargeClock = viewModel.isLargeClockVisible.value override val captureSmartspace = false init { duration = - if (viewModel.isLargeClockVisible.value) STATUS_AREA_MOVE_UP_MILLIS - else STATUS_AREA_MOVE_DOWN_MILLIS + if (isLargeClock) STATUS_AREA_MOVE_UP_MILLIS else STATUS_AREA_MOVE_DOWN_MILLIS interpolator = Interpolators.EMPHASIZED addTarget(sharedR.id.date_smartspace_view) - addTarget(sharedR.id.weather_smartspace_view) addTarget(sharedR.id.bc_smartspace_view) // Notifications normally and media on split shade needs to be moved @@ -391,6 +361,6 @@ class ClockSizeTransition( } companion object { - val DEBUG = true + val DEBUG = false } } 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 c05a1b732a50..d9a6d6401d64 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 @@ -29,6 +29,7 @@ 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.KeyguardState import com.android.systemui.keyguard.ui.StateToValue import com.android.systemui.res.R import javax.inject.Inject @@ -111,8 +112,8 @@ constructor( params: BurnInParameters, ): Flow<BurnInModel> { return combine( - keyguardTransitionInteractor.dozeAmountTransition.map { - Interpolators.FAST_OUT_SLOW_IN.getInterpolation(it.value) + keyguardTransitionInteractor.transitionValue(KeyguardState.AOD).map { + Interpolators.FAST_OUT_SLOW_IN.getInterpolation(it) }, burnInInteractor.burnIn( xDimenResourceId = R.dimen.burn_in_prevention_offset_x, 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 0f1f5c1f1cb5..06b76b3c0f37 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 @@ -52,8 +52,11 @@ constructor( udfpsOverlayInteractor: UdfpsOverlayInteractor, ) { private val isShowingAodOrDozing: Flow<Boolean> = - transitionInteractor.startedKeyguardState.map { keyguardState -> - keyguardState == KeyguardState.AOD || keyguardState == KeyguardState.DOZING + combine( + transitionInteractor.startedKeyguardState, + transitionInteractor.transitionValue(KeyguardState.DOZING), + ) { startedKeyguardState, dozingTransitionValue -> + startedKeyguardState == KeyguardState.AOD || dozingTransitionValue == 1f } private fun getColor(usingBackgroundProtection: Boolean): Int { diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModel.kt index fa43ec2cbc06..92bba38d0fe1 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModel.kt @@ -264,7 +264,7 @@ constructor( accessibilityInteractor.isEnabled.flatMapLatest { touchExplorationEnabled -> if (touchExplorationEnabled) { combine(iconType, isInteractive) { iconType, isInteractive -> - if (isInteractive) { + if (isInteractive || iconType == DeviceEntryIconView.IconType.LOCK) { iconType.toAccessibilityHintType() } else { DeviceEntryIconView.AccessibilityHintType.NONE diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDozingTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDozingTransitionViewModel.kt index 70c0032a30b3..c62e4f4ba243 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDozingTransitionViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDozingTransitionViewModel.kt @@ -60,6 +60,14 @@ constructor( onFinish = { 1f }, ) + val notificationAlpha: Flow<Float> = + transitionAnimation.sharedFlow( + duration = 500.milliseconds, + onStep = { 1f - it }, + // Needs to be 1f in order for HUNs to appear on AOD + onFinish = { 1f }, + ) + val deviceEntryBackgroundViewAlpha: Flow<Float> = transitionAnimation.immediatelyTransitionTo(0f) diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToGlanceableHubTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToGlanceableHubTransitionViewModel.kt new file mode 100644 index 000000000000..8eab406a1273 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToGlanceableHubTransitionViewModel.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.keyguard.ui.viewmodel + +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.keyguard.domain.interactor.FromGoneTransitionInteractor.Companion.TO_GLANCEABLE_HUB_DURATION +import com.android.systemui.keyguard.shared.model.Edge +import com.android.systemui.keyguard.shared.model.KeyguardState.GLANCEABLE_HUB +import com.android.systemui.keyguard.shared.model.KeyguardState.GONE +import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow +import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition +import com.android.systemui.scene.shared.model.Scenes +import javax.inject.Inject +import kotlin.time.Duration.Companion.milliseconds +import kotlinx.coroutines.flow.Flow + +@SysUISingleton +class GoneToGlanceableHubTransitionViewModel +@Inject +constructor( + animationFlow: KeyguardTransitionAnimationFlow, +) : DeviceEntryIconTransition { + + private val transitionAnimation = + animationFlow + .setup(duration = TO_GLANCEABLE_HUB_DURATION, edge = Edge.create(GONE, Scenes.Communal)) + .setupWithoutSceneContainer(edge = Edge.create(GONE, GLANCEABLE_HUB)) + + override val deviceEntryParentViewAlpha: Flow<Float> = + transitionAnimation.sharedFlow( + duration = 167.milliseconds, + onStep = { it }, + onCancel = { 0f }, + onFinish = { 1f }, + ) +} diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBlueprintViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBlueprintViewModel.kt index 7ac03bffd4f3..c6efcfad8da7 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBlueprintViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBlueprintViewModel.kt @@ -130,6 +130,6 @@ constructor( companion object { private const val TAG = "KeyguardBlueprintViewModel" - private const val DEBUG = true + private const val DEBUG = false } } 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 940f42377b42..f5c521a3d8c7 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 @@ -32,6 +32,7 @@ import com.android.systemui.res.R import com.android.systemui.shade.domain.interactor.ShadeInteractor import com.android.systemui.shade.shared.model.ShadeMode import com.android.systemui.statusbar.notification.domain.interactor.NotificationsKeyguardInteractor +import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconContainerAlwaysOnDisplayViewModel import com.android.systemui.statusbar.ui.SystemBarUtilsProxy import javax.inject.Inject import kotlinx.coroutines.CoroutineScope @@ -48,6 +49,7 @@ class KeyguardClockViewModel constructor( keyguardClockInteractor: KeyguardClockInteractor, @Application private val applicationScope: CoroutineScope, + aodNotificationIconViewModel: NotificationIconContainerAlwaysOnDisplayViewModel, notifsKeyguardInteractor: NotificationsKeyguardInteractor, @get:VisibleForTesting val shadeInteractor: ShadeInteractor, private val systemBarUtils: SystemBarUtilsProxy, @@ -105,8 +107,13 @@ constructor( initialValue = false ) - val isAodIconsVisible: StateFlow<Boolean> = - notifsKeyguardInteractor.areNotificationsFullyHidden.stateIn( + // To translate elements below smartspace in weather clock to avoid overlapping between date + // element in weather clock and aod icons + val isAodIconsVisible: StateFlow<Boolean> = combine(aodNotificationIconViewModel.icons.map { + it.visibleIcons.isNotEmpty() + }, notifsKeyguardInteractor.areNotificationsFullyHidden) { hasIcons, visible -> + hasIcons && visible + }.stateIn( scope = applicationScope, started = SharingStarted.WhileSubscribed(), initialValue = false diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt index ee52ad024e24..aefff7d0f79b 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt @@ -66,7 +66,6 @@ import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.combineTransform import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.filter -import kotlinx.coroutines.flow.filterNotNull import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.merge import kotlinx.coroutines.flow.onStart @@ -203,7 +202,7 @@ constructor( combine( communalInteractor.isIdleOnCommunal, keyguardTransitionInteractor - .transitionValue(GONE) + .transitionValue(scene = Scenes.Gone, stateWithoutSceneContainer = GONE) .map { it == 1f } .onStart { emit(false) }, keyguardTransitionInteractor @@ -236,7 +235,7 @@ constructor( // value emitted by any of them. Do not add flows that cannot make this guarantee. merge( alphaOnShadeExpansion, - keyguardInteractor.dismissAlpha.filterNotNull(), + keyguardInteractor.dismissAlpha, alternateBouncerToGoneTransitionViewModel.lockscreenAlpha(viewState), aodToGoneTransitionViewModel.lockscreenAlpha(viewState), aodToLockscreenTransitionViewModel.lockscreenAlpha(viewState), diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModel.kt index 02e48fc5a09b..10cfd6ba44d8 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModel.kt @@ -27,11 +27,13 @@ import com.android.systemui.communal.domain.interactor.CommunalInteractor import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor +import com.android.systemui.scene.shared.model.SceneFamilies import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.scene.shared.model.TransitionKeys.ToSplitShade import com.android.systemui.shade.domain.interactor.ShadeInteractor import com.android.systemui.shade.shared.model.ShadeMode import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationsPlaceholderViewModel +import com.android.systemui.util.kotlin.filterValuesNotNull import javax.inject.Inject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.ExperimentalCoroutinesApi @@ -89,37 +91,36 @@ constructor( isCommunalAvailable: Boolean, shadeMode: ShadeMode, ): Map<UserAction, UserActionResult> { - val shadeSceneKey = + val notifShadeSceneKey = UserActionResult( - toScene = - if (shadeMode is ShadeMode.Dual) Scenes.NotificationsShade else Scenes.Shade, + toScene = SceneFamilies.NotifShade, transitionKey = ToSplitShade.takeIf { shadeMode is ShadeMode.Split }, ) - val quickSettingsIfSingleShade = - if (shadeMode is ShadeMode.Single) UserActionResult(Scenes.QuickSettings) - else shadeSceneKey - return mapOf( Swipe.Left to UserActionResult(Scenes.Communal).takeIf { isCommunalAvailable }, Swipe.Up to if (isDeviceUnlocked) Scenes.Gone else Scenes.Bouncer, // Swiping down from the top edge goes to QS (or shade if in split shade mode). - swipeDownFromTop(pointerCount = 1) to quickSettingsIfSingleShade, - swipeDownFromTop(pointerCount = 2) to - // TODO(b/338577208): Remove 'Dual' once we add Dual Shade invocation zones. - if (shadeMode is ShadeMode.Dual) { - UserActionResult(Scenes.QuickSettingsShade) + swipeDownFromTop(pointerCount = 1) to + if (shadeMode is ShadeMode.Single) { + UserActionResult(Scenes.QuickSettings) } else { - quickSettingsIfSingleShade + notifShadeSceneKey }, - // Swiping down, not from the edge, always navigates to the shade scene. - swipeDown(pointerCount = 1) to shadeSceneKey, - swipeDown(pointerCount = 2) to shadeSceneKey, + // TODO(b/338577208): Remove once we add Dual Shade invocation zones. + swipeDownFromTop(pointerCount = 2) to + UserActionResult( + toScene = SceneFamilies.QuickSettings, + transitionKey = ToSplitShade.takeIf { shadeMode is ShadeMode.Split } + ), + + // Swiping down, not from the edge, always navigates to the notif shade scene. + swipeDown(pointerCount = 1) to notifShadeSceneKey, + swipeDown(pointerCount = 2) to notifShadeSceneKey, ) - .filterValues { it != null } - .mapValues { checkNotNull(it.value) } + .filterValuesNotNull() } private fun swipeDownFromTop(pointerCount: Int): Swipe { diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/data/repository/MediaFilterRepository.kt b/packages/SystemUI/src/com/android/systemui/media/controls/data/repository/MediaFilterRepository.kt index 0c70f10bba21..6a91d1b15325 100644 --- a/packages/SystemUI/src/com/android/systemui/media/controls/data/repository/MediaFilterRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/media/controls/data/repository/MediaFilterRepository.kt @@ -218,9 +218,9 @@ constructor( mediaFromRecPackageName = null _currentMedia.value = sortedMap.values.toList() } - } else if (sortedMap.size > sortedMedia.size) { + } else if (sortedMap.size > _currentMedia.value.size && it.active) { _currentMedia.value = sortedMap.values.toList() - } else if (sortedMap.size == sortedMedia.size) { + } else { // When loading an update for an existing media control. val currentList = mutableListOf<MediaCommonModel>().apply { addAll(_currentMedia.value) } @@ -296,6 +296,18 @@ constructor( mediaFromRecPackageName = packageName } + fun hasActiveMedia(): Boolean { + return _selectedUserEntries.value.any { it.value.active } + } + + fun hasAnyMedia(): Boolean { + return _selectedUserEntries.value.entries.isNotEmpty() + } + + fun isRecommendationActive(): Boolean { + return _smartspaceMediaData.value.isActive + } + private fun canBeRemoved(data: MediaData): Boolean { return data.isPlaying?.let { !it } ?: data.isClearable && !data.active } diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataProcessor.kt b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataProcessor.kt index 8e985e11732f..37dffd1955d6 100644 --- a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataProcessor.kt +++ b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataProcessor.kt @@ -107,6 +107,7 @@ import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onStart import kotlinx.coroutines.launch @@ -371,6 +372,7 @@ class MediaDataProcessor( .onStart { emit(Unit) } .map { allowMediaRecommendations() } .distinctUntilChanged() + .flowOn(backgroundDispatcher) // only track the most recent emission .collectLatest { allowMediaRecommendations = it diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManager.kt b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManager.kt index aa93df7f1474..8b2f619374d1 100644 --- a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManager.kt +++ b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManager.kt @@ -358,7 +358,12 @@ constructor( // LocalMediaManager. Override with routing session name if available to // show dynamic group name. connectedDevice?.copy(name = it.name ?: connectedDevice.name) - } + } ?: MediaDeviceData( + enabled = false, + icon = context.getDrawable(R.drawable.ic_media_home_devices), + name = context.getString(R.string.media_seamless_other_device), + showBroadcastButton = false + ) } else { // Prefer SASS if available when playback is local. activeDevice = getSassDevice() ?: connectedDevice diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaCarouselInteractor.kt b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaCarouselInteractor.kt index b4bd4fd2c266..0630cbd3f3be 100644 --- a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaCarouselInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaCarouselInteractor.kt @@ -25,7 +25,6 @@ import com.android.internal.logging.InstanceId import com.android.systemui.CoreStartable import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application -import com.android.systemui.media.controls.data.repository.MediaDataRepository import com.android.systemui.media.controls.data.repository.MediaFilterRepository import com.android.systemui.media.controls.domain.pipeline.MediaDataCombineLatest import com.android.systemui.media.controls.domain.pipeline.MediaDataFilterImpl @@ -45,7 +44,6 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.combine -import kotlinx.coroutines.flow.mapLatest import kotlinx.coroutines.flow.stateIn /** Encapsulates business logic for media pipeline. */ @@ -55,7 +53,6 @@ class MediaCarouselInteractor @Inject constructor( @Application applicationScope: CoroutineScope, - private val mediaDataRepository: MediaDataRepository, private val mediaDataProcessor: MediaDataProcessor, private val mediaTimeoutListener: MediaTimeoutListener, private val mediaResumeListener: MediaResumeListener, @@ -103,26 +100,6 @@ constructor( initialValue = false, ) - /** Are there any media notifications active, excluding the recommendations? */ - val hasActiveMedia: StateFlow<Boolean> = - mediaFilterRepository.selectedUserEntries - .mapLatest { entries -> entries.any { it.value.active } } - .stateIn( - scope = applicationScope, - started = SharingStarted.WhileSubscribed(), - initialValue = false, - ) - - /** Are there any media notifications, excluding the recommendations? */ - val hasAnyMedia: StateFlow<Boolean> = - mediaFilterRepository.selectedUserEntries - .mapLatest { entries -> entries.isNotEmpty() } - .stateIn( - scope = applicationScope, - started = SharingStarted.WhileSubscribed(), - initialValue = false, - ) - /** The current list for user media instances */ val currentMedia: StateFlow<List<MediaCommonModel>> = mediaFilterRepository.currentMedia @@ -235,11 +212,11 @@ constructor( override fun hasAnyMediaOrRecommendation() = hasAnyMediaOrRecommendation.value - override fun hasActiveMedia() = hasActiveMedia.value + override fun hasActiveMedia() = mediaFilterRepository.hasActiveMedia() - override fun hasAnyMedia() = hasAnyMedia.value + override fun hasAnyMedia() = mediaFilterRepository.hasAnyMedia() - override fun isRecommendationActive() = mediaDataRepository.smartspaceMediaData.value.isActive + override fun isRecommendationActive() = mediaFilterRepository.isRecommendationActive() fun reorderMedia() { mediaFilterRepository.setOrderedMedia() diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselController.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselController.kt index edead51dd8e0..53794d2d2225 100644 --- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselController.kt +++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselController.kt @@ -42,7 +42,6 @@ import com.android.keyguard.KeyguardUpdateMonitor import com.android.keyguard.KeyguardUpdateMonitorCallback import com.android.systemui.Dumpable 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.dump.DumpManager @@ -124,7 +123,6 @@ private val DEBUG = Log.isLoggable(TAG, Log.DEBUG) class MediaCarouselController @Inject constructor( - @Application applicationScope: CoroutineScope, private val context: Context, private val mediaControlPanelFactory: Provider<MediaControlPanel>, private val visualStabilityProvider: VisualStabilityProvider, @@ -217,7 +215,7 @@ constructor( private var carouselLocale: Locale? = null private val animationScaleObserver: ContentObserver = - object : ContentObserver(null) { + object : ContentObserver(executor, 0) { override fun onChange(selfChange: Boolean) { if (!mediaFlags.isSceneContainerEnabled()) { MediaPlayerData.players().forEach { it.updateAnimatorDurationScale() } @@ -389,18 +387,20 @@ constructor( repeatOnLifecycle(Lifecycle.State.STARTED) { listenForAnyStateToGoneKeyguardTransition(this) listenForAnyStateToLockscreenTransition(this) + listenForLockscreenSettingChanges(this) if (!mediaFlags.isSceneContainerEnabled()) return@repeatOnLifecycle listenForMediaItemsChanges(this) } } - listenForLockscreenSettingChanges(applicationScope) // Notifies all active players about animation scale changes. - globalSettings.registerContentObserverSync( - Settings.Global.getUriFor(Settings.Global.ANIMATOR_DURATION_SCALE), - animationScaleObserver - ) + bgExecutor.execute { + globalSettings.registerContentObserverSync( + Settings.Global.getUriFor(Settings.Global.ANIMATOR_DURATION_SCALE), + animationScaleObserver + ) + } } private fun setUpListeners() { @@ -884,7 +884,8 @@ constructor( val previousVisibleIndex = MediaPlayerData.playerKeys().indexOfFirst { key -> it == key } mediaCarouselScrollHandler.scrollToPlayer(previousVisibleIndex, mediaIndex) - } ?: mediaCarouselScrollHandler.scrollToPlayer(destIndex = mediaIndex) + } + ?: mediaCarouselScrollHandler.scrollToPlayer(destIndex = mediaIndex) } } else if (isRtl && mediaContent.childCount > 0) { // In RTL, Scroll to the first player as it is the rightmost player in media carousel. diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManager.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManager.kt index 601d563ffc45..88a28bf998da 100644 --- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManager.kt +++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManager.kt @@ -36,6 +36,7 @@ import androidx.annotation.VisibleForTesting import com.android.app.animation.Interpolators import com.android.app.tracing.traceSection import com.android.keyguard.KeyguardViewController +import com.android.systemui.Flags.mediaControlsLockscreenShadeBugFix import com.android.systemui.communal.ui.viewmodel.CommunalTransitionViewModel import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application @@ -483,8 +484,7 @@ constructor( object : StatusBarStateController.StateListener { override fun onStatePreChange(oldState: Int, newState: Int) { // We're updating the location before the state change happens, since we want - // the - // location of the previous state to still be up to date when the animation + // the location of the previous state to still be up to date when the animation // starts if ( newState == StatusBarState.SHADE_LOCKED && @@ -588,6 +588,17 @@ constructor( } } + if (mediaControlsLockscreenShadeBugFix()) { + coroutineScope.launch { + shadeInteractor.shadeExpansion.collect { expansion -> + if (expansion >= 1f || expansion <= 0f) { + // Shade has fully expanded or collapsed: force transition amount update + setTransitionToFullShadeAmount(expansion) + } + } + } + } + val settingsObserver: ContentObserver = object : ContentObserver(handler) { override fun onChange(selfChange: Boolean, uri: Uri?) { diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaCarouselViewModel.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaCarouselViewModel.kt index 4e9093642c6b..315a9fb0c592 100644 --- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaCarouselViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaCarouselViewModel.kt @@ -201,6 +201,7 @@ constructor( ) { if (immediatelyRemove || isReorderingAllowed()) { interactor.dismissSmartspaceRecommendation(commonModel.recsLoadingModel.key, 0L) + mediaRecs = null if (!immediatelyRemove) { // Although it wasn't requested, we were able to process the removal // immediately since reordering is allowed. So, notify hosts to update diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaItem.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaItem.java index fbb84defc372..4496b258bde4 100644 --- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaItem.java +++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaItem.java @@ -16,7 +16,9 @@ package com.android.systemui.media.dialog; -import androidx.annotation.IntDef; +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.annotation.Nullable; import com.android.settingslib.media.MediaDevice; import com.android.systemui.res.R; @@ -46,40 +48,50 @@ public class MediaItem { int TYPE_PAIR_NEW_DEVICE = 2; } - public MediaItem() { - this.mMediaDeviceOptional = Optional.empty(); - this.mTitle = null; - this.mMediaItemType = MediaItemType.TYPE_PAIR_NEW_DEVICE; + /** + * Returns a new {@link MediaItemType#TYPE_DEVICE} {@link MediaItem} with its {@link + * #getMediaDevice() media device} set to {@code device} and its title set to {@code device}'s + * name. + */ + public static MediaItem createDeviceMediaItem(@NonNull MediaDevice device) { + return new MediaItem(device, device.getName(), MediaItemType.TYPE_DEVICE); } - public MediaItem(String title, int mediaItemType) { - this.mMediaDeviceOptional = Optional.empty(); - this.mTitle = title; - this.mMediaItemType = mediaItemType; + /** + * Returns a new {@link MediaItemType#TYPE_PAIR_NEW_DEVICE} {@link MediaItem} with both {@link + * #getMediaDevice() media device} and title set to {@code null}. + */ + public static MediaItem createPairNewDeviceMediaItem() { + return new MediaItem( + /* device */ null, /* title */ null, MediaItemType.TYPE_PAIR_NEW_DEVICE); + } + + /** + * Returns a new {@link MediaItemType#TYPE_GROUP_DIVIDER} {@link MediaItem} with the specified + * title and a {@code null} {@link #getMediaDevice() media device}. + */ + public static MediaItem createGroupDividerMediaItem(@Nullable String title) { + return new MediaItem(/* device */ null, title, MediaItemType.TYPE_GROUP_DIVIDER); } - public MediaItem(MediaDevice mediaDevice) { - this.mMediaDeviceOptional = Optional.of(mediaDevice); - this.mTitle = mediaDevice.getName(); - this.mMediaItemType = MediaItemType.TYPE_DEVICE; + private MediaItem( + @Nullable MediaDevice device, @Nullable String title, @MediaItemType int type) { + this.mMediaDeviceOptional = Optional.ofNullable(device); + this.mTitle = title; + this.mMediaItemType = type; } public Optional<MediaDevice> getMediaDevice() { return mMediaDeviceOptional; } - /** - * Get layout id based on media item Type. - */ - public static int getMediaLayoutId(int mediaItemType) { - switch (mediaItemType) { - case MediaItemType.TYPE_DEVICE: - case MediaItemType.TYPE_PAIR_NEW_DEVICE: - return R.layout.media_output_list_item_advanced; - case MediaItemType.TYPE_GROUP_DIVIDER: - default: - return R.layout.media_output_list_group_divider; - } + /** Get layout id based on media item Type. */ + public static int getMediaLayoutId(@MediaItemType int mediaItemType) { + return switch (mediaItemType) { + case MediaItemType.TYPE_DEVICE, MediaItemType.TYPE_PAIR_NEW_DEVICE -> + R.layout.media_output_list_item_advanced; + default -> R.layout.media_output_list_group_divider; + }; } public String getTitle() { diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java index c2cfdbe410b8..1e8656311f1d 100644 --- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java +++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java @@ -68,6 +68,7 @@ import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; import androidx.core.graphics.drawable.IconCompat; +import com.android.internal.annotations.GuardedBy; import com.android.settingslib.RestrictedLockUtilsInternal; import com.android.settingslib.Utils; import com.android.settingslib.bluetooth.BluetoothUtils; @@ -656,10 +657,16 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback, if (DEBUG) { Log.d(TAG, "No connected media device or muting expected device exist."); } - return categorizeMediaItems(null, devices, needToHandleMutingExpectedDevice); + return categorizeMediaItemsLocked( + /* connectedMediaDevice */ null, + devices, + needToHandleMutingExpectedDevice); } // selected device exist - return categorizeMediaItems(connectedMediaDevice, devices, false); + return categorizeMediaItemsLocked( + connectedMediaDevice, + devices, + /* needToHandleMutingExpectedDevice */ false); } // To keep the same list order final List<MediaDevice> targetMediaDevices = new ArrayList<>(); @@ -682,8 +689,9 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback, devices.removeAll(targetMediaDevices); targetMediaDevices.addAll(devices); } - List<MediaItem> finalMediaItems = targetMediaDevices.stream().map( - MediaItem::new).collect(Collectors.toList()); + List<MediaItem> finalMediaItems = targetMediaDevices.stream() + .map(MediaItem::createDeviceMediaItem) + .collect(Collectors.toList()); dividerItems.forEach(finalMediaItems::add); attachConnectNewDeviceItemIfNeeded(finalMediaItems); return finalMediaItems; @@ -694,51 +702,50 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback, * Initial categorization of current devices, will not be called for updates to the devices * list. */ - private List<MediaItem> categorizeMediaItems(MediaDevice connectedMediaDevice, + @GuardedBy("mMediaDevicesLock") + private List<MediaItem> categorizeMediaItemsLocked(MediaDevice connectedMediaDevice, List<MediaDevice> devices, boolean needToHandleMutingExpectedDevice) { - synchronized (mMediaDevicesLock) { - List<MediaItem> finalMediaItems = new ArrayList<>(); - Set<String> selectedDevicesIds = getSelectedMediaDevice().stream().map( - MediaDevice::getId).collect(Collectors.toSet()); - if (connectedMediaDevice != null) { - selectedDevicesIds.add(connectedMediaDevice.getId()); - } - boolean suggestedDeviceAdded = false; - boolean displayGroupAdded = false; - for (MediaDevice device : devices) { - if (needToHandleMutingExpectedDevice && device.isMutingExpectedDevice()) { - finalMediaItems.add(0, new MediaItem(device)); - } else if (!needToHandleMutingExpectedDevice && selectedDevicesIds.contains( - device.getId())) { - finalMediaItems.add(0, new MediaItem(device)); - } else { - if (device.isSuggestedDevice() && !suggestedDeviceAdded) { - attachGroupDivider(finalMediaItems, mContext.getString( - R.string.media_output_group_title_suggested_device)); - suggestedDeviceAdded = true; - } else if (!device.isSuggestedDevice() && !displayGroupAdded) { - attachGroupDivider(finalMediaItems, mContext.getString( - R.string.media_output_group_title_speakers_and_displays)); - displayGroupAdded = true; - } - finalMediaItems.add(new MediaItem(device)); + List<MediaItem> finalMediaItems = new ArrayList<>(); + Set<String> selectedDevicesIds = getSelectedMediaDevice().stream() + .map(MediaDevice::getId) + .collect(Collectors.toSet()); + if (connectedMediaDevice != null) { + selectedDevicesIds.add(connectedMediaDevice.getId()); + } + boolean suggestedDeviceAdded = false; + boolean displayGroupAdded = false; + for (MediaDevice device : devices) { + if (needToHandleMutingExpectedDevice && device.isMutingExpectedDevice()) { + finalMediaItems.add(0, MediaItem.createDeviceMediaItem(device)); + } else if (!needToHandleMutingExpectedDevice && selectedDevicesIds.contains( + device.getId())) { + finalMediaItems.add(0, MediaItem.createDeviceMediaItem(device)); + } else { + if (device.isSuggestedDevice() && !suggestedDeviceAdded) { + attachGroupDivider(finalMediaItems, mContext.getString( + R.string.media_output_group_title_suggested_device)); + suggestedDeviceAdded = true; + } else if (!device.isSuggestedDevice() && !displayGroupAdded) { + attachGroupDivider(finalMediaItems, mContext.getString( + R.string.media_output_group_title_speakers_and_displays)); + displayGroupAdded = true; } + finalMediaItems.add(MediaItem.createDeviceMediaItem(device)); } - attachConnectNewDeviceItemIfNeeded(finalMediaItems); - return finalMediaItems; } + attachConnectNewDeviceItemIfNeeded(finalMediaItems); + return finalMediaItems; } private void attachGroupDivider(List<MediaItem> mediaItems, String title) { - mediaItems.add( - new MediaItem(title, MediaItem.MediaItemType.TYPE_GROUP_DIVIDER)); + mediaItems.add(MediaItem.createGroupDividerMediaItem(title)); } private void attachConnectNewDeviceItemIfNeeded(List<MediaItem> mediaItems) { // Attach "Connect a device" item only when current output is not remote and not a group if (!isCurrentConnectedDeviceRemote() && getSelectedMediaDevice().size() == 1) { - mediaItems.add(new MediaItem()); + mediaItems.add(MediaItem.createPairNewDeviceMediaItem()); } } diff --git a/packages/SystemUI/src/com/android/systemui/media/muteawait/MediaMuteAwaitConnectionManager.kt b/packages/SystemUI/src/com/android/systemui/media/muteawait/MediaMuteAwaitConnectionManager.kt index f47954a23890..3a292e7a6ddd 100644 --- a/packages/SystemUI/src/com/android/systemui/media/muteawait/MediaMuteAwaitConnectionManager.kt +++ b/packages/SystemUI/src/com/android/systemui/media/muteawait/MediaMuteAwaitConnectionManager.kt @@ -94,7 +94,7 @@ class MediaMuteAwaitConnectionManager constructor( } private fun AudioDeviceAttributes.getIcon(): Drawable { - return deviceIconUtil.getIconFromAudioDeviceType(this.type, context) + return deviceIconUtil.getIconFromAudioDeviceType(this.type) } private fun IntArray.hasMedia() = USAGE_MEDIA in this 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 596c18f04134..01b1be9d02b7 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 @@ -53,8 +53,13 @@ constructor( withContext(coroutineDispatcher) { val groupedTasks: List<GroupedRecentTaskInfo> = recents?.getTasks() ?: emptyList() // Note: the returned task list is from the most-recent to least-recent order. - // The last foreground task is at index 1, because at index 0 will be our app selector. - val foregroundGroup = groupedTasks.elementAtOrNull(1) + // 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 + // opening the app selector in split screen mode, the foreground task will be the second + // task in index 0. + val foregroundGroup = + if (groupedTasks.firstOrNull()?.splitBounds != null) groupedTasks.first() + else groupedTasks.elementAtOrNull(1) val foregroundTaskId1 = foregroundGroup?.taskInfo1?.taskId val foregroundTaskId2 = foregroundGroup?.taskInfo2?.taskId val foregroundTaskIds = listOfNotNull(foregroundTaskId1, foregroundTaskId2) diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/data/model/MediaProjectionState.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/data/model/MediaProjectionState.kt index 1d5f6f52000c..de300b2ff900 100644 --- a/packages/SystemUI/src/com/android/systemui/mediaprojection/data/model/MediaProjectionState.kt +++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/data/model/MediaProjectionState.kt @@ -20,7 +20,21 @@ import android.app.ActivityManager.RunningTaskInfo /** Represents the state of media projection. */ sealed interface MediaProjectionState { - object NotProjecting : MediaProjectionState - object EntireScreen : MediaProjectionState - data class SingleTask(val task: RunningTaskInfo) : MediaProjectionState + /** There is no media being projected. */ + data object NotProjecting : MediaProjectionState + + /** + * Media is currently being projected. + * + * @property hostPackage the package name of the app that is receiving the content of the media + * projection (aka which app the phone screen contents are being sent to). + */ + sealed class Projecting(open val hostPackage: String) : MediaProjectionState { + /** The entire screen is being projected. */ + data class EntireScreen(override val hostPackage: String) : Projecting(hostPackage) + + /** Only a single task is being projected. */ + data class SingleTask(override val hostPackage: String, val task: RunningTaskInfo) : + Projecting(hostPackage) + } } diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/data/repository/MediaProjectionManagerRepository.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/data/repository/MediaProjectionManagerRepository.kt index 3ce0a1e00910..8a9adc7a5c88 100644 --- a/packages/SystemUI/src/com/android/systemui/mediaprojection/data/repository/MediaProjectionManagerRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/data/repository/MediaProjectionManagerRepository.kt @@ -64,6 +64,10 @@ constructor( } } + override suspend fun stopProjecting() { + withContext(backgroundDispatcher) { mediaProjectionManager.stopActiveProjection() } + } + override val mediaProjectionState: Flow<MediaProjectionState> = conflatedCallbackFlow { val callback = @@ -83,7 +87,9 @@ constructor( session: ContentRecordingSession? ) { Log.d(TAG, "MediaProjectionManager.Callback#onSessionStarted: $session") - launch { trySendWithFailureLogging(stateForSession(session), TAG) } + launch { + trySendWithFailureLogging(stateForSession(info, session), TAG) + } } } mediaProjectionManager.addCallback(callback, handler) @@ -95,19 +101,23 @@ constructor( initialValue = MediaProjectionState.NotProjecting, ) - private suspend fun stateForSession(session: ContentRecordingSession?): MediaProjectionState { + private suspend fun stateForSession( + info: MediaProjectionInfo, + session: ContentRecordingSession? + ): MediaProjectionState { if (session == null) { return MediaProjectionState.NotProjecting } + + val hostPackage = info.packageName if (session.contentToRecord == RECORD_CONTENT_DISPLAY || session.tokenToRecord == null) { - return MediaProjectionState.EntireScreen + return MediaProjectionState.Projecting.EntireScreen(hostPackage) } val matchingTask = tasksRepository.findRunningTaskFromWindowContainerToken( checkNotNull(session.tokenToRecord) - ) - ?: return MediaProjectionState.EntireScreen - return MediaProjectionState.SingleTask(matchingTask) + ) ?: return MediaProjectionState.Projecting.EntireScreen(hostPackage) + return MediaProjectionState.Projecting.SingleTask(hostPackage, matchingTask) } companion object { diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/data/repository/MediaProjectionRepository.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/data/repository/MediaProjectionRepository.kt index 21300dbff929..50182d7a3b33 100644 --- a/packages/SystemUI/src/com/android/systemui/mediaprojection/data/repository/MediaProjectionRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/data/repository/MediaProjectionRepository.kt @@ -26,6 +26,9 @@ interface MediaProjectionRepository { /** Switches the task that should be projected. */ suspend fun switchProjectedTask(task: RunningTaskInfo) + /** Stops the currently active projection. */ + suspend fun stopProjecting() + /** Represents the current [MediaProjectionState]. */ val mediaProjectionState: Flow<MediaProjectionState> } 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 e861ddf69aa6..f004c3a8916f 100644 --- a/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java +++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java @@ -23,6 +23,7 @@ import static android.media.projection.IMediaProjectionManager.EXTRA_USER_REVIEW import static android.media.projection.MediaProjectionManager.OVERRIDE_DISABLE_MEDIA_PROJECTION_SINGLE_APP_OPTION; import static android.media.projection.ReviewGrantedConsentResult.RECORD_CANCEL; import static android.media.projection.ReviewGrantedConsentResult.RECORD_CONTENT_DISPLAY; +import static android.os.UserHandle.USER_SYSTEM; import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS; import static com.android.systemui.mediaprojection.permission.ScreenShareOptionKt.ENTIRE_SCREEN; @@ -31,7 +32,6 @@ import static com.android.systemui.mediaprojection.permission.ScreenShareOptionK import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.app.Activity; -import android.app.ActivityManager; import android.app.ActivityOptions.LaunchCookie; import android.app.AlertDialog; import android.app.StatusBarManager; @@ -366,11 +366,11 @@ public class MediaProjectionPermissionActivity extends Activity intent.putExtra(EXTRA_USER_REVIEW_GRANTED_CONSENT, mReviewGrantedConsentRequired); intent.setFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT); - // Start activity from the current foreground user to avoid creating a separate - // SystemUI process without access to recent tasks because it won't have - // WM Shell running inside. + // Start activity as system user and manually show app selector to all users to + // avoid creating a separate SystemUI process without access to recent tasks + // because it won't have WM Shell running inside. mUserSelectingTask = true; - startActivityAsUser(intent, UserHandle.of(ActivityManager.getCurrentUser())); + startActivityAsUser(intent, UserHandle.of(USER_SYSTEM)); // close shade if it's open mStatusBarManager.collapsePanels(); } diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/domain/interactor/TaskSwitchInteractor.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/domain/interactor/TaskSwitchInteractor.kt index c232d4d16294..118639c521f6 100644 --- a/packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/domain/interactor/TaskSwitchInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/domain/interactor/TaskSwitchInteractor.kt @@ -57,7 +57,7 @@ constructor( mediaProjectionRepository.mediaProjectionState.flatMapLatest { projectionState -> Log.d(TAG, "MediaProjectionState -> $projectionState") when (projectionState) { - is MediaProjectionState.SingleTask -> { + is MediaProjectionState.Projecting.SingleTask -> { val projectedTask = projectionState.task tasksRepository.foregroundTask.map { foregroundTask -> if (hasForegroundTaskSwitched(projectedTask, foregroundTask)) { @@ -67,7 +67,7 @@ constructor( } } } - is MediaProjectionState.EntireScreen, + is MediaProjectionState.Projecting.EntireScreen, is MediaProjectionState.NotProjecting -> { flowOf(TaskSwitchState.NotProjectingTask) } diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java index 0e819c2b0a4f..07289cb7b809 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java @@ -2028,12 +2028,15 @@ public class NavigationBar extends ViewController<NavigationBarView> implements getBarTransitions().setBackgroundOverrideAlpha(1f); } } - updateScreenPinningGestures(); + + // Update the window layout params when the nav mode changes as that will affect the + // system gesture insets + setNavBarMode(mode); + repositionNavigationBar(mCurrentRotation); if (!canShowSecondaryHandle()) { resetSecondaryHandle(); } - setNavBarMode(mode); mView.setShouldShowSwipeUpUi(mOverviewProxyService.shouldShowSwipeUpUI()); } }; diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/DeadZone.java b/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/DeadZone.java index 326830600635..8177fdec86e6 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/DeadZone.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/DeadZone.java @@ -61,6 +61,7 @@ public class DeadZone { } }; + private final boolean mUseDeadZone; private final NavigationBarController mNavBarController; private final NavigationBarView mNavigationBarView; @@ -86,9 +87,12 @@ public class DeadZone { @Inject public DeadZone(NavigationBarView view) { + mUseDeadZone = view.getResources().getBoolean(R.bool.config_useDeadZone); + mNavigationBarView = view; mNavBarController = Dependency.get(NavigationBarController.class); mDisplayId = view.getContext().getDisplayId(); + onConfigurationChanged(HORIZONTAL); } @@ -108,12 +112,20 @@ public class DeadZone { } public void setFlashOnTouchCapture(boolean dbg) { + if (!mUseDeadZone) { + return; + } + mShouldFlash = dbg; mFlashFrac = 0f; mNavigationBarView.postInvalidate(); } public void onConfigurationChanged(int rotation) { + if (!mUseDeadZone) { + return; + } + mDisplayRotation = rotation; final Resources res = mNavigationBarView.getResources(); @@ -134,6 +146,10 @@ public class DeadZone { // I made you a touch event... public boolean onTouchEvent(MotionEvent event) { + if (!mUseDeadZone) { + return false; + } + if (DEBUG) { Slog.v(TAG, this + " onTouch: " + MotionEvent.actionToString(event.getAction())); } @@ -187,17 +203,17 @@ public class DeadZone { if (mShouldFlash) mNavigationBarView.postInvalidate(); } - public void setFlash(float f) { + private void setFlash(float f) { mFlashFrac = f; mNavigationBarView.postInvalidate(); } - public float getFlash() { + private float getFlash() { return mFlashFrac; } public void onDraw(Canvas can) { - if (!mShouldFlash || mFlashFrac <= 0f) { + if (!mUseDeadZone || !mShouldFlash || mFlashFrac <= 0f) { return; } 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 41cd2c46b998..99c95b54a4ca 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java @@ -24,6 +24,7 @@ import static com.android.systemui.Flags.edgebackGestureHandlerGetRunningTasksBa import static com.android.systemui.classifier.Classifier.BACK_GESTURE; import static com.android.systemui.navigationbar.gestural.Utilities.isTrackpadScroll; import static com.android.systemui.navigationbar.gestural.Utilities.isTrackpadThreeFingerSwipe; +import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_TOUCHPAD_GESTURES_DISABLED; import static java.util.stream.Collectors.joining; @@ -1021,8 +1022,10 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack if (mIsTrackpadThreeFingerSwipe) { // Trackpad back gestures don't have zones, so we don't need to check if the down // event is within insets. - mAllowGesture = isBackAllowedCommon && isValidTrackpadBackGesture( - true /* isTrackpadEvent */); + boolean trackpadGesturesEnabled = + (mSysUiFlags & SYSUI_STATE_TOUCHPAD_GESTURES_DISABLED) == 0; + mAllowGesture = isBackAllowedCommon && trackpadGesturesEnabled + && isValidTrackpadBackGesture(true /* isTrackpadEvent */); } else { mAllowGesture = isBackAllowedCommon && !mUsingThreeButtonNav && isWithinInsets && isWithinTouchRegion((int) ev.getX(), (int) ev.getY()) diff --git a/packages/SystemUI/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeSceneViewModel.kt b/packages/SystemUI/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeSceneViewModel.kt index f677ec1b31bb..d0c7fbcac189 100644 --- a/packages/SystemUI/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeSceneViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeSceneViewModel.kt @@ -17,41 +17,25 @@ package com.android.systemui.notifications.ui.viewmodel import com.android.compose.animation.scene.Back -import com.android.compose.animation.scene.SceneKey import com.android.compose.animation.scene.Swipe import com.android.compose.animation.scene.UserAction import com.android.compose.animation.scene.UserActionResult import com.android.systemui.dagger.SysUISingleton -import com.android.systemui.dagger.qualifiers.Application -import com.android.systemui.shade.ui.viewmodel.OverlayShadeViewModel +import com.android.systemui.scene.shared.model.SceneFamilies import javax.inject.Inject -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow -import kotlinx.coroutines.flow.map -import kotlinx.coroutines.flow.stateIn +import kotlinx.coroutines.flow.asStateFlow /** Models UI state and handles user input for the Notifications Shade scene. */ @SysUISingleton -class NotificationsShadeSceneViewModel -@Inject -constructor( - @Application private val applicationScope: CoroutineScope, - overlayShadeViewModel: OverlayShadeViewModel, -) { +class NotificationsShadeSceneViewModel @Inject constructor() { val destinationScenes: StateFlow<Map<UserAction, UserActionResult>> = - overlayShadeViewModel.backgroundScene - .map(::destinationScenes) - .stateIn( - scope = applicationScope, - started = SharingStarted.WhileSubscribed(), - initialValue = destinationScenes(overlayShadeViewModel.backgroundScene.value), + MutableStateFlow( + mapOf( + Swipe.Up to SceneFamilies.Home, + Back to SceneFamilies.Home, + ) ) - - private fun destinationScenes(backgroundScene: SceneKey): Map<UserAction, UserActionResult> { - return mapOf( - Swipe.Up to backgroundScene, - Back to backgroundScene, - ) - } + .asStateFlow() } diff --git a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java index 9e313797674d..0a880293ca76 100644 --- a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java +++ b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java @@ -23,7 +23,6 @@ import static android.app.NotificationManager.INTERRUPTION_FILTER_NONE; import static android.app.NotificationManager.INTERRUPTION_FILTER_PRIORITY; import static android.appwidget.AppWidgetProviderInfo.WIDGET_CATEGORY_HOME_SCREEN; import static android.appwidget.AppWidgetProviderInfo.WIDGET_CATEGORY_KEYGUARD; -import static android.appwidget.flags.Flags.drawDataParcel; import static android.appwidget.flags.Flags.generatedPreviews; import static android.content.Intent.ACTION_BOOT_COMPLETED; import static android.content.Intent.ACTION_PACKAGE_ADDED; @@ -72,7 +71,6 @@ import android.content.pm.PackageManager; import android.content.pm.ShortcutInfo; import android.graphics.drawable.Icon; import android.net.Uri; -import android.os.Build; import android.os.Bundle; import android.os.RemoteException; import android.os.ServiceManager; @@ -113,8 +111,6 @@ import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection; import com.android.wm.shell.bubbles.Bubbles; -import java.io.IOException; -import java.io.InputStream; import java.io.PrintWriter; import java.util.ArrayList; import java.util.Arrays; @@ -1456,54 +1452,13 @@ public class PeopleSpaceWidgetManager implements Dumpable { if (DEBUG) { Log.d(TAG, "Updating People Space widget preview for user " + user.getIdentifier()); } - if (!drawDataParcel() || (!Build.IS_USERDEBUG && !Build.IS_ENG)) { - updateGeneratedPreviewForUserInternal(provider, user, - new RemoteViews(mContext.getPackageName(), - R.layout.people_space_placeholder_layout)); - } else { - mBgExecutor.execute(updateGeneratedPreviewFromDrawInstructionsForUser(provider, user)); - } - } - - private void updateGeneratedPreviewForUserInternal(@NonNull final ComponentName provider, - @NonNull final UserHandle user, @NonNull final RemoteViews rv) { boolean success = mAppWidgetManager.setWidgetPreview( provider, WIDGET_CATEGORY_HOME_SCREEN | WIDGET_CATEGORY_KEYGUARD, - rv); + new RemoteViews(mContext.getPackageName(), + R.layout.people_space_placeholder_layout)); if (DEBUG && !success) { Log.d(TAG, "Failed to update generated preview for user " + user.getIdentifier()); } mUpdatedPreviews.put(user.getIdentifier(), success); } - - private Runnable updateGeneratedPreviewFromDrawInstructionsForUser( - @NonNull final ComponentName provider, @NonNull final UserHandle user) { - return () -> { - if (DEBUG) { - Log.d(TAG, "Parsing People Space widget preview from binary for user " - + user.getIdentifier()); - } - if (!generatedPreviews() || mUpdatedPreviews.get(user.getIdentifier()) - || !mUserManager.isUserUnlocked(user)) { - // Conditions may have changed given this is called from background thread - return; - } - try (InputStream is = mContext.getResources().openRawResource(R.raw.widget) - ) { - final byte[] preview = new byte[(int) is.available()]; - final int result = is.read(preview); - if (DEBUG && result == -1) { - Log.d(TAG, "Failed parsing previews from binary for user " - + user.getIdentifier()); - } - updateGeneratedPreviewForUserInternal(provider, user, new RemoteViews( - new RemoteViews.DrawInstructions.Builder( - Collections.singletonList(preview)).build())); - } catch (IOException e) { - if (DEBUG) { - Log.e(TAG, "Failed to generate preview for people widget", e); - } - } - }; - } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java index 9c8c17bb1ca0..abc2b7fcf3ac 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java @@ -113,6 +113,9 @@ public class QSPanel extends LinearLayout implements Tunable { private boolean mSceneContainerEnabled; + @Nullable + private View mMediaViewPlaceHolderForScene; + public QSPanel(Context context, AttributeSet attrs) { super(context, attrs); mUsingMediaPlayer = useQsMediaPlayer(context); @@ -125,7 +128,6 @@ public class QSPanel extends LinearLayout implements Tunable { setOrientation(VERTICAL); mMovableContentStartIndex = getChildCount(); - } void initialize(QSLogger qsLogger, boolean usingMediaPlayer) { @@ -133,7 +135,7 @@ public class QSPanel extends LinearLayout implements Tunable { mUsingMediaPlayer = usingMediaPlayer; mTileLayout = getOrCreateTileLayout(); - if (mUsingMediaPlayer) { + if (mUsingMediaPlayer || SceneContainerFlag.isEnabled()) { mHorizontalLinearLayout = new RemeasuringLinearLayout(mContext); mHorizontalLinearLayout.setOrientation(LinearLayout.HORIZONTAL); mHorizontalLinearLayout.setVisibility( @@ -151,6 +153,13 @@ public class QSPanel extends LinearLayout implements Tunable { lp.setMarginEnd(marginSize); lp.gravity = Gravity.CENTER_VERTICAL; mHorizontalLinearLayout.addView(mHorizontalContentContainer, lp); + if (SceneContainerFlag.isEnabled()) { + int mediaHeight = mContext.getResources() + .getDimensionPixelSize(R.dimen.qs_media_session_height_expanded); + lp = new LayoutParams(0, mediaHeight, 1); + mMediaViewPlaceHolderForScene = new View(mContext); + mHorizontalLinearLayout.addView(mMediaViewPlaceHolderForScene, lp); + } lp = new LayoutParams(LayoutParams.MATCH_PARENT, 0, 1); addView(mHorizontalLinearLayout, lp); @@ -208,10 +217,13 @@ public class QSPanel extends LinearLayout implements Tunable { private void setBrightnessViewMargin() { if (mBrightnessView != null) { MarginLayoutParams lp = (MarginLayoutParams) mBrightnessView.getLayoutParams(); + // For Brightness Slider to extend its boundary to draw focus background + int offset = getResources() + .getDimensionPixelSize(R.dimen.rounded_slider_boundary_offset); lp.topMargin = mContext.getResources() - .getDimensionPixelSize(R.dimen.qs_brightness_margin_top); + .getDimensionPixelSize(R.dimen.qs_brightness_margin_top) - offset; lp.bottomMargin = mContext.getResources() - .getDimensionPixelSize(R.dimen.qs_brightness_margin_bottom); + .getDimensionPixelSize(R.dimen.qs_brightness_margin_bottom) - offset; mBrightnessView.setLayoutParams(lp); } } @@ -383,6 +395,13 @@ public class QSPanel extends LinearLayout implements Tunable { if (mTileLayout != null) { mTileLayout.updateResources(); } + + if (mMediaViewPlaceHolderForScene != null) { + ViewGroup.LayoutParams lp = mMediaViewPlaceHolderForScene.getLayoutParams(); + lp.height = mContext.getResources() + .getDimensionPixelSize(R.dimen.qs_media_session_height_expanded); + mMediaViewPlaceHolderForScene.setLayoutParams(lp); + } } protected void updatePadding() { @@ -417,7 +436,8 @@ public class QSPanel extends LinearLayout implements Tunable { } private void updateHorizontalLinearLayoutMargins() { - if (mUsingMediaPlayer && mHorizontalLinearLayout != null && !displayMediaMarginsOnMedia()) { + if ((mUsingMediaPlayer || SceneContainerFlag.isEnabled()) && mHorizontalLinearLayout != null + && !displayMediaMarginsOnMedia()) { LayoutParams lp = (LayoutParams) mHorizontalLinearLayout.getLayoutParams(); lp.bottomMargin = Math.max(mMediaTotalBottomMargin - getPaddingBottom(), 0); mHorizontalLinearLayout.setLayoutParams(lp); @@ -632,6 +652,7 @@ public class QSPanel extends LinearLayout implements Tunable { // using media, the parent should always be this. ViewGroup newParent = horizontal && mUsingMediaPlayer ? mHorizontalContentContainer : this; + if (SceneContainerFlag.isEnabled()) return; switchAllContentToParent(newParent, mTileLayout); reAttachMediaHost(mediaHostView, horizontal); if (needsDynamicRowsAndColumns()) { @@ -647,6 +668,19 @@ public class QSPanel extends LinearLayout implements Tunable { void setColumnRowLayout(boolean withMedia) { mTileLayout.setMinRows(withMedia ? 2 : 1); mTileLayout.setMaxColumns(withMedia ? 2 : 4); + placeTileLayoutForScene(withMedia); + } + + protected void placeTileLayoutForScene(boolean withMedia) { + // The tile layout should be reparented if horizontal and we are using media. If not + // using media, the parent should always be this. + ViewGroup newParent = withMedia ? mHorizontalContentContainer : this; + if (mTileLayout != null && ((View) mTileLayout).getParent() != newParent) { + switchAllContentToParent(newParent, mTileLayout); + } + if (mHorizontalLinearLayout != null) { + mHorizontalLinearLayout.setVisibility(withMedia ? View.VISIBLE : View.GONE); + } } private void updateMargins(ViewGroup mediaHostView) { @@ -699,6 +733,12 @@ public class QSPanel extends LinearLayout implements Tunable { mCanCollapse = canCollapse; } + @Nullable + @VisibleForTesting + View getMediaPlaceholder() { + return mMediaViewPlaceHolderForScene; + } + public interface QSTileLayout { /** */ default void saveInstanceState(Bundle outState) {} diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java index 3b5cc61057e6..13cedc2d2f3b 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java @@ -456,7 +456,7 @@ public abstract class QSPanelControllerBase<T extends QSPanel> extends ViewContr boolean switchTileLayout(boolean force) { /* Whether or not the panel currently contains a media player. */ boolean horizontal = shouldUseHorizontalLayout(); - if (horizontal != mUsingHorizontalLayout || force) { + if ((!SceneContainerFlag.isEnabled() && horizontal != mUsingHorizontalLayout) || force) { mQSLogger.logSwitchTileLayout(horizontal, mUsingHorizontalLayout, force, mView.getDumpableTag()); mUsingHorizontalLayout = horizontal; @@ -470,7 +470,7 @@ public abstract class QSPanelControllerBase<T extends QSPanel> extends ViewContr return false; } - void setLayoutForMediaInScene() { + private void setLayoutForMediaInScene() { boolean withMedia = shouldUseHorizontalInScene(); mView.setColumnRowLayout(withMedia); } diff --git a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSModule.java b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSModule.java index ea89be61d773..b705a0389300 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSModule.java +++ b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSModule.java @@ -21,7 +21,6 @@ import static com.android.systemui.qs.dagger.QSFlagsModule.RBC_AVAILABLE; import android.content.Context; import android.os.Handler; -import com.android.systemui.bluetooth.qsdialog.BluetoothTileDialogModule; import com.android.systemui.dagger.NightDisplayListenerModule; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Background; @@ -61,7 +60,6 @@ import javax.inject.Named; */ @Module(subcomponents = {QSFragmentComponent.class, QSSceneComponent.class}, includes = { - BluetoothTileDialogModule.class, MediaModule.class, PanelsModule.class, QSExternalModule.class, diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java b/packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java index d457e88fcf14..fb47b4053b60 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java +++ b/packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java @@ -332,7 +332,7 @@ public class TileServices extends IQSService.Stub { if (info.applicationInfo.isSystemApp()) { final StatusBarIcon statusIcon = icon != null ? new StatusBarIcon(userHandle, packageName, icon, 0, 0, - contentDescription) + contentDescription, StatusBarIcon.Type.SystemIcon) : null; final String slot = getStatusBarIconSlotName(componentName); mMainHandler.post(new Runnable() { diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/PartitionedGridLayout.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/PartitionedGridLayout.kt index d60076745a78..9233e76f09e6 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/PartitionedGridLayout.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/PartitionedGridLayout.kt @@ -16,7 +16,6 @@ package com.android.systemui.qs.panels.ui.compose -import androidx.compose.foundation.border import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column @@ -40,6 +39,13 @@ import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.rememberUpdatedState import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.drawWithContent +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.Path +import androidx.compose.ui.graphics.PathEffect +import androidx.compose.ui.graphics.Shape +import androidx.compose.ui.graphics.addOutline +import androidx.compose.ui.graphics.drawscope.Stroke import androidx.compose.ui.res.dimensionResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.Dp @@ -268,10 +274,9 @@ class PartitionedGridLayout @Inject constructor(private val viewModel: Partition private fun CurrentTilesContainer(content: @Composable () -> Unit) { Box( Modifier.fillMaxWidth() - .border( - width = 1.dp, - color = MaterialTheme.colorScheme.onBackground, - shape = RoundedCornerShape(dimensionResource(R.dimen.qs_corner_radius)) + .dashedBorder( + color = MaterialTheme.colorScheme.onBackground.copy(alpha = .5f), + shape = Dimensions.ContainerShape, ) .padding(dimensionResource(R.dimen.qs_tile_margin_vertical)) ) { @@ -284,9 +289,9 @@ class PartitionedGridLayout @Inject constructor(private val viewModel: Partition Box( Modifier.fillMaxWidth() .background( - color = MaterialTheme.colorScheme.surfaceVariant, + color = MaterialTheme.colorScheme.background, alpha = { 1f }, - shape = RoundedCornerShape(dimensionResource(R.dimen.qs_corner_radius)) + shape = Dimensions.ContainerShape, ) .padding(dimensionResource(R.dimen.qs_tile_margin_vertical)) ) { @@ -305,4 +310,27 @@ class PartitionedGridLayout @Inject constructor(private val viewModel: Partition item(span = { GridItemSpan(maxCurrentLineSpan) }) { Spacer(Modifier) } } } + + private fun Modifier.dashedBorder( + color: Color, + shape: Shape, + ): Modifier { + return this.drawWithContent { + val outline = shape.createOutline(size, layoutDirection, this) + val path = Path() + path.addOutline(outline) + val stroke = + Stroke( + width = 1.dp.toPx(), + pathEffect = PathEffect.dashPathEffect(floatArrayOf(10f, 10f)) + ) + this.drawContent() + drawPath(path = path, style = stroke, color = color) + } + } + + private object Dimensions { + // Corner radius is half the height of a tile + padding + val ContainerShape = RoundedCornerShape(48.dp) + } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/Tile.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/Tile.kt index f776bf08c9e4..bbb98d3ca277 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/Tile.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/Tile.kt @@ -14,9 +14,13 @@ * limitations under the License. */ +@file:OptIn(ExperimentalFoundationApi::class) + package com.android.systemui.qs.panels.ui.compose import android.graphics.drawable.Animatable +import android.service.quicksettings.Tile.STATE_ACTIVE +import android.service.quicksettings.Tile.STATE_INACTIVE import android.text.TextUtils import androidx.appcompat.content.res.AppCompatResources import androidx.compose.animation.graphics.ExperimentalAnimationGraphicsApi @@ -25,17 +29,17 @@ import androidx.compose.animation.graphics.res.rememberAnimatedVectorPainter import androidx.compose.animation.graphics.vector.AnimatedImageVector import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.Image -import androidx.compose.foundation.background import androidx.compose.foundation.basicMarquee -import androidx.compose.foundation.clickable import androidx.compose.foundation.combinedClickable import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement.spacedBy import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.BoxScope import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.aspectRatio import androidx.compose.foundation.layout.fillMaxHeight -import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size @@ -44,11 +48,7 @@ import androidx.compose.foundation.lazy.grid.GridItemSpan import androidx.compose.foundation.lazy.grid.LazyGridScope import androidx.compose.foundation.lazy.grid.LazyVerticalGrid import androidx.compose.foundation.shape.CircleShape -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.Remove -import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect @@ -65,7 +65,6 @@ import androidx.compose.ui.graphics.ColorFilter import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.dimensionResource import androidx.compose.ui.res.stringResource -import androidx.compose.ui.semantics.contentDescription import androidx.compose.ui.semantics.onClick import androidx.compose.ui.semantics.semantics import androidx.compose.ui.semantics.stateDescription @@ -75,14 +74,13 @@ import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.android.compose.animation.Expandable -import com.android.compose.theme.colorAttr +import com.android.systemui.animation.Expandable 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.qs.panels.ui.viewmodel.ActiveTileColorAttributes +import com.android.systemui.plugins.qs.QSTile import com.android.systemui.qs.panels.ui.viewmodel.AvailableEditActions import com.android.systemui.qs.panels.ui.viewmodel.EditTileViewModel -import com.android.systemui.qs.panels.ui.viewmodel.TileColorAttributes import com.android.systemui.qs.panels.ui.viewmodel.TileUiState import com.android.systemui.qs.panels.ui.viewmodel.TileViewModel import com.android.systemui.qs.panels.ui.viewmodel.toUiState @@ -90,13 +88,14 @@ import com.android.systemui.qs.pipeline.domain.interactor.CurrentTilesInteractor import com.android.systemui.qs.pipeline.shared.TileSpec import com.android.systemui.qs.tileimpl.QSTileImpl import com.android.systemui.res.R +import java.util.function.Supplier import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.delay import kotlinx.coroutines.flow.mapLatest object TileType -@OptIn(ExperimentalCoroutinesApi::class, ExperimentalFoundationApi::class) +@OptIn(ExperimentalCoroutinesApi::class) @Composable fun Tile( tile: TileViewModel, @@ -108,46 +107,145 @@ fun Tile( tile.state .mapLatest { it.toUiState() } .collectAsStateWithLifecycle(tile.currentState.toUiState()) - val context = LocalContext.current + val colors = TileDefaults.getColorForState(state.state) - Expandable( - color = colorAttr(state.colors.background), - shape = RoundedCornerShape(dimensionResource(R.dimen.qs_corner_radius)), + TileContainer( + colors = colors, + showLabels = showLabels, + label = state.label.toString(), + iconOnly = iconOnly, + onClick = tile::onClick, + onLongClick = tile::onLongClick, + modifier = modifier, ) { - Row( - modifier = - modifier - .combinedClickable( - onClick = { tile.onClick(it) }, - onLongClick = { tile.onLongClick(it) } - ) - .tileModifier(state.colors), - verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = tileHorizontalArrangement(iconOnly), - ) { - val icon = - remember(state.icon) { - state.icon.get().let { - if (it is QSTileImpl.ResourceIcon) { - Icon.Resource(it.resId, null) - } else { - Icon.Loaded(it.getDrawable(context), null) - } - } - } - TileContent( + val icon = getTileIcon(icon = state.icon) + if (iconOnly) { + TileIcon(icon = icon, color = colors.icon, modifier = Modifier.align(Alignment.Center)) + } else { + LargeTileContent( label = state.label.toString(), secondaryLabel = state.secondaryLabel.toString(), icon = icon, - colors = state.colors, - iconOnly = iconOnly, - showLabels = showLabels, + colors = colors, + onClick = tile::onSecondaryClick, + onLongClick = tile::onLongClick, + ) + } + } +} + +@Composable +private fun TileContainer( + colors: TileColors, + showLabels: Boolean, + label: String, + iconOnly: Boolean, + clickEnabled: Boolean = true, + onClick: (Expandable) -> Unit = {}, + onLongClick: (Expandable) -> Unit = {}, + modifier: Modifier = Modifier, + content: @Composable BoxScope.() -> Unit, +) { + Column( + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = + spacedBy(dimensionResource(id = R.dimen.qs_label_container_margin), Alignment.Top), + modifier = modifier, + ) { + val backgroundColor = + if (iconOnly) { + colors.iconBackground + } else { + colors.background + } + Expandable( + color = backgroundColor, + shape = TileDefaults.TileShape, + modifier = + Modifier.height(dimensionResource(id = R.dimen.qs_tile_height)) + .clip(TileDefaults.TileShape) + ) { + Box( + modifier = + Modifier.fillMaxSize() + .combinedClickable( + enabled = clickEnabled, + onClick = { onClick(it) }, + onLongClick = { onLongClick(it) } + ) + .tilePadding(), + ) { + content() + } + } + + if (showLabels && iconOnly) { + Text( + label, + maxLines = 2, + color = colors.label, + overflow = TextOverflow.Ellipsis, + textAlign = TextAlign.Center, ) } } } @Composable +private fun LargeTileContent( + label: String, + secondaryLabel: String?, + icon: Icon, + colors: TileColors, + clickEnabled: Boolean = true, + onClick: (Expandable) -> Unit = {}, + onLongClick: (Expandable) -> Unit = {}, +) { + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = tileHorizontalArrangement() + ) { + Expandable( + color = colors.iconBackground, + shape = TileDefaults.TileShape, + modifier = Modifier.fillMaxHeight().aspectRatio(1f) + ) { + Box( + modifier = + Modifier.fillMaxSize() + .clip(TileDefaults.TileShape) + .combinedClickable( + enabled = clickEnabled, + onClick = { onClick(it) }, + onLongClick = { onLongClick(it) } + ) + ) { + TileIcon( + icon = icon, + color = colors.icon, + modifier = Modifier.align(Alignment.Center) + ) + } + } + + Column(verticalArrangement = Arrangement.Center, modifier = Modifier.fillMaxHeight()) { + Text( + label, + color = colors.label, + modifier = Modifier.basicMarquee(), + ) + if (!TextUtils.isEmpty(secondaryLabel)) { + Text( + secondaryLabel ?: "", + color = colors.secondaryLabel, + modifier = Modifier.basicMarquee(), + ) + } + } + } +} + +@Composable fun TileLazyGrid( modifier: Modifier = Modifier, columns: GridCells, @@ -245,41 +343,19 @@ fun LazyGridScope.editTiles( "" } - Box( + val iconOnly = isIconOnly(viewModel.tileSpec) + val tileHeight = tileHeight(iconOnly && showLabels) + EditTile( + tileViewModel = viewModel, + iconOnly = iconOnly, + showLabels = showLabels, + clickEnabled = canClick, + onClick = { onClick.invoke(viewModel.tileSpec) }, modifier = - Modifier.clickable(enabled = canClick) { onClick.invoke(viewModel.tileSpec) } - .animateItem() - .semantics { - onClick(onClickActionName) { false } - this.stateDescription = stateDescription - } - ) { - val iconOnly = isIconOnly(viewModel.tileSpec) - val tileHeight = tileHeight(iconOnly && showLabels) - EditTile( - tileViewModel = viewModel, - iconOnly = iconOnly, - showLabels = showLabels, - modifier = Modifier.height(tileHeight) - ) - if (canClick) { - Badge(clickAction, Modifier.align(Alignment.TopEnd)) - } - } - } -} - -@Composable -fun Badge(action: ClickAction, modifier: Modifier = Modifier) { - Box(modifier = modifier.size(16.dp).background(Color.Cyan, shape = CircleShape)) { - Icon( - imageVector = - when (action) { - ClickAction.ADD -> Icons.Filled.Add - ClickAction.REMOVE -> Icons.Filled.Remove - }, - "", - tint = Color.Black, + Modifier.height(tileHeight).animateItem().semantics { + onClick(onClickActionName) { false } + this.stateDescription = stateDescription + } ) } } @@ -289,25 +365,40 @@ fun EditTile( tileViewModel: EditTileViewModel, iconOnly: Boolean, showLabels: Boolean, + clickEnabled: Boolean, + onClick: () -> Unit, modifier: Modifier = Modifier, ) { val label = tileViewModel.label.load() ?: tileViewModel.tileSpec.spec - val colors = ActiveTileColorAttributes + val colors = TileDefaults.inactiveTileColors() - Row( - modifier = modifier.tileModifier(colors).semantics { this.contentDescription = label }, - verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = tileHorizontalArrangement(iconOnly) + TileContainer( + colors = colors, + showLabels = showLabels, + label = label, + iconOnly = iconOnly, + clickEnabled = clickEnabled, + onClick = { onClick() }, + onLongClick = { onClick() }, + modifier = modifier, ) { - TileContent( - label = label, - secondaryLabel = tileViewModel.appName?.load(), - colors = colors, - icon = tileViewModel.icon, - iconOnly = iconOnly, - showLabels = showLabels, - animateIconToEnd = true, - ) + if (iconOnly) { + TileIcon( + icon = tileViewModel.icon, + color = colors.icon, + modifier = Modifier.align(Alignment.Center) + ) + } else { + LargeTileContent( + label = label, + secondaryLabel = tileViewModel.appName?.load(), + icon = tileViewModel.icon, + colors = colors, + clickEnabled = clickEnabled, + onClick = { onClick() }, + onLongClick = { onClick() }, + ) + } } } @@ -316,14 +407,27 @@ enum class ClickAction { REMOVE, } +@Composable +private fun getTileIcon(icon: Supplier<QSTile.Icon>): Icon { + val context = LocalContext.current + return icon.get().let { + if (it is QSTileImpl.ResourceIcon) { + Icon.Resource(it.resId, null) + } else { + Icon.Loaded(it.getDrawable(context), null) + } + } +} + @OptIn(ExperimentalAnimationGraphicsApi::class) @Composable private fun TileIcon( icon: Icon, color: Color, animateToEnd: Boolean = false, + modifier: Modifier = Modifier, ) { - val modifier = Modifier.size(dimensionResource(id = R.dimen.qs_icon_size)) + val iconModifier = modifier.size(dimensionResource(id = R.dimen.qs_icon_size)) val context = LocalContext.current val loadedDrawable = remember(icon, context) { @@ -336,7 +440,7 @@ private fun TileIcon( Icon( icon = icon, tint = color, - modifier = modifier, + modifier = iconModifier, ) } else if (icon is Icon.Resource) { val image = AnimatedImageVector.animatedVectorResource(id = icon.res) @@ -355,88 +459,81 @@ private fun TileIcon( painter = painter, contentDescription = null, colorFilter = ColorFilter.tint(color = color), - modifier = modifier + modifier = iconModifier ) } } @Composable -private fun Modifier.tileModifier(colors: TileColorAttributes): Modifier { - return fillMaxWidth() - .clip(RoundedCornerShape(dimensionResource(R.dimen.qs_corner_radius))) - .background(colorAttr(colors.background)) - .padding(horizontal = dimensionResource(id = R.dimen.qs_label_container_margin)) +private fun Modifier.tilePadding(): Modifier { + return padding(dimensionResource(id = R.dimen.qs_label_container_margin)) } @Composable -private fun tileHorizontalArrangement(iconOnly: Boolean): Arrangement.Horizontal { - val horizontalAlignment = - if (iconOnly) { - Alignment.CenterHorizontally - } else { - Alignment.Start - } +private fun tileHorizontalArrangement(): Arrangement.Horizontal { return spacedBy( space = dimensionResource(id = R.dimen.qs_label_container_margin), - alignment = horizontalAlignment + alignment = Alignment.Start ) } @Composable -private fun TileContent( - label: String, - secondaryLabel: String?, - icon: Icon, - colors: TileColorAttributes, - iconOnly: Boolean, - showLabels: Boolean = false, - animateIconToEnd: Boolean = false, -) { - Column( - horizontalAlignment = Alignment.CenterHorizontally, - verticalArrangement = Arrangement.Center, - modifier = Modifier.fillMaxHeight() - ) { - TileIcon(icon, colorAttr(colors.icon), animateIconToEnd) - - if (iconOnly && showLabels) { - Text( - label, - maxLines = 2, - color = colorAttr(colors.label), - overflow = TextOverflow.Ellipsis, - textAlign = TextAlign.Center, - ) - } - } - - if (!iconOnly) { - Column(verticalArrangement = Arrangement.Center, modifier = Modifier.fillMaxHeight()) { - Text( - label, - color = colorAttr(colors.label), - modifier = Modifier.basicMarquee(), - ) - if (!TextUtils.isEmpty(secondaryLabel)) { - Text( - secondaryLabel ?: "", - color = colorAttr(colors.secondaryLabel), - modifier = Modifier.basicMarquee(), - ) - } - } - } -} - -@Composable fun tileHeight(iconWithLabel: Boolean = false): Dp { return if (iconWithLabel) { - TileDimensions.IconTileWithLabelHeight + TileDefaults.IconTileWithLabelHeight } else { dimensionResource(id = R.dimen.qs_tile_height) } } -private object TileDimensions { - val IconTileWithLabelHeight = 100.dp +private data class TileColors( + val background: Color, + val iconBackground: Color, + val label: Color, + val secondaryLabel: Color, + val icon: Color, +) + +private object TileDefaults { + val TileShape = CircleShape + val IconTileWithLabelHeight = 140.dp + + @Composable + fun activeTileColors(): TileColors = + TileColors( + background = MaterialTheme.colorScheme.surfaceVariant, + iconBackground = MaterialTheme.colorScheme.primary, + label = MaterialTheme.colorScheme.onSurfaceVariant, + secondaryLabel = MaterialTheme.colorScheme.onSurfaceVariant, + icon = MaterialTheme.colorScheme.onPrimary, + ) + + @Composable + fun inactiveTileColors(): TileColors = + TileColors( + background = MaterialTheme.colorScheme.surfaceVariant, + iconBackground = MaterialTheme.colorScheme.surfaceVariant, + label = MaterialTheme.colorScheme.onSurfaceVariant, + secondaryLabel = MaterialTheme.colorScheme.onSurfaceVariant, + icon = MaterialTheme.colorScheme.onSurfaceVariant, + ) + + @Composable + fun unavailableTileColors(): TileColors = + TileColors( + background = MaterialTheme.colorScheme.surface, + iconBackground = MaterialTheme.colorScheme.surface, + label = MaterialTheme.colorScheme.onSurface, + secondaryLabel = MaterialTheme.colorScheme.onSurface, + icon = MaterialTheme.colorScheme.onSurface, + ) + + @Composable + fun getColorForState(state: Int): TileColors { + return when (state) { + STATE_ACTIVE -> activeTileColors() + STATE_INACTIVE -> inactiveTileColors() + else -> unavailableTileColors() + } + } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/TileColorAttributes.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/TileColorAttributes.kt deleted file mode 100644 index 1290bf34d1f1..000000000000 --- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/TileColorAttributes.kt +++ /dev/null @@ -1,61 +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.qs.panels.ui.viewmodel - -import android.service.quicksettings.Tile -import androidx.annotation.AttrRes -import com.android.systemui.plugins.qs.QSTile -import com.android.systemui.res.R - -data class TileColorAttributes( - @AttrRes val background: Int = 0, - @AttrRes val label: Int = 0, - @AttrRes val secondaryLabel: Int = 0, - @AttrRes val icon: Int = 0, -) - -val ActiveTileColorAttributes = - TileColorAttributes( - background = R.attr.shadeActive, - label = R.attr.onShadeActive, - secondaryLabel = R.attr.onShadeActiveVariant, - icon = R.attr.onShadeActive, - ) - -val InactiveTileColorAttributes = - TileColorAttributes( - background = R.attr.shadeInactive, - label = R.attr.onShadeInactive, - secondaryLabel = R.attr.onShadeInactiveVariant, - icon = R.attr.onShadeInactiveVariant, - ) - -val UnavailableTileColorAttributes = - TileColorAttributes( - background = R.attr.shadeDisabled, - label = R.attr.outline, - secondaryLabel = R.attr.outline, - icon = R.attr.outline, - ) - -fun QSTile.State.colors(): TileColorAttributes = - when (state) { - Tile.STATE_UNAVAILABLE -> UnavailableTileColorAttributes - Tile.STATE_ACTIVE -> ActiveTileColorAttributes - Tile.STATE_INACTIVE -> InactiveTileColorAttributes - else -> TileColorAttributes() - } 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 58d07c368456..578a292deb7c 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 @@ -22,7 +22,7 @@ import java.util.function.Supplier data class TileUiState( val label: CharSequence, val secondaryLabel: CharSequence, - val colors: TileColorAttributes, + val state: Int, val icon: Supplier<QSTile.Icon>, ) @@ -30,7 +30,7 @@ fun QSTile.State.toUiState(): TileUiState { return TileUiState( label ?: "", secondaryLabel ?: "", - colors(), + state, icon?.let { Supplier { icon } } ?: iconSupplier, ) } diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/TileViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/TileViewModel.kt index a6cfa75a7583..7505b90ee844 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/TileViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/TileViewModel.kt @@ -48,6 +48,10 @@ class TileViewModel(private val tile: QSTile, val spec: TileSpec) { tile.longClick(expandable) } + fun onSecondaryClick(expandable: Expandable?) { + tile.secondaryClick(expandable) + } + fun startListening(token: Any) = tile.setListening(token, true) fun stopListening(token: Any) = tile.setListening(token, false) diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt index 762dacdfdddb..b1b67cf51d20 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt @@ -40,26 +40,29 @@ import android.view.Gravity import android.view.LayoutInflater import android.view.MotionEvent import android.view.View +import android.view.ViewConfiguration import android.view.ViewGroup import android.view.accessibility.AccessibilityEvent import android.view.accessibility.AccessibilityNodeInfo +import android.view.animation.AccelerateDecelerateInterpolator import android.widget.Button import android.widget.ImageView import android.widget.LinearLayout import android.widget.Switch import android.widget.TextView import androidx.annotation.VisibleForTesting +import androidx.core.animation.doOnCancel +import androidx.core.animation.doOnEnd +import androidx.core.animation.doOnStart import androidx.core.graphics.drawable.updateBounds import com.android.app.tracing.traceSection import com.android.settingslib.Utils import com.android.systemui.Flags -import com.android.systemui.Flags.quickSettingsVisualHapticsLongpress import com.android.systemui.FontSizeUtils import com.android.systemui.animation.Expandable import com.android.systemui.animation.LaunchableView import com.android.systemui.animation.LaunchableViewDelegate import com.android.systemui.haptics.qs.QSLongPressEffect -import com.android.systemui.haptics.qs.QSLongPressEffectViewBinder import com.android.systemui.plugins.qs.QSIconView import com.android.systemui.plugins.qs.QSTile import com.android.systemui.plugins.qs.QSTile.AdapterState @@ -67,11 +70,13 @@ import com.android.systemui.plugins.qs.QSTileView import com.android.systemui.qs.logging.QSLogger import com.android.systemui.qs.tileimpl.QSIconViewImpl.QS_ANIM_LENGTH import com.android.systemui.res.R -import kotlinx.coroutines.DisposableHandle import java.util.Objects private const val TAG = "QSTileViewImpl" -open class QSTileViewImpl @JvmOverloads constructor( + +open class QSTileViewImpl +@JvmOverloads +constructor( context: Context, private val collapsed: Boolean = false, private val longPressEffect: QSLongPressEffect? = null, @@ -85,12 +90,9 @@ open class QSTileViewImpl @JvmOverloads constructor( private const val CHEVRON_NAME = "chevron" private const val OVERLAY_NAME = "overlay" const val UNAVAILABLE_ALPHA = 0.3f - @VisibleForTesting - internal const val TILE_STATE_RES_PREFIX = "tile_states_" - @VisibleForTesting - internal const val LONG_PRESS_EFFECT_WIDTH_SCALE = 1.1f - @VisibleForTesting - internal const val LONG_PRESS_EFFECT_HEIGHT_SCALE = 1.2f + @VisibleForTesting internal const val TILE_STATE_RES_PREFIX = "tile_states_" + @VisibleForTesting internal const val LONG_PRESS_EFFECT_WIDTH_SCALE = 1.1f + @VisibleForTesting internal const val LONG_PRESS_EFFECT_HEIGHT_SCALE = 1.2f } private val icon: QSIconViewImpl = QSIconViewImpl(context) @@ -118,22 +120,25 @@ open class QSTileViewImpl @JvmOverloads constructor( private val colorInactive = Utils.getColorAttrDefaultColor(context, R.attr.shadeInactive) private val colorUnavailable = Utils.getColorAttrDefaultColor(context, R.attr.shadeDisabled) - private val overlayColorActive = Utils.applyAlpha( - /* alpha= */ 0.11f, - Utils.getColorAttrDefaultColor(context, R.attr.onShadeActive)) - private val overlayColorInactive = Utils.applyAlpha( - /* alpha= */ 0.08f, - Utils.getColorAttrDefaultColor(context, R.attr.onShadeInactive)) + private val overlayColorActive = + Utils.applyAlpha( + /* alpha= */ 0.11f, + Utils.getColorAttrDefaultColor(context, R.attr.onShadeActive) + ) + private val overlayColorInactive = + Utils.applyAlpha( + /* alpha= */ 0.08f, + Utils.getColorAttrDefaultColor(context, R.attr.onShadeInactive) + ) private val colorLabelActive = Utils.getColorAttrDefaultColor(context, R.attr.onShadeActive) private val colorLabelInactive = Utils.getColorAttrDefaultColor(context, R.attr.onShadeInactive) - private val colorLabelUnavailable = - Utils.getColorAttrDefaultColor(context, R.attr.outline) + private val colorLabelUnavailable = Utils.getColorAttrDefaultColor(context, R.attr.outline) private val colorSecondaryLabelActive = Utils.getColorAttrDefaultColor(context, R.attr.onShadeActiveVariant) private val colorSecondaryLabelInactive = - Utils.getColorAttrDefaultColor(context, R.attr.onShadeInactiveVariant) + Utils.getColorAttrDefaultColor(context, R.attr.onShadeInactiveVariant) private val colorSecondaryLabelUnavailable = Utils.getColorAttrDefaultColor(context, R.attr.outline) @@ -145,9 +150,7 @@ open class QSTileViewImpl @JvmOverloads constructor( private lateinit var chevronView: ImageView private var mQsLogger: QSLogger? = null - /** - * Controls if tile background is set to a [RippleDrawable] see [setClickable] - */ + /** Controls if tile background is set to a [RippleDrawable] see [setClickable] */ protected var showRippleEffect = true private lateinit var qsTileBackground: RippleDrawable @@ -159,20 +162,21 @@ open class QSTileViewImpl @JvmOverloads constructor( private var backgroundColor: Int = 0 private var backgroundOverlayColor: Int = 0 - private val singleAnimator: ValueAnimator = ValueAnimator().apply { - setDuration(QS_ANIM_LENGTH) - addUpdateListener { animation -> - setAllColors( - // These casts will throw an exception if some property is missing. We should - // always have all properties. - animation.getAnimatedValue(BACKGROUND_NAME) as Int, - animation.getAnimatedValue(LABEL_NAME) as Int, - animation.getAnimatedValue(SECONDARY_LABEL_NAME) as Int, - animation.getAnimatedValue(CHEVRON_NAME) as Int, - animation.getAnimatedValue(OVERLAY_NAME) as Int, - ) + private val singleAnimator: ValueAnimator = + ValueAnimator().apply { + setDuration(QS_ANIM_LENGTH) + addUpdateListener { animation -> + setAllColors( + // These casts will throw an exception if some property is missing. We should + // always have all properties. + animation.getAnimatedValue(BACKGROUND_NAME) as Int, + animation.getAnimatedValue(LABEL_NAME) as Int, + animation.getAnimatedValue(SECONDARY_LABEL_NAME) as Int, + animation.getAnimatedValue(CHEVRON_NAME) as Int, + animation.getAnimatedValue(OVERLAY_NAME) as Int, + ) + } } - } private var accessibilityClass: String? = null private var stateDescriptionDeltas: CharSequence? = null @@ -180,32 +184,37 @@ open class QSTileViewImpl @JvmOverloads constructor( private var tileState = false private var lastState = INVALID private var lastIconTint = 0 - private val launchableViewDelegate = LaunchableViewDelegate( - this, - superSetVisibility = { super.setVisibility(it) }, - ) + private val launchableViewDelegate = + LaunchableViewDelegate( + this, + superSetVisibility = { super.setVisibility(it) }, + ) private var lastDisabledByPolicy = false private val locInScreen = IntArray(2) /** Visuo-haptic long-press effects */ + private var longPressEffectAnimator: ValueAnimator? = null var haveLongPressPropertiesBeenReset = true private set + private var paddingForLaunch = Rect() private var initialLongPressProperties: QSLongPressProperties? = null private var finalLongPressProperties: QSLongPressProperties? = null private val colorEvaluator = ArgbEvaluator.getInstance() val isLongPressEffectInitialized: Boolean get() = longPressEffect?.hasInitialized == true - private var longPressEffectHandle: DisposableHandle? = null - val isLongPressEffectBound: Boolean - get() = longPressEffectHandle != null + + val areLongPressEffectPropertiesSet: Boolean + get() = initialLongPressProperties != null && finalLongPressProperties != null init { val typedValue = TypedValue() if (!getContext().theme.resolveAttribute(R.attr.isQsTheme, typedValue, true)) { - throw IllegalStateException("QSViewImpl must be inflated with a theme that contains " + - "Theme.SystemUI.QuickSettings") + throw IllegalStateException( + "QSViewImpl must be inflated with a theme that contains " + + "Theme.SystemUI.QuickSettings" + ) } setId(generateViewId()) orientation = LinearLayout.HORIZONTAL @@ -263,13 +272,9 @@ open class QSTileViewImpl @JvmOverloads constructor( setPaddingRelative(startPadding, padding, padding, padding) val labelMargin = resources.getDimensionPixelSize(R.dimen.qs_label_container_margin) - (labelContainer.layoutParams as MarginLayoutParams).apply { - marginStart = labelMargin - } + (labelContainer.layoutParams as MarginLayoutParams).apply { marginStart = labelMargin } - (sideView.layoutParams as MarginLayoutParams).apply { - marginStart = labelMargin - } + (sideView.layoutParams as MarginLayoutParams).apply { marginStart = labelMargin } (chevronView.layoutParams as MarginLayoutParams).apply { height = iconSize width = iconSize @@ -287,8 +292,9 @@ open class QSTileViewImpl @JvmOverloads constructor( } private fun createAndAddLabels() { - labelContainer = LayoutInflater.from(context) - .inflate(R.layout.qs_tile_label, this, false) as IgnorableChildLinearLayout + labelContainer = + LayoutInflater.from(context).inflate(R.layout.qs_tile_label, this, false) + as IgnorableChildLinearLayout label = labelContainer.requireViewById(R.id.tile_label) secondaryLabel = labelContainer.requireViewById(R.id.app_label) if (collapsed) { @@ -306,8 +312,9 @@ open class QSTileViewImpl @JvmOverloads constructor( } private fun createAndAddSideView() { - sideView = LayoutInflater.from(context) - .inflate(R.layout.qs_tile_side_icon, this, false) as ViewGroup + sideView = + LayoutInflater.from(context).inflate(R.layout.qs_tile_side_icon, this, false) + as ViewGroup customDrawableView = sideView.requireViewById(R.id.customDrawable) chevronView = sideView.requireViewById(R.id.chevron) setChevronColor(getChevronColorForState(QSTile.State.DEFAULT_STATE)) @@ -315,11 +322,12 @@ open class QSTileViewImpl @JvmOverloads constructor( } private fun createTileBackground(): Drawable { - qsTileBackground = if (Flags.qsTileFocusState()) { - mContext.getDrawable(R.drawable.qs_tile_background_flagged) as RippleDrawable - } else { - mContext.getDrawable(R.drawable.qs_tile_background) as RippleDrawable - } + qsTileBackground = + if (Flags.qsTileFocusState()) { + mContext.getDrawable(R.drawable.qs_tile_background_flagged) as RippleDrawable + } else { + mContext.getDrawable(R.drawable.qs_tile_background) as RippleDrawable + } qsTileFocusBackground = mContext.getDrawable(R.drawable.qs_tile_focused_background)!! backgroundDrawable = qsTileBackground.findDrawableByLayerId(R.id.background) as LayerDrawable @@ -334,21 +342,21 @@ open class QSTileViewImpl @JvmOverloads constructor( override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) { super.onLayout(changed, l, t, r, b) updateHeight() - maybeUpdateLongPressEffectDimensions() + maybeUpdateLongPressEffectWidth(measuredWidth.toFloat()) } - private fun maybeUpdateLongPressEffectDimensions() { + private fun maybeUpdateLongPressEffectWidth(width: Float) { if (!isLongClickable || longPressEffect == null) return - val actualHeight = if (heightOverride != HeightOverrideable.NO_OVERRIDE) { - heightOverride - } else { - measuredHeight - } - initialLongPressProperties?.height = actualHeight.toFloat() - initialLongPressProperties?.width = measuredWidth.toFloat() - finalLongPressProperties?.height = LONG_PRESS_EFFECT_HEIGHT_SCALE * actualHeight - finalLongPressProperties?.width = LONG_PRESS_EFFECT_WIDTH_SCALE * measuredWidth + initialLongPressProperties?.width = width + finalLongPressProperties?.width = LONG_PRESS_EFFECT_WIDTH_SCALE * width + } + + private fun maybeUpdateLongPressEffectHeight(height: Float) { + if (!isLongClickable || longPressEffect == null) return + + initialLongPressProperties?.height = height + finalLongPressProperties?.height = LONG_PRESS_EFFECT_HEIGHT_SCALE * height } override fun onFocusChanged(gainFocus: Boolean, direction: Int, previouslyFocusedRect: Rect?) { @@ -362,6 +370,7 @@ open class QSTileViewImpl @JvmOverloads constructor( } } } + private fun updateHeight() { // TODO(b/332900989): Find a more robust way of resetting the tile if not reset by the // launch animation. @@ -370,16 +379,18 @@ open class QSTileViewImpl @JvmOverloads constructor( // we must do it here resetLongPressEffectProperties() } - val actualHeight = if (heightOverride != HeightOverrideable.NO_OVERRIDE) { - heightOverride - } else { - measuredHeight - } + val actualHeight = + if (heightOverride != HeightOverrideable.NO_OVERRIDE) { + heightOverride + } else { + measuredHeight + } // Limit how much we affect the height, so we don't have rounding artifacts when the tile // is too short. val constrainedSquishiness = constrainSquishiness(squishinessFraction) bottom = top + (actualHeight * constrainedSquishiness).toInt() scrollY = (actualHeight - height) / 2 + maybeUpdateLongPressEffectHeight(actualHeight.toFloat()) } override fun updateAccessibilityOrder(previousView: View?): View { @@ -397,12 +408,13 @@ open class QSTileViewImpl @JvmOverloads constructor( override fun init(tile: QSTile) { val expandable = Expandable.fromView(this) - if (quickSettingsVisualHapticsLongpress()) { + if (longPressEffect != null) { isHapticFeedbackEnabled = false - longPressEffect?.qsTile = tile - longPressEffect?.expandable = expandable + longPressEffect.qsTile = tile + longPressEffect.expandable = expandable + initLongPressEffectCallback() init( - { _: View? -> longPressEffect?.onTileClick() }, + { _: View -> longPressEffect.onTileClick() }, null, // Haptics and long-clicks will be handled by the [QSLongPressEffect] ) } else { @@ -416,10 +428,57 @@ open class QSTileViewImpl @JvmOverloads constructor( } } - private fun init( - click: OnClickListener?, - longClick: OnLongClickListener? - ) { + private fun initLongPressEffectCallback() { + longPressEffect?.callback = + object : QSLongPressEffect.Callback { + + override fun onPrepareForLaunch() { + prepareForLaunch() + } + + override fun onResetProperties() { + resetLongPressEffectProperties() + } + + override fun onStartAnimator() { + if (longPressEffectAnimator?.isRunning != true) { + longPressEffectAnimator = + ValueAnimator.ofFloat(0f, 1f).apply { + this.duration = longPressEffect?.effectDuration?.toLong() ?: 0L + interpolator = AccelerateDecelerateInterpolator() + + doOnStart { longPressEffect?.handleAnimationStart() } + addUpdateListener { + val value = animatedValue as Float + if (value == 0f) { + bringToFront() + } else { + updateLongPressEffectProperties(value) + } + } + doOnEnd { longPressEffect?.handleAnimationComplete() } + doOnCancel { longPressEffect?.handleAnimationCancel() } + start() + } + } + } + + override fun onReverseAnimator() { + longPressEffectAnimator?.let { + val pausedProgress = it.animatedFraction + longPressEffect?.playReverseHaptics(pausedProgress) + it.reverse() + } + } + + override fun onCancelAnimator() { + resetLongPressEffectProperties() + longPressEffectAnimator?.cancel() + } + } + } + + private fun init(click: OnClickListener?, longClick: OnLongClickListener?) { setOnClickListener(click) onLongClickListener = longClick } @@ -447,16 +506,18 @@ open class QSTileViewImpl @JvmOverloads constructor( override fun setClickable(clickable: Boolean) { super.setClickable(clickable) - if (!Flags.qsTileFocusState()){ - background = if (clickable && showRippleEffect) { - qsTileBackground.also { - // In case that the colorBackgroundDrawable was used as the background, make sure - // it has the correct callback instead of null - backgroundDrawable.callback = it + if (!Flags.qsTileFocusState()) { + background = + if (clickable && showRippleEffect) { + qsTileBackground.also { + // In case that the colorBackgroundDrawable was used as the background, make + // sure + // it has the correct callback instead of null + backgroundDrawable.callback = it + } + } else { + backgroundDrawable } - } else { - backgroundDrawable - } } } @@ -491,8 +552,10 @@ open class QSTileViewImpl @JvmOverloads constructor( if (!TextUtils.isEmpty(accessibilityClass)) { event.className = accessibilityClass } - if (event.contentChangeTypes == AccessibilityEvent.CONTENT_CHANGE_TYPE_STATE_DESCRIPTION && - stateDescriptionDeltas != null) { + if ( + event.contentChangeTypes == AccessibilityEvent.CONTENT_CHANGE_TYPE_STATE_DESCRIPTION && + stateDescriptionDeltas != null + ) { event.text.add(stateDescriptionDeltas) stateDescriptionDeltas = null } @@ -502,36 +565,39 @@ open class QSTileViewImpl @JvmOverloads constructor( super.onInitializeAccessibilityNodeInfo(info) // Clear selected state so it is not announce by talkback. info.isSelected = false - info.text = if (TextUtils.isEmpty(secondaryLabel.text)) { - "${label.text}" - } else { - "${label.text}, ${secondaryLabel.text}" - } + info.text = + if (TextUtils.isEmpty(secondaryLabel.text)) { + "${label.text}" + } else { + "${label.text}, ${secondaryLabel.text}" + } if (lastDisabledByPolicy) { info.addAction( - AccessibilityNodeInfo.AccessibilityAction( - AccessibilityNodeInfo.AccessibilityAction.ACTION_CLICK.id, - resources.getString( - R.string.accessibility_tile_disabled_by_policy_action_description - ) + AccessibilityNodeInfo.AccessibilityAction( + AccessibilityNodeInfo.AccessibilityAction.ACTION_CLICK.id, + resources.getString( + R.string.accessibility_tile_disabled_by_policy_action_description ) + ) ) } if (!TextUtils.isEmpty(accessibilityClass)) { - info.className = if (lastDisabledByPolicy) { - Button::class.java.name - } else { - accessibilityClass - } + info.className = + if (lastDisabledByPolicy) { + Button::class.java.name + } else { + accessibilityClass + } if (Switch::class.java.name == accessibilityClass) { info.isChecked = tileState info.isCheckable = true if (isLongClickable) { info.addAction( - AccessibilityNodeInfo.AccessibilityAction( - AccessibilityNodeInfo.AccessibilityAction.ACTION_LONG_CLICK.id, - resources.getString( - R.string.accessibility_long_click_tile))) + AccessibilityNodeInfo.AccessibilityAction( + AccessibilityNodeInfo.AccessibilityAction.ACTION_LONG_CLICK.id, + resources.getString(R.string.accessibility_long_click_tile) + ) + ) } } } @@ -556,7 +622,15 @@ open class QSTileViewImpl @JvmOverloads constructor( val result = super.onTouchEvent(event) if (longPressEffect != null) { when (event?.actionMasked) { - MotionEvent.ACTION_DOWN -> longPressEffect.handleActionDown() + MotionEvent.ACTION_DOWN -> { + longPressEffect.handleActionDown() + if (isLongClickable) { + postDelayed( + { longPressEffect.handleTimeoutComplete() }, + ViewConfiguration.getTapTimeout().toLong(), + ) + } + } MotionEvent.ACTION_UP -> longPressEffect.handleActionUp() MotionEvent.ACTION_CANCEL -> longPressEffect.handleActionCancel() } @@ -588,8 +662,11 @@ open class QSTileViewImpl @JvmOverloads constructor( if (!TextUtils.isEmpty(state.stateDescription)) { stateDescription.append(", ") stateDescription.append(state.stateDescription) - if (lastState != INVALID && state.state == lastState && - state.stateDescription != lastStateDescription) { + if ( + lastState != INVALID && + state.state == lastState && + state.stateDescription != lastStateDescription + ) { stateDescriptionDeltas = state.stateDescription } } @@ -597,11 +674,12 @@ open class QSTileViewImpl @JvmOverloads constructor( setStateDescription(stateDescription.toString()) lastStateDescription = state.stateDescription - accessibilityClass = if (state.state == Tile.STATE_UNAVAILABLE) { - null - } else { - state.expandedAccessibilityClassName - } + accessibilityClass = + if (state.state == Tile.STATE_UNAVAILABLE) { + null + } else { + state.expandedAccessibilityClassName + } if (state is AdapterState) { val newState = state.value @@ -616,49 +694,51 @@ open class QSTileViewImpl @JvmOverloads constructor( } if (!Objects.equals(secondaryLabel.text, state.secondaryLabel)) { secondaryLabel.text = state.secondaryLabel - secondaryLabel.visibility = if (TextUtils.isEmpty(state.secondaryLabel)) { - GONE - } else { - VISIBLE - } + secondaryLabel.visibility = + if (TextUtils.isEmpty(state.secondaryLabel)) { + GONE + } else { + VISIBLE + } } // Colors if (state.state != lastState || state.disabledByPolicy != lastDisabledByPolicy) { singleAnimator.cancel() mQsLogger?.logTileBackgroundColorUpdateIfInternetTile( - state.spec, - state.state, - state.disabledByPolicy, - getBackgroundColorForState(state.state, state.disabledByPolicy)) + state.spec, + state.state, + state.disabledByPolicy, + getBackgroundColorForState(state.state, state.disabledByPolicy) + ) if (allowAnimations) { singleAnimator.setValues( - colorValuesHolder( - BACKGROUND_NAME, - backgroundColor, - getBackgroundColorForState(state.state, state.disabledByPolicy) - ), - colorValuesHolder( - LABEL_NAME, - label.currentTextColor, - getLabelColorForState(state.state, state.disabledByPolicy) - ), - colorValuesHolder( - SECONDARY_LABEL_NAME, - secondaryLabel.currentTextColor, - getSecondaryLabelColorForState(state.state, state.disabledByPolicy) - ), - colorValuesHolder( - CHEVRON_NAME, - chevronView.imageTintList?.defaultColor ?: 0, - getChevronColorForState(state.state, state.disabledByPolicy) - ), - colorValuesHolder( - OVERLAY_NAME, - backgroundOverlayColor, - getOverlayColorForState(state.state) - ) + colorValuesHolder( + BACKGROUND_NAME, + backgroundColor, + getBackgroundColorForState(state.state, state.disabledByPolicy) + ), + colorValuesHolder( + LABEL_NAME, + label.currentTextColor, + getLabelColorForState(state.state, state.disabledByPolicy) + ), + colorValuesHolder( + SECONDARY_LABEL_NAME, + secondaryLabel.currentTextColor, + getSecondaryLabelColorForState(state.state, state.disabledByPolicy) + ), + colorValuesHolder( + CHEVRON_NAME, + chevronView.imageTintList?.defaultColor ?: 0, + getChevronColorForState(state.state, state.disabledByPolicy) + ), + colorValuesHolder( + OVERLAY_NAME, + backgroundOverlayColor, + getOverlayColorForState(state.state) ) + ) singleAnimator.start() } else { setAllColors( @@ -681,24 +761,16 @@ open class QSTileViewImpl @JvmOverloads constructor( lastIconTint = icon.getColor(state) // Long-press effects - if (state.handlesLongClick && - longPressEffect?.initializeEffect(longPressEffectDuration) == true) { - // bind the long-press effect and set it as the touch listener - if (!isLongPressEffectBound) { - longPressEffectHandle = - QSLongPressEffectViewBinder.bind( - this, - longPressEffect, - state.spec, - ) - } + if ( + state.handlesLongClick && + longPressEffect?.initializeEffect(longPressEffectDuration) == true + ) { showRippleEffect = false initializeLongPressProperties(measuredHeight, measuredWidth) } else { // Long-press effects might have been enabled before but the new state does not // handle a long-press. In this case, we go back to the behaviour of a regular tile // and clean-up the resources - unbindLongPressEffect() showRippleEffect = isClickable initialLongPressProperties = null finalLongPressProperties = null @@ -813,7 +885,7 @@ open class QSTileViewImpl @JvmOverloads constructor( } private fun getChevronColorForState(state: Int, disabledByPolicy: Boolean = false): Int = - getSecondaryLabelColorForState(state, disabledByPolicy) + getSecondaryLabelColorForState(state, disabledByPolicy) private fun getOverlayColorForState(state: Int): Int { return when (state) { @@ -850,16 +922,18 @@ open class QSTileViewImpl @JvmOverloads constructor( // Dimensions change val newHeight = interpolateFloat( - effectProgress, - initialLongPressProperties?.height ?: 0f, - finalLongPressProperties?.height ?: 0f, - ).toInt() + effectProgress, + initialLongPressProperties?.height ?: 0f, + finalLongPressProperties?.height ?: 0f, + ) + .toInt() val newWidth = interpolateFloat( - effectProgress, - initialLongPressProperties?.width ?: 0f, - finalLongPressProperties?.width ?: 0f, - ).toInt() + effectProgress, + initialLongPressProperties?.width ?: 0f, + finalLongPressProperties?.width ?: 0f, + ) + .toInt() val startingHeight = initialLongPressProperties?.height?.toInt() ?: 0 val startingWidth = initialLongPressProperties?.width?.toInt() ?: 0 @@ -920,11 +994,6 @@ open class QSTileViewImpl @JvmOverloads constructor( ) } - private fun unbindLongPressEffect() { - longPressEffectHandle?.dispose() - longPressEffectHandle = null - } - private fun interpolateFloat(fraction: Float, start: Float, end: Float): Float = start + fraction * (end - start) @@ -986,12 +1055,13 @@ open class QSTileViewImpl @JvmOverloads constructor( } @VisibleForTesting - internal fun getCurrentColors(): List<Int> = listOf( + internal fun getCurrentColors(): List<Int> = + listOf( backgroundColor, label.currentTextColor, secondaryLabel.currentTextColor, chevronView.imageTintList?.defaultColor ?: 0 - ) + ) inner class StateChangeRunnable(private val state: QSTile.State) : Runnable { override fun run() { diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/HearingDevicesTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/HearingDevicesTile.java index 183c1a4a7ce7..b96e83d43e32 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/HearingDevicesTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/HearingDevicesTile.java @@ -19,34 +19,53 @@ package com.android.systemui.qs.tiles; import android.content.Intent; import android.os.Handler; import android.os.Looper; +import android.os.UserManager; import android.provider.Settings; +import android.service.quicksettings.Tile; import androidx.annotation.Nullable; import com.android.internal.logging.MetricsLogger; import com.android.systemui.Flags; +import com.android.systemui.accessibility.hearingaid.HearingDevicesChecker; import com.android.systemui.accessibility.hearingaid.HearingDevicesDialogManager; 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.State; +import com.android.systemui.plugins.qs.QSTile.BooleanState; 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.res.R; +import com.android.systemui.statusbar.policy.BluetoothController; import javax.inject.Inject; /** Quick settings tile: Hearing Devices **/ -public class HearingDevicesTile extends QSTileImpl<State> { - +public class HearingDevicesTile extends QSTileImpl<BooleanState> { + //TODO(b/338520598): Transform the current implementation into new QS architecture + // and use Kotlin except Tile class. public static final String TILE_SPEC = "hearing_devices"; private final HearingDevicesDialogManager mDialogManager; + private final HearingDevicesChecker mDevicesChecker; + private final BluetoothController mBluetoothController; + + private final BluetoothController.Callback mCallback = new BluetoothController.Callback() { + @Override + public void onBluetoothStateChange(boolean enabled) { + refreshState(); + } + + @Override + public void onBluetoothDevicesChanged() { + refreshState(); + } + }; @Inject public HearingDevicesTile( @@ -59,16 +78,20 @@ public class HearingDevicesTile extends QSTileImpl<State> { StatusBarStateController statusBarStateController, ActivityStarter activityStarter, QSLogger qsLogger, - HearingDevicesDialogManager hearingDevicesDialogManager - ) { + HearingDevicesDialogManager hearingDevicesDialogManager, + HearingDevicesChecker hearingDevicesChecker, + BluetoothController bluetoothController) { super(host, uiEventLogger, backgroundLooper, mainHandler, falsingManager, metricsLogger, statusBarStateController, activityStarter, qsLogger); mDialogManager = hearingDevicesDialogManager; + mDevicesChecker = hearingDevicesChecker; + mBluetoothController = bluetoothController; + mBluetoothController.observe(getLifecycle(), mCallback); } @Override - public State newTileState() { - return new State(); + public BooleanState newTileState() { + return new BooleanState(); } @Override @@ -77,9 +100,28 @@ public class HearingDevicesTile extends QSTileImpl<State> { } @Override - protected void handleUpdateState(State state, Object arg) { + protected void handleUpdateState(BooleanState state, Object arg) { + checkIfRestrictionEnforcedByAdminOnly(state, UserManager.DISALLOW_BLUETOOTH); + state.label = mContext.getString(R.string.quick_settings_hearing_devices_label); state.icon = ResourceIcon.get(R.drawable.qs_hearing_devices_icon); + state.forceExpandIcon = true; + + boolean isBonded = mDevicesChecker.isAnyPairedHearingDevice(); + boolean isActive = mDevicesChecker.isAnyActiveHearingDevice(); + + if (isActive) { + state.state = Tile.STATE_ACTIVE; + state.secondaryLabel = mContext.getString( + R.string.quick_settings_hearing_devices_connected); + } else if (isBonded) { + state.state = Tile.STATE_INACTIVE; + state.secondaryLabel = mContext.getString( + R.string.quick_settings_hearing_devices_disconnected); + } else { + state.state = Tile.STATE_INACTIVE; + state.secondaryLabel = ""; + } } @Nullable diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/RecordIssueTile.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/RecordIssueTile.kt index e6801022ad0e..70f3b847ce07 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/RecordIssueTile.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/RecordIssueTile.kt @@ -52,7 +52,6 @@ import com.android.systemui.screenrecord.RecordingService import com.android.systemui.settings.UserContextProvider import com.android.systemui.statusbar.phone.KeyguardDismissUtil import com.android.systemui.statusbar.policy.KeyguardStateController -import com.android.traceur.TraceUtils.PresetTraceType import java.util.concurrent.Executor import javax.inject.Inject @@ -131,15 +130,11 @@ constructor( } } - private fun startIssueRecordingService(screenRecord: Boolean, traceType: PresetTraceType) = + private fun startIssueRecordingService() = PendingIntent.getForegroundService( userContextProvider.userContext, RecordingService.REQUEST_CODE, - IssueRecordingService.getStartIntent( - userContextProvider.userContext, - screenRecord, - traceType - ), + IssueRecordingService.getStartIntent(userContextProvider.userContext), PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE ) .send(BroadcastOptions.makeBasic().apply { isInteractive = true }.toBundle()) @@ -157,7 +152,7 @@ constructor( val dialog: AlertDialog = delegateFactory .create { - startIssueRecordingService(it.screenRecord, it.traceType) + startIssueRecordingService() dialogTransitionAnimator.disableAllCurrentDialogsExitAnimations() panelInteractor.collapsePanels() } @@ -169,8 +164,7 @@ constructor( if (expandable != null && !keyguardStateController.isShowing) { expandable .dialogTransitionController(DialogCuj(CUJ_SHADE_DIALOG_OPEN, TILE_SPEC)) - ?.let { dialogTransitionAnimator.show(dialog, it) } - ?: dialog.show() + ?.let { dialogTransitionAnimator.show(dialog, it) } ?: dialog.show() } else { dialog.show() } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java index c091ac3dea50..df7430a0f729 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java @@ -118,8 +118,6 @@ import javax.inject.Inject; public class InternetDialogController implements AccessPointController.AccessPointCallback { private static final String TAG = "InternetDialogController"; - private static final String ACTION_NETWORK_PROVIDER_SETTINGS = - "android.settings.NETWORK_PROVIDER_SETTINGS"; private static final String ACTION_WIFI_SCANNING_SETTINGS = "android.settings.WIFI_SCANNING_SETTINGS"; /** @@ -363,7 +361,8 @@ public class InternetDialogController implements AccessPointController.AccessPoi @VisibleForTesting protected Intent getSettingsIntent() { - return new Intent(ACTION_NETWORK_PROVIDER_SETTINGS).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + return new Intent(Settings.ACTION_NETWORK_PROVIDER_SETTINGS).addFlags( + Intent.FLAG_ACTIVITY_NEW_TASK); } @Nullable 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 79720c1d8df6..5637115cd6da 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 @@ -35,6 +35,7 @@ import com.android.systemui.qs.tiles.base.interactor.QSTileUserActionInteractor import com.android.systemui.qs.tiles.viewmodel.QSTileUserAction import com.android.systemui.screenrecord.RecordingController import com.android.systemui.screenrecord.data.model.ScreenRecordModel +import com.android.systemui.screenrecord.data.repository.ScreenRecordRepository import com.android.systemui.statusbar.phone.KeyguardDismissUtil import javax.inject.Inject import kotlin.coroutines.CoroutineContext @@ -47,6 +48,7 @@ constructor( @Application private val context: Context, @Main private val mainContext: CoroutineContext, @Background private val backgroundContext: CoroutineContext, + private val screenRecordRepository: ScreenRecordRepository, private val recordingController: RecordingController, private val keyguardInteractor: KeyguardInteractor, private val keyguardDismissUtil: KeyguardDismissUtil, @@ -65,8 +67,7 @@ constructor( Log.d(TAG, "Cancelling countdown") withContext(backgroundContext) { recordingController.cancelCountdown() } } - is ScreenRecordModel.Recording -> - withContext(backgroundContext) { recordingController.stopRecording() } + is ScreenRecordModel.Recording -> screenRecordRepository.stopRecording() is ScreenRecordModel.DoingNothing -> withContext(mainContext) { showPrompt(action.expandable, user.identifier) @@ -122,8 +123,7 @@ constructor( controller, animateBackgroundBoundsChange = true, ) - } - ?: dialog.show() + } ?: dialog.show() } else { dialog.show() } 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 c7326b08e315..bb36fd5ea979 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 @@ -33,6 +33,7 @@ import com.android.systemui.plugins.qs.QSContainerController import com.android.systemui.qs.QSContainerImpl import com.android.systemui.qs.QSImpl import com.android.systemui.qs.dagger.QSSceneComponent +import com.android.systemui.qs.tiles.viewmodel.StubQSTileViewModel.state import com.android.systemui.res.R import com.android.systemui.settings.brightness.MirrorController import com.android.systemui.shade.domain.interactor.ShadeInteractor @@ -267,6 +268,7 @@ constructor( override val qqsHeight: Int get() = qsImpl.value?.qqsHeight ?: 0 + override val qsHeight: Int get() = qsImpl.value?.qsHeight ?: 0 @@ -375,8 +377,10 @@ constructor( qs.view.setPadding(0, 0, 0, 0) qs.setContainerController(this@QSSceneAdapterImpl) qs.applyState(state.value) + applyLatestExpansionAndSquishiness() } } + override fun setState(state: QSSceneAdapter.State) { this.state.value = state } diff --git a/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModel.kt index 6cf2e52ff3d9..79cdfec182ae 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModel.kt @@ -14,8 +14,6 @@ * limitations under the License. */ -@file:OptIn(ExperimentalCoroutinesApi::class) - package com.android.systemui.qs.ui.viewmodel import androidx.lifecycle.LifecycleOwner @@ -28,12 +26,12 @@ import com.android.compose.animation.scene.UserAction import com.android.compose.animation.scene.UserActionResult import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application -import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor import com.android.systemui.media.controls.domain.pipeline.interactor.MediaCarouselInteractor import com.android.systemui.qs.FooterActionsController import com.android.systemui.qs.footer.ui.viewmodel.FooterActionsViewModel import com.android.systemui.qs.ui.adapter.QSSceneAdapter import com.android.systemui.scene.domain.interactor.SceneBackInteractor +import com.android.systemui.scene.shared.model.SceneFamilies import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.settings.brightness.ui.viewModel.BrightnessMirrorViewModel import com.android.systemui.shade.ui.viewmodel.ShadeHeaderViewModel @@ -41,7 +39,6 @@ import com.android.systemui.statusbar.notification.stack.ui.viewmodel.Notificati import java.util.concurrent.atomic.AtomicBoolean 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.combine @@ -55,7 +52,6 @@ class QuickSettingsSceneViewModel @Inject constructor( @Application private val applicationScope: CoroutineScope, - deviceEntryInteractor: DeviceEntryInteractor, val brightnessMirrorViewModel: BrightnessMirrorViewModel, val shadeHeaderViewModel: ShadeHeaderViewModel, val qsSceneAdapter: QSSceneAdapter, @@ -77,25 +73,15 @@ constructor( val destinationScenes: StateFlow<Map<UserAction, UserActionResult>> = combine( - deviceEntryInteractor.isUnlocked, - deviceEntryInteractor.canSwipeToEnter, qsSceneAdapter.isCustomizerShowing, backScene, - ) { isUnlocked, canSwipeToDismiss, isCustomizerShowing, backScene -> - destinationScenes( - isUnlocked, - canSwipeToDismiss, - isCustomizerShowing, - backScene, - ) - } + transform = ::destinationScenes, + ) .stateIn( scope = applicationScope, started = SharingStarted.WhileSubscribed(), initialValue = destinationScenes( - isUnlocked = deviceEntryInteractor.isUnlocked.value, - canSwipeToDismiss = deviceEntryInteractor.canSwipeToEnter.value, isCustomizing = qsSceneAdapter.isCustomizerShowing.value, backScene = backScene.value, ), @@ -104,18 +90,9 @@ constructor( val isMediaVisible: StateFlow<Boolean> = mediaCarouselInteractor.hasAnyMediaOrRecommendation private fun destinationScenes( - isUnlocked: Boolean, - canSwipeToDismiss: Boolean?, isCustomizing: Boolean, backScene: SceneKey?, ): Map<UserAction, UserActionResult> { - val upBottomEdge = - when { - canSwipeToDismiss == true -> Scenes.Lockscreen - isUnlocked -> Scenes.Gone - else -> Scenes.Lockscreen - } - return buildMap { if (isCustomizing) { // TODO(b/332749288) Empty map so there are no back handlers and back can close @@ -127,11 +104,8 @@ constructor( put(Back, UserActionResult(backScene ?: Scenes.Shade)) put(Swipe(SwipeDirection.Up), UserActionResult(backScene ?: Scenes.Shade)) put( - Swipe( - fromSource = Edge.Bottom, - direction = SwipeDirection.Up, - ), - UserActionResult(upBottomEdge), + Swipe(fromSource = Edge.Bottom, direction = SwipeDirection.Up), + UserActionResult(SceneFamilies.Home), ) } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeSceneViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeSceneViewModel.kt index c1a56465064f..bd748d5c2174 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeSceneViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeSceneViewModel.kt @@ -17,30 +17,26 @@ package com.android.systemui.qs.ui.viewmodel import com.android.compose.animation.scene.Back -import com.android.compose.animation.scene.SceneKey import com.android.compose.animation.scene.Swipe import com.android.compose.animation.scene.UserAction import com.android.compose.animation.scene.UserActionResult import com.android.systemui.brightness.ui.viewmodel.BrightnessSliderViewModel import com.android.systemui.dagger.SysUISingleton -import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.qs.panels.ui.viewmodel.EditModeViewModel import com.android.systemui.qs.panels.ui.viewmodel.TileGridViewModel import com.android.systemui.qs.ui.adapter.QSSceneAdapter +import com.android.systemui.scene.shared.model.SceneFamilies import com.android.systemui.shade.ui.viewmodel.OverlayShadeViewModel import javax.inject.Inject -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow -import kotlinx.coroutines.flow.map -import kotlinx.coroutines.flow.stateIn +import kotlinx.coroutines.flow.asStateFlow /** Models UI state and handles user input for the Quick Settings Shade scene. */ @SysUISingleton class QuickSettingsShadeSceneViewModel @Inject constructor( - @Application private val applicationScope: CoroutineScope, val overlayShadeViewModel: OverlayShadeViewModel, val brightnessSliderViewModel: BrightnessSliderViewModel, val tileGridViewModel: TileGridViewModel, @@ -48,18 +44,11 @@ constructor( val qsSceneAdapter: QSSceneAdapter, ) { val destinationScenes: StateFlow<Map<UserAction, UserActionResult>> = - overlayShadeViewModel.backgroundScene - .map(::destinationScenes) - .stateIn( - scope = applicationScope, - started = SharingStarted.WhileSubscribed(), - initialValue = destinationScenes(overlayShadeViewModel.backgroundScene.value), + MutableStateFlow( + mapOf( + Swipe.Up to SceneFamilies.Home, + Back to SceneFamilies.Home, + ) ) - - private fun destinationScenes(backgroundScene: SceneKey): Map<UserAction, UserActionResult> { - return mapOf( - Swipe.Up to backgroundScene, - Back to backgroundScene, - ) - } + .asStateFlow() } diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java index 0327ec760ace..23faf7d52fed 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java +++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java @@ -73,7 +73,6 @@ import android.view.inputmethod.InputMethodManager; import androidx.annotation.NonNull; -import com.android.compose.animation.scene.SceneKey; import com.android.internal.accessibility.dialog.AccessibilityButtonChooserActivity; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.app.AssistUtils; @@ -99,12 +98,10 @@ import com.android.systemui.navigationbar.buttons.KeyButtonView; import com.android.systemui.recents.OverviewProxyService.OverviewProxyListener; 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.scene.shared.model.SceneFamilies; import com.android.systemui.settings.DisplayTracker; import com.android.systemui.settings.UserTracker; import com.android.systemui.shade.ShadeViewController; -import com.android.systemui.shade.domain.interactor.ShadeInteractor; -import com.android.systemui.shade.shared.model.ShadeMode; import com.android.systemui.shared.recents.IOverviewProxy; import com.android.systemui.shared.recents.ISystemUiProxy; import com.android.systemui.shared.system.QuickStepContract; @@ -158,7 +155,6 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis private final ScreenPinningRequest mScreenPinningRequest; private final NotificationShadeWindowController mStatusBarWinController; private final Provider<SceneInteractor> mSceneInteractor; - private final Provider<ShadeInteractor> mShadeInteractor; private final Runnable mConnectionRunnable = () -> internalConnectToCurrentUser("runnable: startConnectionToCurrentUser"); @@ -247,7 +243,7 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis // Gesture was too short to be picked up by scene container touch // handling; programmatically start the transition to shade scene. mSceneInteractor.get().changeScene( - getShadeSceneKey(), + SceneFamilies.NotifShade, "short launcher swipe" ); } @@ -267,7 +263,7 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis "trackpad swipe"); } else if (action == ACTION_UP) { mSceneInteractor.get().changeScene( - getShadeSceneKey(), + SceneFamilies.NotifShade, "short trackpad swipe" ); } @@ -632,7 +628,6 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis NotificationShadeWindowController statusBarWinController, SysUiState sysUiState, Provider<SceneInteractor> sceneInteractor, - Provider<ShadeInteractor> shadeInteractor, UserTracker userTracker, WakefulnessLifecycle wakefulnessLifecycle, UiEventLogger uiEventLogger, @@ -659,7 +654,6 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis mScreenPinningRequest = screenPinningRequest; mStatusBarWinController = statusBarWinController; mSceneInteractor = sceneInteractor; - mShadeInteractor = shadeInteractor; mUserTracker = userTracker; mConnectionBackoffAttempts = 0; mRecentsComponentName = ComponentName.unflattenFromString(context.getString( @@ -925,12 +919,6 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis } } - private SceneKey getShadeSceneKey() { - return mShadeInteractor.get().getShadeMode().getValue() == ShadeMode.dual() - ? Scenes.NotificationsShade - : Scenes.Shade; - } - private void notifyHomeRotationEnabled(boolean enabled) { for (int i = mConnectionCallbacks.size() - 1; i >= 0; --i) { mConnectionCallbacks.get(i).onHomeRotationEnabled(enabled); diff --git a/packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingService.kt b/packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingService.kt index 5ede64a4fc26..4a4c73ba9c81 100644 --- a/packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingService.kt +++ b/packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingService.kt @@ -35,8 +35,6 @@ import com.android.systemui.screenrecord.RecordingService import com.android.systemui.screenrecord.RecordingServiceStrings import com.android.systemui.settings.UserContextProvider import com.android.systemui.statusbar.phone.KeyguardDismissUtil -import com.android.traceur.MessageConstants.INTENT_EXTRA_TRACE_TYPE -import com.android.traceur.TraceUtils.PresetTraceType import java.util.concurrent.Executor import javax.inject.Inject @@ -76,15 +74,10 @@ constructor( when (intent?.action) { ACTION_START -> { bgExecutor.execute { - traceurMessageSender.startTracing( - intent.getSerializableExtra( - INTENT_EXTRA_TRACE_TYPE, - PresetTraceType::class.java - ) - ) + traceurMessageSender.startTracing(issueRecordingState.traceType) } issueRecordingState.isRecording = true - if (!intent.getBooleanExtra(EXTRA_SCREEN_RECORD, false)) { + if (!issueRecordingState.recordScreen) { // If we don't want to record the screen, the ACTION_SHOW_START_NOTIF action // will circumvent the RecordingService's screen recording start code. return super.onStartCommand(Intent(ACTION_SHOW_START_NOTIF), flags, startId) @@ -107,7 +100,7 @@ constructor( ) val screenRecording = intent.getParcelableExtra(EXTRA_PATH, Uri::class.java) - if (issueRecordingState.takeBugReport) { + if (issueRecordingState.takeBugreport) { iActivityManager.requestBugReportWithExtraAttachment(screenRecording) } else { traceurMessageSender.shareTraces(applicationContext, screenRecording) @@ -130,7 +123,6 @@ constructor( companion object { private const val TAG = "IssueRecordingService" private const val CHANNEL_ID = "issue_record" - private const val EXTRA_SCREEN_RECORD = "extra_screenRecord" /** * Get an intent to stop the issue recording service. @@ -148,35 +140,36 @@ constructor( * * @param context Context from the requesting activity */ - fun getStartIntent( - context: Context, - screenRecord: Boolean, - traceType: PresetTraceType, - ): Intent = - Intent(context, IssueRecordingService::class.java) - .setAction(ACTION_START) - .putExtra(EXTRA_SCREEN_RECORD, screenRecord) - .putExtra(INTENT_EXTRA_TRACE_TYPE, traceType) + fun getStartIntent(context: Context): Intent = + Intent(context, IssueRecordingService::class.java).setAction(ACTION_START) } } private class IrsStrings(private val res: Resources) : RecordingServiceStrings(res) { override val title get() = res.getString(R.string.issuerecord_title) + override val notificationChannelDescription get() = res.getString(R.string.issuerecord_channel_description) + override val startErrorResId get() = R.string.issuerecord_start_error + override val startError get() = res.getString(R.string.issuerecord_start_error) + override val saveErrorResId get() = R.string.issuerecord_save_error + override val saveError get() = res.getString(R.string.issuerecord_save_error) + override val ongoingRecording get() = res.getString(R.string.issuerecord_ongoing_screen_only) + override val backgroundProcessingLabel get() = res.getString(R.string.issuerecord_background_processing_label) + override val saveTitle get() = res.getString(R.string.issuerecord_save_title) } diff --git a/packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingState.kt b/packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingState.kt index 12ed06d75ce3..4ea334522ad4 100644 --- a/packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingState.kt +++ b/packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingState.kt @@ -16,16 +16,51 @@ package com.android.systemui.recordissue +import android.content.Context import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.qs.tiles.RecordIssueTile +import com.android.systemui.res.R +import com.android.systemui.settings.UserFileManager +import com.android.systemui.settings.UserTracker +import com.android.traceur.TraceUtils.PresetTraceType import java.util.concurrent.CopyOnWriteArrayList import javax.inject.Inject @SysUISingleton -class IssueRecordingState @Inject constructor() { +class IssueRecordingState +@Inject +constructor( + userTracker: UserTracker, + userFileManager: UserFileManager, +) { - private val listeners = CopyOnWriteArrayList<Runnable>() + private val prefs = + userFileManager.getSharedPreferences( + RecordIssueTile.TILE_SPEC, + Context.MODE_PRIVATE, + userTracker.userId + ) + + var takeBugreport + get() = prefs.getBoolean(KEY_TAKE_BUG_REPORT, false) + set(value) = prefs.edit().putBoolean(KEY_TAKE_BUG_REPORT, value).apply() + + var recordScreen + get() = prefs.getBoolean(KEY_RECORD_SCREEN, false) + set(value) = prefs.edit().putBoolean(KEY_RECORD_SCREEN, value).apply() + + var hasUserApprovedScreenRecording + get() = prefs.getBoolean(HAS_APPROVED_SCREEN_RECORDING, false) + private set(value) = prefs.edit().putBoolean(HAS_APPROVED_SCREEN_RECORDING, value).apply() + + var issueTypeRes + get() = prefs.getInt(KEY_ISSUE_TYPE_RES, ISSUE_TYPE_NOT_SET) + set(value) = prefs.edit().putInt(KEY_ISSUE_TYPE_RES, value).apply() + + val traceType: PresetTraceType + get() = ALL_ISSUE_TYPES[issueTypeRes] ?: PresetTraceType.UNSET - var takeBugReport: Boolean = false + private val listeners = CopyOnWriteArrayList<Runnable>() var isRecording = false set(value) { @@ -33,6 +68,10 @@ class IssueRecordingState @Inject constructor() { listeners.forEach(Runnable::run) } + fun markUserApprovalForScreenRecording() { + hasUserApprovedScreenRecording = true + } + fun addListener(listener: Runnable) { listeners.add(listener) } @@ -40,4 +79,20 @@ class IssueRecordingState @Inject constructor() { fun removeListener(listener: Runnable) { listeners.remove(listener) } + + companion object { + private const val KEY_TAKE_BUG_REPORT = "key_takeBugReport" + private const val HAS_APPROVED_SCREEN_RECORDING = "HasApprovedScreenRecord" + private const val KEY_RECORD_SCREEN = "key_recordScreen" + const val KEY_ISSUE_TYPE_RES = "key_issueTypeRes" + const val ISSUE_TYPE_NOT_SET = -1 + + val ALL_ISSUE_TYPES: Map<Int, PresetTraceType> = + hashMapOf( + Pair(R.string.performance, PresetTraceType.PERFORMANCE), + Pair(R.string.user_interface, PresetTraceType.UI), + Pair(R.string.battery, PresetTraceType.BATTERY), + Pair(R.string.thermal, PresetTraceType.THERMAL) + ) + } } diff --git a/packages/SystemUI/src/com/android/systemui/recordissue/RecordIssueDialogDelegate.kt b/packages/SystemUI/src/com/android/systemui/recordissue/RecordIssueDialogDelegate.kt index 84a063a36740..bbf4e51b35e9 100644 --- a/packages/SystemUI/src/com/android/systemui/recordissue/RecordIssueDialogDelegate.kt +++ b/packages/SystemUI/src/com/android/systemui/recordissue/RecordIssueDialogDelegate.kt @@ -17,7 +17,7 @@ package com.android.systemui.recordissue import android.annotation.SuppressLint -import android.app.AlertDialog +import android.app.AlertDialog.BUTTON_POSITIVE import android.content.Context import android.content.Intent import android.content.res.ColorStateList @@ -41,18 +41,16 @@ 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.qs.tiles.RecordIssueTile +import com.android.systemui.recordissue.IssueRecordingState.Companion.ALL_ISSUE_TYPES +import com.android.systemui.recordissue.IssueRecordingState.Companion.ISSUE_TYPE_NOT_SET +import com.android.systemui.recordissue.IssueRecordingState.Companion.KEY_ISSUE_TYPE_RES import com.android.systemui.res.R -import com.android.systemui.settings.UserFileManager import com.android.systemui.settings.UserTracker import com.android.systemui.statusbar.phone.SystemUIDialog -import com.android.traceur.MessageConstants.INTENT_EXTRA_TRACE_TYPE -import com.android.traceur.TraceUtils.PresetTraceType import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject import java.util.concurrent.Executor -import java.util.function.Consumer class RecordIssueDialogDelegate @AssistedInject @@ -64,31 +62,20 @@ constructor( @Main private val mainExecutor: Executor, private val devicePolicyResolver: dagger.Lazy<ScreenCaptureDevicePolicyResolver>, private val mediaProjectionMetricsLogger: MediaProjectionMetricsLogger, - private val userFileManager: UserFileManager, private val screenCaptureDisabledDialogDelegate: ScreenCaptureDisabledDialogDelegate, - private val issueRecordingState: IssueRecordingState, + private val state: IssueRecordingState, private val traceurMessageSender: TraceurMessageSender, - @Assisted private val onStarted: Consumer<IssueRecordingConfig>, + @Assisted private val onStarted: Runnable, ) : SystemUIDialog.Delegate { - private val issueTypeOptions: Map<Int, PresetTraceType> = - hashMapOf( - Pair(R.string.performance, PresetTraceType.PERFORMANCE), - Pair(R.string.user_interface, PresetTraceType.UI), - Pair(R.string.battery, PresetTraceType.BATTERY), - Pair(R.string.thermal, PresetTraceType.THERMAL) - ) - private var selectedIssueType: PresetTraceType? = null - /** To inject dependencies and allow for easier testing */ @AssistedFactory interface Factory { /** Create a dialog object */ - fun create(onStarted: Consumer<IssueRecordingConfig>): RecordIssueDialogDelegate + fun create(onStarted: Runnable): RecordIssueDialogDelegate } @SuppressLint("UseSwitchCompatOrMaterialCode") private lateinit var screenRecordSwitch: Switch - @SuppressLint("UseSwitchCompatOrMaterialCode") private lateinit var bugReportSwitch: Switch private lateinit var issueTypeButton: Button @MainThread @@ -97,21 +84,8 @@ constructor( setView(LayoutInflater.from(context).inflate(R.layout.record_issue_dialog, null)) setTitle(context.getString(R.string.qs_record_issue_label)) setIcon(R.drawable.qs_record_issue_icon_off) - setNegativeButton(R.string.cancel) { _, _ -> dismiss() } - setPositiveButton( - R.string.qs_record_issue_start, - { _, _ -> - issueRecordingState.takeBugReport = bugReportSwitch.isChecked - onStarted.accept( - IssueRecordingConfig( - screenRecordSwitch.isChecked, - selectedIssueType ?: PresetTraceType.UNSET - ) - ) - dismiss() - }, - false - ) + setNegativeButton(R.string.cancel) { _, _ -> } + setPositiveButton(R.string.qs_record_issue_start) { _, _ -> onStarted.run() } } bgExecutor.execute { traceurMessageSender.bindToTraceur(dialog.context) } } @@ -121,22 +95,39 @@ constructor( @MainThread override fun onCreate(dialog: SystemUIDialog, savedInstanceState: Bundle?) { dialog.apply { - window?.addPrivateFlags(WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS) - window?.setGravity(Gravity.CENTER) + window?.apply { + addPrivateFlags(WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS) + setGravity(Gravity.CENTER) + } - screenRecordSwitch = requireViewById(R.id.screenrecord_switch) - screenRecordSwitch.setOnCheckedChangeListener { _, isEnabled -> - if (isEnabled) { - bgExecutor.execute { onScreenRecordSwitchClicked() } + screenRecordSwitch = + requireViewById<Switch>(R.id.screenrecord_switch).apply { + isChecked = state.recordScreen + setOnCheckedChangeListener { _, isChecked -> + state.recordScreen = isChecked + if (isChecked) { + bgExecutor.execute { onScreenRecordSwitchClicked() } + } + } } + + requireViewById<Switch>(R.id.bugreport_switch).apply { + isChecked = state.takeBugreport + setOnCheckedChangeListener { _, isChecked -> state.takeBugreport = isChecked } } - bugReportSwitch = requireViewById(R.id.bugreport_switch) - val startButton = dialog.getButton(AlertDialog.BUTTON_POSITIVE) - issueTypeButton = requireViewById(R.id.issue_type_button) - issueTypeButton.setOnClickListener { - onIssueTypeClicked(context) { startButton.isEnabled = true } - } - startButton.isEnabled = false + + issueTypeButton = + requireViewById<Button>(R.id.issue_type_button).apply { + val startButton = dialog.getButton(BUTTON_POSITIVE) + if (state.issueTypeRes != ISSUE_TYPE_NOT_SET) { + setText(state.issueTypeRes) + } else { + startButton.isEnabled = false + } + setOnClickListener { + onIssueTypeClicked(context) { startButton.isEnabled = true } + } + } } } @@ -160,19 +151,14 @@ constructor( SessionCreationSource.SYSTEM_UI_SCREEN_RECORDER ) - if (flags.isEnabled(Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING)) { - val prefs = - userFileManager.getSharedPreferences( - RecordIssueTile.TILE_SPEC, - Context.MODE_PRIVATE, - userTracker.userId - ) - if (!prefs.getBoolean(HAS_APPROVED_SCREEN_RECORDING, false)) { - mainExecutor.execute { - ScreenCapturePermissionDialogDelegate(factory, prefs).createDialog().apply { - setOnCancelListener { screenRecordSwitch.isChecked = false } - show() - } + if ( + flags.isEnabled(Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING) && + !state.hasUserApprovedScreenRecording + ) { + mainExecutor.execute { + ScreenCapturePermissionDialogDelegate(factory, state).createDialog().apply { + setOnCancelListener { screenRecordSwitch.isChecked = false } + show() } } } @@ -182,23 +168,21 @@ constructor( private fun onIssueTypeClicked(context: Context, onIssueTypeSelected: Runnable) { val popupMenu = PopupMenu(context, issueTypeButton) - issueTypeOptions.keys.forEach { + ALL_ISSUE_TYPES.keys.forEach { popupMenu.menu.add(it).apply { setIcon(R.drawable.arrow_pointing_down) - if (issueTypeOptions[it] != selectedIssueType) { + if (it != state.issueTypeRes) { iconTintList = ColorStateList.valueOf(Color.TRANSPARENT) } - intent = Intent().putExtra(INTENT_EXTRA_TRACE_TYPE, issueTypeOptions[it]) + intent = Intent().putExtra(KEY_ISSUE_TYPE_RES, it) } } popupMenu.apply { setOnMenuItemClickListener { issueTypeButton.text = it.title - selectedIssueType = - it.intent?.getSerializableExtra( - INTENT_EXTRA_TRACE_TYPE, - PresetTraceType::class.java - ) + state.issueTypeRes = + it.intent?.getIntExtra(KEY_ISSUE_TYPE_RES, ISSUE_TYPE_NOT_SET) + ?: ISSUE_TYPE_NOT_SET onIssueTypeSelected.run() true } diff --git a/packages/SystemUI/src/com/android/systemui/recordissue/ScreenCapturePermissionDialogDelegate.kt b/packages/SystemUI/src/com/android/systemui/recordissue/ScreenCapturePermissionDialogDelegate.kt index de6d3f698285..b029d077b6d1 100644 --- a/packages/SystemUI/src/com/android/systemui/recordissue/ScreenCapturePermissionDialogDelegate.kt +++ b/packages/SystemUI/src/com/android/systemui/recordissue/ScreenCapturePermissionDialogDelegate.kt @@ -16,18 +16,15 @@ package com.android.systemui.recordissue -import android.content.SharedPreferences import android.os.Bundle import android.view.Gravity import android.view.WindowManager import com.android.systemui.res.R import com.android.systemui.statusbar.phone.SystemUIDialog -const val HAS_APPROVED_SCREEN_RECORDING = "HasApprovedScreenRecord" - class ScreenCapturePermissionDialogDelegate( private val dialogFactory: SystemUIDialog.Factory, - private val sharedPreferences: SharedPreferences, + private val state: IssueRecordingState, ) : SystemUIDialog.Delegate { override fun beforeCreate(dialog: SystemUIDialog, savedInstanceState: Bundle?) { @@ -37,7 +34,7 @@ class ScreenCapturePermissionDialogDelegate( setMessage(R.string.screenrecord_permission_dialog_warning_entire_screen) setNegativeButton(R.string.slice_permission_deny) { _, _ -> cancel() } setPositiveButton(R.string.slice_permission_allow) { _, _ -> - sharedPreferences.edit().putBoolean(HAS_APPROVED_SCREEN_RECORDING, true).apply() + state.markUserApprovalForScreenRecording() dismiss() } window?.addPrivateFlags(WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS) diff --git a/packages/SystemUI/src/com/android/systemui/recordissue/TraceurMessageSender.kt b/packages/SystemUI/src/com/android/systemui/recordissue/TraceurMessageSender.kt index 189336cf69bc..51744aa47c1a 100644 --- a/packages/SystemUI/src/com/android/systemui/recordissue/TraceurMessageSender.kt +++ b/packages/SystemUI/src/com/android/systemui/recordissue/TraceurMessageSender.kt @@ -93,7 +93,7 @@ class TraceurMessageSender @Inject constructor(@Background private val backgroun } @WorkerThread - fun startTracing(traceType: PresetTraceType?) { + fun startTracing(traceType: PresetTraceType) { val data = Bundle().apply { putSerializable(MessageConstants.INTENT_EXTRA_TRACE_TYPE, traceType) } notifyTraceur(MessageConstants.START_WHAT, data) diff --git a/packages/SystemUI/src/com/android/systemui/scene/KeyguardlessSceneContainerFrameworkModule.kt b/packages/SystemUI/src/com/android/systemui/scene/KeyguardlessSceneContainerFrameworkModule.kt index 7a9d09ad815a..323ca871e8b0 100644 --- a/packages/SystemUI/src/com/android/systemui/scene/KeyguardlessSceneContainerFrameworkModule.kt +++ b/packages/SystemUI/src/com/android/systemui/scene/KeyguardlessSceneContainerFrameworkModule.kt @@ -18,7 +18,11 @@ package com.android.systemui.scene import com.android.systemui.CoreStartable import com.android.systemui.notifications.ui.composable.NotificationsShadeSessionModule +import com.android.systemui.scene.domain.SceneDomainModule import com.android.systemui.scene.domain.interactor.WindowRootViewVisibilityInteractor +import com.android.systemui.scene.domain.resolver.HomeSceneFamilyResolverModule +import com.android.systemui.scene.domain.resolver.NotifShadeSceneFamilyResolverModule +import com.android.systemui.scene.domain.resolver.QuickSettingsSceneFamilyResolverModule import com.android.systemui.scene.domain.startable.SceneContainerStartable import com.android.systemui.scene.domain.startable.ScrimStartable import com.android.systemui.scene.shared.model.SceneContainerConfig @@ -40,6 +44,12 @@ import dagger.multibindings.IntoMap NotificationsShadeSessionModule::class, QuickSettingsSceneModule::class, ShadeSceneModule::class, + SceneDomainModule::class, + + // List SceneResolver modules for supported SceneFamilies + HomeSceneFamilyResolverModule::class, + NotifShadeSceneFamilyResolverModule::class, + QuickSettingsSceneFamilyResolverModule::class, ], ) interface KeyguardlessSceneContainerFrameworkModule { diff --git a/packages/SystemUI/src/com/android/systemui/scene/SceneContainerFrameworkModule.kt b/packages/SystemUI/src/com/android/systemui/scene/SceneContainerFrameworkModule.kt index 7e6dfb8762cd..4691ebaf95b3 100644 --- a/packages/SystemUI/src/com/android/systemui/scene/SceneContainerFrameworkModule.kt +++ b/packages/SystemUI/src/com/android/systemui/scene/SceneContainerFrameworkModule.kt @@ -19,7 +19,11 @@ package com.android.systemui.scene import com.android.systemui.CoreStartable import com.android.systemui.bouncer.shared.flag.ComposeBouncerFlagsModule import com.android.systemui.notifications.ui.composable.NotificationsShadeSessionModule +import com.android.systemui.scene.domain.SceneDomainModule import com.android.systemui.scene.domain.interactor.WindowRootViewVisibilityInteractor +import com.android.systemui.scene.domain.resolver.HomeSceneFamilyResolverModule +import com.android.systemui.scene.domain.resolver.NotifShadeSceneFamilyResolverModule +import com.android.systemui.scene.domain.resolver.QuickSettingsSceneFamilyResolverModule import com.android.systemui.scene.domain.startable.SceneContainerStartable import com.android.systemui.scene.domain.startable.ScrimStartable import com.android.systemui.scene.shared.model.SceneContainerConfig @@ -46,6 +50,12 @@ import dagger.multibindings.IntoMap QuickSettingsShadeSceneModule::class, NotificationsShadeSceneModule::class, NotificationsShadeSessionModule::class, + SceneDomainModule::class, + + // List SceneResolver modules for supported SceneFamilies + HomeSceneFamilyResolverModule::class, + NotifShadeSceneFamilyResolverModule::class, + QuickSettingsSceneFamilyResolverModule::class, ], ) interface SceneContainerFrameworkModule { diff --git a/packages/SystemUI/src/com/android/systemui/scene/ShadelessSceneContainerFrameworkModule.kt b/packages/SystemUI/src/com/android/systemui/scene/ShadelessSceneContainerFrameworkModule.kt index b918277bd3a4..9a7eef8f76b9 100644 --- a/packages/SystemUI/src/com/android/systemui/scene/ShadelessSceneContainerFrameworkModule.kt +++ b/packages/SystemUI/src/com/android/systemui/scene/ShadelessSceneContainerFrameworkModule.kt @@ -16,6 +16,8 @@ package com.android.systemui.scene +import com.android.systemui.scene.domain.SceneDomainModule +import com.android.systemui.scene.domain.resolver.HomeSceneFamilyResolverModule import com.android.systemui.scene.shared.model.SceneContainerConfig import com.android.systemui.scene.shared.model.Scenes import dagger.Module @@ -29,6 +31,10 @@ import dagger.Provides EmptySceneModule::class, GoneSceneModule::class, LockscreenSceneModule::class, + SceneDomainModule::class, + + // List SceneResolver modules for supported SceneFamilies + HomeSceneFamilyResolverModule::class, ], ) object ShadelessSceneContainerFrameworkModule { @@ -47,11 +53,12 @@ object ShadelessSceneContainerFrameworkModule { Scenes.Bouncer, ), initialSceneKey = Scenes.Lockscreen, - mapOf( - Scenes.Gone to 0, - Scenes.Lockscreen to 0, - Scenes.Bouncer to 1, - ) + navigationDistances = + mapOf( + Scenes.Gone to 0, + Scenes.Lockscreen to 0, + Scenes.Bouncer to 1, + ) ) } } diff --git a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogModule.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/SceneDomainModule.kt index 2e9169e03d80..be792df340c9 100644 --- a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogModule.kt +++ b/packages/SystemUI/src/com/android/systemui/scene/domain/SceneDomainModule.kt @@ -13,17 +13,16 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.android.systemui.bluetooth.qsdialog -import com.android.systemui.dagger.SysUISingleton -import dagger.Binds +package com.android.systemui.scene.domain + +import com.android.systemui.scene.domain.resolver.SceneResolverModule import dagger.Module -@Module -interface BluetoothTileDialogModule { - @Binds - @SysUISingleton - fun bindDeviceItemActionInteractor( - impl: DeviceItemActionInteractorImpl - ): DeviceItemActionInteractor -} +@Module( + includes = + [ + SceneResolverModule::class, + ] +) +object SceneDomainModule diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt index b1700e377725..c98a49bd711d 100644 --- a/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt @@ -23,9 +23,12 @@ import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.deviceentry.domain.interactor.DeviceUnlockedInteractor import com.android.systemui.scene.data.repository.SceneContainerRepository +import com.android.systemui.scene.domain.resolver.SceneResolver import com.android.systemui.scene.shared.logger.SceneLogger +import com.android.systemui.scene.shared.model.SceneFamilies import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.util.kotlin.pairwiseBy +import dagger.Lazy import javax.inject.Inject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.ExperimentalCoroutinesApi @@ -33,7 +36,9 @@ import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.emitAll import kotlinx.coroutines.flow.flatMapLatest +import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.stateIn @@ -52,6 +57,7 @@ constructor( @Application private val applicationScope: CoroutineScope, private val repository: SceneContainerRepository, private val logger: SceneLogger, + private val sceneFamilyResolvers: Lazy<Map<SceneKey, @JvmSuppressWildcards SceneResolver>>, private val deviceUnlockedInteractor: DeviceUnlockedInteractor, ) { @@ -152,6 +158,28 @@ constructor( ) /** + * The amount of transition into or out of the given [scene]. + * + * The value will be `0` if not in this scene or `1` when fully in the given scene. + */ + fun transitionProgress(scene: SceneKey): Flow<Float> { + return transitionState.flatMapLatest { transition -> + when (transition) { + is ObservableTransitionState.Idle -> { + flowOf(if (transition.currentScene == scene) 1f else 0f) + } + is ObservableTransitionState.Transition -> { + when { + transition.toScene == scene -> transition.progress + transition.fromScene == scene -> transition.progress.map { 1f - it } + else -> flowOf(0f) + } + } + } + } + } + + /** * Returns the keys of all scenes in the container. * * The scenes will be sorted in z-order such that the last one is the one that should be @@ -180,10 +208,11 @@ constructor( sceneState: Any? = null, ) { val currentSceneKey = currentScene.value + val resolvedScene = sceneFamilyResolvers.get()[toScene]?.resolvedScene?.value ?: toScene if ( !validateSceneChange( from = currentSceneKey, - to = toScene, + to = resolvedScene, loggingReason = loggingReason, ) ) { @@ -192,13 +221,13 @@ constructor( logger.logSceneChangeRequested( from = currentSceneKey, - to = toScene, + to = resolvedScene, reason = loggingReason, isInstant = false, ) - onSceneAboutToChangeListener.forEach { it.onSceneAboutToChange(toScene, sceneState) } - repository.changeScene(toScene, transitionKey) + onSceneAboutToChangeListener.forEach { it.onSceneAboutToChange(resolvedScene, sceneState) } + repository.changeScene(resolvedScene, transitionKey) } /** @@ -212,10 +241,18 @@ constructor( loggingReason: String, ) { val currentSceneKey = currentScene.value + val resolvedScene = + sceneFamilyResolvers.get()[toScene]?.let { familyResolver -> + if (familyResolver.includesScene(currentSceneKey)) { + return + } else { + familyResolver.resolvedScene.value + } + } ?: toScene if ( !validateSceneChange( from = currentSceneKey, - to = toScene, + to = resolvedScene, loggingReason = loggingReason, ) ) { @@ -224,12 +261,12 @@ constructor( logger.logSceneChangeRequested( from = currentSceneKey, - to = toScene, + to = resolvedScene, reason = loggingReason, isInstant = true, ) - repository.snapToScene(toScene) + repository.snapToScene(resolvedScene) } /** @@ -288,6 +325,14 @@ constructor( repository.setTransitionState(transitionState) } + /** + * Returns the [concrete scene][Scenes] for [sceneKey] if it is a [scene family][SceneFamilies], + * otherwise returns a singleton [Flow] containing [sceneKey]. + */ + fun resolveSceneFamily(sceneKey: SceneKey): Flow<SceneKey> = flow { + emitAll(sceneFamilyResolvers.get()[sceneKey]?.resolvedScene ?: flowOf(sceneKey)) + } + private fun isVisibleInternal( raw: Boolean = repository.isVisible.value, isRemoteUserInteractionOngoing: Boolean = repository.isRemoteUserInteractionOngoing.value, @@ -330,4 +375,12 @@ constructor( return from != to } + + /** Returns a flow indicating if the currently visible scene can be resolved from [family]. */ + fun isCurrentSceneInFamily(family: SceneKey): Flow<Boolean> = + currentScene.map { currentScene -> isSceneInFamily(currentScene, family) } + + /** Returns `true` if [scene] can be resolved from [family]. */ + fun isSceneInFamily(scene: SceneKey, family: SceneKey): Boolean = + sceneFamilyResolvers.get()[family]?.includesScene(scene) == true } diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/resolver/HomeSceneFamilyResolver.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/resolver/HomeSceneFamilyResolver.kt new file mode 100644 index 000000000000..9e91b6604dfc --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/scene/domain/resolver/HomeSceneFamilyResolver.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. + */ + +@file:OptIn(ExperimentalCoroutinesApi::class) + +package com.android.systemui.scene.domain.resolver + +import com.android.compose.animation.scene.SceneKey +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.dagger.qualifiers.Application +import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor +import com.android.systemui.scene.shared.model.SceneFamilies +import com.android.systemui.scene.shared.model.Scenes +import dagger.Binds +import dagger.Module +import dagger.multibindings.IntoSet +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.combine +import kotlinx.coroutines.flow.stateIn + +/** + * Resolver for [SceneFamilies.Home]. The "home" scene family resolves to the scene that is + * currently underneath any "overlay" scene, such as shades or bouncer. + */ +@SysUISingleton +class HomeSceneFamilyResolver +@Inject +constructor( + @Application private val applicationScope: CoroutineScope, + deviceEntryInteractor: DeviceEntryInteractor, +) : SceneResolver { + override val targetFamily: SceneKey = SceneFamilies.Home + + override val resolvedScene: StateFlow<SceneKey> = + combine( + deviceEntryInteractor.canSwipeToEnter, + deviceEntryInteractor.isDeviceEntered, + deviceEntryInteractor.isUnlocked, + transform = ::homeScene, + ) + .stateIn( + scope = applicationScope, + started = SharingStarted.Eagerly, + initialValue = + homeScene( + deviceEntryInteractor.canSwipeToEnter.value, + deviceEntryInteractor.isDeviceEntered.value, + deviceEntryInteractor.isUnlocked.value, + ) + ) + + override fun includesScene(scene: SceneKey): Boolean = scene in homeScenes + + private fun homeScene( + canSwipeToEnter: Boolean?, + isDeviceEntered: Boolean, + isUnlocked: Boolean, + ): SceneKey = + when { + canSwipeToEnter == true -> Scenes.Lockscreen + !isDeviceEntered -> Scenes.Lockscreen + !isUnlocked -> Scenes.Lockscreen + else -> Scenes.Gone + } + + companion object { + val homeScenes = + setOf( + Scenes.Gone, + Scenes.Lockscreen, + ) + } +} + +@Module +interface HomeSceneFamilyResolverModule { + @Binds @IntoSet fun provideSceneResolver(interactor: HomeSceneFamilyResolver): SceneResolver +} diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/resolver/NotifShadeSceneFamilyResolver.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/resolver/NotifShadeSceneFamilyResolver.kt new file mode 100644 index 000000000000..99e554ea5595 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/scene/domain/resolver/NotifShadeSceneFamilyResolver.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.scene.domain.resolver + +import com.android.compose.animation.scene.SceneKey +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.dagger.qualifiers.Application +import com.android.systemui.scene.shared.model.SceneFamilies +import com.android.systemui.scene.shared.model.Scenes +import com.android.systemui.shade.domain.interactor.ShadeInteractor +import com.android.systemui.shade.shared.model.ShadeMode +import dagger.Binds +import dagger.Module +import dagger.multibindings.IntoSet +import javax.inject.Inject +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.stateIn + +@SysUISingleton +class NotifShadeSceneFamilyResolver +@Inject +constructor( + @Application applicationScope: CoroutineScope, + shadeInteractor: ShadeInteractor, +) : SceneResolver { + override val targetFamily: SceneKey = SceneFamilies.NotifShade + + override val resolvedScene: StateFlow<SceneKey> = + shadeInteractor.shadeMode + .map(::notifShadeScene) + .stateIn( + applicationScope, + started = SharingStarted.Eagerly, + initialValue = notifShadeScene(shadeInteractor.shadeMode.value), + ) + + override fun includesScene(scene: SceneKey): Boolean = scene in notifShadeScenes + + private fun notifShadeScene(shadeMode: ShadeMode) = + when (shadeMode) { + is ShadeMode.Single -> Scenes.Shade + is ShadeMode.Dual -> Scenes.NotificationsShade + is ShadeMode.Split -> Scenes.Shade + } + + companion object { + val notifShadeScenes = + setOf( + Scenes.NotificationsShade, + Scenes.Shade, + ) + } +} + +@Module +interface NotifShadeSceneFamilyResolverModule { + @Binds @IntoSet fun bindResolver(interactor: NotifShadeSceneFamilyResolver): SceneResolver +} diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/resolver/QuickSettingsSceneFamilyResolver.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/resolver/QuickSettingsSceneFamilyResolver.kt new file mode 100644 index 000000000000..2962a3ec903d --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/scene/domain/resolver/QuickSettingsSceneFamilyResolver.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.scene.domain.resolver + +import com.android.compose.animation.scene.SceneKey +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.dagger.qualifiers.Application +import com.android.systemui.scene.shared.model.SceneFamilies +import com.android.systemui.scene.shared.model.Scenes +import com.android.systemui.shade.domain.interactor.ShadeInteractor +import com.android.systemui.shade.shared.model.ShadeMode +import dagger.Binds +import dagger.Module +import dagger.multibindings.IntoSet +import javax.inject.Inject +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.stateIn + +@SysUISingleton +class QuickSettingsSceneFamilyResolver +@Inject +constructor( + @Application applicationScope: CoroutineScope, + shadeInteractor: ShadeInteractor, +) : SceneResolver { + override val targetFamily: SceneKey = SceneFamilies.QuickSettings + + override val resolvedScene: StateFlow<SceneKey> = + shadeInteractor.shadeMode + .map(::quickSettingsScene) + .stateIn( + applicationScope, + started = SharingStarted.Eagerly, + initialValue = quickSettingsScene(shadeInteractor.shadeMode.value), + ) + + override fun includesScene(scene: SceneKey): Boolean = scene in quickSettingsScenes + + private fun quickSettingsScene(shadeMode: ShadeMode) = + when (shadeMode) { + is ShadeMode.Single -> Scenes.QuickSettings + is ShadeMode.Dual -> Scenes.QuickSettingsShade + is ShadeMode.Split -> Scenes.Shade + } + + companion object { + val quickSettingsScenes = + setOf( + Scenes.QuickSettings, + Scenes.QuickSettingsShade, + Scenes.Shade, + ) + } +} + +@Module +interface QuickSettingsSceneFamilyResolverModule { + @Binds @IntoSet fun bindResolver(interactor: QuickSettingsSceneFamilyResolver): SceneResolver +} diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/resolver/SceneResolver.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/resolver/SceneResolver.kt new file mode 100644 index 000000000000..8d7247beb18d --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/scene/domain/resolver/SceneResolver.kt @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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.scene.domain.resolver + +import com.android.compose.animation.scene.SceneKey +import dagger.Module +import dagger.Provides +import dagger.multibindings.Multibinds +import kotlinx.coroutines.flow.StateFlow + +/** Resolves [concrete scenes][Scenes] from a [scene family][SceneFamilies]. */ +interface SceneResolver { + /** The scene family that this resolves. */ + val targetFamily: SceneKey + + /** The concrete scene that [targetFamily] is currently resolved to. */ + val resolvedScene: StateFlow<SceneKey> + + /** Returns `true` if [scene] can be resolved from [targetFamily]. */ + fun includesScene(scene: SceneKey): Boolean +} + +@Module +interface SceneResolverModule { + + @Multibinds fun resolverSet(): Set<@JvmSuppressWildcards SceneResolver> + + companion object { + @Provides + fun provideResolverMap( + resolverSet: Set<@JvmSuppressWildcards SceneResolver> + ): Map<SceneKey, SceneResolver> = resolverSet.associateBy { it.targetFamily } + } +} 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 0304e730cb68..1e689bd11fa3 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 @@ -39,6 +39,7 @@ import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor import com.android.systemui.deviceentry.domain.interactor.DeviceUnlockedInteractor import com.android.systemui.deviceentry.shared.model.DeviceUnlockSource 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 @@ -81,6 +82,7 @@ import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.filterIsInstance import kotlinx.coroutines.flow.filterNot +import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.map @@ -120,6 +122,7 @@ constructor( private val uiEventLogger: UiEventLogger, private val sceneBackInteractor: SceneBackInteractor, private val shadeSessionStorage: SessionStorage, + private val windowMgrLockscreenVisInteractor: WindowManagerLockscreenVisibilityInteractor, ) : CoreStartable { private val centralSurfaces: CentralSurfaces? get() = centralSurfacesOptLazy.get().getOrNull() @@ -227,6 +230,25 @@ constructor( handleDeviceUnlockStatus() handlePowerState() handleShadeTouchability() + handleSurfaceBehindKeyguardVisibility() + } + + private fun handleSurfaceBehindKeyguardVisibility() { + applicationScope.launch { + sceneInteractor.currentScene.collectLatest { currentScene -> + if (currentScene == Scenes.Lockscreen) { + // 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() { @@ -329,8 +351,7 @@ constructor( Scenes.Gone to "device was unlocked in Bouncer scene" } else { val prevScene = previousScene.value - (prevScene - ?: Scenes.Gone) to + (prevScene ?: Scenes.Gone) to "device was unlocked in Bouncer scene, from sceneKey=$prevScene" } isOnLockscreen -> diff --git a/packages/SystemUI/src/com/android/systemui/scene/shared/model/Scenes.kt b/packages/SystemUI/src/com/android/systemui/scene/shared/model/Scenes.kt index 6d139da99345..ef5290ffca65 100644 --- a/packages/SystemUI/src/com/android/systemui/scene/shared/model/Scenes.kt +++ b/packages/SystemUI/src/com/android/systemui/scene/shared/model/Scenes.kt @@ -64,7 +64,7 @@ object Scenes { * * For the "split" shade configuration (on large screens or unfolded foldables, where * notifications and quick settings are shown side-by-side in their own columns), the [Shade] - * scene is used]. + * scene is used. * * For the dual shade configuration, where there are two separate shades: one for notifications * and one for quick settings, [NotificationsShade] and [QuickSettingsShade] scenes are used @@ -97,3 +97,29 @@ object Scenes { */ @JvmField val Shade = SceneKey("shade") } + +/** + * Keys of all known scene families. A scene family is an alias that is resolved to a specific scene + * from [Scenes] dynamically. + * + * PLEASE KEEP THE KEYS SORTED ALPHABETICALLY. + */ +object SceneFamilies { + /** + * The "base" scene, from the user's perspective. This is generally Gone or Lockscreen, + * depending on whether the device is unlocked and has been entered. + */ + @JvmField val Home = SceneKey(debugName = "scene_family_home") + + /** + * The scene that contains the full, interactive notification shade. The specific scene it + * resolves to can depend on dual / split / single shade settings. + */ + @JvmField val NotifShade = SceneKey(debugName = "scene_family_notif_shade") + + /** + * The scene that contains the full QuickSettings (not to be confused with Quick-QuickSettings). + * The specific scene it resolves to can depend on dual / split / single shade settings. + */ + @JvmField val QuickSettings = SceneKey(debugName = "scene_family_quick_settings") +} diff --git a/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/GoneSceneViewModel.kt b/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/GoneSceneViewModel.kt index 99118bcfc322..c34a6cdf17df 100644 --- a/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/GoneSceneViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/GoneSceneViewModel.kt @@ -23,7 +23,7 @@ import com.android.compose.animation.scene.UserAction import com.android.compose.animation.scene.UserActionResult import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application -import com.android.systemui.scene.shared.model.Scenes +import com.android.systemui.scene.shared.model.SceneFamilies import com.android.systemui.scene.shared.model.TransitionKeys.ToSplitShade import com.android.systemui.shade.domain.interactor.ShadeInteractor import com.android.systemui.shade.shared.model.ShadeMode @@ -43,39 +43,42 @@ constructor( ) { val destinationScenes: StateFlow<Map<UserAction, UserActionResult>> = shadeInteractor.shadeMode - .map { shadeMode -> destinationScenes(shadeMode = shadeMode) } + .map(::destinationScenes) .stateIn( scope = applicationScope, started = SharingStarted.WhileSubscribed(), - initialValue = destinationScenes(shadeMode = shadeInteractor.shadeMode.value) + initialValue = + destinationScenes( + shadeMode = shadeInteractor.shadeMode.value, + ) ) - private fun destinationScenes(shadeMode: ShadeMode): Map<UserAction, UserActionResult> { + private fun destinationScenes( + shadeMode: ShadeMode, + ): Map<UserAction, UserActionResult> { return buildMap { - if (shadeMode is ShadeMode.Single) { - this[ + if ( + shadeMode is ShadeMode.Single || + // TODO(b/338577208): Remove this once we add Dual Shade invocation zones. + shadeMode is ShadeMode.Dual + ) { + put( Swipe( pointerCount = 2, fromSource = Edge.Top, direction = SwipeDirection.Down, - )] = UserActionResult(Scenes.QuickSettings) + ), + UserActionResult(SceneFamilies.QuickSettings) + ) } - // TODO(b/338577208): Remove this once we add Dual Shade invocation zones. - if (shadeMode is ShadeMode.Dual) { - this[ - Swipe( - pointerCount = 2, - fromSource = Edge.Top, - direction = SwipeDirection.Down, - )] = UserActionResult(Scenes.QuickSettingsShade) - } - - val downSceneKey = - if (shadeMode is ShadeMode.Dual) Scenes.NotificationsShade else Scenes.Shade - val downTransitionKey = ToSplitShade.takeIf { shadeMode is ShadeMode.Split } - this[Swipe(direction = SwipeDirection.Down)] = - UserActionResult(downSceneKey, downTransitionKey) + put( + Swipe(direction = SwipeDirection.Down), + UserActionResult( + SceneFamilies.NotifShade, + ToSplitShade.takeIf { shadeMode is ShadeMode.Split } + ) + ) } } } diff --git a/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModel.kt b/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModel.kt index 09c80b09a388..ab24e0bbb690 100644 --- a/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModel.kt @@ -34,6 +34,8 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.stateIn /** Models UI state for the scene container. */ @@ -61,7 +63,9 @@ constructor( val isVisible: StateFlow<Boolean> = sceneInteractor.isVisible private val destinationScenesBySceneKey = - scenes.associate { scene -> scene.key to scene.destinationScenes } + scenes.associate { scene -> + scene.key to scene.destinationScenes.flatMapLatestConflated { replaceSceneFamilies(it) } + } fun currentDestinationScenes( scope: CoroutineScope, @@ -140,7 +144,24 @@ constructor( val fromLockscreenScene = currentScene.value == Scenes.Lockscreen !fromLockscreenScene || !isFalseTouch - } - ?: true + } ?: true + } + + private fun replaceSceneFamilies( + destinationScenes: Map<UserAction, UserActionResult>, + ): Flow<Map<UserAction, UserActionResult>> { + return destinationScenes + .mapValues { (_, actionResult) -> + sceneInteractor.resolveSceneFamily(actionResult.toScene).map { scene -> + actionResult.copy(toScene = scene) + } + } + .combineValueFlows() } } + +private fun <K, V> Map<K, Flow<V>>.combineValueFlows(): Flow<Map<K, V>> = + combine( + asIterable().map { (k, fv) -> fv.map { k to it } }, + transform = Array<Pair<K, V>>::toMap, + ) diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/data/repository/ScreenRecordRepository.kt b/packages/SystemUI/src/com/android/systemui/screenrecord/data/repository/ScreenRecordRepository.kt index d59d220abe3f..9eeb3b9576d8 100644 --- a/packages/SystemUI/src/com/android/systemui/screenrecord/data/repository/ScreenRecordRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/screenrecord/data/repository/ScreenRecordRepository.kt @@ -28,6 +28,7 @@ import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.onStart +import kotlinx.coroutines.withContext /** * Repository storing information about the state of screen recording. @@ -38,6 +39,9 @@ import kotlinx.coroutines.flow.onStart interface ScreenRecordRepository { /** The current screen recording state. Note that this is a cold flow. */ val screenRecordState: Flow<ScreenRecordModel> + + /** Stops the recording. */ + suspend fun stopRecording() } @SysUISingleton @@ -90,4 +94,8 @@ constructor( ScreenRecordModel.DoingNothing } } + + override suspend fun stopRecording() { + withContext(bgCoroutineContext) { recordingController.stopRecording() } + } } 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 80aa0efb0a19..4f27b9e4dbf0 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/policy/PolicyRequestProcessor.kt +++ b/packages/SystemUI/src/com/android/systemui/screenshot/policy/PolicyRequestProcessor.kt @@ -79,7 +79,10 @@ class PolicyRequestProcessor( } /** Produce a new [ScreenshotData] using [CaptureParameters] */ - suspend fun modify(original: ScreenshotData, updates: CaptureParameters): ScreenshotData { + private suspend fun modify( + original: ScreenshotData, + updates: CaptureParameters, + ): ScreenshotData { // Update and apply bitmap capture depending on the parameters. val updated = when (val type = updates.type) { @@ -117,7 +120,7 @@ class PolicyRequestProcessor( return replaceWithScreenshot( original = original, componentName = topMainRootTask?.topActivity ?: defaultComponent, - owner = topMainRootTask?.userId?.let { UserHandle.of(it) } ?: defaultOwner, + owner = defaultOwner, displayId = original.displayId ) } diff --git a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderView.java b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderView.java index 92006a473ed8..a39d25a5ef30 100644 --- a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderView.java +++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderView.java @@ -67,6 +67,7 @@ public class BrightnessSliderView extends FrameLayout { mSlider = requireViewById(R.id.slider); mSlider.setAccessibilityLabel(getContentDescription().toString()); + setBoundaryOffset(); // Finds the progress drawable. Assumes brightness_progress_drawable.xml try { @@ -80,6 +81,17 @@ public class BrightnessSliderView extends FrameLayout { } } + private void setBoundaryOffset() { + // BrightnessSliderView uses hardware layer; if the background of its children exceed its + // boundary, it'll be cropped. We need to expand its boundary so that the background of + // ToggleSeekBar (i.e. the focus state) can be correctly rendered. + int offset = getResources().getDimensionPixelSize(R.dimen.rounded_slider_boundary_offset); + MarginLayoutParams lp = (MarginLayoutParams) getLayoutParams(); + lp.setMargins(-offset, -offset, -offset, -offset); + setLayoutParams(lp); + setPadding(offset, offset, offset, offset); + } + /** * Attaches a listener to relay touch events. * @param listener use {@code null} to remove listener diff --git a/packages/SystemUI/src/com/android/systemui/settings/brightness/ToggleSeekBar.java b/packages/SystemUI/src/com/android/systemui/settings/brightness/ToggleSeekBar.java index 84156eeb9264..30b6892731f1 100644 --- a/packages/SystemUI/src/com/android/systemui/settings/brightness/ToggleSeekBar.java +++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/ToggleSeekBar.java @@ -16,12 +16,16 @@ package com.android.systemui.settings.brightness; +import static com.android.systemui.Flags.brightnessSliderFocusState; + import android.content.Context; import android.util.AttributeSet; import android.view.MotionEvent; import android.view.accessibility.AccessibilityNodeInfo; import android.widget.SeekBar; +import com.android.systemui.res.R; + public class ToggleSeekBar extends SeekBar { private String mAccessibilityLabel; @@ -61,6 +65,14 @@ public class ToggleSeekBar extends SeekBar { return true; } + @Override + protected void onFinishInflate() { + super.onFinishInflate(); + if (brightnessSliderFocusState()) { + setBackground(mContext.getDrawable(R.drawable.brightness_slider_focus_bg)); + } + } + public void setAccessibilityLabel(String label) { mAccessibilityLabel = label; } diff --git a/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt b/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt index bf0843b8fa4e..2def6c7cfdc2 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt @@ -157,9 +157,15 @@ constructor( private var anyBouncerShowing = false /** - * True if the shade is fully expanded, meaning the hub should not receive any touch input. + * True if the shade is fully expanded and the user is not interacting with it anymore, meaning + * the hub should not receive any touch input. * - * Tracks [ShadeInteractor.isAnyFullyExpanded]. + * We need to not pause the touch handling lifecycle as soon as the shade opens because if the + * user swipes down, then back up without lifting their finger, the lifecycle will be paused + * then resumed, and resuming force-stops all active touch sessions. This means the shade will + * not receive the end of the gesture and will be stuck open. + * + * Based on [ShadeInteractor.isAnyFullyExpanded] and [ShadeInteractor.isUserInteracting]. */ private var shadeShowing = false diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelUnfoldAnimationController.kt b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelUnfoldAnimationController.kt index 14659e7909ab..f463cb5bcbd6 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelUnfoldAnimationController.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelUnfoldAnimationController.kt @@ -47,6 +47,7 @@ constructor( viewsIdToTranslate = 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) } @@ -55,9 +56,8 @@ constructor( UnfoldConstantTranslateAnimator( viewsIdToTranslate = setOf( - ViewIdToTranslate(R.id.statusIcons, END, filterShade), + ViewIdToTranslate(R.id.shade_header_system_icons, END, filterShade), ViewIdToTranslate(R.id.privacy_container, END, filterShade), - ViewIdToTranslate(R.id.batteryRemainingIcon, END, filterShade), ViewIdToTranslate(R.id.carrier_group, END, filterShade), ViewIdToTranslate(R.id.clock, START, filterShade), ViewIdToTranslate(R.id.date, START, filterShade) diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java index 3826b50ab024..262befc1e9ad 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java +++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java @@ -537,8 +537,6 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump private final KeyguardMediaController mKeyguardMediaController; private final Optional<KeyguardUnfoldTransition> mKeyguardUnfoldTransition; - private final Optional<NotificationPanelUnfoldAnimationController> - mNotificationPanelUnfoldAnimationController; /** The drag distance required to fully expand the split shade. */ private int mSplitShadeFullTransitionDistance; @@ -964,8 +962,6 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump mKeyguardUnfoldTransition = unfoldComponent.map( SysUIUnfoldComponent::getKeyguardUnfoldTransition); - mNotificationPanelUnfoldAnimationController = unfoldComponent.map( - SysUIUnfoldComponent::getNotificationPanelUnfoldAnimationController); updateUserSwitcherFlags(); mKeyguardBottomAreaViewModel = keyguardBottomAreaViewModel; @@ -1131,9 +1127,6 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump mShadeHeaderController.init(); mShadeHeaderController.setShadeCollapseAction( () -> collapse(/* delayed= */ false , /* speedUpFactor= */ 1.0f)); - mKeyguardUnfoldTransition.ifPresent(u -> u.setup(mView)); - mNotificationPanelUnfoldAnimationController.ifPresent(controller -> - controller.setup(mNotificationContainerParent)); // Dreaming->Lockscreen collectFlow( @@ -1511,9 +1504,6 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump if (!KeyguardBottomAreaRefactor.isEnabled()) { setKeyguardBottomAreaVisibility(mBarState, false); } - - mKeyguardUnfoldTransition.ifPresent(u -> u.setup(mView)); - mNotificationPanelUnfoldAnimationController.ifPresent(u -> u.setup(mView)); } private void attachSplitShadeMediaPlayerContainer(FrameLayout container) { @@ -1797,6 +1787,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump private void updateKeyguardStatusViewAlignment(boolean animate) { boolean shouldBeCentered = shouldKeyguardStatusViewBeCentered(); + mKeyguardUnfoldTransition.ifPresent(t -> t.setStatusViewCentered(shouldBeCentered)); if (MigrateClocksToBlueprint.isEnabled()) { mKeyguardInteractor.setClockShouldBeCentered(shouldBeCentered); return; @@ -1804,7 +1795,6 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump ConstraintLayout layout = mNotificationContainerParent; mKeyguardStatusViewController.updateAlignment( layout, mSplitShadeEnabled, shouldBeCentered, animate); - mKeyguardUnfoldTransition.ifPresent(t -> t.setStatusViewCentered(shouldBeCentered)); } private boolean shouldKeyguardStatusViewBeCentered() { diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java index 6efa6334b968..e41f99b99e35 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java +++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java @@ -32,6 +32,7 @@ import android.view.ViewGroup; import com.android.internal.annotations.VisibleForTesting; import com.android.keyguard.AuthKeyguardMessageArea; +import com.android.keyguard.KeyguardUnfoldTransition; import com.android.keyguard.LockIconViewController; import com.android.systemui.Dumpable; import com.android.systemui.animation.ActivityTransitionAnimator; @@ -72,6 +73,7 @@ import com.android.systemui.statusbar.phone.DozeServiceHost; import com.android.systemui.statusbar.phone.PhoneStatusBarViewController; import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager; import com.android.systemui.statusbar.window.StatusBarWindowStateController; +import com.android.systemui.unfold.SysUIUnfoldComponent; import com.android.systemui.unfold.UnfoldTransitionProgressProvider; import com.android.systemui.util.time.SystemClock; @@ -138,6 +140,11 @@ public class NotificationShadeWindowViewController implements Dumpable { private final PanelExpansionInteractor mPanelExpansionInteractor; private final ShadeExpansionStateManager mShadeExpansionStateManager; + /** + * If {@code true}, an external touch sent in {@link #handleExternalTouch(MotionEvent)} has been + * intercepted and all future touch events for the gesture should be processed by this view. + */ + private boolean mExternalTouchIntercepted = false; private boolean mIsTrackingBarGesture = false; private boolean mIsOcclusionTransitionRunning = false; private DisableSubpixelTextTransitionListener mDisableSubpixelTextTransitionListener; @@ -169,6 +176,7 @@ public class NotificationShadeWindowViewController implements Dumpable { DozeScrimController dozeScrimController, NotificationShadeWindowController controller, Optional<UnfoldTransitionProgressProvider> unfoldTransitionProgressProvider, + Optional<SysUIUnfoldComponent> unfoldComponent, KeyguardUnlockAnimationController keyguardUnlockAnimationController, NotificationInsetsController notificationInsetsController, AmbientState ambientState, @@ -222,13 +230,21 @@ public class NotificationShadeWindowViewController implements Dumpable { bouncerViewBinder.bind(mView.findViewById(R.id.keyguard_bouncer_container)); collectFlow(mView, keyguardTransitionInteractor.transition( - Edge.Companion.create(LOCKSCREEN, DREAMING)), + Edge.create(LOCKSCREEN, DREAMING)), mLockscreenToDreamingTransition); collectFlow( mView, notificationLaunchAnimationInteractor.isLaunchAnimationRunning(), this::setExpandAnimationRunning); + var keyguardUnfoldTransition = unfoldComponent.map( + SysUIUnfoldComponent::getKeyguardUnfoldTransition); + var notificationPanelUnfoldAnimationController = unfoldComponent.map( + SysUIUnfoldComponent::getNotificationPanelUnfoldAnimationController); + + keyguardUnfoldTransition.ifPresent(KeyguardUnfoldTransition::setup); + notificationPanelUnfoldAnimationController.ifPresent(u -> u.setup(mView)); + mClock = clock; if (featureFlagsClassic.isEnabled(Flags.SPLIT_SHADE_SUBPIXEL_OPTIMIZATION)) { unfoldTransitionProgressProvider.ifPresent( @@ -255,11 +271,28 @@ public class NotificationShadeWindowViewController implements Dumpable { } /** - * Handle a touch event while dreaming by forwarding the event to the content view. + * Handle a touch event while dreaming or on the hub by forwarding the event to the content + * view. + * <p> + * Since important logic for handling touches lives in the dispatch/intercept phases, we + * simulate going through all of these stages before sending onTouchEvent if intercepted. + * * @param event The event to forward. */ - public void handleDreamTouch(MotionEvent event) { - mView.dispatchTouchEvent(event); + public void handleExternalTouch(MotionEvent event) { + if (event.getActionMasked() == MotionEvent.ACTION_DOWN) { + mExternalTouchIntercepted = false; + } + + if (!mView.dispatchTouchEvent(event)) { + return; + } + if (!mExternalTouchIntercepted) { + mExternalTouchIntercepted = mView.onInterceptTouchEvent(event); + } + if (mExternalTouchIntercepted) { + mView.onTouchEvent(event); + } } /** Inflates the {@link R.layout#status_bar_expanded} layout and sets it up. */ diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerSceneImpl.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerSceneImpl.kt index 884ccef3a080..004db16b7e92 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerSceneImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerSceneImpl.kt @@ -17,21 +17,16 @@ package com.android.systemui.shade import android.view.MotionEvent -import com.android.compose.animation.scene.SceneKey import com.android.systemui.assist.AssistManager import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.dagger.qualifiers.Main -import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor -import com.android.systemui.deviceentry.domain.interactor.DeviceUnlockedInteractor -import com.android.systemui.log.LogBuffer -import com.android.systemui.log.dagger.ShadeTouchLog import com.android.systemui.scene.domain.interactor.SceneInteractor +import com.android.systemui.scene.shared.model.SceneFamilies import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.scene.shared.model.TransitionKeys.SlightlyFasterShadeCollapse import com.android.systemui.shade.ShadeController.ShadeVisibilityListener import com.android.systemui.shade.domain.interactor.ShadeInteractor -import com.android.systemui.shade.shared.model.ShadeMode import com.android.systemui.statusbar.CommandQueue import com.android.systemui.statusbar.NotificationShadeWindowController import com.android.systemui.statusbar.VibratorHelper @@ -61,10 +56,7 @@ constructor( @Background private val scope: CoroutineScope, private val shadeInteractor: ShadeInteractor, private val sceneInteractor: SceneInteractor, - private val deviceEntryInteractor: DeviceEntryInteractor, - private val deviceUnlockedInteractor: DeviceUnlockedInteractor, private val notificationStackScrollLayout: NotificationStackScrollLayout, - @ShadeTouchLog private val touchLog: LogBuffer, private val vibratorHelper: VibratorHelper, commandQueue: CommandQueue, statusBarKeyguardViewManager: StatusBarKeyguardViewManager, @@ -100,7 +92,7 @@ constructor( override fun instantCollapseShade() { sceneInteractor.snapToScene( - getCollapseDestinationScene(), + SceneFamilies.Home, "hide shade", ) } @@ -140,24 +132,12 @@ constructor( private fun animateCollapseShadeInternal() { sceneInteractor.changeScene( - getCollapseDestinationScene(), // TODO(b/336581871): add sceneState? + SceneFamilies.Home, // TODO(b/336581871): add sceneState? "ShadeController.animateCollapseShade", SlightlyFasterShadeCollapse, ) } - private fun getCollapseDestinationScene(): SceneKey { - // Always check whether device is unlocked before transitioning to gone scene. - return if ( - deviceUnlockedInteractor.deviceUnlockStatus.value.isUnlocked && - deviceEntryInteractor.isDeviceEntered.value - ) { - Scenes.Gone - } else { - Scenes.Lockscreen - } - } - override fun cancelExpansionAndCollapseShade() { // TODO do we need to actually cancel the touch session? animateCollapseShade() @@ -192,19 +172,14 @@ constructor( } override fun expandToNotifications() { - val shadeMode = shadeInteractor.shadeMode.value sceneInteractor.changeScene( - if (shadeMode is ShadeMode.Dual) Scenes.NotificationsShade else Scenes.Shade, - "ShadeController.animateExpandShade" + SceneFamilies.NotifShade, + "ShadeController.animateExpandShade", ) } override fun expandToQs() { - val shadeMode = shadeInteractor.shadeMode.value - sceneInteractor.changeScene( - if (shadeMode is ShadeMode.Dual) Scenes.QuickSettingsShade else Scenes.QuickSettings, - "ShadeController.animateExpandQs" - ) + sceneInteractor.changeScene(SceneFamilies.QuickSettings, "ShadeController.animateExpandQs") } override fun setVisibilityListener(listener: ShadeVisibilityListener) { diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeBackActionInteractorImpl.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeBackActionInteractorImpl.kt index 55bd8c6c0834..3a483f460db7 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeBackActionInteractorImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeBackActionInteractorImpl.kt @@ -18,6 +18,7 @@ package com.android.systemui.shade.domain.interactor import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor import com.android.systemui.scene.domain.interactor.SceneInteractor +import com.android.systemui.scene.shared.model.SceneFamilies import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.shade.shared.model.ShadeMode import javax.inject.Inject @@ -36,11 +37,7 @@ constructor( if (shadeInteractor.isQsExpanded.value) { val key = if (fullyCollapse || shadeInteractor.shadeMode.value is ShadeMode.Dual) { - if (deviceEntryInteractor.isDeviceEntered.value) { - Scenes.Gone - } else { - Scenes.Lockscreen - } + SceneFamilies.Home } else { Scenes.Shade } diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorSceneContainerImpl.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorSceneContainerImpl.kt index fe16fc03ce40..d5b4f4d7e623 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorSceneContainerImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorSceneContainerImpl.kt @@ -21,25 +21,23 @@ import com.android.compose.animation.scene.SceneKey import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.scene.domain.interactor.SceneInteractor -import com.android.systemui.scene.shared.model.Scenes +import com.android.systemui.scene.shared.model.SceneFamilies import com.android.systemui.shade.data.repository.ShadeRepository import com.android.systemui.shade.shared.model.ShadeMode import com.android.systemui.statusbar.notification.stack.domain.interactor.SharedNotificationContainerInteractor +import com.android.systemui.utils.coroutines.flow.flatMapLatestConflated import javax.inject.Inject import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.distinctUntilChanged -import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.stateIn /** ShadeInteractor implementation for Scene Container. */ -@OptIn(ExperimentalCoroutinesApi::class) @SysUISingleton class ShadeInteractorSceneContainerImpl @Inject @@ -52,10 +50,11 @@ constructor( override val shadeMode: StateFlow<ShadeMode> = shadeRepository.shadeMode override val shadeExpansion: StateFlow<Float> = - sceneBasedExpansion(sceneInteractor, notificationsScene) + sceneBasedExpansion(sceneInteractor, SceneFamilies.NotifShade) .stateIn(scope, SharingStarted.Eagerly, 0f) - private val sceneBasedQsExpansion = sceneBasedExpansion(sceneInteractor, quickSettingsScene) + private val sceneBasedQsExpansion = + sceneBasedExpansion(sceneInteractor, SceneFamilies.QuickSettings) override val qsExpansion: StateFlow<Float> = combine( @@ -78,23 +77,38 @@ constructor( .stateIn(scope, SharingStarted.Eagerly, false) override val isQsBypassingShade: Flow<Boolean> = - sceneInteractor.transitionState - .map { state -> - when (state) { - is ObservableTransitionState.Idle -> false - is ObservableTransitionState.Transition -> - state.toScene == quickSettingsScene && state.fromScene != notificationsScene - } + combine( + sceneInteractor.resolveSceneFamily(SceneFamilies.QuickSettings), + sceneInteractor.resolveSceneFamily(SceneFamilies.NotifShade), + ::Pair + ) + .flatMapLatestConflated { (quickSettingsScene, notificationsScene) -> + sceneInteractor.transitionState + .map { state -> + when (state) { + is ObservableTransitionState.Idle -> false + is ObservableTransitionState.Transition -> + state.toScene == quickSettingsScene && + state.fromScene != notificationsScene + } + } + .distinctUntilChanged() } .distinctUntilChanged() override val isQsFullscreen: Flow<Boolean> = - sceneInteractor.transitionState - .map { state -> - when (state) { - is ObservableTransitionState.Idle -> state.currentScene == quickSettingsScene - is ObservableTransitionState.Transition -> false - } + sceneInteractor + .resolveSceneFamily(SceneFamilies.QuickSettings) + .flatMapLatestConflated { quickSettingsScene -> + sceneInteractor.transitionState + .map { state -> + when (state) { + is ObservableTransitionState.Idle -> + state.currentScene == quickSettingsScene + is ObservableTransitionState.Transition -> false + } + } + .distinctUntilChanged() } .distinctUntilChanged() @@ -108,34 +122,39 @@ constructor( .stateIn(scope, SharingStarted.Eagerly, false) override val isUserInteractingWithShade: Flow<Boolean> = - sceneBasedInteracting(sceneInteractor, notificationsScene) + sceneBasedInteracting(sceneInteractor, SceneFamilies.NotifShade) override val isUserInteractingWithQs: Flow<Boolean> = - sceneBasedInteracting(sceneInteractor, quickSettingsScene) + sceneBasedInteracting(sceneInteractor, SceneFamilies.QuickSettings) /** * Returns a flow that uses scene transition progress to and from a scene that is pulled down * from the top of the screen to a 0-1 expansion amount float. */ fun sceneBasedExpansion(sceneInteractor: SceneInteractor, sceneKey: SceneKey) = - sceneInteractor.transitionState - .flatMapLatest { state -> - when (state) { - is ObservableTransitionState.Idle -> - if (state.currentScene == sceneKey) { - flowOf(1f) - } else { - flowOf(0f) + sceneInteractor + .resolveSceneFamily(sceneKey) + .flatMapLatestConflated { resolvedSceneKey -> + sceneInteractor.transitionState + .flatMapLatestConflated { state -> + when (state) { + is ObservableTransitionState.Idle -> + if (state.currentScene == resolvedSceneKey) { + flowOf(1f) + } else { + flowOf(0f) + } + is ObservableTransitionState.Transition -> + if (state.toScene == resolvedSceneKey) { + state.progress + } else if (state.fromScene == resolvedSceneKey) { + state.progress.map { progress -> 1 - progress } + } else { + flowOf(0f) + } } - is ObservableTransitionState.Transition -> - if (state.toScene == sceneKey) { - state.progress - } else if (state.fromScene == sceneKey) { - state.progress.map { progress -> 1 - progress } - } else { - flowOf(0f) - } - } + } + .distinctUntilChanged() } .distinctUntilChanged() @@ -145,29 +164,16 @@ constructor( */ fun sceneBasedInteracting(sceneInteractor: SceneInteractor, sceneKey: SceneKey) = sceneInteractor.transitionState - .map { state -> + .flatMapLatestConflated { state -> when (state) { - is ObservableTransitionState.Idle -> false + is ObservableTransitionState.Idle -> flowOf(false) is ObservableTransitionState.Transition -> - state.isInitiatedByUserInput && - (state.toScene == sceneKey || state.fromScene == sceneKey) + sceneInteractor.resolveSceneFamily(sceneKey).map { resolvedSceneKey -> + state.isInitiatedByUserInput && + (state.toScene == resolvedSceneKey || + state.fromScene == resolvedSceneKey) + } } } .distinctUntilChanged() - - private val notificationsScene: SceneKey - get() = - if (shadeMode.value is ShadeMode.Dual) { - Scenes.NotificationsShade - } else { - Scenes.Shade - } - - private val quickSettingsScene: SceneKey - get() = - if (shadeMode.value is ShadeMode.Dual) { - Scenes.QuickSettingsShade - } else { - Scenes.QuickSettings - } } diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeLockscreenInteractorImpl.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeLockscreenInteractorImpl.kt index e7fc18ebfc21..558f179a5f55 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeLockscreenInteractorImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeLockscreenInteractorImpl.kt @@ -17,12 +17,11 @@ package com.android.systemui.shade.domain.interactor import com.android.keyguard.LockIconViewController -import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.scene.domain.interactor.SceneInteractor +import com.android.systemui.scene.shared.model.SceneFamilies import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.shade.data.repository.ShadeRepository -import com.android.systemui.shade.shared.model.ShadeMode import javax.inject.Inject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.delay @@ -31,7 +30,6 @@ import kotlinx.coroutines.launch class ShadeLockscreenInteractorImpl @Inject constructor( - @Application private val applicationScope: CoroutineScope, @Background private val backgroundScope: CoroutineScope, private val shadeInteractor: ShadeInteractor, private val sceneInteractor: SceneInteractor, @@ -69,6 +67,7 @@ constructor( override fun setPulsing(pulsing: Boolean) { // Now handled elsewhere. Do nothing. } + override fun transitionToExpandedShade(delay: Long) { backgroundScope.launch { delay(delay) @@ -98,12 +97,9 @@ constructor( } private fun changeToShadeScene() { - applicationScope.launch { - val shadeMode = shadeInteractor.shadeMode.value - sceneInteractor.changeScene( - if (shadeMode is ShadeMode.Dual) Scenes.NotificationsShade else Scenes.Shade, - "ShadeLockscreenInteractorImpl.expandToNotifications", - ) - } + sceneInteractor.changeScene( + SceneFamilies.NotifShade, + "ShadeLockscreenInteractorImpl.expandToNotifications", + ) } } diff --git a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/OverlayShadeViewModel.kt b/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/OverlayShadeViewModel.kt index b8dd62897587..0314091e792e 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/OverlayShadeViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/OverlayShadeViewModel.kt @@ -19,49 +19,41 @@ package com.android.systemui.shade.ui.viewmodel import com.android.compose.animation.scene.SceneKey import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application -import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor import com.android.systemui.scene.domain.interactor.SceneInteractor +import com.android.systemui.scene.shared.model.SceneFamilies import com.android.systemui.scene.shared.model.Scenes 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.map import kotlinx.coroutines.flow.stateIn /** * Models UI state and handles user input for the overlay shade UI, which shows a shade as an * overlay on top of another scene UI. */ -@OptIn(ExperimentalCoroutinesApi::class) @SysUISingleton class OverlayShadeViewModel @Inject constructor( - @Application private val applicationScope: CoroutineScope, + @Application applicationScope: CoroutineScope, private val sceneInteractor: SceneInteractor, - deviceEntryInteractor: DeviceEntryInteractor, ) { /** The scene to show in the background when the overlay shade is open. */ val backgroundScene: StateFlow<SceneKey> = - deviceEntryInteractor.isDeviceEntered - .map(::backgroundScene) + sceneInteractor + .resolveSceneFamily(SceneFamilies.Home) .stateIn( scope = applicationScope, started = SharingStarted.WhileSubscribed(), - initialValue = backgroundScene(deviceEntryInteractor.isDeviceEntered.value) + initialValue = Scenes.Lockscreen, ) /** Notifies that the user has clicked the semi-transparent background scrim. */ fun onScrimClicked() { sceneInteractor.changeScene( - toScene = backgroundScene.value, + toScene = SceneFamilies.Home, loggingReason = "Shade scrim clicked", ) } - - private fun backgroundScene(isDeviceEntered: Boolean): SceneKey { - return if (isDeviceEntered) Scenes.Gone else Scenes.Lockscreen - } } diff --git a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModel.kt b/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModel.kt index 6c7606170d9f..b2e0cd04687c 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModel.kt @@ -30,6 +30,9 @@ import com.android.systemui.plugins.ActivityStarter import com.android.systemui.privacy.OngoingPrivacyChip import com.android.systemui.privacy.PrivacyItem import com.android.systemui.res.R +import com.android.systemui.scene.domain.interactor.SceneInteractor +import com.android.systemui.scene.shared.model.SceneFamilies +import com.android.systemui.scene.shared.model.TransitionKeys import com.android.systemui.shade.domain.interactor.PrivacyChipInteractor import com.android.systemui.shade.domain.interactor.ShadeHeaderClockInteractor import com.android.systemui.shade.domain.interactor.ShadeInteractor @@ -57,6 +60,7 @@ constructor( @Application private val applicationScope: CoroutineScope, context: Context, private val activityStarter: ActivityStarter, + private val sceneInteractor: SceneInteractor, shadeInteractor: ShadeInteractor, mobileIconsInteractor: MobileIconsInteractor, val mobileIconsViewModel: MobileIconsViewModel, @@ -139,6 +143,15 @@ constructor( clockInteractor.launchClockActivity() } + /** Notifies that the system icons container was clicked. */ + fun onSystemIconContainerClicked() { + sceneInteractor.changeScene( + SceneFamilies.Home, + "ShadeHeaderViewModel.onSystemIconContainerClicked", + TransitionKeys.SlightlyFasterShadeCollapse, + ) + } + /** Notifies that the shadeCarrierGroup was clicked. */ fun onShadeCarrierGroupClicked() { activityStarter.postStartActivityDismissingKeyguard( diff --git a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModel.kt b/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModel.kt index e4a2424e1ead..b0100b9642c2 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModel.kt @@ -26,12 +26,12 @@ import com.android.compose.animation.scene.UserAction import com.android.compose.animation.scene.UserActionResult import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application -import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor import com.android.systemui.media.controls.domain.pipeline.interactor.MediaCarouselInteractor import com.android.systemui.qs.FooterActionsController import com.android.systemui.qs.footer.ui.viewmodel.FooterActionsViewModel import com.android.systemui.qs.ui.adapter.QSSceneAdapter import com.android.systemui.scene.domain.interactor.SceneInteractor +import com.android.systemui.scene.shared.model.SceneFamilies import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.scene.shared.model.TransitionKeys.ToSplitShade import com.android.systemui.settings.brightness.ui.viewModel.BrightnessMirrorViewModel @@ -39,6 +39,7 @@ import com.android.systemui.shade.domain.interactor.ShadeInteractor import com.android.systemui.shade.shared.model.ShadeMode import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationsPlaceholderViewModel import com.android.systemui.unfold.domain.interactor.UnfoldTransitionInteractor +import com.android.systemui.utils.coroutines.flow.flatMapLatestConflated import java.util.concurrent.atomic.AtomicBoolean import javax.inject.Inject import kotlinx.coroutines.CoroutineScope @@ -47,6 +48,7 @@ import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.stateIn @@ -56,7 +58,6 @@ class ShadeSceneViewModel @Inject constructor( @Application private val applicationScope: CoroutineScope, - deviceEntryInteractor: DeviceEntryInteractor, val qsSceneAdapter: QSSceneAdapter, val shadeHeaderViewModel: ShadeHeaderViewModel, val notifications: NotificationsPlaceholderViewModel, @@ -70,16 +71,12 @@ constructor( ) { val destinationScenes: StateFlow<Map<UserAction, UserActionResult>> = combine( - deviceEntryInteractor.isUnlocked, - deviceEntryInteractor.canSwipeToEnter, shadeInteractor.shadeMode, - qsSceneAdapter.isCustomizerShowing - ) { isUnlocked, canSwipeToDismiss, shadeMode, isCustomizerShowing -> + qsSceneAdapter.isCustomizerShowing, + ) { shadeMode, isCustomizerShowing -> destinationScenes( - isUnlocked = isUnlocked, - canSwipeToDismiss = canSwipeToDismiss, shadeMode = shadeMode, - isCustomizing = isCustomizerShowing + isCustomizing = isCustomizerShowing, ) } .stateIn( @@ -87,8 +84,6 @@ constructor( started = SharingStarted.WhileSubscribed(), initialValue = destinationScenes( - isUnlocked = deviceEntryInteractor.isUnlocked.value, - canSwipeToDismiss = deviceEntryInteractor.canSwipeToEnter.value, shadeMode = shadeInteractor.shadeMode.value, isCustomizing = qsSceneAdapter.isCustomizerShowing.value, ), @@ -100,6 +95,9 @@ constructor( /** Whether or not the shade container should be clickable. */ val isClickable: StateFlow<Boolean> = upDestinationSceneKey + .flatMapLatestConflated { key -> + key?.let { sceneInteractor.resolveSceneFamily(key) } ?: flowOf(null) + } .map { it == Scenes.Lockscreen } .stateIn( scope = applicationScope, @@ -138,27 +136,22 @@ constructor( } private fun destinationScenes( - isUnlocked: Boolean, - canSwipeToDismiss: Boolean?, shadeMode: ShadeMode, isCustomizing: Boolean, ): Map<UserAction, UserActionResult> { - val up = - when { - canSwipeToDismiss == true -> Scenes.Lockscreen - isUnlocked -> Scenes.Gone - else -> Scenes.Lockscreen - } - - val upTransitionKey = ToSplitShade.takeIf { shadeMode is ShadeMode.Split } - - val down = Scenes.QuickSettings.takeIf { shadeMode is ShadeMode.Single } - return buildMap { if (!isCustomizing) { - this[Swipe(SwipeDirection.Up)] = UserActionResult(up, upTransitionKey) + set( + Swipe(SwipeDirection.Up), + UserActionResult( + SceneFamilies.Home, + ToSplitShade.takeIf { shadeMode is ShadeMode.Split } + ) + ) } // TODO(b/330200163) Add an else to be able to collapse the shade while customizing - down?.let { this[Swipe(SwipeDirection.Down)] = UserActionResult(down) } + if (shadeMode is ShadeMode.Single) { + set(Swipe(SwipeDirection.Down), UserActionResult(Scenes.QuickSettings)) + } } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ImmersiveModeConfirmation.java b/packages/SystemUI/src/com/android/systemui/statusbar/ImmersiveModeConfirmation.java index 2dfc920b4f20..abf258c44556 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/ImmersiveModeConfirmation.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/ImmersiveModeConfirmation.java @@ -24,6 +24,7 @@ import static android.app.StatusBarManager.DISABLE_RECENT; import static android.view.Display.DEFAULT_DISPLAY; import static android.view.ViewRootImpl.CLIENT_IMMERSIVE_CONFIRMATION; import static android.view.ViewRootImpl.CLIENT_TRANSIENT; +import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; import static android.window.DisplayAreaOrganizer.FEATURE_UNDEFINED; import static android.window.DisplayAreaOrganizer.KEY_ROOT_DISPLAY_AREA_ID; @@ -36,12 +37,11 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; -import android.content.res.ColorStateList; import android.content.res.Resources; -import android.content.res.TypedArray; import android.database.ContentObserver; import android.graphics.Insets; import android.graphics.PixelFormat; +import android.graphics.Rect; import android.graphics.drawable.ColorDrawable; import android.os.Binder; import android.os.Bundle; @@ -71,7 +71,7 @@ import android.view.animation.AnimationUtils; import android.view.animation.Interpolator; import android.widget.Button; import android.widget.FrameLayout; -import android.widget.ImageView; +import android.widget.RelativeLayout; import com.android.systemui.CoreStartable; import com.android.systemui.res.R; @@ -263,6 +263,7 @@ public class ImmersiveModeConfirmation implements CoreStartable, CommandQueue.Ca | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL, PixelFormat.TRANSLUCENT); lp.setFitInsetsTypes(lp.getFitInsetsTypes() & ~Type.statusBars()); + lp.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; // Trusted overlay so touches outside the touchable area are allowed to pass through lp.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS | WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY @@ -275,13 +276,20 @@ public class ImmersiveModeConfirmation implements CoreStartable, CommandQueue.Ca private FrameLayout.LayoutParams getBubbleLayoutParams() { return new FrameLayout.LayoutParams( - mSysUiContext.getResources().getDimensionPixelSize( - R.dimen.immersive_mode_cling_width), + getClingWindowWidth(), ViewGroup.LayoutParams.WRAP_CONTENT, Gravity.CENTER_HORIZONTAL | Gravity.TOP); } /** + * Returns the width of the cling window. + */ + private int getClingWindowWidth() { + return mSysUiContext.getResources().getDimensionPixelSize( + R.dimen.immersive_mode_cling_width); + } + + /** * @return the window token that's used by all ImmersiveModeConfirmation windows. */ IBinder getWindowToken() { @@ -409,21 +417,6 @@ public class ImmersiveModeConfirmation implements CoreStartable, CommandQueue.Ca mClingLayout = (ViewGroup) View.inflate(mSysUiContext, R.layout.immersive_mode_cling, null); - TypedArray ta = mDisplayContext.obtainStyledAttributes( - new int[]{android.R.attr.colorAccent}); - int colorAccent = ta.getColor(0, 0); - ta.recycle(); - mClingLayout.setBackgroundColor(colorAccent); - ImageView expandMore = mClingLayout.findViewById(R.id.immersive_cling_ic_expand_more); - if (expandMore != null) { - expandMore.setImageTintList(ColorStateList.valueOf(colorAccent)); - } - ImageView lightBgCirc = mClingLayout.findViewById(R.id.immersive_cling_back_bg_light); - if (lightBgCirc != null) { - // Set transparency to 50% - lightBgCirc.setImageAlpha(128); - } - final Button ok = mClingLayout.findViewById(R.id.ok); ok.setOnClickListener(new OnClickListener() { @Override @@ -482,6 +475,24 @@ public class ImmersiveModeConfirmation implements CoreStartable, CommandQueue.Ca @Override public WindowInsets onApplyWindowInsets(WindowInsets insets) { + // If the top display cutout overlaps with the full-width (windowWidth=-1)/centered + // dialog, then adjust the dialog contents by the cutout + final int width = getWidth(); + final int windowWidth = getClingWindowWidth(); + final Rect topDisplayCutout = insets.getDisplayCutout() != null + ? insets.getDisplayCutout().getBoundingRectTop() + : new Rect(); + final boolean intersectsTopCutout = topDisplayCutout.intersects( + width - (windowWidth / 2), 0, + width + (windowWidth / 2), topDisplayCutout.bottom); + if (mClingWindow != null && + (windowWidth < 0 || (width > 0 && intersectsTopCutout))) { + final View iconView = mClingWindow.findViewById(R.id.immersive_cling_icon); + RelativeLayout.LayoutParams lp = (RelativeLayout.LayoutParams) + iconView.getLayoutParams(); + lp.topMargin = topDisplayCutout.bottom; + iconView.setLayoutParams(lp); + } // we will be hiding the nav bar, so layout as if it's already hidden return new WindowInsets.Builder(insets).setInsets( Type.systemBars(), Insets.NONE).build(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java index c742f6413022..5c4a63cdb993 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java @@ -455,7 +455,7 @@ public final class KeyboardShortcuts { if (mReceivedAppShortcutGroups == null || mReceivedImeShortcutGroups == null) { return; } - List<KeyboardShortcutGroup> shortcutGroups = mReceivedAppShortcutGroups; + List<KeyboardShortcutGroup> shortcutGroups = new ArrayList<>(mReceivedAppShortcutGroups); shortcutGroups.addAll(mReceivedImeShortcutGroups); mReceivedAppShortcutGroups = null; mReceivedImeShortcutGroups = null; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java index 47939ae07539..337ffa412e31 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java @@ -20,6 +20,7 @@ import static android.adaptiveauth.Flags.enableAdaptiveAuth; import static android.app.admin.DevicePolicyManager.DEVICE_OWNER_TYPE_FINANCED; import static android.app.admin.DevicePolicyResources.Strings.SystemUi.KEYGUARD_MANAGEMENT_DISCLOSURE; import static android.app.admin.DevicePolicyResources.Strings.SystemUi.KEYGUARD_NAMED_MANAGEMENT_DISCLOSURE; +import static android.hardware.biometrics.BiometricFaceConstants.FACE_ACQUIRED_START; import static android.hardware.biometrics.BiometricFaceConstants.FACE_ACQUIRED_TOO_DARK; import static android.hardware.biometrics.BiometricFaceConstants.FACE_ERROR_TIMEOUT; import static android.hardware.biometrics.BiometricSourceType.FACE; @@ -1296,6 +1297,12 @@ public class KeyguardIndicationController { @Override public void onBiometricAcquired(BiometricSourceType biometricSourceType, int acquireInfo) { if (biometricSourceType == FACE) { + if (acquireInfo == FACE_ACQUIRED_START) { + // Let's hide any previous messages when authentication starts, otherwise + // multiple auth attempts would overlap. + hideBiometricMessage(); + mBiometricErrorMessageToShowOnScreenOn = null; + } mFaceAcquiredMessageDeferral.processFrame(acquireInfo); } } @@ -1485,13 +1492,6 @@ public class KeyguardIndicationController { @Override public void onBiometricRunningStateChanged(boolean running, BiometricSourceType biometricSourceType) { - if (running && biometricSourceType == FACE) { - // Let's hide any previous messages when authentication starts, otherwise - // multiple auth attempts would overlap. - hideBiometricMessage(); - mBiometricErrorMessageToShowOnScreenOn = null; - } - if (!running && biometricSourceType == FACE) { showTrustAgentErrorMessage(mTrustAgentErrorMessage); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java index 6d34a0fa9c24..04a413acdf0b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java @@ -303,7 +303,12 @@ public class NotificationMediaManager implements Dumpable { // TODO(b/169655907): get the semi-filtered notifications for current user Collection<NotificationEntry> allNotifications = mNotifPipeline.getAllNotifs(); if (notificationMediaManagerBackgroundExecution()) { - mBackgroundExecutor.execute(() -> findPlayingMediaNotification(allNotifications)); + // Create new sbn list to be accessed in background thread. + List<StatusBarNotification> statusBarNotifications = new ArrayList<>(); + for (NotificationEntry entry: allNotifications) { + statusBarNotifications.add(entry.getSbn()); + } + mBackgroundExecutor.execute(() -> findPlayingMediaNotification(statusBarNotifications)); } else { findPlayingMediaNotification(allNotifications); } @@ -341,6 +346,51 @@ public class NotificationMediaManager implements Dumpable { } } + StatusBarNotification statusBarNotification = null; + if (mediaNotification != null) { + statusBarNotification = mediaNotification.getSbn(); + } + setUpControllerAndKey(controller, statusBarNotification); + } + + /** + * Find a notification and media controller associated with the playing media session, and + * update this manager's internal state. + * This method must be called in background. + * TODO(b/273443374) check this method + */ + void findPlayingMediaNotification(@NonNull List<StatusBarNotification> allNotifications) { + // Promote the media notification with a controller in 'playing' state, if any. + StatusBarNotification statusBarNotification = null; + MediaController controller = null; + for (StatusBarNotification sbn : allNotifications) { + Notification notif = sbn.getNotification(); + if (notif.isMediaNotification()) { + final MediaSession.Token token = + sbn.getNotification().extras.getParcelable( + Notification.EXTRA_MEDIA_SESSION, MediaSession.Token.class); + if (token != null) { + MediaController aController = new MediaController(mContext, token); + if (PlaybackState.STATE_PLAYING + == getMediaControllerPlaybackState(aController)) { + if (DEBUG_MEDIA) { + Log.v(TAG, "DEBUG_MEDIA: found mediastyle controller matching " + + sbn.getKey()); + } + statusBarNotification = sbn; + controller = aController; + break; + } + } + } + } + + setUpControllerAndKey(controller, statusBarNotification); + } + + private void setUpControllerAndKey( + MediaController controller, + StatusBarNotification mediaNotification) { if (controller != null && !sameSessions(mMediaController, controller)) { // We have a new media session clearCurrentMediaNotificationSession(); @@ -354,8 +404,8 @@ public class NotificationMediaManager implements Dumpable { } if (mediaNotification != null - && !mediaNotification.getSbn().getKey().equals(mMediaNotificationKey)) { - mMediaNotificationKey = mediaNotification.getSbn().getKey(); + && !mediaNotification.getKey().equals(mMediaNotificationKey)) { + mMediaNotificationKey = mediaNotification.getKey(); if (DEBUG_MEDIA) { Log.v(TAG, "DEBUG_MEDIA: Found new media notification: key=" + mMediaNotificationKey); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java index f8193a4a1b93..d0702fcc0c5e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java @@ -27,6 +27,7 @@ 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; @@ -35,6 +36,7 @@ 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; @@ -94,6 +96,8 @@ public class StatusBarIconView extends AnimatedImageView implements StatusIconDi public static final int STATE_DOT = 1; public static final int STATE_HIDDEN = 2; + public static final float APP_ICON_SCALE = .75f; + @Retention(RetentionPolicy.SOURCE) @IntDef({STATE_ICON, STATE_DOT, STATE_HIDDEN}) public @interface VisibleState { } @@ -499,7 +503,12 @@ public class StatusBarIconView extends AnimatedImageView implements StatusIconDi userId = UserHandle.USER_SYSTEM; } - Drawable icon = statusBarIcon.icon.loadDrawableAsUser(context, userId); + // 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); + } TypedValue typedValue = new TypedValue(); sysuiContext.getResources().getValue(R.dimen.status_bar_icon_scale_factor, @@ -526,6 +535,26 @@ public class StatusBarIconView extends AnimatedImageView implements StatusIconDi return new ScalingDrawableWrapper(icon, scaleFactor); } + @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 null; + } + public StatusBarIcon getStatusBarIcon() { return mIcon; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/domain/interactor/OngoingActivityChipInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/domain/interactor/OngoingActivityChipInteractor.kt index c3d37fb3d952..086a32dfe49c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/domain/interactor/OngoingActivityChipInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/domain/interactor/OngoingActivityChipInteractor.kt @@ -16,11 +16,37 @@ package com.android.systemui.statusbar.chips.domain.interactor +import android.view.View +import com.android.systemui.animation.DialogTransitionAnimator +import com.android.systemui.res.R import com.android.systemui.statusbar.chips.domain.model.OngoingActivityChipModel +import com.android.systemui.statusbar.chips.ui.view.ChipBackgroundContainer +import com.android.systemui.statusbar.phone.SystemUIDialog import kotlinx.coroutines.flow.StateFlow /** Interface for an interactor that knows the state of a single type of ongoing activity chip. */ interface OngoingActivityChipInteractor { /** A flow modeling the chip that should be shown. */ val chip: StateFlow<OngoingActivityChipModel> + + companion object { + /** Creates a chip click listener that launches a dialog created by [dialogDelegate]. */ + fun createDialogLaunchOnClickListener( + dialogDelegate: SystemUIDialog.Delegate, + dialogTransitionAnimator: DialogTransitionAnimator, + ): View.OnClickListener { + return View.OnClickListener { view -> + val dialog = dialogDelegate.createDialog() + val launchableView = + view.requireViewById<ChipBackgroundContainer>( + R.id.ongoing_activity_chip_background + ) + // TODO(b/343699052): This makes a beautiful animate-in, but the + // animate-out looks odd because the dialog animates back into the chip + // but then the chip disappears. If we aren't able to address + // b/343699052 in time for launch, we should just use `dialog.show`. + dialogTransitionAnimator.showFromView(dialog, launchableView) + } + } + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/mediaprojection/domain/interactor/MediaProjectionChipInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/mediaprojection/domain/interactor/MediaProjectionChipInteractor.kt index ac16d26e415c..6611434b661e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/mediaprojection/domain/interactor/MediaProjectionChipInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/mediaprojection/domain/interactor/MediaProjectionChipInteractor.kt @@ -16,6 +16,9 @@ package com.android.systemui.statusbar.chips.mediaprojection.domain.interactor +import android.content.pm.PackageManager +import androidx.annotation.DrawableRes +import com.android.systemui.animation.DialogTransitionAnimator import com.android.systemui.common.shared.model.ContentDescription import com.android.systemui.common.shared.model.Icon import com.android.systemui.dagger.SysUISingleton @@ -24,7 +27,12 @@ import com.android.systemui.mediaprojection.data.model.MediaProjectionState import com.android.systemui.mediaprojection.data.repository.MediaProjectionRepository import com.android.systemui.res.R import com.android.systemui.statusbar.chips.domain.interactor.OngoingActivityChipInteractor +import com.android.systemui.statusbar.chips.domain.interactor.OngoingActivityChipInteractor.Companion.createDialogLaunchOnClickListener import com.android.systemui.statusbar.chips.domain.model.OngoingActivityChipModel +import com.android.systemui.statusbar.chips.mediaprojection.ui.view.EndCastToOtherDeviceDialogDelegate +import com.android.systemui.statusbar.chips.mediaprojection.ui.view.EndShareToAppDialogDelegate +import com.android.systemui.statusbar.phone.SystemUIDialog +import com.android.systemui.util.Utils import com.android.systemui.util.time.SystemClock import javax.inject.Inject import kotlinx.coroutines.CoroutineScope @@ -32,6 +40,7 @@ import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.stateIn +import kotlinx.coroutines.launch /** * Interactor for media-projection-related chips in the status bar. @@ -47,33 +56,88 @@ import kotlinx.coroutines.flow.stateIn class MediaProjectionChipInteractor @Inject constructor( - @Application scope: CoroutineScope, - mediaProjectionRepository: MediaProjectionRepository, - val systemClock: SystemClock, + @Application private val scope: CoroutineScope, + private val mediaProjectionRepository: MediaProjectionRepository, + private val packageManager: PackageManager, + private val systemClock: SystemClock, + private val dialogFactory: SystemUIDialog.Factory, + private val dialogTransitionAnimator: DialogTransitionAnimator, ) : OngoingActivityChipInteractor { override val chip: StateFlow<OngoingActivityChipModel> = mediaProjectionRepository.mediaProjectionState .map { state -> when (state) { is MediaProjectionState.NotProjecting -> OngoingActivityChipModel.Hidden - is MediaProjectionState.EntireScreen, - is MediaProjectionState.SingleTask -> { - // TODO(b/332662551): Distinguish between cast-to-other-device and - // share-to-app. - OngoingActivityChipModel.Shown( - icon = - Icon.Resource( - R.drawable.ic_cast_connected, - ContentDescription.Resource(R.string.accessibility_casting) - ), - // TODO(b/332662551): See if we can use a MediaProjection API to fetch - // this time. - startTimeMs = systemClock.elapsedRealtime() - ) { - // TODO(b/332662551): Implement the pause dialog. + is MediaProjectionState.Projecting -> { + if (isProjectionToOtherDevice(state.hostPackage)) { + createCastToOtherDeviceChip() + } else { + createShareToAppChip() } } } } .stateIn(scope, SharingStarted.WhileSubscribed(), OngoingActivityChipModel.Hidden) + + /** Stops the currently active projection. */ + fun stopProjecting() { + scope.launch { mediaProjectionRepository.stopProjecting() } + } + + /** + * Returns true iff projecting to the given [packageName] means that we're projecting to a + * *different* device (as opposed to projecting to some application on *this* device). + */ + private fun isProjectionToOtherDevice(packageName: String?): Boolean { + // The [isHeadlessRemoteDisplayProvider] check approximates whether a projection is to a + // different device or the same device, because headless remote display packages are the + // only kinds of packages that do cast-to-other-device. This isn't exactly perfect, + // because it means that any projection by those headless remote display packages will be + // marked as going to a different device, even if that isn't always true. See b/321078669. + return Utils.isHeadlessRemoteDisplayProvider(packageManager, packageName) + } + + private fun createCastToOtherDeviceChip(): OngoingActivityChipModel.Shown { + return OngoingActivityChipModel.Shown( + icon = + Icon.Resource( + CAST_TO_OTHER_DEVICE_ICON, + ContentDescription.Resource(R.string.accessibility_casting) + ), + // TODO(b/332662551): Maybe use a MediaProjection API to fetch this time. + startTimeMs = systemClock.elapsedRealtime(), + createDialogLaunchOnClickListener( + castToOtherDeviceDialogDelegate, + dialogTransitionAnimator, + ), + ) + } + + private val castToOtherDeviceDialogDelegate = + EndCastToOtherDeviceDialogDelegate( + dialogFactory, + this@MediaProjectionChipInteractor, + ) + + private fun createShareToAppChip(): OngoingActivityChipModel.Shown { + return OngoingActivityChipModel.Shown( + // TODO(b/332662551): Use the right content description. + icon = Icon.Resource(SHARE_TO_APP_ICON, contentDescription = null), + // TODO(b/332662551): Maybe use a MediaProjection API to fetch this time. + startTimeMs = systemClock.elapsedRealtime(), + createDialogLaunchOnClickListener(shareToAppDialogDelegate, dialogTransitionAnimator), + ) + } + + private val shareToAppDialogDelegate = + EndShareToAppDialogDelegate( + dialogFactory, + this@MediaProjectionChipInteractor, + ) + + companion object { + // TODO(b/332662551): Use the right icon. + @DrawableRes val SHARE_TO_APP_ICON = R.drawable.ic_screenshot_share + @DrawableRes val CAST_TO_OTHER_DEVICE_ICON = R.drawable.ic_cast_connected + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/mediaprojection/ui/view/EndCastToOtherDeviceDialogDelegate.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/mediaprojection/ui/view/EndCastToOtherDeviceDialogDelegate.kt new file mode 100644 index 000000000000..33cec9755b1f --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/mediaprojection/ui/view/EndCastToOtherDeviceDialogDelegate.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.statusbar.chips.mediaprojection.ui.view + +import android.os.Bundle +import com.android.systemui.res.R +import com.android.systemui.statusbar.chips.mediaprojection.domain.interactor.MediaProjectionChipInteractor +import com.android.systemui.statusbar.phone.SystemUIDialog + +/** A dialog that lets the user stop an ongoing cast-screen-to-other-device event. */ +class EndCastToOtherDeviceDialogDelegate( + private val systemUIDialogFactory: SystemUIDialog.Factory, + private val interactor: MediaProjectionChipInteractor, +) : SystemUIDialog.Delegate { + override fun createDialog(): SystemUIDialog { + return systemUIDialogFactory.create(this) + } + + override fun beforeCreate(dialog: SystemUIDialog, savedInstanceState: Bundle?) { + with(dialog) { + setIcon(MediaProjectionChipInteractor.CAST_TO_OTHER_DEVICE_ICON) + setTitle(R.string.cast_to_other_device_stop_dialog_title) + // TODO(b/332662551): Use a different message if they're sharing just a single app. + setMessage(R.string.cast_to_other_device_stop_dialog_message) + // No custom on-click, because the dialog will automatically be dismissed when the + // button is clicked anyway. + setNegativeButton(R.string.close_dialog_button, /* onClick= */ null) + setPositiveButton(R.string.cast_to_other_device_stop_dialog_button) { _, _ -> + interactor.stopProjecting() + } + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/mediaprojection/ui/view/EndShareToAppDialogDelegate.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/mediaprojection/ui/view/EndShareToAppDialogDelegate.kt new file mode 100644 index 000000000000..3a863b10ef82 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/mediaprojection/ui/view/EndShareToAppDialogDelegate.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.statusbar.chips.mediaprojection.ui.view + +import android.os.Bundle +import com.android.systemui.res.R +import com.android.systemui.statusbar.chips.mediaprojection.domain.interactor.MediaProjectionChipInteractor +import com.android.systemui.statusbar.phone.SystemUIDialog + +/** A dialog that lets the user stop an ongoing share-screen-to-app event. */ +class EndShareToAppDialogDelegate( + private val systemUIDialogFactory: SystemUIDialog.Factory, + private val interactor: MediaProjectionChipInteractor, +) : SystemUIDialog.Delegate { + override fun createDialog(): SystemUIDialog { + return systemUIDialogFactory.create(this) + } + + override fun beforeCreate(dialog: SystemUIDialog, savedInstanceState: Bundle?) { + with(dialog) { + setIcon(MediaProjectionChipInteractor.SHARE_TO_APP_ICON) + setTitle(R.string.share_to_app_stop_dialog_title) + // TODO(b/332662551): Use a different message if they're sharing just a single app. + setMessage(R.string.share_to_app_stop_dialog_message) + // No custom on-click, because the dialog will automatically be dismissed when the + // button is clicked anyway. + setNegativeButton(R.string.close_dialog_button, /* onClick= */ null) + setPositiveButton(R.string.share_to_app_stop_dialog_button) { _, _ -> + interactor.stopProjecting() + } + } + } +} 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 585ff5f78ff8..4959b0950fb5 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,8 @@ package com.android.systemui.statusbar.chips.screenrecord.domain.interactor +import androidx.annotation.DrawableRes +import com.android.systemui.animation.DialogTransitionAnimator import com.android.systemui.common.shared.model.Icon import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application @@ -23,7 +25,10 @@ import com.android.systemui.res.R import com.android.systemui.screenrecord.data.model.ScreenRecordModel import com.android.systemui.screenrecord.data.repository.ScreenRecordRepository import com.android.systemui.statusbar.chips.domain.interactor.OngoingActivityChipInteractor +import com.android.systemui.statusbar.chips.domain.interactor.OngoingActivityChipInteractor.Companion.createDialogLaunchOnClickListener import com.android.systemui.statusbar.chips.domain.model.OngoingActivityChipModel +import com.android.systemui.statusbar.chips.screenrecord.ui.view.EndScreenRecordingDialogDelegate +import com.android.systemui.statusbar.phone.SystemUIDialog import com.android.systemui.util.time.SystemClock import javax.inject.Inject import kotlinx.coroutines.CoroutineScope @@ -31,15 +36,18 @@ import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.stateIn +import kotlinx.coroutines.launch /** Interactor for the screen recording chip shown in the status bar. */ @SysUISingleton class ScreenRecordChipInteractor @Inject constructor( - @Application scope: CoroutineScope, - screenRecordRepository: ScreenRecordRepository, - val systemClock: SystemClock, + @Application private val scope: CoroutineScope, + private val screenRecordRepository: ScreenRecordRepository, + private val systemClock: SystemClock, + private val dialogFactory: SystemUIDialog.Factory, + private val dialogTransitionAnimator: DialogTransitionAnimator, ) : OngoingActivityChipInteractor { override val chip: StateFlow<OngoingActivityChipModel> = screenRecordRepository.screenRecordState @@ -51,16 +59,29 @@ constructor( is ScreenRecordModel.Recording -> OngoingActivityChipModel.Shown( // TODO(b/332662551): Also provide a content description. - icon = - Icon.Resource( - R.drawable.stat_sys_screen_record, - contentDescription = null - ), - startTimeMs = systemClock.elapsedRealtime() - ) { - // TODO(b/332662551): Implement the pause dialog. - } + icon = Icon.Resource(ICON, contentDescription = null), + startTimeMs = systemClock.elapsedRealtime(), + createDialogLaunchOnClickListener( + dialogDelegate, + dialogTransitionAnimator + ), + ) } } .stateIn(scope, SharingStarted.WhileSubscribed(), OngoingActivityChipModel.Hidden) + + /** Stops the recording. */ + fun stopRecording() { + scope.launch { screenRecordRepository.stopRecording() } + } + + private val dialogDelegate = + EndScreenRecordingDialogDelegate( + dialogFactory, + this@ScreenRecordChipInteractor, + ) + + companion object { + @DrawableRes val ICON = R.drawable.ic_screenrecord + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/screenrecord/ui/view/EndScreenRecordingDialogDelegate.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/screenrecord/ui/view/EndScreenRecordingDialogDelegate.kt new file mode 100644 index 000000000000..b8e8cfa11afc --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/screenrecord/ui/view/EndScreenRecordingDialogDelegate.kt @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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.chips.screenrecord.ui.view + +import android.os.Bundle +import com.android.systemui.res.R +import com.android.systemui.statusbar.chips.screenrecord.domain.interactor.ScreenRecordChipInteractor +import com.android.systemui.statusbar.phone.SystemUIDialog + +/** A dialog that lets the user stop an ongoing screen recording. */ +class EndScreenRecordingDialogDelegate( + private val systemUIDialogFactory: SystemUIDialog.Factory, + private val interactor: ScreenRecordChipInteractor, +) : SystemUIDialog.Delegate { + + override fun createDialog(): SystemUIDialog { + return systemUIDialogFactory.create(this) + } + + override fun beforeCreate(dialog: SystemUIDialog, savedInstanceState: Bundle?) { + with(dialog) { + setIcon(ScreenRecordChipInteractor.ICON) + setTitle(R.string.screenrecord_stop_dialog_title) + // TODO(b/332662551): Use a different message if they're sharing just a single app. + setMessage(R.string.screenrecord_stop_dialog_message) + // No custom on-click, because the dialog will automatically be dismissed when the + // button is clicked anyway. + setNegativeButton(R.string.close_dialog_button, /* onClick= */ null) + setPositiveButton(R.string.screenrecord_stop_dialog_button) { _, _ -> + interactor.stopRecording() + } + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotViewController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotViewController.kt index 658625996575..9eb9ed5f2063 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotViewController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotViewController.kt @@ -26,6 +26,7 @@ import android.widget.FrameLayout import androidx.core.animation.Animator import com.android.app.animation.Interpolators import com.android.internal.annotations.GuardedBy +import com.android.systemui.Flags.privacyDotUnfoldWrongCornerFix import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.dagger.qualifiers.Main @@ -505,7 +506,9 @@ open class PrivacyDotViewController @Inject constructor( return } - if (state.rotation != currentViewState.rotation) { + val designatedCornerChanged = state.designatedCorner != currentViewState.designatedCorner + val rotationChanged = state.rotation != currentViewState.rotation + if (rotationChanged || (designatedCornerChanged && privacyDotUnfoldWrongCornerFix())) { // A rotation has started, hide the views to avoid flicker updateRotations(state.rotation, state.paddingTop) } @@ -515,7 +518,7 @@ open class PrivacyDotViewController @Inject constructor( views.forEach { it.requestLayout() } } - if (state.designatedCorner != currentViewState.designatedCorner) { + if (designatedCornerChanged) { currentViewState.designatedCorner?.contentDescription = null state.designatedCorner?.contentDescription = state.contentDescription diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt index 2e87a5bedda5..ee2c9cc9acde 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt @@ -38,6 +38,7 @@ import android.view.View import android.view.ViewGroup import androidx.annotation.VisibleForTesting import com.android.keyguard.KeyguardUpdateMonitor +import com.android.keyguard.KeyguardUpdateMonitorCallback import com.android.settingslib.Utils import com.android.systemui.Dumpable import com.android.systemui.Flags.smartspaceLockscreenViewmodel @@ -53,6 +54,7 @@ import com.android.systemui.plugins.BcSmartspaceConfigPlugin 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.TimeChangedDelegate import com.android.systemui.plugins.FalsingManager import com.android.systemui.plugins.clocks.WeatherData import com.android.systemui.plugins.statusbar.StatusBarStateController @@ -411,6 +413,7 @@ constructor( val ssView = plugin.getView(parent) configPlugin?.let { ssView.registerConfigProvider(it) } ssView.setUiSurface(BcSmartspaceDataPlugin.UI_SURFACE_LOCK_SCREEN_AOD) + ssView.setTimeChangedDelegate(SmartspaceTimeChangedDelegate(keyguardUpdateMonitor)) ssView.registerDataProvider(plugin) ssView.setIntentStarter(object : BcSmartspaceDataPlugin.IntentStarter { @@ -682,5 +685,28 @@ constructor( } } } + + private class SmartspaceTimeChangedDelegate( + private val keyguardUpdateMonitor: KeyguardUpdateMonitor + ) : TimeChangedDelegate { + private var keyguardUpdateMonitorCallback: KeyguardUpdateMonitorCallback? = null + override fun register(callback: Runnable) { + if (keyguardUpdateMonitorCallback != null) { + unregister() + } + keyguardUpdateMonitorCallback = object : KeyguardUpdateMonitorCallback() { + override fun onTimeChanged() { + callback.run() + } + } + keyguardUpdateMonitor.registerCallback(keyguardUpdateMonitorCallback) + callback.run() + } + + override fun unregister() { + keyguardUpdateMonitor.removeCallback(keyguardUpdateMonitorCallback) + keyguardUpdateMonitorCallback = null + } + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationSectionsFeatureManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationSectionsFeatureManager.kt index 2f293e072c84..f62b24aa96a3 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationSectionsFeatureManager.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationSectionsFeatureManager.kt @@ -18,33 +18,29 @@ package com.android.systemui.statusbar.notification import android.content.Context import android.provider.DeviceConfig - import com.android.internal.annotations.VisibleForTesting import com.android.internal.config.sysui.SystemUiDeviceConfigFlags.NOTIFICATIONS_USE_PEOPLE_FILTERING import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.statusbar.notification.shared.NotificationMinimalismPrototype import com.android.systemui.statusbar.notification.shared.PriorityPeopleSection import com.android.systemui.statusbar.notification.stack.BUCKET_ALERTING import com.android.systemui.statusbar.notification.stack.BUCKET_FOREGROUND_SERVICE import com.android.systemui.statusbar.notification.stack.BUCKET_HEADS_UP import com.android.systemui.statusbar.notification.stack.BUCKET_MEDIA_CONTROLS import com.android.systemui.statusbar.notification.stack.BUCKET_PEOPLE -import com.android.systemui.statusbar.notification.stack.BUCKET_PRIORITY_PEOPLE import com.android.systemui.statusbar.notification.stack.BUCKET_SILENT +import com.android.systemui.statusbar.notification.stack.PriorityBucket import com.android.systemui.util.DeviceConfigProxy import com.android.systemui.util.Utils - import javax.inject.Inject private var sUsePeopleFiltering: Boolean? = null -/** - * Feature controller for the NOTIFICATIONS_USE_PEOPLE_FILTERING config. - */ +/** Feature controller for the NOTIFICATIONS_USE_PEOPLE_FILTERING config. */ @SysUISingleton -class NotificationSectionsFeatureManager @Inject constructor( - val proxy: DeviceConfigProxy, - val context: Context -) { +class NotificationSectionsFeatureManager +@Inject +constructor(val proxy: DeviceConfigProxy, val context: Context) { fun isFilteringEnabled(): Boolean { return usePeopleFiltering(proxy) @@ -55,30 +51,37 @@ class NotificationSectionsFeatureManager @Inject constructor( } fun getNotificationBuckets(): IntArray { - if (PriorityPeopleSection.isEnabled) { + if (PriorityPeopleSection.isEnabled || NotificationMinimalismPrototype.V2.isEnabled) { // We don't need this list to be adaptive, it can be the superset of all features. - return intArrayOf( - BUCKET_MEDIA_CONTROLS, + return PriorityBucket.getAllInOrder() + } + return when { + isFilteringEnabled() && isMediaControlsEnabled() -> + intArrayOf( BUCKET_HEADS_UP, BUCKET_FOREGROUND_SERVICE, - BUCKET_PRIORITY_PEOPLE, + BUCKET_MEDIA_CONTROLS, BUCKET_PEOPLE, BUCKET_ALERTING, - BUCKET_SILENT, + BUCKET_SILENT ) - } - return when { - isFilteringEnabled() && isMediaControlsEnabled() -> - intArrayOf(BUCKET_HEADS_UP, BUCKET_FOREGROUND_SERVICE, BUCKET_MEDIA_CONTROLS, - BUCKET_PEOPLE, BUCKET_ALERTING, BUCKET_SILENT) !isFilteringEnabled() && isMediaControlsEnabled() -> - intArrayOf(BUCKET_HEADS_UP, BUCKET_FOREGROUND_SERVICE, BUCKET_MEDIA_CONTROLS, - BUCKET_ALERTING, BUCKET_SILENT) + intArrayOf( + BUCKET_HEADS_UP, + BUCKET_FOREGROUND_SERVICE, + BUCKET_MEDIA_CONTROLS, + BUCKET_ALERTING, + BUCKET_SILENT + ) isFilteringEnabled() && !isMediaControlsEnabled() -> - intArrayOf(BUCKET_HEADS_UP, BUCKET_FOREGROUND_SERVICE, BUCKET_PEOPLE, - BUCKET_ALERTING, BUCKET_SILENT) - else -> - intArrayOf(BUCKET_ALERTING, BUCKET_SILENT) + intArrayOf( + BUCKET_HEADS_UP, + BUCKET_FOREGROUND_SERVICE, + BUCKET_PEOPLE, + BUCKET_ALERTING, + BUCKET_SILENT + ) + else -> intArrayOf(BUCKET_ALERTING, BUCKET_SILENT) } } @@ -94,8 +97,12 @@ class NotificationSectionsFeatureManager @Inject constructor( private fun usePeopleFiltering(proxy: DeviceConfigProxy): Boolean { if (sUsePeopleFiltering == null) { - sUsePeopleFiltering = proxy.getBoolean( - DeviceConfig.NAMESPACE_SYSTEMUI, NOTIFICATIONS_USE_PEOPLE_FILTERING, true) + sUsePeopleFiltering = + proxy.getBoolean( + DeviceConfig.NAMESPACE_SYSTEMUI, + NOTIFICATIONS_USE_PEOPLE_FILTERING, + true + ) } return sUsePeopleFiltering!! 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 b397906fc06f..1adfef061235 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 @@ -68,6 +68,9 @@ import com.android.systemui.statusbar.notification.icon.IconPack; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRowController; import com.android.systemui.statusbar.notification.row.NotificationGuts; +import com.android.systemui.statusbar.notification.row.shared.HeadsUpStatusBarModel; +import com.android.systemui.statusbar.notification.row.shared.NotificationContentModel; +import com.android.systemui.statusbar.notification.row.shared.NotificationRowContentBinderRefactor; import com.android.systemui.statusbar.notification.stack.PriorityBucket; import com.android.systemui.util.ListenerSet; @@ -951,6 +954,7 @@ public final class NotificationEntry extends ListEntry { * heads up. */ public void setHeadsUpStatusBarText(CharSequence headsUpStatusBarText) { + NotificationRowContentBinderRefactor.assertInLegacyMode(); this.mHeadsUpStatusBarText.setValue(headsUpStatusBarText); } @@ -964,6 +968,7 @@ public final class NotificationEntry extends ListEntry { * heads up, and its content is sensitive right now. */ public void setHeadsUpStatusBarTextPublic(CharSequence headsUpStatusBarTextPublic) { + NotificationRowContentBinderRefactor.assertInLegacyMode(); this.mHeadsUpStatusBarTextPublic.setValue(headsUpStatusBarTextPublic); } @@ -1036,6 +1041,14 @@ public final class NotificationEntry extends ListEntry { == Notification.VISIBILITY_PRIVATE; } + /** Set the content generated by the notification inflater. */ + public void setContentModel(NotificationContentModel contentModel) { + if (NotificationRowContentBinderRefactor.isUnexpectedlyInLegacyMode()) return; + HeadsUpStatusBarModel headsUpStatusBarModel = contentModel.getHeadsUpStatusBarModel(); + this.mHeadsUpStatusBarText.setValue(headsUpStatusBarModel.getPrivateText()); + this.mHeadsUpStatusBarTextPublic.setValue(headsUpStatusBarModel.getPublicText()); + } + /** Information about a suggestion that is being edited. */ public static class EditedSuggestionInfo { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ColorizedFgsCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ColorizedFgsCoordinator.java index 63997f8f2457..47a04291dd49 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ColorizedFgsCoordinator.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ColorizedFgsCoordinator.java @@ -60,22 +60,27 @@ public class ColorizedFgsCoordinator implements Coordinator { public boolean isInSection(ListEntry entry) { NotificationEntry notificationEntry = entry.getRepresentativeEntry(); if (notificationEntry != null) { - return isColorizedForegroundService(notificationEntry) || isCall(notificationEntry); + return isRichOngoing(notificationEntry); } return false; } + }; - private boolean isColorizedForegroundService(NotificationEntry entry) { - Notification notification = entry.getSbn().getNotification(); - return notification.isForegroundService() - && notification.isColorized() - && entry.getImportance() > IMPORTANCE_MIN; - } + /** Determines if the given notification is a colorized or call notification */ + public static boolean isRichOngoing(NotificationEntry entry) { + return isColorizedForegroundService(entry) || isCall(entry); + } - private boolean isCall(NotificationEntry entry) { - Notification notification = entry.getSbn().getNotification(); - return entry.getImportance() > IMPORTANCE_MIN - && notification.isStyle(Notification.CallStyle.class); - } - }; + private static boolean isColorizedForegroundService(NotificationEntry entry) { + Notification notification = entry.getSbn().getNotification(); + return notification.isForegroundService() + && notification.isColorized() + && entry.getImportance() > IMPORTANCE_MIN; + } + + private static boolean isCall(NotificationEntry entry) { + Notification notification = entry.getSbn().getNotification(); + return entry.getImportance() > IMPORTANCE_MIN + && notification.isStyle(Notification.CallStyle.class); + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.kt index 071192b02ee0..5a1146d2472f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.kt @@ -18,6 +18,7 @@ package com.android.systemui.statusbar.notification.collection.coordinator +import android.app.NotificationManager import android.os.UserHandle import android.provider.Settings import androidx.annotation.VisibleForTesting @@ -44,7 +45,8 @@ import com.android.systemui.statusbar.notification.collection.provider.SectionHe import com.android.systemui.statusbar.notification.domain.interactor.SeenNotificationsInteractor import com.android.systemui.statusbar.notification.interruption.KeyguardNotificationVisibilityProvider import com.android.systemui.statusbar.notification.shared.NotificationMinimalismPrototype -import com.android.systemui.statusbar.notification.stack.BUCKET_FOREGROUND_SERVICE +import com.android.systemui.statusbar.notification.stack.BUCKET_TOP_ONGOING +import com.android.systemui.statusbar.notification.stack.BUCKET_TOP_UNSEEN import com.android.systemui.statusbar.policy.HeadsUpManager import com.android.systemui.statusbar.policy.headsUpEvents import com.android.systemui.util.asIndenting @@ -113,7 +115,7 @@ constructor( private fun attachUnseenFilter(pipeline: NotifPipeline) { if (NotificationMinimalismPrototype.V2.isEnabled) { pipeline.addPromoter(unseenNotifPromoter) - pipeline.addOnBeforeTransformGroupsListener(::pickOutTopUnseenNotif) + pipeline.addOnBeforeTransformGroupsListener(::pickOutTopUnseenNotifs) } pipeline.addFinalizeFilter(unseenNotifFilter) pipeline.addCollectionListener(collectionListener) @@ -347,15 +349,16 @@ constructor( } } - private fun pickOutTopUnseenNotif(list: List<ListEntry>) { + private fun pickOutTopUnseenNotifs(list: List<ListEntry>) { if (NotificationMinimalismPrototype.V2.isUnexpectedlyInLegacyMode()) return // Only ever elevate a top unseen notification on keyguard, not even locked shade if (statusBarStateController.state != StatusBarState.KEYGUARD) { + seenNotificationsInteractor.setTopOngoingNotification(null) seenNotificationsInteractor.setTopUnseenNotification(null) return } // On keyguard pick the top-ranked unseen or ongoing notification to elevate - seenNotificationsInteractor.setTopUnseenNotification( + val nonSummaryEntries: Sequence<NotificationEntry> = list .asSequence() .flatMap { @@ -365,7 +368,15 @@ constructor( else -> error("unhandled type of $it") } } - .filter { shouldIgnoreUnseenCheck(it) || it in unseenNotifications } + .filter { it.importance >= NotificationManager.IMPORTANCE_DEFAULT } + seenNotificationsInteractor.setTopOngoingNotification( + nonSummaryEntries + .filter { ColorizedFgsCoordinator.isRichOngoing(it) } + .minByOrNull { it.ranking.rank } + ) + seenNotificationsInteractor.setTopUnseenNotification( + nonSummaryEntries + .filter { !ColorizedFgsCoordinator.isRichOngoing(it) && it in unseenNotifications } .minByOrNull { it.ranking.rank } ) } @@ -375,29 +386,39 @@ constructor( object : NotifPromoter("$TAG-unseen") { override fun shouldPromoteToTopLevel(child: NotificationEntry): Boolean = if (NotificationMinimalismPrototype.V2.isUnexpectedlyInLegacyMode()) false + else if (!NotificationMinimalismPrototype.V2.ungroupTopUnseen) false else - seenNotificationsInteractor.isTopUnseenNotification(child) && - NotificationMinimalismPrototype.V2.ungroupTopUnseen + seenNotificationsInteractor.isTopOngoingNotification(child) || + seenNotificationsInteractor.isTopUnseenNotification(child) } - val unseenNotifSectioner = - object : NotifSectioner("Unseen", BUCKET_FOREGROUND_SERVICE) { + val topOngoingSectioner = + object : NotifSectioner("TopOngoing", BUCKET_TOP_ONGOING) { override fun isInSection(entry: ListEntry): Boolean { if (NotificationMinimalismPrototype.V2.isUnexpectedlyInLegacyMode()) return false - if ( - seenNotificationsInteractor.isTopUnseenNotification(entry.representativeEntry) - ) { - return true + return entry.anyEntry { notificationEntry -> + seenNotificationsInteractor.isTopOngoingNotification(notificationEntry) } - if (entry !is GroupEntry) { - return false - } - return entry.children.any { - seenNotificationsInteractor.isTopUnseenNotification(it) + } + } + + val topUnseenSectioner = + object : NotifSectioner("TopUnseen", BUCKET_TOP_UNSEEN) { + override fun isInSection(entry: ListEntry): Boolean { + if (NotificationMinimalismPrototype.V2.isUnexpectedlyInLegacyMode()) return false + return entry.anyEntry { notificationEntry -> + seenNotificationsInteractor.isTopUnseenNotification(notificationEntry) } } } + private fun ListEntry.anyEntry(predicate: (NotificationEntry?) -> Boolean) = + when { + predicate(representativeEntry) -> true + this !is GroupEntry -> false + else -> children.any(predicate) + } + @VisibleForTesting internal val unseenNotifFilter = object : NotifFilter("$TAG-unseen") { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.kt index 4506385a2fb9..e41352254bac 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.kt @@ -116,11 +116,14 @@ constructor( } // Manually add Ordered Sections + if (NotificationMinimalismPrototype.V2.isEnabled) { + mOrderedSections.add(keyguardCoordinator.topOngoingSectioner) // Top Ongoing + } mOrderedSections.add(headsUpCoordinator.sectioner) // HeadsUp - mOrderedSections.add(colorizedFgsCoordinator.sectioner) // ForegroundService if (NotificationMinimalismPrototype.V2.isEnabled) { - mOrderedSections.add(keyguardCoordinator.unseenNotifSectioner) // Unseen (FGS) + mOrderedSections.add(keyguardCoordinator.topUnseenSectioner) // Top Unseen } + mOrderedSections.add(colorizedFgsCoordinator.sectioner) // ForegroundService if (PriorityPeopleSection.isEnabled) { mOrderedSections.add(conversationCoordinator.priorityPeopleSectioner) // Priority People } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/data/repository/ActiveNotificationListRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/data/repository/ActiveNotificationListRepository.kt index e2c9e02672d2..45d1034f2cfb 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/data/repository/ActiveNotificationListRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/data/repository/ActiveNotificationListRepository.kt @@ -42,6 +42,9 @@ class ActiveNotificationListRepository @Inject constructor() { /** Stats about the list of notifications attached to the shade */ val notifStats = MutableStateFlow(NotifStats.empty) + /** The key of the top ongoing notification */ + val topOngoingNotificationKey = MutableStateFlow<String?>(null) + /** The key of the top unseen notification */ val topUnseenNotificationKey = MutableStateFlow<String?>(null) } @@ -75,6 +78,7 @@ data class ActiveNotificationsStore( /** Unique key identifying an [ActiveNotificationEntryModel] in the store. */ sealed class Key { data class Individual(val key: String) : Key() + data class Group(val key: String) : Key() } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/SeenNotificationsInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/SeenNotificationsInteractor.kt index 42828d99c7e4..85c66bd6f25a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/SeenNotificationsInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/SeenNotificationsInteractor.kt @@ -39,6 +39,18 @@ constructor( notificationListRepository.hasFilteredOutSeenNotifications.value = value } + /** Set the entry that is identified as the top ongoing notification. */ + fun setTopOngoingNotification(entry: NotificationEntry?) { + if (NotificationMinimalismPrototype.V2.isUnexpectedlyInLegacyMode()) return + notificationListRepository.topOngoingNotificationKey.value = entry?.key + } + + /** Determine if the given notification is the top ongoing notification. */ + fun isTopOngoingNotification(entry: NotificationEntry?): Boolean = + if (NotificationMinimalismPrototype.V2.isUnexpectedlyInLegacyMode()) false + else + entry != null && notificationListRepository.topOngoingNotificationKey.value == entry.key + /** Set the entry that is identified as the top unseen notification. */ fun setTopUnseenNotification(entry: NotificationEntry?) { if (NotificationMinimalismPrototype.V2.isUnexpectedlyInLegacyMode()) return 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 3df9374da914..331d3cc4c21b 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 @@ -20,8 +20,6 @@ import android.app.Notification import android.app.Notification.MessagingStyle import android.app.Person import android.content.pm.LauncherApps -import android.graphics.drawable.AdaptiveIconDrawable -import android.graphics.drawable.Drawable import android.graphics.drawable.Icon import android.os.Build import android.os.Bundle @@ -226,28 +224,22 @@ constructor( } val n = entry.sbn.notification - var usingMonochromeAppIcon = false - val icon: Icon? - if (showPeopleAvatar) { - icon = createPeopleAvatar(entry) - } else if (android.app.Flags.notificationsUseMonochromeAppIcon()) { - if (n.shouldUseAppIcon()) { - icon = - getMonochromeAppIcon(entry)?.also { usingMonochromeAppIcon = true } - ?: n.smallIcon + 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 { - icon = n.smallIcon + n.smallIcon to StatusBarIcon.Type.NotifSmallIcon } - } else { - icon = n.smallIcon - } - if (icon == null) { throw InflationException("No icon in notification from ${entry.sbn.packageName}") } - val sbi = icon.toStatusBarIcon(entry) - cacheIconDescriptor(entry, sbi, showPeopleAvatar, usingMonochromeAppIcon) + val sbi = icon.toStatusBarIcon(entry, type) + cacheIconDescriptor(entry, sbi) return sbi } @@ -269,29 +261,24 @@ constructor( } } - private fun cacheIconDescriptor( - entry: NotificationEntry, - descriptor: StatusBarIcon, - showPeopleAvatar: Boolean, - usingMonochromeAppIcon: Boolean - ) { - if (android.app.Flags.notificationsUseAppIcon() || - android.app.Flags.notificationsUseMonochromeAppIcon() + 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 (showPeopleAvatar) { - entry.icons.peopleAvatarDescriptor = descriptor - } else if (usingMonochromeAppIcon) { + when (descriptor.type) { + StatusBarIcon.Type.PeopleAvatar -> entry.icons.peopleAvatarDescriptor = descriptor // When notificationsUseMonochromeAppIcon is enabled, we use the appIconDescriptor. - entry.icons.appIconDescriptor = descriptor - } else { + 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. - entry.icons.smallIconDescriptor = descriptor + else -> entry.icons.smallIconDescriptor = descriptor } } else if (isImportantConversation(entry)) { // Old approach: cache only if important conversation. - if (showPeopleAvatar) { + if (descriptor.type == StatusBarIcon.Type.PeopleAvatar) { entry.icons.peopleAvatarDescriptor = descriptor } else { entry.icons.smallIconDescriptor = descriptor @@ -312,7 +299,10 @@ constructor( } } - private fun Icon.toStatusBarIcon(entry: NotificationEntry): StatusBarIcon { + private fun Icon.toStatusBarIcon( + entry: NotificationEntry, + type: StatusBarIcon.Type + ): StatusBarIcon { val n = entry.sbn.notification return StatusBarIcon( entry.sbn.user, @@ -320,33 +310,11 @@ constructor( /* icon = */ this, n.iconLevel, n.number, - iconBuilder.getIconContentDescription(n) + iconBuilder.getIconContentDescription(n), + type ) } - // TODO(b/335211019): Should we merge this with the method in GroupHelper? - private fun getMonochromeAppIcon(entry: NotificationEntry): Icon? { - // TODO(b/335211019): This should be done in the background. - var monochromeIcon: Icon? = null - try { - val appIcon: Drawable = iconBuilder.getAppIcon(entry.sbn.notification) - if (appIcon is AdaptiveIconDrawable) { - if (appIcon.monochrome != null) { - monochromeIcon = - Icon.createWithResourceAdaptiveDrawable( - /* resPackage = */ entry.sbn.packageName, - /* resId = */ appIcon.sourceDrawableResId, - /* useMonochrome = */ true, - /* inset = */ -3.0f * AdaptiveIconDrawable.getExtraInsetFraction() - ) - } - } - } catch (e: Exception) { - Log.e(TAG, "Failed to getAppIcon() in getMonochromeAppIcon()", e) - } - return monochromeIcon - } - private suspend fun getLauncherShortcutIconForPeopleAvatar(entry: NotificationEntry) = withContext(bgCoroutineContext) { var icon: Icon? = null @@ -365,7 +333,8 @@ constructor( // Once we have the icon, updating it should happen on the main thread. if (icon != null) { withContext(mainCoroutineContext) { - val iconDescriptor = icon.toStatusBarIcon(entry) + val iconDescriptor = + icon.toStatusBarIcon(entry, StatusBarIcon.Type.PeopleAvatar) // Cache the value entry.icons.peopleAvatarDescriptor = iconDescriptor diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/AvalancheProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/AvalancheProvider.kt index c29d700396af..a8fd082dd1e1 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/AvalancheProvider.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/AvalancheProvider.kt @@ -24,6 +24,7 @@ import android.util.Log import com.android.internal.logging.UiEventLogger import com.android.systemui.broadcast.BroadcastDispatcher import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.statusbar.notification.interruption.AvalancheSuppressor.AvalancheEvent import javax.inject.Inject // Class to track avalanche trigger event time. @@ -31,37 +32,41 @@ import javax.inject.Inject class AvalancheProvider @Inject constructor( - private val broadcastDispatcher: BroadcastDispatcher, - private val logger: VisualInterruptionDecisionLogger, - private val uiEventLogger: UiEventLogger, + private val broadcastDispatcher: BroadcastDispatcher, + private val logger: VisualInterruptionDecisionLogger, + private val uiEventLogger: UiEventLogger, ) { val TAG = "AvalancheProvider" val timeoutMs = 120000 var startTime: Long = 0L - private val avalancheTriggerIntents = mutableSetOf( + private val avalancheTriggerIntents = + mutableSetOf( Intent.ACTION_AIRPLANE_MODE_CHANGED, Intent.ACTION_BOOT_COMPLETED, Intent.ACTION_MANAGED_PROFILE_AVAILABLE, Intent.ACTION_USER_SWITCHED - ) + ) - private val broadcastReceiver: BroadcastReceiver = object : BroadcastReceiver() { - override fun onReceive(context: Context, intent: Intent) { - if (intent.action in avalancheTriggerIntents) { + private val broadcastReceiver: BroadcastReceiver = + object : BroadcastReceiver() { + override fun onReceive(context: Context, intent: Intent) { + if (intent.action in avalancheTriggerIntents) { - // Ignore when airplane mode turned on - if (intent.action == Intent.ACTION_AIRPLANE_MODE_CHANGED - && intent.getBooleanExtra(/* name= */ "state", /* defaultValue */ false)) { - Log.d(TAG, "broadcastReceiver: ignore airplane mode on") - return + // Ignore when airplane mode turned on + if ( + intent.action == Intent.ACTION_AIRPLANE_MODE_CHANGED && + intent.getBooleanExtra(/* name= */ "state", /* defaultValue */ false) + ) { + Log.d(TAG, "broadcastReceiver: ignore airplane mode on") + return + } + Log.d(TAG, "broadcastReceiver received intent.action=" + intent.action) + uiEventLogger.log(AvalancheEvent.AVALANCHE_SUPPRESSOR_RECEIVED_TRIGGERING_EVENT) + startTime = System.currentTimeMillis() } - Log.d(TAG, "broadcastReceiver received intent.action=" + intent.action) - uiEventLogger.log(AvalancheSuppressor.AvalancheEvent.START); - startTime = System.currentTimeMillis() } } - } fun register() { val intentFilter = IntentFilter() @@ -70,4 +75,4 @@ constructor( } broadcastDispatcher.registerReceiver(broadcastReceiver, intentFilter) } -}
\ No newline at end of file +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/CommonVisualInterruptionSuppressors.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/CommonVisualInterruptionSuppressors.kt index f84b5f48c864..367aaadf2942 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/CommonVisualInterruptionSuppressors.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/CommonVisualInterruptionSuppressors.kt @@ -270,32 +270,26 @@ class AvalancheSuppressor( } enum class AvalancheEvent(private val id: Int) : UiEventLogger.UiEventEnum { - @UiEvent( - doc = - "An avalanche event occurred but this notification was suppressed by a " + - "non-avalanche suppressor." - ) - START(1802), - @UiEvent(doc = "HUN was suppressed in avalanche.") SUPPRESS(1803), - @UiEvent(doc = "HUN allowed during avalanche because it is high priority.") - ALLOW_CONVERSATION_AFTER_AVALANCHE(1804), - @UiEvent(doc = "HUN allowed during avalanche because it is a high priority conversation.") - ALLOW_HIGH_PRIORITY_CONVERSATION_ANY_TIME(1805), - @UiEvent(doc = "HUN allowed during avalanche because it is a call.") ALLOW_CALLSTYLE(1806), - @UiEvent(doc = "HUN allowed during avalanche because it is a calendar notification.") - ALLOW_CATEGORY_REMINDER(1807), + @UiEvent(doc = "An avalanche event occurred, and a suppression period will start now.") + AVALANCHE_SUPPRESSOR_RECEIVED_TRIGGERING_EVENT(1824), + @UiEvent(doc = "HUN was suppressed in avalanche.") + AVALANCHE_SUPPRESSOR_HUN_SUPPRESSED(1825), + @UiEvent(doc = "HUN allowed during avalanche because conversation newer than the trigger.") + AVALANCHE_SUPPRESSOR_HUN_ALLOWED_NEW_CONVERSATION(1826), + @UiEvent(doc = "HUN allowed during avalanche because it is a priority conversation.") + AVALANCHE_SUPPRESSOR_HUN_ALLOWED_PRIORITY_CONVERSATION(1827), + @UiEvent(doc = "HUN allowed during avalanche because it is a CallStyle notification.") + AVALANCHE_SUPPRESSOR_HUN_ALLOWED_CALL_STYLE(1828), + @UiEvent(doc = "HUN allowed during avalanche because it is a reminder notification.") + AVALANCHE_SUPPRESSOR_HUN_ALLOWED_CATEGORY_REMINDER(1829), @UiEvent(doc = "HUN allowed during avalanche because it is a calendar notification.") - ALLOW_CATEGORY_EVENT(1808), - @UiEvent( - doc = - "HUN allowed during avalanche because it has a full screen intent and " + - "the full screen intent permission is granted." - ) - ALLOW_FSI_WITH_PERMISSION_ON(1809), + AVALANCHE_SUPPRESSOR_HUN_ALLOWED_CATEGORY_EVENT(1830), + @UiEvent(doc = "HUN allowed during avalanche because it has FSI.") + AVALANCHE_SUPPRESSOR_HUN_ALLOWED_FSI_WITH_PERMISSION(1831), @UiEvent(doc = "HUN allowed during avalanche because it is colorized.") - ALLOW_COLORIZED(1810), + AVALANCHE_SUPPRESSOR_HUN_ALLOWED_COLORIZED(1832), @UiEvent(doc = "HUN allowed during avalanche because it is an emergency notification.") - ALLOW_EMERGENCY(1811); + AVALANCHE_SUPPRESSOR_HUN_ALLOWED_EMERGENCY(1833); override fun getId(): Int { return id @@ -323,46 +317,46 @@ class AvalancheSuppressor( entry.ranking.isConversation && entry.sbn.notification.getWhen() > avalancheProvider.startTime ) { - uiEventLogger.log(AvalancheEvent.ALLOW_CONVERSATION_AFTER_AVALANCHE) + uiEventLogger.log(AvalancheEvent.AVALANCHE_SUPPRESSOR_HUN_ALLOWED_NEW_CONVERSATION) return State.ALLOW_CONVERSATION_AFTER_AVALANCHE } if (entry.channel?.isImportantConversation == true) { - uiEventLogger.log(AvalancheEvent.ALLOW_HIGH_PRIORITY_CONVERSATION_ANY_TIME) + uiEventLogger.log(AvalancheEvent.AVALANCHE_SUPPRESSOR_HUN_ALLOWED_PRIORITY_CONVERSATION) return State.ALLOW_HIGH_PRIORITY_CONVERSATION_ANY_TIME } if (entry.sbn.notification.isStyle(Notification.CallStyle::class.java)) { - uiEventLogger.log(AvalancheEvent.ALLOW_CALLSTYLE) + uiEventLogger.log(AvalancheEvent.AVALANCHE_SUPPRESSOR_HUN_ALLOWED_CALL_STYLE) return State.ALLOW_CALLSTYLE } if (entry.sbn.notification.category == CATEGORY_REMINDER) { - uiEventLogger.log(AvalancheEvent.ALLOW_CATEGORY_REMINDER) + uiEventLogger.log(AvalancheEvent.AVALANCHE_SUPPRESSOR_HUN_ALLOWED_CATEGORY_REMINDER) return State.ALLOW_CATEGORY_REMINDER } if (entry.sbn.notification.category == CATEGORY_EVENT) { - uiEventLogger.log(AvalancheEvent.ALLOW_CATEGORY_EVENT) + uiEventLogger.log(AvalancheEvent.AVALANCHE_SUPPRESSOR_HUN_ALLOWED_CATEGORY_EVENT) return State.ALLOW_CATEGORY_EVENT } if (entry.sbn.notification.fullScreenIntent != null) { - uiEventLogger.log(AvalancheEvent.ALLOW_FSI_WITH_PERMISSION_ON) + uiEventLogger.log(AvalancheEvent.AVALANCHE_SUPPRESSOR_HUN_ALLOWED_FSI_WITH_PERMISSION) return State.ALLOW_FSI_WITH_PERMISSION_ON } if (entry.sbn.notification.isColorized) { - uiEventLogger.log(AvalancheEvent.ALLOW_COLORIZED) + uiEventLogger.log(AvalancheEvent.AVALANCHE_SUPPRESSOR_HUN_ALLOWED_COLORIZED) return State.ALLOW_COLORIZED } if ( packageManager.checkPermission(RECEIVE_EMERGENCY_BROADCAST, entry.sbn.packageName) == PERMISSION_GRANTED ) { - uiEventLogger.log(AvalancheEvent.ALLOW_EMERGENCY) + uiEventLogger.log(AvalancheEvent.AVALANCHE_SUPPRESSOR_HUN_ALLOWED_EMERGENCY) return State.ALLOW_EMERGENCY } - uiEventLogger.log(AvalancheEvent.SUPPRESS) + uiEventLogger.log(AvalancheEvent.AVALANCHE_SUPPRESSOR_HUN_SUPPRESSED) return State.SUPPRESS } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java index 6ba26d99419e..af5117e0b561 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java @@ -56,6 +56,7 @@ import com.android.systemui.statusbar.notification.InflationException; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.row.shared.AsyncGroupHeaderViewInflation; import com.android.systemui.statusbar.notification.row.shared.AsyncHybridViewInflation; +import com.android.systemui.statusbar.notification.row.shared.NotificationRowContentBinderRefactor; import com.android.systemui.statusbar.notification.row.ui.viewbinder.SingleLineConversationViewBinder; import com.android.systemui.statusbar.notification.row.ui.viewbinder.SingleLineViewBinder; import com.android.systemui.statusbar.notification.row.ui.viewmodel.SingleLineViewModel; @@ -105,6 +106,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder NotifLayoutInflaterFactory.Provider notifLayoutInflaterFactoryProvider, HeadsUpStyleProvider headsUpStyleProvider, NotificationRowContentBinderLogger logger) { + NotificationRowContentBinderRefactor.assertInLegacyMode(); mRemoteViewCache = remoteViewCache; mRemoteInputManager = remoteInputManager; mConversationProcessor = conversationProcessor; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinder.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinder.java index c1302a0d3e57..fe0ee5235808 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinder.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinder.java @@ -147,11 +147,6 @@ public interface NotificationRowContentBinder { * Use increased height when binding heads up views. */ public boolean usesIncreasedHeadsUpHeight; - - /** - * Is group summary notification - */ - public boolean mIsGroupSummary; } /** 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 new file mode 100644 index 000000000000..e70414084598 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImpl.kt @@ -0,0 +1,1569 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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 android.annotation.SuppressLint +import android.app.Notification +import android.content.Context +import android.content.ContextWrapper +import android.content.pm.ApplicationInfo +import android.content.pm.PackageManager +import android.content.res.Resources +import android.os.AsyncTask +import android.os.Build +import android.os.CancellationSignal +import android.os.Trace +import android.os.UserHandle +import android.service.notification.StatusBarNotification +import android.util.Log +import android.view.NotificationHeaderView +import android.view.View +import android.view.ViewGroup +import android.widget.RemoteViews +import android.widget.RemoteViews.InteractionHandler +import android.widget.RemoteViews.OnViewAppliedListener +import com.android.app.tracing.TraceUtils +import com.android.internal.annotations.VisibleForTesting +import com.android.internal.widget.ImageMessageConsumer +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.dagger.qualifiers.NotifInflation +import com.android.systemui.res.R +import com.android.systemui.statusbar.InflationTask +import com.android.systemui.statusbar.NotificationRemoteInputManager +import com.android.systemui.statusbar.notification.ConversationNotificationProcessor +import com.android.systemui.statusbar.notification.InflationException +import com.android.systemui.statusbar.notification.collection.NotificationEntry +import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.BindParams +import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_CONTRACTED +import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_EXPANDED +import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_HEADS_UP +import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_PUBLIC +import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_SINGLE_LINE +import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_GROUP_SUMMARY_HEADER +import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_LOW_PRIORITY_GROUP_SUMMARY_HEADER +import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationCallback +import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag +import com.android.systemui.statusbar.notification.row.shared.AsyncGroupHeaderViewInflation +import com.android.systemui.statusbar.notification.row.shared.AsyncHybridViewInflation +import com.android.systemui.statusbar.notification.row.shared.HeadsUpStatusBarModel +import com.android.systemui.statusbar.notification.row.shared.NewRemoteViews +import com.android.systemui.statusbar.notification.row.shared.NotificationContentModel +import com.android.systemui.statusbar.notification.row.shared.NotificationRowContentBinderRefactor +import com.android.systemui.statusbar.notification.row.ui.viewbinder.SingleLineConversationViewBinder +import com.android.systemui.statusbar.notification.row.ui.viewbinder.SingleLineViewBinder +import com.android.systemui.statusbar.notification.row.wrapper.NotificationViewWrapper +import com.android.systemui.statusbar.notification.stack.NotificationChildrenContainer +import com.android.systemui.statusbar.policy.InflatedSmartReplyState +import com.android.systemui.statusbar.policy.InflatedSmartReplyViewHolder +import com.android.systemui.statusbar.policy.SmartReplyStateInflater +import com.android.systemui.util.Assert +import java.util.concurrent.Executor +import java.util.function.Consumer +import javax.inject.Inject + +/** + * [NotificationRowContentBinderImpl] binds content to a [ExpandableNotificationRow] by + * asynchronously building the content's [RemoteViews] and applying it to the row. + */ +@SysUISingleton +@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) +class NotificationRowContentBinderImpl +@Inject +constructor( + private val remoteViewCache: NotifRemoteViewCache, + private val remoteInputManager: NotificationRemoteInputManager, + private val conversationProcessor: ConversationNotificationProcessor, + @NotifInflation private val inflationExecutor: Executor, + private val smartReplyStateInflater: SmartReplyStateInflater, + private val notifLayoutInflaterFactoryProvider: NotifLayoutInflaterFactory.Provider, + private val headsUpStyleProvider: HeadsUpStyleProvider, + private val logger: NotificationRowContentBinderLogger +) : NotificationRowContentBinder { + + init { + /* check if */ NotificationRowContentBinderRefactor.isUnexpectedlyInLegacyMode() + } + + private var inflateSynchronously = false + + override fun bindContent( + entry: NotificationEntry, + row: ExpandableNotificationRow, + @InflationFlag contentToBind: Int, + bindParams: BindParams, + forceInflate: Boolean, + callback: InflationCallback? + ) { + if (row.isRemoved) { + // We don't want to reinflate anything for removed notifications. Otherwise views might + // be readded to the stack, leading to leaks. This may happen with low-priority groups + // where the removal of already removed children can lead to a reinflation. + logger.logNotBindingRowWasRemoved(entry) + return + } + logger.logBinding(entry, contentToBind) + val sbn: StatusBarNotification = entry.sbn + + // To check if the notification has inline image and preload inline image if necessary. + row.imageResolver.preloadImages(sbn.notification) + if (forceInflate) { + remoteViewCache.clearCache(entry) + } + + // Cancel any pending frees on any view we're trying to bind since we should be bound after. + cancelContentViewFrees(row, contentToBind) + val task = + AsyncInflationTask( + inflationExecutor, + inflateSynchronously, + /* reInflateFlags = */ contentToBind, + remoteViewCache, + entry, + conversationProcessor, + row, + bindParams.isMinimized, + bindParams.usesIncreasedHeight, + bindParams.usesIncreasedHeadsUpHeight, + callback, + remoteInputManager.remoteViewsOnClickHandler, + /* isMediaFlagEnabled = */ smartReplyStateInflater, + notifLayoutInflaterFactoryProvider, + headsUpStyleProvider, + logger + ) + if (inflateSynchronously) { + task.onPostExecute(task.doInBackground()) + } else { + task.executeOnExecutor(inflationExecutor) + } + } + + @VisibleForTesting + fun inflateNotificationViews( + entry: NotificationEntry, + row: ExpandableNotificationRow, + bindParams: BindParams, + inflateSynchronously: Boolean, + @InflationFlag reInflateFlags: Int, + builder: Notification.Builder, + packageContext: Context, + smartRepliesInflater: SmartReplyStateInflater + ): InflationProgress { + val systemUIContext = row.context + val result = + beginInflationAsync( + reInflateFlags = reInflateFlags, + entry = entry, + builder = builder, + isMinimized = bindParams.isMinimized, + usesIncreasedHeight = bindParams.usesIncreasedHeight, + usesIncreasedHeadsUpHeight = bindParams.usesIncreasedHeadsUpHeight, + systemUIContext = systemUIContext, + packageContext = packageContext, + row = row, + notifLayoutInflaterFactoryProvider = notifLayoutInflaterFactoryProvider, + headsUpStyleProvider = headsUpStyleProvider, + conversationProcessor = conversationProcessor, + logger = logger, + ) + inflateSmartReplyViews( + result, + reInflateFlags, + entry, + systemUIContext, + packageContext, + row.existingSmartReplyState, + smartRepliesInflater, + logger, + ) + if (AsyncHybridViewInflation.isEnabled) { + result.inflatedSingleLineView = + result.contentModel.singleLineViewModel?.let { viewModel -> + SingleLineViewInflater.inflateSingleLineViewHolder( + viewModel.isConversation(), + reInflateFlags, + entry, + systemUIContext, + logger, + ) + } + } + apply( + inflationExecutor, + inflateSynchronously, + bindParams.isMinimized, + result, + reInflateFlags, + remoteViewCache, + entry, + row, + remoteInputManager.remoteViewsOnClickHandler, + /* callback= */ null, + logger + ) + return result + } + + override fun cancelBind(entry: NotificationEntry, row: ExpandableNotificationRow): Boolean { + val abortedTask: Boolean = entry.abortTask() + if (abortedTask) { + logger.logCancelBindAbortedTask(entry) + } + return abortedTask + } + + @SuppressLint("WrongConstant") + override fun unbindContent( + entry: NotificationEntry, + row: ExpandableNotificationRow, + @InflationFlag contentToUnbind: Int + ) { + logger.logUnbinding(entry, contentToUnbind) + var curFlag = 1 + var contentLeftToUnbind = contentToUnbind + while (contentLeftToUnbind != 0) { + if (contentLeftToUnbind and curFlag != 0) { + freeNotificationView(entry, row, curFlag) + } + contentLeftToUnbind = contentLeftToUnbind and curFlag.inv() + curFlag = curFlag shl 1 + } + } + + /** + * Frees the content view associated with the inflation flag as soon as the view is not showing. + * + * @param inflateFlag the flag corresponding to the content view which should be freed + */ + private fun freeNotificationView( + entry: NotificationEntry, + row: ExpandableNotificationRow, + @InflationFlag inflateFlag: Int + ) { + when (inflateFlag) { + FLAG_CONTENT_VIEW_CONTRACTED -> + row.privateLayout.performWhenContentInactive( + NotificationContentView.VISIBLE_TYPE_CONTRACTED + ) { + row.privateLayout.setContractedChild(null) + remoteViewCache.removeCachedView(entry, FLAG_CONTENT_VIEW_CONTRACTED) + } + FLAG_CONTENT_VIEW_EXPANDED -> + row.privateLayout.performWhenContentInactive( + NotificationContentView.VISIBLE_TYPE_EXPANDED + ) { + row.privateLayout.setExpandedChild(null) + remoteViewCache.removeCachedView(entry, FLAG_CONTENT_VIEW_EXPANDED) + } + FLAG_CONTENT_VIEW_HEADS_UP -> + row.privateLayout.performWhenContentInactive( + NotificationContentView.VISIBLE_TYPE_HEADSUP + ) { + row.privateLayout.setHeadsUpChild(null) + remoteViewCache.removeCachedView(entry, FLAG_CONTENT_VIEW_HEADS_UP) + row.privateLayout.setHeadsUpInflatedSmartReplies(null) + } + FLAG_CONTENT_VIEW_PUBLIC -> + row.publicLayout.performWhenContentInactive( + NotificationContentView.VISIBLE_TYPE_CONTRACTED + ) { + row.publicLayout.setContractedChild(null) + remoteViewCache.removeCachedView(entry, FLAG_CONTENT_VIEW_PUBLIC) + } + FLAG_CONTENT_VIEW_SINGLE_LINE -> { + if (AsyncHybridViewInflation.isEnabled) { + row.privateLayout.performWhenContentInactive( + NotificationContentView.VISIBLE_TYPE_SINGLELINE + ) { + row.privateLayout.setSingleLineView(null) + } + } + } + else -> {} + } + } + + /** + * Cancel any pending content view frees from [.freeNotificationView] for the provided content + * views. + * + * @param row top level notification row containing the content views + * @param contentViews content views to cancel pending frees on + */ + private fun cancelContentViewFrees( + row: ExpandableNotificationRow, + @InflationFlag contentViews: Int + ) { + if (contentViews and FLAG_CONTENT_VIEW_CONTRACTED != 0) { + row.privateLayout.removeContentInactiveRunnable( + NotificationContentView.VISIBLE_TYPE_CONTRACTED + ) + } + if (contentViews and FLAG_CONTENT_VIEW_EXPANDED != 0) { + row.privateLayout.removeContentInactiveRunnable( + NotificationContentView.VISIBLE_TYPE_EXPANDED + ) + } + if (contentViews and FLAG_CONTENT_VIEW_HEADS_UP != 0) { + row.privateLayout.removeContentInactiveRunnable( + NotificationContentView.VISIBLE_TYPE_HEADSUP + ) + } + if (contentViews and FLAG_CONTENT_VIEW_PUBLIC != 0) { + row.publicLayout.removeContentInactiveRunnable( + NotificationContentView.VISIBLE_TYPE_CONTRACTED + ) + } + if ( + AsyncHybridViewInflation.isEnabled && + contentViews and FLAG_CONTENT_VIEW_SINGLE_LINE != 0 + ) { + row.privateLayout.removeContentInactiveRunnable( + NotificationContentView.VISIBLE_TYPE_SINGLELINE + ) + } + } + + /** + * Sets whether to perform inflation on the same thread as the caller. This method should only + * be used in tests, not in production. + */ + @VisibleForTesting + override fun setInflateSynchronously(inflateSynchronously: Boolean) { + this.inflateSynchronously = inflateSynchronously + } + + class AsyncInflationTask( + private val inflationExecutor: Executor, + private val inflateSynchronously: Boolean, + @get:InflationFlag @get:VisibleForTesting @InflationFlag val reInflateFlags: Int, + private val remoteViewCache: NotifRemoteViewCache, + private val entry: NotificationEntry, + private val conversationProcessor: ConversationNotificationProcessor, + private val row: ExpandableNotificationRow, + private val isMinimized: Boolean, + private val usesIncreasedHeight: Boolean, + private val usesIncreasedHeadsUpHeight: Boolean, + private val callback: InflationCallback?, + private val remoteViewClickHandler: InteractionHandler?, + private val smartRepliesInflater: SmartReplyStateInflater, + private val notifLayoutInflaterFactoryProvider: NotifLayoutInflaterFactory.Provider, + private val headsUpStyleProvider: HeadsUpStyleProvider, + private val logger: NotificationRowContentBinderLogger + ) : AsyncTask<Void, Void, Result<InflationProgress>>(), InflationCallback, InflationTask { + private val context: Context + get() = row.context + + private var cancellationSignal: CancellationSignal? = null + + init { + entry.setInflationTask(this) + } + + private fun updateApplicationInfo(sbn: StatusBarNotification) { + val packageName: String = sbn.packageName + val userId: Int = UserHandle.getUserId(sbn.uid) + val appInfo: ApplicationInfo + try { + // This method has an internal cache, so we don't need to add our own caching here. + appInfo = + context.packageManager.getApplicationInfoAsUser( + packageName, + PackageManager.MATCH_UNINSTALLED_PACKAGES, + userId + ) + } catch (e: PackageManager.NameNotFoundException) { + return + } + Notification.addFieldsFromContext(appInfo, sbn.notification) + } + + override fun onPreExecute() { + Trace.beginAsyncSection(ASYNC_TASK_TRACE_METHOD, System.identityHashCode(this)) + } + + public override fun doInBackground(vararg params: Void): Result<InflationProgress> { + return TraceUtils.trace( + "NotificationContentInflater.AsyncInflationTask#doInBackground" + ) { + try { + return@trace Result.success(doInBackgroundInternal()) + } catch (e: Exception) { + logger.logAsyncTaskException(entry, "inflating", e) + return@trace Result.failure(e) + } + } + } + + private fun doInBackgroundInternal(): InflationProgress { + val sbn: StatusBarNotification = entry.sbn + // Ensure the ApplicationInfo is updated before a builder is recovered. + updateApplicationInfo(sbn) + val recoveredBuilder = Notification.Builder.recoverBuilder(context, sbn.notification) + var packageContext: Context = sbn.getPackageContext(context) + if (recoveredBuilder.usesTemplate()) { + // For all of our templates, we want it to be RTL + packageContext = RtlEnabledContext(packageContext) + } + val inflationProgress = + beginInflationAsync( + reInflateFlags = reInflateFlags, + entry = entry, + builder = recoveredBuilder, + isMinimized = isMinimized, + usesIncreasedHeight = usesIncreasedHeight, + usesIncreasedHeadsUpHeight = usesIncreasedHeadsUpHeight, + systemUIContext = context, + packageContext = packageContext, + row = row, + notifLayoutInflaterFactoryProvider = notifLayoutInflaterFactoryProvider, + headsUpStyleProvider = headsUpStyleProvider, + conversationProcessor = conversationProcessor, + logger = logger + ) + logger.logAsyncTaskProgress( + entry, + "getting existing smart reply state (on wrong thread!)" + ) + val previousSmartReplyState: InflatedSmartReplyState? = row.existingSmartReplyState + logger.logAsyncTaskProgress(entry, "inflating smart reply views") + inflateSmartReplyViews( + /* result = */ inflationProgress, + reInflateFlags, + entry, + context, + packageContext, + previousSmartReplyState, + smartRepliesInflater, + logger, + ) + if (AsyncHybridViewInflation.isEnabled) { + logger.logAsyncTaskProgress(entry, "inflating single line view") + inflationProgress.inflatedSingleLineView = + inflationProgress.contentModel.singleLineViewModel?.let { + SingleLineViewInflater.inflateSingleLineViewHolder( + it.isConversation(), + reInflateFlags, + entry, + context, + logger + ) + } + } + logger.logAsyncTaskProgress(entry, "getting row image resolver (on wrong thread!)") + val imageResolver = row.imageResolver + // wait for image resolver to finish preloading + logger.logAsyncTaskProgress(entry, "waiting for preloaded images") + imageResolver.waitForPreloadedImages(IMG_PRELOAD_TIMEOUT_MS) + return inflationProgress + } + + public override fun onPostExecute(result: Result<InflationProgress>) { + Trace.endAsyncSection(ASYNC_TASK_TRACE_METHOD, System.identityHashCode(this)) + result + .onSuccess { progress -> + // Logged in detail in apply. + cancellationSignal = + apply( + inflationExecutor, + inflateSynchronously, + isMinimized, + progress, + reInflateFlags, + remoteViewCache, + entry, + row, + remoteViewClickHandler, + this /* callback */, + logger + ) + } + .onFailure { error -> handleError(error as Exception) } + } + + override fun onCancelled(result: Result<InflationProgress>) { + Trace.endAsyncSection(ASYNC_TASK_TRACE_METHOD, System.identityHashCode(this)) + } + + private fun handleError(e: Exception) { + entry.onInflationTaskFinished() + val sbn: StatusBarNotification = entry.sbn + val ident: String = (sbn.packageName + "/0x" + Integer.toHexString(sbn.id)) + Log.e(TAG, "couldn't inflate view for notification $ident", e) + callback?.handleInflationException( + row.entry, + InflationException("Couldn't inflate contentViews$e") + ) + + // Cancel any image loading tasks, not useful any more + row.imageResolver.cancelRunningTasks() + } + + override fun abort() { + logger.logAsyncTaskProgress(entry, "cancelling inflate") + cancel(/* mayInterruptIfRunning= */ true) + if (cancellationSignal != null) { + logger.logAsyncTaskProgress(entry, "cancelling apply") + cancellationSignal!!.cancel() + } + logger.logAsyncTaskProgress(entry, "aborted") + } + + override fun handleInflationException(entry: NotificationEntry, e: Exception) { + handleError(e) + } + + override fun onAsyncInflationFinished(entry: NotificationEntry) { + this.entry.onInflationTaskFinished() + row.onNotificationUpdated() + callback?.onAsyncInflationFinished(this.entry) + + // Notify the resolver that the inflation task has finished, + // try to purge unnecessary cached entries. + row.imageResolver.purgeCache() + + // Cancel any image loading tasks that have not completed at this point + row.imageResolver.cancelRunningTasks() + } + + class RtlEnabledContext(packageContext: Context) : ContextWrapper(packageContext) { + override fun getApplicationInfo(): ApplicationInfo { + val applicationInfo = ApplicationInfo(super.getApplicationInfo()) + applicationInfo.flags = applicationInfo.flags or ApplicationInfo.FLAG_SUPPORTS_RTL + return applicationInfo + } + } + + companion object { + private const val IMG_PRELOAD_TIMEOUT_MS = 1000L + } + } + + @VisibleForTesting + class InflationProgress( + @VisibleForTesting val packageContext: Context, + val remoteViews: NewRemoteViews, + val contentModel: NotificationContentModel, + ) { + + var inflatedContentView: View? = null + var inflatedHeadsUpView: View? = null + var inflatedExpandedView: View? = null + var inflatedPublicView: View? = null + var inflatedGroupHeaderView: NotificationHeaderView? = null + var inflatedMinimizedGroupHeaderView: NotificationHeaderView? = null + var inflatedSmartReplyState: InflatedSmartReplyState? = null + var expandedInflatedSmartReplies: InflatedSmartReplyViewHolder? = null + var headsUpInflatedSmartReplies: InflatedSmartReplyViewHolder? = null + + // Inflated SingleLineView that lacks the UI State + var inflatedSingleLineView: HybridNotificationView? = null + } + + @VisibleForTesting + abstract class ApplyCallback { + abstract fun setResultView(v: View) + + abstract val remoteView: RemoteViews + } + + companion object { + const val TAG = "NotifContentInflater" + + private fun inflateSmartReplyViews( + result: InflationProgress, + @InflationFlag reInflateFlags: Int, + entry: NotificationEntry, + context: Context, + packageContext: Context, + previousSmartReplyState: InflatedSmartReplyState?, + inflater: SmartReplyStateInflater, + logger: NotificationRowContentBinderLogger + ) { + val inflateContracted = + (reInflateFlags and FLAG_CONTENT_VIEW_CONTRACTED != 0 && + result.remoteViews.contracted != null) + val inflateExpanded = + (reInflateFlags and FLAG_CONTENT_VIEW_EXPANDED != 0 && + result.remoteViews.expanded != null) + val inflateHeadsUp = + (reInflateFlags and FLAG_CONTENT_VIEW_HEADS_UP != 0 && + result.remoteViews.headsUp != null) + if (inflateContracted || inflateExpanded || inflateHeadsUp) { + logger.logAsyncTaskProgress(entry, "inflating contracted smart reply state") + result.inflatedSmartReplyState = inflater.inflateSmartReplyState(entry) + } + if (inflateExpanded) { + logger.logAsyncTaskProgress(entry, "inflating expanded smart reply state") + result.expandedInflatedSmartReplies = + inflater.inflateSmartReplyViewHolder( + context, + packageContext, + entry, + previousSmartReplyState, + result.inflatedSmartReplyState!! + ) + } + if (inflateHeadsUp) { + logger.logAsyncTaskProgress(entry, "inflating heads up smart reply state") + result.headsUpInflatedSmartReplies = + inflater.inflateSmartReplyViewHolder( + context, + packageContext, + entry, + previousSmartReplyState, + result.inflatedSmartReplyState!! + ) + } + } + + private fun beginInflationAsync( + @InflationFlag reInflateFlags: Int, + entry: NotificationEntry, + builder: Notification.Builder, + isMinimized: Boolean, + usesIncreasedHeight: Boolean, + usesIncreasedHeadsUpHeight: Boolean, + systemUIContext: Context, + packageContext: Context, + row: ExpandableNotificationRow, + notifLayoutInflaterFactoryProvider: NotifLayoutInflaterFactory.Provider, + headsUpStyleProvider: HeadsUpStyleProvider, + conversationProcessor: ConversationNotificationProcessor, + logger: NotificationRowContentBinderLogger + ): InflationProgress { + // process conversations and extract the messaging style + val messagingStyle = + if (entry.ranking.isConversation) { + conversationProcessor.processNotification(entry, builder, logger) + } else null + + val remoteViews = + createRemoteViews( + reInflateFlags = reInflateFlags, + builder = builder, + isMinimized = isMinimized, + usesIncreasedHeight = usesIncreasedHeight, + usesIncreasedHeadsUpHeight = usesIncreasedHeadsUpHeight, + row = row, + notifLayoutInflaterFactoryProvider = notifLayoutInflaterFactoryProvider, + headsUpStyleProvider = headsUpStyleProvider, + logger = logger, + ) + + val singleLineViewModel = + if ( + AsyncHybridViewInflation.isEnabled && + reInflateFlags and FLAG_CONTENT_VIEW_SINGLE_LINE != 0 + ) { + logger.logAsyncTaskProgress(entry, "inflating single line view model") + SingleLineViewInflater.inflateSingleLineViewModel( + notification = entry.sbn.notification, + messagingStyle = messagingStyle, + builder = builder, + systemUiContext = systemUIContext, + ) + } else null + + val headsUpStatusBarModel = + HeadsUpStatusBarModel( + privateText = builder.getHeadsUpStatusBarText(/* publicMode= */ false), + publicText = builder.getHeadsUpStatusBarText(/* publicMode= */ true), + ) + + val contentModel = + NotificationContentModel( + headsUpStatusBarModel = headsUpStatusBarModel, + singleLineViewModel = singleLineViewModel, + ) + + return InflationProgress( + packageContext = packageContext, + remoteViews = remoteViews, + contentModel = contentModel, + ) + } + + private fun createRemoteViews( + @InflationFlag reInflateFlags: Int, + builder: Notification.Builder, + isMinimized: Boolean, + usesIncreasedHeight: Boolean, + usesIncreasedHeadsUpHeight: Boolean, + row: ExpandableNotificationRow, + notifLayoutInflaterFactoryProvider: NotifLayoutInflaterFactory.Provider, + headsUpStyleProvider: HeadsUpStyleProvider, + logger: NotificationRowContentBinderLogger + ): NewRemoteViews { + return TraceUtils.trace("NotificationContentInflater.createRemoteViews") { + val entryForLogging: NotificationEntry = row.entry + val contracted = + if (reInflateFlags and FLAG_CONTENT_VIEW_CONTRACTED != 0) { + logger.logAsyncTaskProgress( + entryForLogging, + "creating contracted remote view" + ) + createContentView(builder, isMinimized, usesIncreasedHeight) + } else null + val expanded = + if (reInflateFlags and FLAG_CONTENT_VIEW_EXPANDED != 0) { + logger.logAsyncTaskProgress( + entryForLogging, + "creating expanded remote view" + ) + createExpandedView(builder, isMinimized) + } else null + val headsUp = + if (reInflateFlags and FLAG_CONTENT_VIEW_HEADS_UP != 0) { + logger.logAsyncTaskProgress( + entryForLogging, + "creating heads up remote view" + ) + val isHeadsUpCompact = headsUpStyleProvider.shouldApplyCompactStyle() + if (isHeadsUpCompact) { + builder.createCompactHeadsUpContentView() + } else { + builder.createHeadsUpContentView(usesIncreasedHeadsUpHeight) + } + } else null + val public = + if (reInflateFlags and FLAG_CONTENT_VIEW_PUBLIC != 0) { + logger.logAsyncTaskProgress(entryForLogging, "creating public remote view") + builder.makePublicContentView(isMinimized) + } else null + val normalGroupHeader = + if ( + AsyncGroupHeaderViewInflation.isEnabled && + reInflateFlags and FLAG_GROUP_SUMMARY_HEADER != 0 + ) { + logger.logAsyncTaskProgress( + entryForLogging, + "creating group summary remote view" + ) + builder.makeNotificationGroupHeader() + } else null + val minimizedGroupHeader = + if ( + AsyncGroupHeaderViewInflation.isEnabled && + reInflateFlags and FLAG_LOW_PRIORITY_GROUP_SUMMARY_HEADER != 0 + ) { + logger.logAsyncTaskProgress( + entryForLogging, + "creating low-priority group summary remote view" + ) + builder.makeLowPriorityContentView(true /* useRegularSubtext */) + } else null + NewRemoteViews( + contracted = contracted, + headsUp = headsUp, + expanded = expanded, + public = public, + normalGroupHeader = normalGroupHeader, + minimizedGroupHeader = minimizedGroupHeader + ) + .withLayoutInflaterFactory(row, notifLayoutInflaterFactoryProvider) + } + } + + private fun NewRemoteViews.withLayoutInflaterFactory( + row: ExpandableNotificationRow, + provider: NotifLayoutInflaterFactory.Provider + ): NewRemoteViews { + contracted?.let { + it.layoutInflaterFactory = provider.provide(row, FLAG_CONTENT_VIEW_CONTRACTED) + } + expanded?.let { + it.layoutInflaterFactory = provider.provide(row, FLAG_CONTENT_VIEW_EXPANDED) + } + headsUp?.let { + it.layoutInflaterFactory = provider.provide(row, FLAG_CONTENT_VIEW_HEADS_UP) + } + public?.let { + it.layoutInflaterFactory = provider.provide(row, FLAG_CONTENT_VIEW_PUBLIC) + } + return this + } + + private fun apply( + inflationExecutor: Executor, + inflateSynchronously: Boolean, + isMinimized: Boolean, + result: InflationProgress, + @InflationFlag reInflateFlags: Int, + remoteViewCache: NotifRemoteViewCache, + entry: NotificationEntry, + row: ExpandableNotificationRow, + remoteViewClickHandler: InteractionHandler?, + callback: InflationCallback?, + logger: NotificationRowContentBinderLogger + ): CancellationSignal { + Trace.beginAsyncSection(APPLY_TRACE_METHOD, System.identityHashCode(row)) + val privateLayout = row.privateLayout + val publicLayout = row.publicLayout + val runningInflations = HashMap<Int, CancellationSignal>() + var flag = FLAG_CONTENT_VIEW_CONTRACTED + if (reInflateFlags and flag != 0) { + val isNewView = + !canReapplyRemoteView( + newView = result.remoteViews.contracted, + oldView = remoteViewCache.getCachedView(entry, FLAG_CONTENT_VIEW_CONTRACTED) + ) + val applyCallback: ApplyCallback = + object : ApplyCallback() { + override fun setResultView(v: View) { + logger.logAsyncTaskProgress(entry, "contracted view applied") + result.inflatedContentView = v + } + + override val remoteView: RemoteViews + get() = result.remoteViews.contracted!! + } + logger.logAsyncTaskProgress(entry, "applying contracted view") + applyRemoteView( + inflationExecutor = inflationExecutor, + inflateSynchronously = inflateSynchronously, + isMinimized = isMinimized, + result = result, + reInflateFlags = reInflateFlags, + inflationId = flag, + remoteViewCache = remoteViewCache, + entry = entry, + row = row, + isNewView = isNewView, + remoteViewClickHandler = remoteViewClickHandler, + callback = callback, + parentLayout = privateLayout, + existingView = privateLayout.contractedChild, + existingWrapper = + privateLayout.getVisibleWrapper( + NotificationContentView.VISIBLE_TYPE_CONTRACTED + ), + runningInflations = runningInflations, + applyCallback = applyCallback, + logger = logger + ) + } + flag = FLAG_CONTENT_VIEW_EXPANDED + if (reInflateFlags and flag != 0) { + if (result.remoteViews.expanded != null) { + val isNewView = + !canReapplyRemoteView( + newView = result.remoteViews.expanded, + oldView = + remoteViewCache.getCachedView(entry, FLAG_CONTENT_VIEW_EXPANDED) + ) + val applyCallback: ApplyCallback = + object : ApplyCallback() { + override fun setResultView(v: View) { + logger.logAsyncTaskProgress(entry, "expanded view applied") + result.inflatedExpandedView = v + } + + override val remoteView: RemoteViews + get() = result.remoteViews.expanded + } + logger.logAsyncTaskProgress(entry, "applying expanded view") + applyRemoteView( + inflationExecutor = inflationExecutor, + inflateSynchronously = inflateSynchronously, + isMinimized = isMinimized, + result = result, + reInflateFlags = reInflateFlags, + inflationId = flag, + remoteViewCache = remoteViewCache, + entry = entry, + row = row, + isNewView = isNewView, + remoteViewClickHandler = remoteViewClickHandler, + callback = callback, + parentLayout = privateLayout, + existingView = privateLayout.expandedChild, + existingWrapper = + privateLayout.getVisibleWrapper( + NotificationContentView.VISIBLE_TYPE_EXPANDED + ), + runningInflations = runningInflations, + applyCallback = applyCallback, + logger = logger + ) + } + } + flag = FLAG_CONTENT_VIEW_HEADS_UP + if (reInflateFlags and flag != 0) { + if (result.remoteViews.headsUp != null) { + val isNewView = + !canReapplyRemoteView( + newView = result.remoteViews.headsUp, + oldView = + remoteViewCache.getCachedView(entry, FLAG_CONTENT_VIEW_HEADS_UP) + ) + val applyCallback: ApplyCallback = + object : ApplyCallback() { + override fun setResultView(v: View) { + logger.logAsyncTaskProgress(entry, "heads up view applied") + result.inflatedHeadsUpView = v + } + + override val remoteView: RemoteViews + get() = result.remoteViews.headsUp + } + logger.logAsyncTaskProgress(entry, "applying heads up view") + applyRemoteView( + inflationExecutor = inflationExecutor, + inflateSynchronously = inflateSynchronously, + isMinimized = isMinimized, + result = result, + reInflateFlags = reInflateFlags, + inflationId = flag, + remoteViewCache = remoteViewCache, + entry = entry, + row = row, + isNewView = isNewView, + remoteViewClickHandler = remoteViewClickHandler, + callback = callback, + parentLayout = privateLayout, + existingView = privateLayout.headsUpChild, + existingWrapper = + privateLayout.getVisibleWrapper( + NotificationContentView.VISIBLE_TYPE_HEADSUP + ), + runningInflations = runningInflations, + applyCallback = applyCallback, + logger = logger + ) + } + } + flag = FLAG_CONTENT_VIEW_PUBLIC + if (reInflateFlags and flag != 0) { + val isNewView = + !canReapplyRemoteView( + newView = result.remoteViews.public, + oldView = remoteViewCache.getCachedView(entry, FLAG_CONTENT_VIEW_PUBLIC) + ) + val applyCallback: ApplyCallback = + object : ApplyCallback() { + override fun setResultView(v: View) { + logger.logAsyncTaskProgress(entry, "public view applied") + result.inflatedPublicView = v + } + + override val remoteView: RemoteViews + get() = result.remoteViews.public!! + } + logger.logAsyncTaskProgress(entry, "applying public view") + applyRemoteView( + inflationExecutor = inflationExecutor, + inflateSynchronously = inflateSynchronously, + isMinimized = isMinimized, + result = result, + reInflateFlags = reInflateFlags, + inflationId = flag, + remoteViewCache = remoteViewCache, + entry = entry, + row = row, + isNewView = isNewView, + remoteViewClickHandler = remoteViewClickHandler, + callback = callback, + parentLayout = publicLayout, + existingView = publicLayout.contractedChild, + existingWrapper = + publicLayout.getVisibleWrapper( + NotificationContentView.VISIBLE_TYPE_CONTRACTED + ), + runningInflations = runningInflations, + applyCallback = applyCallback, + logger = logger + ) + } + if (AsyncGroupHeaderViewInflation.isEnabled) { + val childrenContainer: NotificationChildrenContainer = + row.getChildrenContainerNonNull() + if (reInflateFlags and FLAG_GROUP_SUMMARY_HEADER != 0) { + val isNewView = + !canReapplyRemoteView( + newView = result.remoteViews.normalGroupHeader, + oldView = + remoteViewCache.getCachedView(entry, FLAG_GROUP_SUMMARY_HEADER) + ) + val applyCallback: ApplyCallback = + object : ApplyCallback() { + override fun setResultView(v: View) { + logger.logAsyncTaskProgress(entry, "group header view applied") + result.inflatedGroupHeaderView = v as NotificationHeaderView? + } + + override val remoteView: RemoteViews + get() = result.remoteViews.normalGroupHeader!! + } + logger.logAsyncTaskProgress(entry, "applying group header view") + applyRemoteView( + inflationExecutor = inflationExecutor, + inflateSynchronously = inflateSynchronously, + isMinimized = isMinimized, + result = result, + reInflateFlags = reInflateFlags, + inflationId = FLAG_GROUP_SUMMARY_HEADER, + remoteViewCache = remoteViewCache, + entry = entry, + row = row, + isNewView = isNewView, + remoteViewClickHandler = remoteViewClickHandler, + callback = callback, + parentLayout = childrenContainer, + existingView = childrenContainer.groupHeader, + existingWrapper = childrenContainer.notificationHeaderWrapper, + runningInflations = runningInflations, + applyCallback = applyCallback, + logger = logger + ) + } + if (reInflateFlags and FLAG_LOW_PRIORITY_GROUP_SUMMARY_HEADER != 0) { + val isNewView = + !canReapplyRemoteView( + newView = result.remoteViews.minimizedGroupHeader, + oldView = + remoteViewCache.getCachedView( + entry, + 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" + ) + result.inflatedMinimizedGroupHeaderView = + v as NotificationHeaderView? + } + + override val remoteView: RemoteViews + get() = result.remoteViews.minimizedGroupHeader!! + } + logger.logAsyncTaskProgress(entry, "applying low priority group header view") + applyRemoteView( + inflationExecutor = inflationExecutor, + inflateSynchronously = inflateSynchronously, + isMinimized = isMinimized, + result = result, + reInflateFlags = reInflateFlags, + inflationId = FLAG_LOW_PRIORITY_GROUP_SUMMARY_HEADER, + remoteViewCache = remoteViewCache, + entry = entry, + row = row, + isNewView = isNewView, + remoteViewClickHandler = remoteViewClickHandler, + callback = callback, + parentLayout = childrenContainer, + existingView = childrenContainer.minimizedNotificationHeader, + existingWrapper = childrenContainer.minimizedGroupHeaderWrapper, + runningInflations = runningInflations, + applyCallback = applyCallback, + logger = logger + ) + } + } + + // Let's try to finish, maybe nobody is even inflating anything + finishIfDone( + result, + isMinimized, + reInflateFlags, + remoteViewCache, + runningInflations, + callback, + entry, + row, + logger + ) + val cancellationSignal = CancellationSignal() + cancellationSignal.setOnCancelListener { + logger.logAsyncTaskProgress(entry, "apply cancelled") + Trace.endAsyncSection(APPLY_TRACE_METHOD, System.identityHashCode(row)) + runningInflations.values.forEach( + Consumer { obj: CancellationSignal -> obj.cancel() } + ) + } + return cancellationSignal + } + + @VisibleForTesting + fun applyRemoteView( + inflationExecutor: Executor?, + inflateSynchronously: Boolean, + isMinimized: Boolean, + result: InflationProgress, + @InflationFlag reInflateFlags: Int, + @InflationFlag inflationId: Int, + remoteViewCache: NotifRemoteViewCache, + entry: NotificationEntry, + row: ExpandableNotificationRow, + isNewView: Boolean, + remoteViewClickHandler: InteractionHandler?, + callback: InflationCallback?, + parentLayout: ViewGroup?, + existingView: View?, + existingWrapper: NotificationViewWrapper?, + runningInflations: HashMap<Int, CancellationSignal>, + applyCallback: ApplyCallback, + logger: NotificationRowContentBinderLogger + ) { + val newContentView: RemoteViews = applyCallback.remoteView + if (inflateSynchronously) { + try { + if (isNewView) { + val v: View = + newContentView.apply( + result.packageContext, + parentLayout, + remoteViewClickHandler + ) + validateView(v, entry, row.resources) + applyCallback.setResultView(v) + } else { + requireNotNull(existingView) + requireNotNull(existingWrapper) + newContentView.reapply( + result.packageContext, + existingView, + remoteViewClickHandler + ) + validateView(existingView, entry, row.resources) + existingWrapper.onReinflated() + } + } catch (e: Exception) { + handleInflationError( + runningInflations, + e, + row.entry, + callback, + logger, + "applying view synchronously" + ) + // Add a running inflation to make sure we don't trigger callbacks. + // Safe to do because only happens in tests. + runningInflations[inflationId] = CancellationSignal() + } + return + } + val listener: OnViewAppliedListener = + object : OnViewAppliedListener { + override fun onViewInflated(v: View) { + if (v is ImageMessageConsumer) { + (v as ImageMessageConsumer).setImageResolver(row.imageResolver) + } + } + + override fun onViewApplied(v: View) { + val invalidReason = isValidView(v, entry, row.resources) + if (invalidReason != null) { + handleInflationError( + runningInflations, + InflationException(invalidReason), + row.entry, + callback, + logger, + "applied invalid view" + ) + runningInflations.remove(inflationId) + return + } + if (isNewView) { + applyCallback.setResultView(v) + } else { + existingWrapper?.onReinflated() + } + runningInflations.remove(inflationId) + finishIfDone( + result, + isMinimized, + reInflateFlags, + remoteViewCache, + runningInflations, + callback, + entry, + row, + logger + ) + } + + override fun onError(e: Exception) { + // Uh oh the async inflation failed. Due to some bugs (see b/38190555), this + // could + // actually also be a system issue, so let's try on the UI thread again to + // be safe. + try { + val newView = + if (isNewView) { + newContentView.apply( + result.packageContext, + parentLayout, + remoteViewClickHandler + ) + } else { + newContentView.reapply( + result.packageContext, + existingView, + remoteViewClickHandler + ) + existingView!! + } + Log.wtf( + TAG, + "Async Inflation failed but normal inflation finished normally.", + e + ) + onViewApplied(newView) + } catch (anotherException: Exception) { + runningInflations.remove(inflationId) + handleInflationError( + runningInflations, + e, + row.entry, + callback, + logger, + "applying view" + ) + } + } + } + val cancellationSignal: CancellationSignal = + if (isNewView) { + newContentView.applyAsync( + result.packageContext, + parentLayout, + inflationExecutor, + listener, + remoteViewClickHandler + ) + } else { + newContentView.reapplyAsync( + result.packageContext, + existingView, + inflationExecutor, + listener, + remoteViewClickHandler + ) + } + runningInflations[inflationId] = cancellationSignal + } + + /** + * Checks if the given View is a valid notification View. + * + * @return null == valid, non-null == invalid, String represents reason for rejection. + */ + @VisibleForTesting + fun isValidView(view: View, entry: NotificationEntry, resources: Resources): String? { + return if (!satisfiesMinHeightRequirement(view, entry, resources)) { + "inflated notification does not meet minimum height requirement" + } else null + } + + private fun satisfiesMinHeightRequirement( + view: View, + entry: NotificationEntry, + resources: Resources + ): Boolean { + return if (!requiresHeightCheck(entry)) { + true + } else + TraceUtils.trace("NotificationContentInflater#satisfiesMinHeightRequirement") { + val heightSpec = + View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED) + val referenceWidth = + resources.getDimensionPixelSize( + R.dimen.notification_validation_reference_width + ) + val widthSpec = + View.MeasureSpec.makeMeasureSpec(referenceWidth, View.MeasureSpec.EXACTLY) + view.measure(widthSpec, heightSpec) + val minHeight = + resources.getDimensionPixelSize( + R.dimen.notification_validation_minimum_allowed_height + ) + view.measuredHeight >= minHeight + } + } + + /** + * Notifications with undecorated custom views need to satisfy a minimum height to avoid + * visual issues. + */ + private fun requiresHeightCheck(entry: NotificationEntry): Boolean { + // Undecorated custom views are disallowed from S onwards + if (entry.targetSdk >= Build.VERSION_CODES.S) { + return false + } + // No need to check if the app isn't using any custom views + val notification: Notification = entry.sbn.notification + @Suppress("DEPRECATION") + return !(notification.contentView == null && + notification.bigContentView == null && + notification.headsUpContentView == null) + } + + @Throws(InflationException::class) + private fun validateView(view: View, entry: NotificationEntry, resources: Resources) { + val invalidReason = isValidView(view, entry, resources) + if (invalidReason != null) { + throw InflationException(invalidReason) + } + } + + private fun handleInflationError( + runningInflations: HashMap<Int, CancellationSignal>, + e: Exception, + notification: NotificationEntry, + callback: InflationCallback?, + logger: NotificationRowContentBinderLogger, + logContext: String + ) { + Assert.isMainThread() + logger.logAsyncTaskException(notification, logContext, e) + runningInflations.values.forEach(Consumer { obj: CancellationSignal -> obj.cancel() }) + callback?.handleInflationException(notification, e) + } + + /** + * Finish the inflation of the views + * + * @return true if the inflation was finished + */ + private fun finishIfDone( + result: InflationProgress, + isMinimized: Boolean, + @InflationFlag reInflateFlags: Int, + remoteViewCache: NotifRemoteViewCache, + runningInflations: HashMap<Int, CancellationSignal>, + endListener: InflationCallback?, + entry: NotificationEntry, + row: ExpandableNotificationRow, + logger: NotificationRowContentBinderLogger + ): Boolean { + Assert.isMainThread() + if (runningInflations.isNotEmpty()) { + return false + } + val privateLayout = row.privateLayout + val publicLayout = row.publicLayout + logger.logAsyncTaskProgress(entry, "finishing") + if (reInflateFlags and FLAG_CONTENT_VIEW_CONTRACTED != 0) { + if (result.inflatedContentView != null) { + // New view case + privateLayout.setContractedChild(result.inflatedContentView) + remoteViewCache.putCachedView( + entry, + FLAG_CONTENT_VIEW_CONTRACTED, + result.remoteViews.contracted + ) + } else if (remoteViewCache.hasCachedView(entry, FLAG_CONTENT_VIEW_CONTRACTED)) { + // Reinflation case. Only update if it's still cached (i.e. view has not been + // freed while inflating). + remoteViewCache.putCachedView( + entry, + FLAG_CONTENT_VIEW_CONTRACTED, + result.remoteViews.contracted + ) + } + } + if (reInflateFlags and FLAG_CONTENT_VIEW_EXPANDED != 0) { + if (result.inflatedExpandedView != null) { + privateLayout.setExpandedChild(result.inflatedExpandedView) + remoteViewCache.putCachedView( + entry, + FLAG_CONTENT_VIEW_EXPANDED, + result.remoteViews.expanded + ) + } else if (result.remoteViews.expanded == null) { + privateLayout.setExpandedChild(null) + remoteViewCache.removeCachedView(entry, FLAG_CONTENT_VIEW_EXPANDED) + } else if (remoteViewCache.hasCachedView(entry, FLAG_CONTENT_VIEW_EXPANDED)) { + remoteViewCache.putCachedView( + entry, + FLAG_CONTENT_VIEW_EXPANDED, + result.remoteViews.expanded + ) + } + if (result.remoteViews.expanded != null) { + privateLayout.setExpandedInflatedSmartReplies( + result.expandedInflatedSmartReplies + ) + } else { + privateLayout.setExpandedInflatedSmartReplies(null) + } + row.setExpandable(result.remoteViews.expanded != null) + } + if (reInflateFlags and FLAG_CONTENT_VIEW_HEADS_UP != 0) { + if (result.inflatedHeadsUpView != null) { + privateLayout.setHeadsUpChild(result.inflatedHeadsUpView) + remoteViewCache.putCachedView( + entry, + FLAG_CONTENT_VIEW_HEADS_UP, + result.remoteViews.headsUp + ) + } else if (result.remoteViews.headsUp == null) { + privateLayout.setHeadsUpChild(null) + remoteViewCache.removeCachedView(entry, FLAG_CONTENT_VIEW_HEADS_UP) + } else if (remoteViewCache.hasCachedView(entry, FLAG_CONTENT_VIEW_HEADS_UP)) { + remoteViewCache.putCachedView( + entry, + FLAG_CONTENT_VIEW_HEADS_UP, + result.remoteViews.headsUp + ) + } + if (result.remoteViews.headsUp != null) { + privateLayout.setHeadsUpInflatedSmartReplies(result.headsUpInflatedSmartReplies) + } else { + privateLayout.setHeadsUpInflatedSmartReplies(null) + } + } + if ( + AsyncHybridViewInflation.isEnabled && + reInflateFlags and FLAG_CONTENT_VIEW_SINGLE_LINE != 0 + ) { + val singleLineView = result.inflatedSingleLineView + val viewModel = result.contentModel.singleLineViewModel + if (singleLineView != null && viewModel != null) { + if (viewModel.isConversation()) { + SingleLineConversationViewBinder.bind(viewModel, singleLineView) + } else { + SingleLineViewBinder.bind(viewModel, singleLineView) + } + privateLayout.setSingleLineView(result.inflatedSingleLineView) + } + } + result.inflatedSmartReplyState?.let { privateLayout.setInflatedSmartReplyState(it) } + if (reInflateFlags and FLAG_CONTENT_VIEW_PUBLIC != 0) { + if (result.inflatedPublicView != null) { + publicLayout.setContractedChild(result.inflatedPublicView) + remoteViewCache.putCachedView( + entry, + FLAG_CONTENT_VIEW_PUBLIC, + result.remoteViews.public + ) + } else if (remoteViewCache.hasCachedView(entry, FLAG_CONTENT_VIEW_PUBLIC)) { + remoteViewCache.putCachedView( + entry, + FLAG_CONTENT_VIEW_PUBLIC, + result.remoteViews.public + ) + } + } + if (AsyncGroupHeaderViewInflation.isEnabled) { + if (reInflateFlags and FLAG_GROUP_SUMMARY_HEADER != 0) { + if (result.inflatedGroupHeaderView != null) { + // We need to set if the row is minimized before setting the group header to + // make sure the setting of header view works correctly + row.setIsMinimized(isMinimized) + row.setGroupHeader(/* headerView= */ result.inflatedGroupHeaderView) + remoteViewCache.putCachedView( + entry, + FLAG_GROUP_SUMMARY_HEADER, + result.remoteViews.normalGroupHeader + ) + } else if (remoteViewCache.hasCachedView(entry, FLAG_GROUP_SUMMARY_HEADER)) { + // Re-inflation case. Only update if it's still cached (i.e. view has not + // been freed while inflating). + remoteViewCache.putCachedView( + entry, + FLAG_GROUP_SUMMARY_HEADER, + result.remoteViews.normalGroupHeader + ) + } + } + if (reInflateFlags and FLAG_LOW_PRIORITY_GROUP_SUMMARY_HEADER != 0) { + if (result.inflatedMinimizedGroupHeaderView != null) { + // We need to set if the row is minimized before setting the group header to + // make sure the setting of header view works correctly + row.setIsMinimized(isMinimized) + row.setMinimizedGroupHeader( + /* headerView= */ result.inflatedMinimizedGroupHeaderView + ) + remoteViewCache.putCachedView( + entry, + FLAG_LOW_PRIORITY_GROUP_SUMMARY_HEADER, + result.remoteViews.minimizedGroupHeader + ) + } else if ( + remoteViewCache.hasCachedView(entry, FLAG_LOW_PRIORITY_GROUP_SUMMARY_HEADER) + ) { + // Re-inflation case. Only update if it's still cached (i.e. view has not + // been freed while inflating). + remoteViewCache.putCachedView( + entry, + FLAG_LOW_PRIORITY_GROUP_SUMMARY_HEADER, + result.remoteViews.normalGroupHeader + ) + } + } + } + entry.setContentModel(result.contentModel) + Trace.endAsyncSection(APPLY_TRACE_METHOD, System.identityHashCode(row)) + endListener?.onAsyncInflationFinished(entry) + return true + } + + private fun createExpandedView( + builder: Notification.Builder, + isMinimized: Boolean + ): RemoteViews? { + @Suppress("DEPRECATION") + val bigContentView: RemoteViews? = builder.createBigContentView() + if (bigContentView != null) { + return bigContentView + } + if (isMinimized) { + @Suppress("DEPRECATION") val contentView: RemoteViews = builder.createContentView() + Notification.Builder.makeHeaderExpanded(contentView) + return contentView + } + return null + } + + private fun createContentView( + builder: Notification.Builder, + isMinimized: Boolean, + useLarge: Boolean + ): RemoteViews { + return if (isMinimized) { + builder.makeLowPriorityContentView(false /* useRegularSubtext */) + } else builder.createContentView(useLarge) + } + + /** + * @param newView The new view that will be applied + * @param oldView The old view that was applied to the existing view before + * @return `true` if the RemoteViews are the same and the view can be reused to reapply. + */ + @VisibleForTesting + fun canReapplyRemoteView(newView: RemoteViews?, oldView: RemoteViews?): Boolean { + return newView == null && oldView == null || + newView != null && + oldView != null && + oldView.getPackage() != null && + newView.getPackage() != null && + newView.getPackage() == oldView.getPackage() && + newView.layoutId == oldView.layoutId && + !oldView.hasFlags(RemoteViews.FLAG_REAPPLY_DISALLOWED) + } + + private const val ASYNC_TASK_TRACE_METHOD = + "NotificationRowContentBinderImpl.AsyncInflationTask" + private const val APPLY_TRACE_METHOD = "NotificationRowContentBinderImpl#apply" + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowModule.java index 17c20268bc1c..84f2f6670839 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowModule.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowModule.java @@ -17,9 +17,13 @@ package com.android.systemui.statusbar.notification.row; import com.android.systemui.dagger.SysUISingleton; +import com.android.systemui.statusbar.notification.row.shared.NotificationRowContentBinderRefactor; import dagger.Binds; import dagger.Module; +import dagger.Provides; + +import javax.inject.Provider; /** * Dagger Module containing notification row and view inflation implementations. @@ -30,10 +34,18 @@ public abstract class NotificationRowModule { /** * Provides notification row content binder instance. */ - @Binds + @Provides @SysUISingleton - public abstract NotificationRowContentBinder provideNotificationRowContentBinder( - NotificationContentInflater contentBinderImpl); + public static NotificationRowContentBinder provideNotificationRowContentBinder( + Provider<NotificationContentInflater> legacyImpl, + Provider<NotificationRowContentBinderImpl> refactoredImpl + ) { + if (NotificationRowContentBinderRefactor.isEnabled()) { + return refactoredImpl.get(); + } else { + return legacyImpl.get(); + } + } /** * Provides notification remote view cache instance. diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/shared/HeadsUpStatusBarModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/shared/HeadsUpStatusBarModel.kt new file mode 100644 index 000000000000..e43ce76d6482 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/shared/HeadsUpStatusBarModel.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.statusbar.notification.row.shared + +class HeadsUpStatusBarModel( + val privateText: CharSequence, + val publicText: CharSequence, +) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/shared/NewRemoteViews.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/shared/NewRemoteViews.kt new file mode 100644 index 000000000000..63bba86464b4 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/shared/NewRemoteViews.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.notification.row.shared + +import android.widget.RemoteViews + +class NewRemoteViews( + val contracted: RemoteViews? = null, + val headsUp: RemoteViews? = null, + val expanded: RemoteViews? = null, + val public: RemoteViews? = null, + val normalGroupHeader: RemoteViews? = null, + val minimizedGroupHeader: RemoteViews? = null, +) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/shared/NotificationContentModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/shared/NotificationContentModel.kt new file mode 100644 index 000000000000..b2421bc72d00 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/shared/NotificationContentModel.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.notification.row.shared + +import com.android.systemui.statusbar.notification.row.ui.viewmodel.SingleLineViewModel + +data class NotificationContentModel( + val headsUpStatusBarModel: HeadsUpStatusBarModel, + val singleLineViewModel: SingleLineViewModel? = null, +) 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 d1fabb168d90..939424945038 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 @@ -358,11 +358,7 @@ public class NotificationHeaderViewWrapper extends NotificationViewWrapper imple * @param whenMillis */ public void setNotificationWhen(long whenMillis) { - if (mNotificationHeader == null) { - return; - } - - final View timeView = mNotificationHeader.findViewById(com.android.internal.R.id.time); + final View timeView = mView.findViewById(com.android.internal.R.id.time); if (timeView instanceof DateTimeView) { ((DateTimeView) timeView).setTime(whenMillis); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationPriorityBucket.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationPriorityBucket.kt index fc28a99ef4ef..fabb696d9182 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationPriorityBucket.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationPriorityBucket.kt @@ -13,7 +13,9 @@ import android.annotation.IntDef [ BUCKET_UNKNOWN, BUCKET_MEDIA_CONTROLS, + BUCKET_TOP_ONGOING, BUCKET_HEADS_UP, + BUCKET_TOP_UNSEEN, BUCKET_FOREGROUND_SERVICE, BUCKET_PRIORITY_PEOPLE, BUCKET_PEOPLE, @@ -21,11 +23,28 @@ import android.annotation.IntDef BUCKET_SILENT ] ) -annotation class PriorityBucket +annotation class PriorityBucket { + companion object { + fun getAllInOrder(): IntArray = + intArrayOf( + BUCKET_MEDIA_CONTROLS, + BUCKET_TOP_ONGOING, + BUCKET_HEADS_UP, + BUCKET_TOP_UNSEEN, + BUCKET_FOREGROUND_SERVICE, + BUCKET_PRIORITY_PEOPLE, + BUCKET_PEOPLE, + BUCKET_ALERTING, + BUCKET_SILENT, + ) + } +} const val BUCKET_UNKNOWN = 0 const val BUCKET_MEDIA_CONTROLS = 1 +const val BUCKET_TOP_ONGOING = 8 const val BUCKET_HEADS_UP = 2 +const val BUCKET_TOP_UNSEEN = 9 const val BUCKET_FOREGROUND_SERVICE = 3 const val BUCKET_PRIORITY_PEOPLE = 7 const val BUCKET_PEOPLE = 4 diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java index fe22cc628b5f..71a0b941c350 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java @@ -833,6 +833,23 @@ public class NotificationStackScrollLayout int y = 0; drawDebugInfo(canvas, y, Color.RED, /* label= */ "y = " + y); + if (SceneContainerFlag.isEnabled()) { + y = (int) mScrollViewFields.getStackTop(); + drawDebugInfo(canvas, y, Color.RED, /* label= */ "getStackTop() = " + y); + + y = (int) mScrollViewFields.getStackBottom(); + drawDebugInfo(canvas, y, Color.MAGENTA, /* label= */ "getStackBottom() = " + y); + + y = (int) mScrollViewFields.getHeadsUpTop(); + drawDebugInfo(canvas, y, Color.GREEN, /* label= */ "getHeadsUpTop() = " + y); + + y += getTopHeadsUpHeight(); + drawDebugInfo(canvas, y, Color.BLUE, + /* label= */ "getHeadsUpTop() + getTopHeadsUpHeight() = " + y); + + return; // the rest of the fields are not important in Flexiglass + } + y = getTopPadding(); drawDebugInfo(canvas, y, Color.RED, /* label= */ "getTopPadding() = " + y); @@ -3471,6 +3488,7 @@ public class NotificationStackScrollLayout } if (isUpOrCancel) { + mScrollViewFields.sendCurrentGestureOverscroll(false); setIsBeingDragged(false); } return false; @@ -3606,7 +3624,6 @@ public class NotificationStackScrollLayout if (mIsBeingDragged) { // Defer actual scrolling to the scene framework if enabled if (SceneContainerFlag.isEnabled()) { - setIsBeingDragged(false); return false; } // Scroll to follow the motion event @@ -3704,7 +3721,7 @@ public class NotificationStackScrollLayout protected boolean isInsideQsHeader(MotionEvent ev) { if (SceneContainerFlag.isEnabled()) { - return ev.getY() < mScrollViewFields.getScrimClippingShape().getBounds().getTop(); + return ev.getY() < mScrollViewFields.getStackTop(); } mQsHeader.getBoundsOnScreen(mQsHeaderBound); 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 4b0b1e0029f3..391bc43a784c 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 @@ -73,7 +73,8 @@ constructor( private var maxNotificationsExcludesMedia = false /** Whether we allow keyguard to show less important notifications above the shelf. */ - private var limitLockScreenToImportant = false + private val limitLockScreenToOneImportant + get() = NotificationMinimalismPrototype.V2.isEnabled /** Minimum space between two notifications, see [calculateGapAndDividerHeight]. */ private var dividerHeight by notNull<Float>() @@ -89,7 +90,7 @@ constructor( } private fun allowedByPolicy(stackHeight: StackHeight): Boolean = - if (limitLockScreenToImportant && stackHeight.includesLessImportantNotification) { + if (stackHeight.shouldForceIntoShelf) { log { "\tallowedByPolicy = false" } false } else { @@ -333,8 +334,8 @@ constructor( // changes during the lockscreen <=> full shade transition. val shelfHeightWithSpaceBefore: Float, - /** Whether this stack height includes less at least one important notification. */ - val includesLessImportantNotification: Boolean + /** Whether the stack should actually be forced into the shelf before this height. */ + val shouldForceIntoShelf: Boolean ) private fun computeHeightPerNotificationLimit( @@ -347,7 +348,7 @@ constructor( var previous: ExpandableView? = null val onLockscreen = onLockscreen() - var includesLessImportantNotification = false + val counter = if (limitLockScreenToOneImportant) BucketTypeCounter() else null // Only shelf. This should never happen, since we allow 1 view minimum (EmptyViewState). yield( @@ -355,7 +356,7 @@ constructor( notifsHeight = 0f, notifsHeightSavingSpace = 0f, shelfHeightWithSpaceBefore = shelfHeight, - includesLessImportantNotification = includesLessImportantNotification, + shouldForceIntoShelf = false, ) ) @@ -381,17 +382,9 @@ constructor( spaceBeforeShelf + shelfHeight } - if (limitLockScreenToImportant && !includesLessImportantNotification) { - val bucket = (currentNotification as? ExpandableNotificationRow)?.entry?.bucket - includesLessImportantNotification = - when (bucket) { - null, - BUCKET_MEDIA_CONTROLS, - BUCKET_HEADS_UP, - BUCKET_FOREGROUND_SERVICE, - BUCKET_PRIORITY_PEOPLE -> false - else -> true - } + if (counter != null) { + val entry = (currentNotification as? ExpandableNotificationRow)?.entry + counter.incrementForBucket(entry?.bucket) } log { @@ -404,7 +397,7 @@ constructor( notifsHeight = notifications, notifsHeightSavingSpace = notifsWithCollapsedHun, shelfHeightWithSpaceBefore = shelfWithSpaceBefore, - includesLessImportantNotification = includesLessImportantNotification, + shouldForceIntoShelf = counter?.shouldForceIntoShelf() ?: false ) ) } @@ -415,8 +408,6 @@ constructor( infiniteIfNegative( if (NotificationMinimalismPrototype.V1.isEnabled) { NotificationMinimalismPrototype.V1.maxNotifs - } else if (NotificationMinimalismPrototype.V2.isEnabled) { - 1 } else { resources.getInteger(R.integer.keyguard_max_notification_count) } @@ -424,7 +415,6 @@ constructor( maxNotificationsExcludesMedia = NotificationMinimalismPrototype.V1.isEnabled || NotificationMinimalismPrototype.V2.isEnabled - limitLockScreenToImportant = NotificationMinimalismPrototype.V2.isEnabled dividerHeight = max(1f, resources.getDimensionPixelSize(R.dimen.notification_divider_height).toFloat()) @@ -552,4 +542,24 @@ constructor( /** Returns the last index where [predicate] returns true, or -1 if it was always false. */ private fun <T> Sequence<T>.lastIndexWhile(predicate: (T) -> Boolean): Int = 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, + ) { + fun incrementForBucket(@PriorityBucket bucket: Int?) { + when (bucket) { + BUCKET_MEDIA_CONTROLS, + null -> Unit // not counted as notifications at all + BUCKET_TOP_ONGOING -> ongoing++ + BUCKET_HEADS_UP -> important++ + BUCKET_TOP_UNSEEN -> important++ + else -> other++ + } + } + + fun shouldForceIntoShelf(): Boolean = ongoing > 1 || important > 1 || other > 0 + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java index 5dc544993ddc..058233ffb93e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java @@ -37,6 +37,7 @@ import com.android.systemui.statusbar.NotificationShelf; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.row.ExpandableView; import com.android.systemui.statusbar.notification.row.StackScrollerDecorView; +import com.android.systemui.statusbar.notification.shared.NotificationHeadsUpCycling; import com.android.systemui.statusbar.notification.shared.NotificationsImprovedHunAnimation; import java.util.ArrayList; @@ -178,6 +179,10 @@ public class StackStateAnimator { mHeadsUpDisappearChildren.clear(); mNewEvents.clear(); mNewAddChildren.clear(); + if (NotificationsImprovedHunAnimation.isEnabled() + || NotificationHeadsUpCycling.isEnabled()) { + mAnimationProperties.resetCustomInterpolators(); + } } private void initAnimationProperties(ExpandableView child, @@ -592,6 +597,7 @@ public class StackStateAnimator { Interpolators.LINEAR); mAnimationProperties.getAnimationFilter().animateY = true; mTmpState.animateTo(changingView, mAnimationProperties); + mAnimationProperties.resetCustomInterpolators(); } else if (endRunnable != null) { endRunnable.run(); } @@ -697,6 +703,7 @@ public class StackStateAnimator { Interpolators.FAST_OUT_SLOW_IN_REVERSE); mAnimationProperties.getAnimationFilter().animateY = true; mTmpState.animateTo(changingView, mAnimationProperties); + mAnimationProperties.resetCustomInterpolators(); } } else if (endRunnable != null) { endRunnable.run(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationScrollViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationScrollViewModel.kt index e90a64a32fba..85835d22f183 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationScrollViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationScrollViewModel.kt @@ -24,6 +24,7 @@ import com.android.systemui.dump.DumpManager import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor import com.android.systemui.scene.domain.interactor.SceneInteractor import com.android.systemui.scene.shared.flag.SceneContainerFlag +import com.android.systemui.scene.shared.model.SceneFamilies import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.shade.domain.interactor.ShadeInteractor import com.android.systemui.shade.shared.model.ShadeMode @@ -36,7 +37,6 @@ import com.android.systemui.util.kotlin.FlowDumperImpl import dagger.Lazy import javax.inject.Inject import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.flowOf @@ -50,7 +50,7 @@ constructor( dumpManager: DumpManager, stackAppearanceInteractor: NotificationStackAppearanceInteractor, shadeInteractor: ShadeInteractor, - sceneInteractor: SceneInteractor, + private val sceneInteractor: SceneInteractor, // TODO(b/336364825) Remove Lazy when SceneContainerFlag is released - // while the flag is off, creating this object too early results in a crash keyguardInteractor: Lazy<KeyguardInteractor>, @@ -63,9 +63,11 @@ constructor( val expandFraction: Flow<Float> = combine( shadeInteractor.shadeExpansion, + shadeInteractor.shadeMode, shadeInteractor.qsExpansion, sceneInteractor.transitionState, - ) { shadeExpansion, qsExpansion, transitionState -> + sceneInteractor.resolveSceneFamily(SceneFamilies.QuickSettings), + ) { shadeExpansion, shadeMode, qsExpansion, transitionState, quickSettingsScene -> when (transitionState) { is ObservableTransitionState.Idle -> { if (transitionState.currentScene == Scenes.Lockscreen) { @@ -76,16 +78,16 @@ constructor( } is ObservableTransitionState.Transition -> { if ( - (transitionState.fromScene == notificationsScene && + (transitionState.fromScene in SceneFamilies.NotifShade && transitionState.toScene == quickSettingsScene) || - (transitionState.fromScene == quickSettingsScene && - transitionState.toScene == notificationsScene) + (transitionState.fromScene in quickSettingsScene && + transitionState.toScene in SceneFamilies.NotifShade) ) { 1f } else if ( - (transitionState.fromScene == Scenes.Gone || - transitionState.fromScene == Scenes.Lockscreen) && - transitionState.toScene == quickSettingsScene + shadeMode != ShadeMode.Split && + transitionState.fromScene in SceneFamilies.Home && + transitionState.toScene in quickSettingsScene ) { // during QS expansion, increase fraction at same rate as scrim alpha, // but start when scrim alpha is at EXPANSION_FOR_DELAYED_STACK_FADE_IN. @@ -101,6 +103,9 @@ constructor( .distinctUntilChanged() .dumpWhileCollecting("expandFraction") + private operator fun SceneKey.contains(scene: SceneKey) = + sceneInteractor.isSceneInFamily(scene, this) + /** The bounds of the notification stack in the current scene. */ private val shadeScrimClipping: Flow<ShadeScrimClipping?> = combine( @@ -142,6 +147,7 @@ constructor( /** Receives the amount (px) that the stack should scroll due to internal expansion. */ val syntheticScrollConsumer: (Float) -> Unit = stackAppearanceInteractor::setSyntheticScroll + /** * Receives whether the current touch gesture is overscroll as it has already been consumed by * the stack. @@ -150,10 +156,9 @@ constructor( stackAppearanceInteractor::setCurrentGestureOverscroll /** Whether the notification stack is scrollable or not. */ - val isScrollable: Flow<Boolean> = - sceneInteractor.currentScene - .map { it == notificationsScene } - .dumpWhileCollecting("isScrollable") + val isScrollable: Flow<Boolean> = sceneInteractor.currentScene.map { + sceneInteractor.isSceneInFamily(it, SceneFamilies.NotifShade) || it == Scenes.Lockscreen + }.dumpWhileCollecting("isScrollable") /** Whether the notification stack is displayed in doze mode. */ val isDozing: Flow<Boolean> by lazy { @@ -163,22 +168,4 @@ constructor( keyguardInteractor.get().isDozing.dumpWhileCollecting("isDozing") } } - - private val shadeMode: StateFlow<ShadeMode> = shadeInteractor.shadeMode - - private val notificationsScene: SceneKey - get() = - if (shadeMode.value is ShadeMode.Dual) { - Scenes.NotificationsShade - } else { - Scenes.Shade - } - - private val quickSettingsScene: SceneKey - get() = - if (shadeMode.value is ShadeMode.Dual) { - Scenes.QuickSettingsShade - } else { - Scenes.QuickSettings - } } 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 6dfaec9d8830..1fc2821e057d 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 @@ -53,6 +53,7 @@ import com.android.systemui.keyguard.ui.viewmodel.GlanceableHubToLockscreenTrans import com.android.systemui.keyguard.ui.viewmodel.GoneToAodTransitionViewModel import com.android.systemui.keyguard.ui.viewmodel.GoneToDozingTransitionViewModel import com.android.systemui.keyguard.ui.viewmodel.GoneToDreamingTransitionViewModel +import com.android.systemui.keyguard.ui.viewmodel.GoneToLockscreenTransitionViewModel import com.android.systemui.keyguard.ui.viewmodel.LockscreenToDreamingTransitionViewModel import com.android.systemui.keyguard.ui.viewmodel.LockscreenToGlanceableHubTransitionViewModel import com.android.systemui.keyguard.ui.viewmodel.LockscreenToGoneTransitionViewModel @@ -120,6 +121,7 @@ constructor( private val goneToAodTransitionViewModel: GoneToAodTransitionViewModel, private val goneToDozingTransitionViewModel: GoneToDozingTransitionViewModel, private val goneToDreamingTransitionViewModel: GoneToDreamingTransitionViewModel, + private val goneToLockscreenTransitionViewModel: GoneToLockscreenTransitionViewModel, private val lockscreenToDreamingTransitionViewModel: LockscreenToDreamingTransitionViewModel, private val lockscreenToGlanceableHubTransitionViewModel: LockscreenToGlanceableHubTransitionViewModel, @@ -441,7 +443,7 @@ constructor( anyOf( *toFlowArray(statesForHiddenKeyguard) { state -> keyguardTransitionInteractor - .transitionStepsToState(state) + .transition(Edge.create(to = state)) .map { it.value > 0f && it.transitionState == RUNNING } .onStart { emit(false) } } @@ -472,6 +474,9 @@ constructor( // All transition view models are mututally exclusive, and safe to merge val alphaTransitions = merge( + keyguardInteractor.dismissAlpha.dumpWhileCollecting( + "keyguardInteractor.dismissAlpha" + ), alternateBouncerToGoneTransitionViewModel.notificationAlpha(viewState), aodToGoneTransitionViewModel.notificationAlpha(viewState), aodToLockscreenTransitionViewModel.notificationAlpha, @@ -481,7 +486,8 @@ constructor( dreamingToLockscreenTransitionViewModel.lockscreenAlpha, goneToAodTransitionViewModel.notificationAlpha, goneToDreamingTransitionViewModel.lockscreenAlpha, - goneToDozingTransitionViewModel.lockscreenAlpha, + goneToDozingTransitionViewModel.notificationAlpha, + goneToLockscreenTransitionViewModel.lockscreenAlpha, lockscreenToDreamingTransitionViewModel.lockscreenAlpha, lockscreenToGoneTransitionViewModel.notificationAlpha(viewState), lockscreenToOccludedTransitionViewModel.lockscreenAlpha, @@ -498,24 +504,10 @@ constructor( // These remaining cases handle alpha changes within an existing state, such as // shade expansion or swipe to dismiss combineTransform( - isOnLockscreenWithoutShade, isTransitioningToHiddenKeyguard, - shadeCollapseFadeIn, alphaForShadeAndQsExpansion, - keyguardInteractor.dismissAlpha.dumpWhileCollecting( - "keyguardInteractor.keyguardAlpha" - ), - ) { - isOnLockscreenWithoutShade, - isTransitioningToHiddenKeyguard, - shadeCollapseFadeIn, - alphaForShadeAndQsExpansion, - dismissAlpha -> - if (isOnLockscreenWithoutShade) { - if (!shadeCollapseFadeIn && dismissAlpha != null) { - emit(dismissAlpha) - } - } else if (!isTransitioningToHiddenKeyguard) { + ) { isTransitioningToHiddenKeyguard, alphaForShadeAndQsExpansion -> + if (!isTransitioningToHiddenKeyguard) { emit(alphaForShadeAndQsExpansion) } }, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterImpl.kt index 2ab7aa95ff56..fae0a4681493 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterImpl.kt @@ -85,6 +85,26 @@ constructor( ) } + override fun startPendingIntentWithoutDismissing( + intent: PendingIntent, + dismissShade: Boolean, + intentSentUiThreadCallback: Runnable?, + animationController: ActivityTransitionAnimator.Controller?, + fillInIntent: Intent?, + extraOptions: Bundle? + ) { + activityStarterInternal.startPendingIntentDismissingKeyguard( + intent = intent, + intentSentUiThreadCallback = intentSentUiThreadCallback, + animationController = animationController, + showOverLockscreen = true, + skipLockscreenChecks = true, + dismissShade = dismissShade, + fillInIntent = fillInIntent, + extraOptions = extraOptions, + ) + } + override fun startPendingIntentMaybeDismissingKeyguard( intent: PendingIntent, intentSentUiThreadCallback: Runnable?, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterInternal.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterInternal.kt index c9becb4289b9..cff9f5edad08 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterInternal.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterInternal.kt @@ -39,6 +39,7 @@ interface ActivityStarterInternal { associatedView: View? = null, animationController: ActivityTransitionAnimator.Controller? = null, showOverLockscreen: Boolean = false, + skipLockscreenChecks: Boolean = false, fillInIntent: Intent? = null, extraOptions: Bundle? = null, ) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterInternalImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterInternalImpl.kt index e580f6458be8..dbb95e602e43 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterInternalImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterInternalImpl.kt @@ -40,6 +40,7 @@ class ActivityStarterInternalImpl @Inject constructor() : ActivityStarterInterna associatedView: View?, animationController: ActivityTransitionAnimator.Controller?, showOverLockscreen: Boolean, + skipLockscreenChecks: Boolean, fillInIntent: Intent?, extraOptions: Bundle? ) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java index f83aed8db4b5..97b6f95ef3c4 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java @@ -538,6 +538,9 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback imp // later to awaken. } mNotificationShadeWindowController.setNotificationShadeFocusable(false); + // Notify the interactor first, to prevent race conditions with the screen waking up + // that would show a flicker of the lockscreen on DOZING->GONE + mBiometricUnlockInteractor.setBiometricUnlockState(mode, biometricUnlockSource); mKeyguardViewMediator.onWakeAndUnlocking(wakeInKeyguard); Trace.endSection(); break; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java index 05a43917f7e0..7434891805ca 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java @@ -288,11 +288,12 @@ public interface CentralSurfaces extends Dumpable, LifecycleOwner, CoreStartable void awakenDreams(); /** - * Handle a touch event while dreaming when the touch was initiated within a prescribed - * swipeable area. This method is provided for cases where swiping in certain areas of a dream - * should be handled by CentralSurfaces instead (e.g. swiping communal hub open). + * Handle a touch event while dreaming or on the glanceable hub when the touch was initiated + * within a prescribed swipeable area. This method is provided for cases where swiping in + * certain areas should be handled by CentralSurfaces instead (e.g. swiping hub open, opening + * the notification shade over dream or hub). */ - void handleDreamTouch(MotionEvent event); + void handleExternalShadeWindowTouch(MotionEvent event); boolean isBouncerShowing(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesEmptyImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesEmptyImpl.kt index a7b54847cdf9..906baa2a42f8 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesEmptyImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesEmptyImpl.kt @@ -80,7 +80,7 @@ abstract class CentralSurfacesEmptyImpl : CentralSurfaces { override fun updateScrimController() {} override fun shouldIgnoreTouch() = false override fun isDeviceInteractive() = false - override fun handleDreamTouch(event: MotionEvent?) {} + override fun handleExternalShadeWindowTouch(event: MotionEvent?) {} override fun handleCommunalHubTouch(event: MotionEvent?) {} override fun awakenDreams() {} override fun isBouncerShowing() = false 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 7567f36302b4..42680ab4beba 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java @@ -2954,8 +2954,8 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { }; @Override - public void handleDreamTouch(MotionEvent event) { - getNotificationShadeWindowViewController().handleDreamTouch(event); + public void handleExternalShadeWindowTouch(MotionEvent event) { + getNotificationShadeWindowViewController().handleExternalTouch(event); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java index cff46ab812bf..0ba4aabcb0e2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java @@ -223,7 +223,8 @@ public class DemoStatusIcons extends StatusIconContainer implements DemoMode, Da } return; } - StatusBarIcon icon = new StatusBarIcon(iconPkg, UserHandle.SYSTEM, iconId, 0, 0, "Demo"); + StatusBarIcon icon = new StatusBarIcon(iconPkg, UserHandle.SYSTEM, iconId, 0, 0, "Demo", + StatusBarIcon.Type.SystemIcon); icon.visible = true; StatusBarIconView v = new StatusBarIconView(getContext(), slot, null, false); v.setTag(slot); 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 e44edcbd4ebb..cd59d4ebfd73 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java @@ -21,6 +21,7 @@ import static android.app.StatusBarManager.DISABLE_SYSTEM_INFO; import static com.android.systemui.Flags.updateUserSwitcherBackground; import static com.android.systemui.statusbar.StatusBarState.KEYGUARD; +import static com.android.systemui.util.kotlin.JavaAdapterKt.collectFlow; import android.content.res.Configuration; import android.content.res.Resources; @@ -46,6 +47,7 @@ import com.android.keyguard.KeyguardUpdateMonitor; import com.android.keyguard.KeyguardUpdateMonitorCallback; import com.android.keyguard.logging.KeyguardLogger; 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.log.core.LogLevel; @@ -83,6 +85,7 @@ import java.io.PrintWriter; import java.util.ArrayList; import java.util.List; import java.util.concurrent.Executor; +import java.util.function.Consumer; import javax.inject.Inject; @@ -128,6 +131,7 @@ public class KeyguardStatusBarViewController extends ViewController<KeyguardStat private final Executor mBackgroundExecutor; private final Object mLock = new Object(); private final KeyguardLogger mLogger; + private final CommunalSceneInteractor mCommunalSceneInteractor; private View mSystemIconsContainer; private final StatusOverlayHoverListenerFactory mStatusOverlayHoverListenerFactory; @@ -241,6 +245,12 @@ public class KeyguardStatusBarViewController extends ViewController<KeyguardStat } }; + private boolean mCommunalShowing; + + private final Consumer<Boolean> mCommunalConsumer = (communalShowing) -> { + mCommunalShowing = communalShowing; + updateViewState(); + }; private final DisableStateTracker mDisableStateTracker; @@ -298,7 +308,8 @@ public class KeyguardStatusBarViewController extends ViewController<KeyguardStat @Main Executor mainExecutor, @Background Executor backgroundExecutor, KeyguardLogger logger, - StatusOverlayHoverListenerFactory statusOverlayHoverListenerFactory + StatusOverlayHoverListenerFactory statusOverlayHoverListenerFactory, + CommunalSceneInteractor communalSceneInteractor ) { super(view); mCarrierTextController = carrierTextController; @@ -324,6 +335,7 @@ public class KeyguardStatusBarViewController extends ViewController<KeyguardStat mMainExecutor = mainExecutor; mBackgroundExecutor = backgroundExecutor; mLogger = logger; + mCommunalSceneInteractor = communalSceneInteractor; mFirstBypassAttempt = mKeyguardBypassController.getBypassEnabled(); mKeyguardStateController.addCallback( @@ -405,6 +417,7 @@ public class KeyguardStatusBarViewController extends ViewController<KeyguardStat UserHandle.USER_ALL); updateUserSwitcher(); onThemeChanged(); + collectFlow(mView, mCommunalSceneInteractor.isCommunalVisible(), mCommunalConsumer); } @Override @@ -559,6 +572,7 @@ public class KeyguardStatusBarViewController extends ViewController<KeyguardStat && !mDozing && !hideForBypass && !mDisableStateTracker.isDisabled() + && !mCommunalShowing ? View.VISIBLE : View.INVISIBLE; updateViewState(newAlpha, newVisibility); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LegacyActivityStarterInternalImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LegacyActivityStarterInternalImpl.kt index fc29eaba4b46..e400ab685d8e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LegacyActivityStarterInternalImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LegacyActivityStarterInternalImpl.kt @@ -228,6 +228,7 @@ constructor( associatedView: View?, animationController: ActivityTransitionAnimator.Controller?, showOverLockscreen: Boolean, + skipLockscreenChecks: Boolean, fillInIntent: Intent?, extraOptions: Bundle?, ) { @@ -246,10 +247,10 @@ constructor( val actuallyShowOverLockscreen = showOverLockscreen && intent.isActivity && - activityIntentHelper.wouldPendingShowOverLockscreen( + (skipLockscreenChecks || activityIntentHelper.wouldPendingShowOverLockscreen( intent, lockScreenUserManager.currentUserId - ) + )) val animate = !willLaunchResolverActivity && @@ -469,7 +470,7 @@ constructor( shadeControllerLazy.get().collapseShadeForActivityStart() } if (communalHub()) { - communalSceneInteractor.snapToScene(CommunalScenes.Blank) + communalSceneInteractor.snapToSceneForActivityStart(CommunalScenes.Blank) } return deferred } @@ -555,7 +556,12 @@ constructor( override fun onTransitionAnimationStart(isExpandingFullyAbove: Boolean) { super.onTransitionAnimationStart(isExpandingFullyAbove) - + if (communalHub()) { + communalSceneInteractor.snapToSceneForActivityStart( + CommunalScenes.Blank, + ActivityTransitionAnimator.TIMINGS.totalDuration + ) + } // Double check that the keyguard is still showing and not going // away, but if so set the keyguard occluded. Typically, WM will let // KeyguardViewMediator know directly, but we're overriding that to @@ -581,9 +587,6 @@ constructor( // collapse the shade (or at least run the post collapse runnables) // later on. centralSurfaces?.setIsLaunchingActivityOverLockscreen(false, false) - if (communalHub()) { - communalSceneInteractor.snapToScene(CommunalScenes.Blank) - } delegate.onTransitionAnimationEnd(isExpandingFullyAbove) } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconHolder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconHolder.kt index 08a890dbadb5..d699b3821a36 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconHolder.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconHolder.kt @@ -155,6 +155,7 @@ open class StatusBarIconHolder private constructor() { 0, 0, contentDescription, + StatusBarIcon.Type.SystemIcon, ) holder.tag = state.subId return holder 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 db4f0af5ba9f..b40bf56ee66e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java @@ -73,6 +73,7 @@ import com.android.systemui.keyguard.domain.interactor.KeyguardSurfaceBehindInte import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor; import com.android.systemui.keyguard.domain.interactor.WindowManagerLockscreenVisibilityInteractor; import com.android.systemui.keyguard.shared.model.DismissAction; +import com.android.systemui.keyguard.shared.model.Edge; import com.android.systemui.keyguard.shared.model.KeyguardDone; import com.android.systemui.keyguard.shared.model.KeyguardState; import com.android.systemui.keyguard.shared.model.TransitionStep; @@ -508,8 +509,8 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb mListenForCanShowAlternateBouncer = null; if (!DeviceEntryUdfpsRefactor.isEnabled()) { mListenForAlternateBouncerTransitionSteps = mJavaAdapter.alwaysCollectFlow( - mKeyguardTransitionInteractor.transitionStepsFromState( - KeyguardState.ALTERNATE_BOUNCER), + mKeyguardTransitionInteractor + .transition(Edge.create(KeyguardState.ALTERNATE_BOUNCER)), this::consumeFromAlternateBouncerTransitionSteps ); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ui/StatusBarIconControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ui/StatusBarIconControllerImpl.java index fabf858d0832..85213cb0ebff 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ui/StatusBarIconControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ui/StatusBarIconControllerImpl.java @@ -227,8 +227,8 @@ public class StatusBarIconControllerImpl implements Tunable, StatusBarIconHolder holder = mStatusBarIconList.getIconHolder(slot, 0); if (holder == null) { StatusBarIcon icon = new StatusBarIcon(UserHandle.SYSTEM, mContext.getPackageName(), - Icon.createWithResource( - mContext, resourceId), 0, 0, contentDescription); + Icon.createWithResource(mContext, resourceId), 0, 0, + contentDescription, StatusBarIcon.Type.SystemIcon); holder = StatusBarIconHolder.fromIcon(icon); setIcon(slot, holder); } else { @@ -295,7 +295,7 @@ public class StatusBarIconControllerImpl implements Tunable, } else { holder.setIcon(new StatusBarIcon(UserHandle.SYSTEM, mContext.getPackageName(), Icon.createWithResource(mContext, state.callStrengthResId), 0, 0, - state.callStrengthDescription)); + state.callStrengthDescription, StatusBarIcon.Type.SystemIcon)); } setIcon(slot, holder); } @@ -320,7 +320,7 @@ public class StatusBarIconControllerImpl implements Tunable, } else { holder.setIcon(new StatusBarIcon(UserHandle.SYSTEM, mContext.getPackageName(), Icon.createWithResource(mContext, state.noCallingResId), 0, 0, - state.noCallingDescription)); + state.noCallingDescription, StatusBarIcon.Type.SystemIcon)); } setIcon(slot, holder); } 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 d38e834ff1e2..1d08f2ba9522 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 @@ -25,6 +25,9 @@ import kotlinx.coroutines.flow.StateFlow * given mobile data subscription. */ interface DeviceBasedSatelliteRepository { + /** The current status of satellite provisioning. If not false, we don't want to show an icon */ + val isSatelliteProvisioned: StateFlow<Boolean> + /** See [SatelliteConnectionState] for available states */ val connectionState: StateFlow<SatelliteConnectionState> 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 6b1bc65e86db..58c30e018efd 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,11 @@ constructor( } .stateIn(scope, SharingStarted.WhileSubscribed(), realImpl) + override val isSatelliteProvisioned: StateFlow<Boolean> = + activeRepo + .flatMapLatest { it.isSatelliteProvisioned } + .stateIn(scope, SharingStarted.WhileSubscribed(), realImpl.isSatelliteProvisioned.value) + override val connectionState: StateFlow<SatelliteConnectionState> = activeRepo .flatMapLatest { it.connectionState } 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 56034f08503d..6ad295e82645 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 @@ -36,6 +36,7 @@ constructor( ) : DeviceBasedSatelliteRepository { private var demoCommandJob: Job? = null + override val isSatelliteProvisioned = MutableStateFlow(true) override val connectionState = MutableStateFlow(SatelliteConnectionState.Unknown) override val signalStrength = MutableStateFlow(0) override val isSatelliteAllowedForCurrentLocation = MutableStateFlow(true) 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 1449e535c279..ec3af87f2b9b 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 @@ -23,6 +23,7 @@ import android.telephony.satellite.NtnSignalStrengthCallback import android.telephony.satellite.SatelliteManager import android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_SUCCESS import android.telephony.satellite.SatelliteModemStateCallback +import android.telephony.satellite.SatelliteProvisionStateCallback import android.telephony.satellite.SatelliteSupportedStateCallback import androidx.annotation.VisibleForTesting import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow @@ -337,6 +338,43 @@ constructor( } } + override val isSatelliteProvisioned: StateFlow<Boolean> = + satelliteSupport + .whenSupported( + supported = ::satelliteProvisioned, + orElse = flowOf(false), + retrySignal = telephonyProcessCrashedEvent, + ) + .stateIn(scope, SharingStarted.WhileSubscribed(), false) + + private fun satelliteProvisioned(sm: SupportedSatelliteManager): Flow<Boolean> = + conflatedCallbackFlow { + val callback = SatelliteProvisionStateCallback { provisioned -> + logBuffer.i { + "onSatelliteProvisionStateChanged: " + + if (provisioned) "provisioned" else "not provisioned" + } + trySend(provisioned) + } + + var registered = false + try { + sm.registerForProvisionStateChanged( + bgDispatcher.asExecutor(), + callback, + ) + registered = true + } catch (e: Exception) { + logBuffer.e("error registering for provisioning state callback", e) + } + + awaitClose { + if (registered) { + sm.unregisterForProvisionStateChanged(callback) + } + } + } + /** * Signal that we should start polling [checkIsSatelliteAllowed]. We only need to poll if there * are active listeners to [isSatelliteAllowedForCurrentLocation] 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 b66ace6b0fe3..03f88c74d38a 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 @@ -27,7 +27,6 @@ import com.android.systemui.statusbar.pipeline.satellite.data.DeviceBasedSatelli import com.android.systemui.statusbar.pipeline.satellite.shared.model.SatelliteConnectionState import com.android.systemui.statusbar.pipeline.wifi.domain.interactor.WifiInteractor import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel -import com.android.systemui.statusbar.policy.domain.interactor.DeviceProvisioningInteractor import javax.inject.Inject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.ExperimentalCoroutinesApi @@ -45,7 +44,6 @@ class DeviceBasedSatelliteInteractor constructor( val repo: DeviceBasedSatelliteRepository, iconsInteractor: MobileIconsInteractor, - deviceProvisioningInteractor: DeviceProvisioningInteractor, wifiInteractor: WifiInteractor, @Application scope: CoroutineScope, @DeviceBasedSatelliteInputLog private val logBuffer: LogBuffer, @@ -78,7 +76,7 @@ constructor( } .stateIn(scope, SharingStarted.WhileSubscribed(), 0) - val isDeviceProvisioned: Flow<Boolean> = deviceProvisioningInteractor.isDeviceProvisioned + val isSatelliteProvisioned = repo.isSatelliteProvisioned val isWifiActive: Flow<Boolean> = wifiInteractor.wifiNetwork.map { it is WifiNetworkModel.Active } 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 0ed1b9b0f77a..48278d4208a2 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 @@ -79,11 +79,11 @@ constructor( } else { combine( interactor.isSatelliteAllowed, - interactor.isDeviceProvisioned, + interactor.isSatelliteProvisioned, interactor.isWifiActive, airplaneModeRepository.isAirplaneMode - ) { isSatelliteAllowed, isDeviceProvisioned, isWifiActive, isAirplaneMode -> - isSatelliteAllowed && isDeviceProvisioned && !isWifiActive && !isAirplaneMode + ) { isSatelliteAllowed, isSatelliteProvisioned, isWifiActive, isAirplaneMode -> + isSatelliteAllowed && isSatelliteProvisioned && !isWifiActive && !isAirplaneMode } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/CollapsedStatusBarViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/CollapsedStatusBarViewBinder.kt index a2ec1f21a35c..44b5bafc0859 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/CollapsedStatusBarViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/CollapsedStatusBarViewBinder.kt @@ -94,7 +94,7 @@ class CollapsedStatusBarViewBinderImpl @Inject constructor() : CollapsedStatusBa is OngoingActivityChipModel.Shown -> { IconViewBinder.bind(chipModel.icon, chipIconView) ChipChronometerBinder.bind(chipModel.startTimeMs, chipTimeView) - // TODO(b/332662551): Attach click listener to chip + chipView.setOnClickListener(chipModel.onClickListener) listener.onOngoingActivityStatusChanged( hasOngoingActivity = true diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/AvalancheController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/AvalancheController.kt index a97298527e11..32774e0b270a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/AvalancheController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/AvalancheController.kt @@ -35,10 +35,7 @@ import javax.inject.Inject @SysUISingleton class AvalancheController @Inject -constructor( - dumpManager: DumpManager, - private val uiEventLogger: UiEventLogger -) : Dumpable { +constructor(dumpManager: DumpManager, private val uiEventLogger: UiEventLogger) : Dumpable { private val tag = "AvalancheController" private val debug = Compile.IS_DEBUG && Log.isLoggable(tag, Log.DEBUG) @@ -69,14 +66,11 @@ constructor( @VisibleForTesting var debugDropSet: MutableSet<HeadsUpEntry> = HashSet() enum class ThrottleEvent(private val id: Int) : UiEventLogger.UiEventEnum { - @UiEvent(doc = "HUN was shown.") - SHOWN(1812), - + @UiEvent(doc = "HUN was shown.") AVALANCHE_THROTTLING_HUN_SHOWN(1821), @UiEvent(doc = "HUN was dropped to show higher priority HUNs.") - DROPPED(1813), - + AVALANCHE_THROTTLING_HUN_DROPPED(1822), @UiEvent(doc = "HUN was removed while waiting to show.") - REMOVED(1814); + AVALANCHE_THROTTLING_HUN_REMOVED(1823); override fun getId(): Int { return id @@ -97,7 +91,7 @@ constructor( runnable.run() return } - log { "\n "} + log { "\n " } val fn = "$label => AvalancheController.update ${getKey(entry)}" if (entry == null) { log { "Entry is NULL, stop update." } @@ -129,9 +123,10 @@ constructor( // HeadsUpEntry.updateEntry recursively calls AvalancheController#update // and goes to the isShowing case above headsUpEntryShowing!!.updateEntry( - /* updatePostTime= */ false, - /* updateEarliestRemovalTime= */ false, - /* reason= */ "avalanche duration update") + /* updatePostTime= */ false, + /* updateEarliestRemovalTime= */ false, + /* reason= */ "avalanche duration update" + ) } } logState("after $fn") @@ -152,7 +147,7 @@ constructor( runnable.run() return } - log { "\n "} + log { "\n " } val fn = "$label => AvalancheController.delete " + getKey(entry) if (entry == null) { log { "$fn => entry NULL, running runnable" } @@ -163,7 +158,7 @@ constructor( log { "$fn => remove from next" } if (entry in nextMap) nextMap.remove(entry) if (entry in nextList) nextList.remove(entry) - uiEventLogger.log(ThrottleEvent.REMOVED) + uiEventLogger.log(ThrottleEvent.AVALANCHE_THROTTLING_HUN_REMOVED) } else if (entry in debugDropSet) { log { "$fn => remove from dropset" } debugDropSet.remove(entry) @@ -287,7 +282,7 @@ constructor( private fun showNow(entry: HeadsUpEntry, runnableList: MutableList<Runnable>) { log { "SHOW: " + getKey(entry) } - uiEventLogger.log(ThrottleEvent.SHOWN) + uiEventLogger.log(ThrottleEvent.AVALANCHE_THROTTLING_HUN_SHOWN) headsUpEntryShowing = entry runnableList.forEach { @@ -318,7 +313,7 @@ constructor( // Log dropped HUNs for (e in listToDrop) { - uiEventLogger.log(ThrottleEvent.DROPPED) + uiEventLogger.log(ThrottleEvent.AVALANCHE_THROTTLING_HUN_DROPPED) } if (debug) { diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/GestureViewModelFactory.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/GestureViewModelFactory.kt new file mode 100644 index 000000000000..504bd5fbcb42 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/GestureViewModelFactory.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.touchpad.tutorial.ui + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.ViewModelProvider + +sealed class GestureTutorialViewModel : ViewModel() + +class BackGestureTutorialViewModel : GestureTutorialViewModel() + +class HomeGestureTutorialViewModel : GestureTutorialViewModel() + +class GestureViewModelFactory : ViewModelProvider.Factory { + + @Suppress("UNCHECKED_CAST") + override fun <T : ViewModel> create(modelClass: Class<T>): T { + return when (modelClass) { + BackGestureTutorialViewModel::class.java -> BackGestureTutorialViewModel() + HomeGestureTutorialViewModel::class.java -> HomeGestureTutorialViewModel() + else -> error("Unknown ViewModel class: ${modelClass.name}") + } + as T + } +} diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/TouchpadTutorialViewModel.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/TouchpadTutorialViewModel.kt new file mode 100644 index 000000000000..11740a8a4ecc --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/TouchpadTutorialViewModel.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.touchpad.tutorial.ui + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.ViewModelProvider +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import javax.inject.Inject + +class TouchpadTutorialViewModel : ViewModel() { + + private val _screen = MutableStateFlow(Screen.TUTORIAL_SELECTION) + val screen: StateFlow<Screen> = _screen + + fun goTo(screen: Screen) { + _screen.value = screen + } + + class Factory @Inject constructor() : ViewModelProvider.Factory { + + @Suppress("UNCHECKED_CAST") + override fun <T : ViewModel> create(modelClass: Class<T>): T { + return TouchpadTutorialViewModel() as T + } + } +} + +enum class Screen { + TUTORIAL_SELECTION, + BACK_GESTURE, + HOME_GESTURE, +} diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/view/TouchpadTutorialActivity.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/view/TouchpadTutorialActivity.kt new file mode 100644 index 000000000000..b7629c7160ee --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/view/TouchpadTutorialActivity.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.touchpad.tutorial.ui.view + +import android.os.Bundle +import androidx.activity.ComponentActivity +import androidx.activity.compose.setContent +import androidx.activity.enableEdgeToEdge +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.lifecycle.Lifecycle.State.STARTED +import androidx.lifecycle.ViewModelProvider +import androidx.lifecycle.compose.collectAsStateWithLifecycle +import androidx.lifecycle.viewmodel.compose.viewModel +import com.android.compose.theme.PlatformTheme +import com.android.systemui.touchpad.tutorial.ui.BackGestureTutorialViewModel +import com.android.systemui.touchpad.tutorial.ui.GestureViewModelFactory +import com.android.systemui.touchpad.tutorial.ui.HomeGestureTutorialViewModel +import com.android.systemui.touchpad.tutorial.ui.Screen.BACK_GESTURE +import com.android.systemui.touchpad.tutorial.ui.Screen.HOME_GESTURE +import com.android.systemui.touchpad.tutorial.ui.Screen.TUTORIAL_SELECTION +import com.android.systemui.touchpad.tutorial.ui.TouchpadTutorialViewModel +import javax.inject.Inject + +class TouchpadTutorialActivity +@Inject +constructor( + private val viewModelFactory: TouchpadTutorialViewModel.Factory, +) : ComponentActivity() { + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + enableEdgeToEdge() + setContent { + PlatformTheme { TouchpadTutorialScreen(viewModelFactory, closeTutorial = { finish() }) } + } + } +} + +@Composable +fun TouchpadTutorialScreen(viewModelFactory: ViewModelProvider.Factory, closeTutorial: () -> Unit) { + val vm = viewModel<TouchpadTutorialViewModel>(factory = viewModelFactory) + val activeScreen by vm.screen.collectAsStateWithLifecycle(STARTED) + when (activeScreen) { + TUTORIAL_SELECTION -> + TutorialSelectionScreen( + onBackTutorialClicked = { vm.goTo(BACK_GESTURE) }, + onHomeTutorialClicked = { vm.goTo(HOME_GESTURE) }, + onActionKeyTutorialClicked = {}, + onDoneButtonClicked = closeTutorial + ) + BACK_GESTURE -> BackGestureTutorialScreen() + HOME_GESTURE -> HomeGestureTutorialScreen() + } +} + +@Composable +fun BackGestureTutorialScreen() { + val vm = viewModel<BackGestureTutorialViewModel>(factory = GestureViewModelFactory()) +} + +@Composable +fun HomeGestureTutorialScreen() { + val vm = viewModel<HomeGestureTutorialViewModel>(factory = GestureViewModelFactory()) +} diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/view/TutorialSelectionScreen.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/view/TutorialSelectionScreen.kt new file mode 100644 index 000000000000..532eb1b82197 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/view/TutorialSelectionScreen.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.systemui.touchpad.tutorial.ui.view + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.aspectRatio +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.Button +import androidx.compose.material3.ButtonDefaults +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.unit.dp +import com.android.systemui.res.R + +@Composable +fun TutorialSelectionScreen( + onBackTutorialClicked: () -> Unit, + onHomeTutorialClicked: () -> Unit, + onActionKeyTutorialClicked: () -> Unit, + onDoneButtonClicked: () -> Unit, +) { + Column( + verticalArrangement = Arrangement.Center, + modifier = + Modifier.background( + color = MaterialTheme.colorScheme.surfaceContainer, + ) + .fillMaxSize() + ) { + TutorialSelectionButtons( + onBackTutorialClicked = onBackTutorialClicked, + onHomeTutorialClicked = onHomeTutorialClicked, + onActionKeyTutorialClicked = onActionKeyTutorialClicked, + modifier = Modifier.padding(60.dp) + ) + DoneButton( + onDoneButtonClicked = onDoneButtonClicked, + modifier = Modifier.padding(horizontal = 60.dp) + ) + } +} + +@Composable +private fun TutorialSelectionButtons( + onBackTutorialClicked: () -> Unit, + onHomeTutorialClicked: () -> Unit, + onActionKeyTutorialClicked: () -> Unit, + modifier: Modifier = Modifier +) { + Row( + horizontalArrangement = Arrangement.spacedBy(20.dp), + verticalAlignment = Alignment.CenterVertically, + modifier = modifier + ) { + TutorialButton( + text = stringResource(R.string.touchpad_tutorial_back_gesture_button), + onClick = onBackTutorialClicked, + color = MaterialTheme.colorScheme.primary, + modifier = Modifier.weight(1f) + ) + TutorialButton( + text = stringResource(R.string.touchpad_tutorial_home_gesture_button), + onClick = onHomeTutorialClicked, + color = MaterialTheme.colorScheme.secondary, + modifier = Modifier.weight(1f) + ) + TutorialButton( + text = stringResource(R.string.touchpad_tutorial_action_key_button), + onClick = onActionKeyTutorialClicked, + color = MaterialTheme.colorScheme.tertiary, + modifier = Modifier.weight(1f) + ) + } +} + +@Composable +private fun TutorialButton( + text: String, + onClick: () -> Unit, + color: Color, + modifier: Modifier = Modifier +) { + Button( + onClick = onClick, + shape = RoundedCornerShape(16.dp), + colors = ButtonDefaults.buttonColors(containerColor = color), + modifier = modifier.aspectRatio(0.66f) + ) { + Text(text = text, style = MaterialTheme.typography.headlineLarge) + } +} + +@Composable +private fun DoneButton(onDoneButtonClicked: () -> Unit, modifier: Modifier = Modifier) { + Row( + horizontalArrangement = Arrangement.End, + verticalAlignment = Alignment.CenterVertically, + modifier = modifier.fillMaxWidth() + ) { + Button(onClick = onDoneButtonClicked) { + Text(stringResource(R.string.touchpad_tutorial_done_button)) + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/unfold/SysUIUnfoldModule.kt b/packages/SystemUI/src/com/android/systemui/unfold/SysUIUnfoldModule.kt index 139ac7e4f687..291903d7254f 100644 --- a/packages/SystemUI/src/com/android/systemui/unfold/SysUIUnfoldModule.kt +++ b/packages/SystemUI/src/com/android/systemui/unfold/SysUIUnfoldModule.kt @@ -21,6 +21,7 @@ import com.android.systemui.CoreStartable import com.android.systemui.dagger.SysUISingleton import com.android.systemui.shade.NotificationPanelUnfoldAnimationController import com.android.systemui.statusbar.phone.StatusBarMoveFromCenterAnimationController +import com.android.systemui.unfold.dagger.NaturalRotation import com.android.systemui.unfold.dagger.UnfoldBg import com.android.systemui.unfold.util.NaturalRotationUnfoldProgressProvider import com.android.systemui.unfold.util.ScopedUnfoldTransitionProgressProvider @@ -36,6 +37,7 @@ import dagger.multibindings.IntoMap import dagger.multibindings.IntoSet import java.util.Optional import javax.inject.Named +import javax.inject.Qualifier import javax.inject.Scope @Scope @MustBeDocumented @Retention(AnnotationRetention.RUNTIME) annotation class SysUIUnfoldScope @@ -54,8 +56,17 @@ import javax.inject.Scope @Module(subcomponents = [SysUIUnfoldComponent::class]) class SysUIUnfoldModule { + /** + * Qualifier for dependencies bound in [com.android.systemui.unfold.SysUIUnfoldModule] + */ + @Qualifier + @MustBeDocumented + @Retention(AnnotationRetention.RUNTIME) + annotation class BoundFromSysUiUnfoldModule + @Provides @SysUISingleton + @BoundFromSysUiUnfoldModule fun provideSysUIUnfoldComponent( provider: Optional<UnfoldTransitionProgressProvider>, rotationProvider: Optional<NaturalRotationUnfoldProgressProvider>, @@ -98,6 +109,13 @@ abstract class SysUIUnfoldInternalModule { abstract fun bindsFoldLightRevealOverlayAnimation( anim: FoldLightRevealOverlayAnimation ): FullscreenLightRevealAnimation + + @Binds + @NaturalRotation + @SysUIUnfoldScope + abstract fun bindNaturalRotationUnfoldProgressProvider( + provider: NaturalRotationUnfoldProgressProvider + ): UnfoldTransitionProgressProvider } @SysUIUnfoldScope diff --git a/packages/SystemUI/src/com/android/systemui/util/kotlin/MapUtils.kt b/packages/SystemUI/src/com/android/systemui/util/kotlin/MapUtils.kt index 41cd95b780c9..8d202ac16856 100644 --- a/packages/SystemUI/src/com/android/systemui/util/kotlin/MapUtils.kt +++ b/packages/SystemUI/src/com/android/systemui/util/kotlin/MapUtils.kt @@ -30,3 +30,6 @@ fun <K, V, R, M : MutableMap<in K, in R>> Map<out K, V>.mapValuesNotNullTo( } return destination } + +/** Returns a map with all entries containing `null` values removed. */ +fun <K, V> Map<K, V?>.filterValuesNotNull(): Map<K, V> = mapValuesNotNull { it.value } diff --git a/packages/SystemUI/src/com/android/systemui/util/kotlin/Utils.kt b/packages/SystemUI/src/com/android/systemui/util/kotlin/Utils.kt index 405b57a1a04d..d9a2e956cc86 100644 --- a/packages/SystemUI/src/com/android/systemui/util/kotlin/Utils.kt +++ b/packages/SystemUI/src/com/android/systemui/util/kotlin/Utils.kt @@ -19,19 +19,24 @@ package com.android.systemui.util.kotlin import android.content.Context import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.filter +import kotlinx.coroutines.flow.map class Utils { companion object { fun <A, B, C> toTriple(a: A, bc: Pair<B, C>) = Triple(a, bc.first, bc.second) + fun <A, B, C> toTriple(ab: Pair<A, B>, c: C) = Triple(ab.first, ab.second, c) fun <A, B, C, D> toQuad(a: A, b: B, c: C, d: D) = Quad(a, b, c, d) + fun <A, B, C, D> toQuad(a: A, bcd: Triple<B, C, D>) = Quad(a, bcd.first, bcd.second, bcd.third) fun <A, B, C, D> toQuad(abc: Triple<A, B, C>, d: D) = Quad(abc.first, abc.second, abc.third, d) fun <A, B, C, D, E> toQuint(a: A, b: B, c: C, d: D, e: E) = Quint(a, b, c, d, e) + fun <A, B, C, D, E> toQuint(a: A, bcde: Quad<B, C, D, E>) = Quint(a, bcde.first, bcde.second, bcde.third, bcde.fourth) @@ -50,6 +55,14 @@ class Utils { ) /** + * Samples the provided flow, performs a filter on the sampled value, then returns the + * original value. + */ + fun <A, B> Flow<A>.sampleFilter(b: Flow<B>, predicate: (B) -> Boolean): Flow<A> { + return this.sample(b, ::Pair).filter { (_, b) -> predicate(b) }.map { (a, _) -> a } + } + + /** * Samples the provided flows, emitting a tuple of the original flow's value as well as each * of the combined flows' values. * diff --git a/packages/SystemUI/src/com/android/systemui/util/settings/SettingsProxy.kt b/packages/SystemUI/src/com/android/systemui/util/settings/SettingsProxy.kt index ed522331baef..d92127cf97cb 100644 --- a/packages/SystemUI/src/com/android/systemui/util/settings/SettingsProxy.kt +++ b/packages/SystemUI/src/com/android/systemui/util/settings/SettingsProxy.kt @@ -19,6 +19,7 @@ import android.content.ContentResolver import android.database.ContentObserver import android.net.Uri import android.provider.Settings.SettingNotFoundException +import com.android.app.tracing.TraceUtils.trace /** * Used to interact with mainly with Settings.Global, but can also be used for Settings.System and @@ -37,6 +38,7 @@ import android.provider.Settings.SettingNotFoundException interface SettingsProxy { /** Returns the [ContentResolver] this instance was constructed with. */ fun getContentResolver(): ContentResolver + /** * Construct the content URI for a particular name/value pair, useful for monitoring changes * with a ContentObserver. @@ -45,6 +47,7 @@ interface SettingsProxy { * @return the corresponding content URI, or null if not present */ fun getUriFor(name: String): Uri + /** * Convenience wrapper around [ContentResolver.registerContentObserver].' * @@ -53,9 +56,11 @@ interface SettingsProxy { fun registerContentObserverSync(name: String, settingsObserver: ContentObserver) { registerContentObserverSync(getUriFor(name), settingsObserver) } + /** Convenience wrapper around [ContentResolver.registerContentObserver].' */ fun registerContentObserverSync(uri: Uri, settingsObserver: ContentObserver) = registerContentObserverSync(uri, false, settingsObserver) + /** * Convenience wrapper around [ContentResolver.registerContentObserver].' * @@ -66,15 +71,26 @@ interface SettingsProxy { notifyForDescendants: Boolean, settingsObserver: ContentObserver ) = registerContentObserverSync(getUriFor(name), notifyForDescendants, settingsObserver) + /** Convenience wrapper around [ContentResolver.registerContentObserver].' */ fun registerContentObserverSync( uri: Uri, notifyForDescendants: Boolean, settingsObserver: ContentObserver - ) = getContentResolver().registerContentObserver(uri, notifyForDescendants, settingsObserver) + ) { + trace({ "SP#registerObserver#[$uri]" }) { + getContentResolver() + .registerContentObserver(uri, notifyForDescendants, settingsObserver) + } + } + /** See [ContentResolver.unregisterContentObserver]. */ - fun unregisterContentObserverSync(settingsObserver: ContentObserver) = - getContentResolver().unregisterContentObserver(settingsObserver) + fun unregisterContentObserverSync(settingsObserver: ContentObserver) { + trace({ "SP#unregisterObserver" }) { + getContentResolver().unregisterContentObserver(settingsObserver) + } + } + /** * Look up a name in the database. * @@ -82,6 +98,7 @@ interface SettingsProxy { * @return the corresponding value, or null if not present */ fun getString(name: String): String + /** * Store a name/value pair into the database. * @@ -90,6 +107,7 @@ interface SettingsProxy { * @return true if the value was set, false on database errors */ fun putString(name: String, value: String): Boolean + /** * Store a name/value pair into the database. * @@ -120,6 +138,7 @@ interface SettingsProxy { * @see .resetToDefaults */ fun putString(name: String, value: String, tag: String, makeDefault: Boolean): Boolean + /** * Convenience function for retrieving a single secure settings value as an integer. Note that * internally setting values are always stored as strings; this function converts the string to @@ -138,6 +157,7 @@ interface SettingsProxy { def } } + /** * Convenience function for retrieving a single secure settings value as an integer. Note that * internally setting values are always stored as strings; this function converts the string to @@ -160,6 +180,7 @@ interface SettingsProxy { throw SettingNotFoundException(name) } } + /** * Convenience function for updating a single settings value as an integer. This will either * create a new entry in the table if the given name does not exist, or modify the value of the @@ -173,6 +194,7 @@ interface SettingsProxy { fun putInt(name: String, value: Int): Boolean { return putString(name, value.toString()) } + /** * Convenience function for retrieving a single secure settings value as a boolean. Note that * internally setting values are always stored as strings; this function converts the string to @@ -186,6 +208,7 @@ interface SettingsProxy { fun getBool(name: String, def: Boolean): Boolean { return getInt(name, if (def) 1 else 0) != 0 } + /** * Convenience function for retrieving a single secure settings value as a boolean. Note that * internally setting values are always stored as strings; this function converts the string to @@ -203,6 +226,7 @@ interface SettingsProxy { fun getBool(name: String): Boolean { return getInt(name) != 0 } + /** * Convenience function for updating a single settings value as a boolean. This will either * create a new entry in the table if the given name does not exist, or modify the value of the @@ -216,6 +240,7 @@ interface SettingsProxy { fun putBool(name: String, value: Boolean): Boolean { return putInt(name, if (value) 1 else 0) } + /** * Convenience function for retrieving a single secure settings value as a `long`. Note that * internally setting values are always stored as strings; this function converts the string to @@ -230,6 +255,7 @@ interface SettingsProxy { val valString = getString(name) return parseLongOrUseDefault(valString, def) } + /** * Convenience function for retrieving a single secure settings value as a `long`. Note that * internally setting values are always stored as strings; this function converts the string to @@ -248,6 +274,7 @@ interface SettingsProxy { val valString = getString(name) return parseLongOrThrow(name, valString) } + /** * Convenience function for updating a secure settings value as a long integer. This will either * create a new entry in the table if the given name does not exist, or modify the value of the @@ -261,6 +288,7 @@ interface SettingsProxy { fun putLong(name: String, value: Long): Boolean { return putString(name, value.toString()) } + /** * Convenience function for retrieving a single secure settings value as a floating point * number. Note that internally setting values are always stored as strings; this function @@ -275,6 +303,7 @@ interface SettingsProxy { val v = getString(name) return parseFloat(v, def) } + /** * Convenience function for retrieving a single secure settings value as a float. Note that * internally setting values are always stored as strings; this function converts the string to @@ -293,6 +322,7 @@ interface SettingsProxy { val v = getString(name) return parseFloatOrThrow(name, v) } + /** * Convenience function for updating a single settings value as a floating point number. This * will either create a new entry in the table if the given name does not exist, or modify the @@ -306,6 +336,7 @@ interface SettingsProxy { fun putFloat(name: String, value: Float): Boolean { return putString(name, value.toString()) } + companion object { /** Convert a string to a long, or uses a default if the string is malformed or null */ @JvmStatic @@ -319,6 +350,7 @@ interface SettingsProxy { } return value } + /** Convert a string to a long, or throws an exception if the string is malformed or null */ @JvmStatic @Throws(SettingNotFoundException::class) @@ -332,6 +364,7 @@ interface SettingsProxy { throw SettingNotFoundException(name) } } + /** Convert a string to a float, or uses a default if the string is malformed or null */ @JvmStatic fun parseFloat(v: String?, def: Float): Float { @@ -341,6 +374,7 @@ interface SettingsProxy { def } } + /** * Convert a string to a float, or throws an exception if the string is malformed or null */ diff --git a/packages/SystemUI/src/com/android/systemui/util/settings/UserSettingsProxy.kt b/packages/SystemUI/src/com/android/systemui/util/settings/UserSettingsProxy.kt index ed139434d3bd..ed65f1ae1667 100644 --- a/packages/SystemUI/src/com/android/systemui/util/settings/UserSettingsProxy.kt +++ b/packages/SystemUI/src/com/android/systemui/util/settings/UserSettingsProxy.kt @@ -51,6 +51,7 @@ interface UserSettingsProxy : SettingsProxy { "userId cannot be set in interface, use setter from an implementation instead." ) } + /** * Returns the actual current user handle when querying with the current user. Otherwise, * returns the passed in user id. @@ -60,9 +61,11 @@ interface UserSettingsProxy : SettingsProxy { userHandle } else userTracker.userId } + override fun registerContentObserverSync(uri: Uri, settingsObserver: ContentObserver) { registerContentObserverForUserSync(uri, settingsObserver, userId) } + /** Convenience wrapper around [ContentResolver.registerContentObserver].' */ override fun registerContentObserverSync( uri: Uri, @@ -71,6 +74,7 @@ interface UserSettingsProxy : SettingsProxy { ) { registerContentObserverForUserSync(uri, notifyForDescendants, settingsObserver, userId) } + /** * Convenience wrapper around [ContentResolver.registerContentObserver] * @@ -83,6 +87,7 @@ interface UserSettingsProxy : SettingsProxy { ) { registerContentObserverForUserSync(getUriFor(name), settingsObserver, userHandle) } + /** Convenience wrapper around [ContentResolver.registerContentObserver] */ fun registerContentObserverForUserSync( uri: Uri, @@ -91,6 +96,7 @@ interface UserSettingsProxy : SettingsProxy { ) { registerContentObserverForUserSync(uri, false, settingsObserver, userHandle) } + /** * Convenience wrapper around [ContentResolver.registerContentObserver] * @@ -109,6 +115,7 @@ interface UserSettingsProxy : SettingsProxy { userHandle ) } + /** Convenience wrapper around [ContentResolver.registerContentObserver] */ fun registerContentObserverForUserSync( uri: Uri, @@ -127,6 +134,7 @@ interface UserSettingsProxy : SettingsProxy { Unit } } + /** * Look up a name in the database. * @@ -136,8 +144,10 @@ interface UserSettingsProxy : SettingsProxy { override fun getString(name: String): String { return getStringForUser(name, userId) } + /** See [getString]. */ fun getStringForUser(name: String, userHandle: Int): String + /** * Store a name/value pair into the database. Values written by this method will be overridden * if a restore happens in the future. @@ -147,11 +157,14 @@ interface UserSettingsProxy : SettingsProxy { * @return true if the value was set, false on database errors */ fun putString(name: String, value: String, overrideableByRestore: Boolean): Boolean + override fun putString(name: String, value: String): Boolean { return putStringForUser(name, value, userId) } + /** Similar implementation to [putString] for the specified [userHandle]. */ fun putStringForUser(name: String, value: String, userHandle: Int): Boolean + /** Similar implementation to [putString] for the specified [userHandle]. */ fun putStringForUser( name: String, @@ -161,9 +174,11 @@ interface UserSettingsProxy : SettingsProxy { @UserIdInt userHandle: Int, overrideableByRestore: Boolean ): Boolean + override fun getInt(name: String, def: Int): Int { return getIntForUser(name, def, userId) } + /** Similar implementation to [getInt] for the specified [userHandle]. */ fun getIntForUser(name: String, def: Int, userHandle: Int): Int { val v = getStringForUser(name, userHandle) @@ -173,8 +188,10 @@ interface UserSettingsProxy : SettingsProxy { def } } + @Throws(SettingNotFoundException::class) override fun getInt(name: String) = getIntForUser(name, userId) + /** Similar implementation to [getInt] for the specified [userHandle]. */ @Throws(SettingNotFoundException::class) fun getIntForUser(name: String, userHandle: Int): Int { @@ -185,52 +202,66 @@ interface UserSettingsProxy : SettingsProxy { throw SettingNotFoundException(name) } } + override fun putInt(name: String, value: Int) = putIntForUser(name, value, userId) + /** Similar implementation to [getInt] for the specified [userHandle]. */ fun putIntForUser(name: String, value: Int, userHandle: Int) = putStringForUser(name, value.toString(), userHandle) + override fun getBool(name: String, def: Boolean) = getBoolForUser(name, def, userId) + /** Similar implementation to [getBool] for the specified [userHandle]. */ fun getBoolForUser(name: String, def: Boolean, userHandle: Int) = getIntForUser(name, if (def) 1 else 0, userHandle) != 0 + @Throws(SettingNotFoundException::class) override fun getBool(name: String) = getBoolForUser(name, userId) + /** Similar implementation to [getBool] for the specified [userHandle]. */ @Throws(SettingNotFoundException::class) fun getBoolForUser(name: String, userHandle: Int): Boolean { return getIntForUser(name, userHandle) != 0 } + override fun putBool(name: String, value: Boolean): Boolean { return putBoolForUser(name, value, userId) } + /** Similar implementation to [putBool] for the specified [userHandle]. */ fun putBoolForUser(name: String, value: Boolean, userHandle: Int) = putIntForUser(name, if (value) 1 else 0, userHandle) + /** Similar implementation to [getLong] for the specified [userHandle]. */ fun getLongForUser(name: String, def: Long, userHandle: Int): Long { val valString = getStringForUser(name, userHandle) return parseLongOrUseDefault(valString, def) } + /** Similar implementation to [getLong] for the specified [userHandle]. */ @Throws(SettingNotFoundException::class) fun getLongForUser(name: String, userHandle: Int): Long { val valString = getStringForUser(name, userHandle) return parseLongOrThrow(name, valString) } + /** Similar implementation to [putLong] for the specified [userHandle]. */ fun putLongForUser(name: String, value: Long, userHandle: Int) = putStringForUser(name, value.toString(), userHandle) + /** Similar implementation to [getFloat] for the specified [userHandle]. */ fun getFloatForUser(name: String, def: Float, userHandle: Int): Float { val v = getStringForUser(name, userHandle) return parseFloat(v, def) } + /** Similar implementation to [getFloat] for the specified [userHandle]. */ @Throws(SettingNotFoundException::class) fun getFloatForUser(name: String, userHandle: Int): Float { val v = getStringForUser(name, userHandle) return parseFloatOrThrow(name, v) } + /** Similar implementation to [putFloat] for the specified [userHandle]. */ fun putFloatForUser(name: String, value: Float, userHandle: Int) = putStringForUser(name, value.toString(), userHandle) diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java index ce5545c05e49..f457470af906 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java +++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java @@ -136,6 +136,8 @@ import com.android.systemui.util.AlphaTintDrawableWrapper; import com.android.systemui.util.RoundedCornerProgressDrawable; import com.android.systemui.util.settings.SecureSettings; import com.android.systemui.volume.domain.interactor.VolumePanelNavigationInteractor; +import com.android.systemui.volume.panel.shared.flag.VolumePanelFlag; +import com.android.systemui.volume.ui.binder.VolumeDialogMenuIconBinder; import com.android.systemui.volume.ui.navigation.VolumeNavigator; import dagger.Lazy; @@ -311,6 +313,8 @@ public class VolumeDialogImpl implements VolumeDialog, Dumpable, private int mDialogTimeoutMillis; private final VibratorHelper mVibratorHelper; private final com.android.systemui.util.time.SystemClock mSystemClock; + private final VolumeDialogMenuIconBinder mVolumeDialogMenuIconBinder; + private final VolumePanelFlag mVolumePanelFlag; public VolumeDialogImpl( Context context, @@ -326,9 +330,11 @@ public class VolumeDialogImpl implements VolumeDialog, Dumpable, CsdWarningDialog.Factory csdWarningDialogFactory, DevicePostureController devicePostureController, Looper looper, + VolumePanelFlag volumePanelFlag, DumpManager dumpManager, Lazy<SecureSettings> secureSettings, VibratorHelper vibratorHelper, + VolumeDialogMenuIconBinder volumeDialogMenuIconBinder, com.android.systemui.util.time.SystemClock systemClock) { mContext = new ContextThemeWrapper(context, R.style.volume_dialog_theme); @@ -361,7 +367,9 @@ public class VolumeDialogImpl implements VolumeDialog, Dumpable, mVolumePanelNavigationInteractor = volumePanelNavigationInteractor; mVolumeNavigator = volumeNavigator; mSecureSettings = secureSettings; + mVolumeDialogMenuIconBinder = volumeDialogMenuIconBinder; mDialogTimeoutMillis = DIALOG_TIMEOUT_MILLIS; + mVolumePanelFlag = volumePanelFlag; dumpManager.registerDumpable("VolumeDialogImpl", this); @@ -436,6 +444,7 @@ public class VolumeDialogImpl implements VolumeDialog, Dumpable, if (mDevicePostureController != null) { mDevicePostureController.removeCallback(mDevicePostureControllerCallback); } + mVolumeDialogMenuIconBinder.destroy(); } @Override @@ -671,6 +680,7 @@ public class VolumeDialogImpl implements VolumeDialog, Dumpable, mSettingsView = mDialog.findViewById(R.id.settings_container); mSettingsIcon = mDialog.findViewById(R.id.settings); + mVolumeDialogMenuIconBinder.bind(mSettingsIcon); if (mRows.isEmpty()) { if (!AudioSystem.isSingleVolume(mContext)) { @@ -1358,6 +1368,9 @@ public class VolumeDialogImpl implements VolumeDialog, Dumpable, } private void updateODICaptionsH(boolean isServiceComponentEnabled, boolean fromTooltip) { + // don't show captions view when the new volume panel is enabled. + isServiceComponentEnabled = + isServiceComponentEnabled && !mVolumePanelFlag.canUseNewVolumePanel(); if (mODICaptionsView != null) { mODICaptionsView.setVisibility(isServiceComponentEnabled ? VISIBLE : GONE); } diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogModule.kt b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogModule.kt new file mode 100644 index 000000000000..54dc3db22b65 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogModule.kt @@ -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 com.android.systemui.volume + +import com.android.systemui.volume.panel.shared.flag.VolumePanelFlag +import com.android.systemui.volume.ui.viewmodel.AnimatedVolumeMenuIconViewModel +import com.android.systemui.volume.ui.viewmodel.StaticVolumeMenuIconViewModel +import com.android.systemui.volume.ui.viewmodel.VolumeMenuIconViewModel +import dagger.Lazy +import dagger.Module +import dagger.Provides + +@Module +interface VolumeDialogModule { + + companion object { + + @Provides + fun provideVolumeMenuIconViewModel( + volumePanelFlag: VolumePanelFlag, + static: Lazy<StaticVolumeMenuIconViewModel>, + animated: Lazy<AnimatedVolumeMenuIconViewModel>, + ): VolumeMenuIconViewModel { + return if (volumePanelFlag.canUseNewVolumePanel()) { + animated + } else { + static + } + .get() + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/volume/dagger/VolumeModule.java b/packages/SystemUI/src/com/android/systemui/volume/dagger/VolumeModule.java index dc1e8cf2ea01..f8ddc423e7d8 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/dagger/VolumeModule.java +++ b/packages/SystemUI/src/com/android/systemui/volume/dagger/VolumeModule.java @@ -38,11 +38,14 @@ import com.android.systemui.volume.CsdWarningDialog; import com.android.systemui.volume.VolumeComponent; import com.android.systemui.volume.VolumeDialogComponent; import com.android.systemui.volume.VolumeDialogImpl; +import com.android.systemui.volume.VolumeDialogModule; import com.android.systemui.volume.VolumePanelDialogReceiver; import com.android.systemui.volume.VolumeUI; import com.android.systemui.volume.domain.interactor.VolumePanelNavigationInteractor; import com.android.systemui.volume.panel.dagger.VolumePanelComponent; import com.android.systemui.volume.panel.dagger.factory.VolumePanelComponentFactory; +import com.android.systemui.volume.panel.shared.flag.VolumePanelFlag; +import com.android.systemui.volume.ui.binder.VolumeDialogMenuIconBinder; import com.android.systemui.volume.ui.navigation.VolumeNavigator; import dagger.Binds; @@ -61,6 +64,7 @@ import dagger.multibindings.IntoSet; CaptioningModule.class, MediaDevicesModule.class, SpatializerModule.class, + VolumeDialogModule.class, }, subcomponents = { VolumePanelComponent.class @@ -109,9 +113,11 @@ public interface VolumeModule { VolumeNavigator volumeNavigator, CsdWarningDialog.Factory csdFactory, DevicePostureController devicePostureController, + VolumePanelFlag volumePanelFlag, DumpManager dumpManager, Lazy<SecureSettings> secureSettings, VibratorHelper vibratorHelper, + VolumeDialogMenuIconBinder volumeDialogMenuIconBinder, SystemClock systemClock) { VolumeDialogImpl impl = new VolumeDialogImpl( context, @@ -127,9 +133,11 @@ public interface VolumeModule { csdFactory, devicePostureController, Looper.getMainLooper(), + volumePanelFlag, dumpManager, secureSettings, vibratorHelper, + volumeDialogMenuIconBinder, systemClock); impl.setStreamImportant(AudioManager.STREAM_SYSTEM, false); impl.setAutomute(true); diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaDeviceSessionInteractor.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaDeviceSessionInteractor.kt index 6e1ebc820b08..12e624cae4d4 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaDeviceSessionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaDeviceSessionInteractor.kt @@ -19,10 +19,10 @@ package com.android.systemui.volume.panel.component.mediaoutput.domain.interacto import android.media.session.MediaController import android.media.session.PlaybackState import com.android.settingslib.volume.data.repository.MediaControllerRepository +import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.volume.panel.component.mediaoutput.domain.model.MediaControllerChangeModel import com.android.systemui.volume.panel.component.mediaoutput.shared.model.MediaDeviceSession -import com.android.systemui.volume.panel.dagger.scope.VolumePanelScope import javax.inject.Inject import kotlin.coroutines.CoroutineContext import kotlinx.coroutines.ExperimentalCoroutinesApi @@ -38,7 +38,7 @@ import kotlinx.coroutines.withContext /** Allows to observe and change [MediaDeviceSession] state. */ @OptIn(ExperimentalCoroutinesApi::class) -@VolumePanelScope +@SysUISingleton class MediaDeviceSessionInteractor @Inject constructor( 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 31a89775e916..828221001861 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 @@ -23,11 +23,12 @@ import android.util.Log 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.SysUISingleton +import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.dagger.qualifiers.Background 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 -import com.android.systemui.volume.panel.dagger.scope.VolumePanelScope import com.android.systemui.volume.panel.shared.model.Result import com.android.systemui.volume.panel.shared.model.filterData import com.android.systemui.volume.panel.shared.model.wrapInResult @@ -52,13 +53,13 @@ import kotlinx.coroutines.withContext /** Provides observable models about the current media session state. */ @OptIn(ExperimentalCoroutinesApi::class) -@VolumePanelScope +@SysUISingleton class MediaOutputInteractor @Inject constructor( private val localMediaRepositoryFactory: LocalMediaRepositoryFactory, private val packageManager: PackageManager, - @VolumePanelScope private val coroutineScope: CoroutineScope, + @Application private val coroutineScope: CoroutineScope, @Background private val backgroundCoroutineContext: CoroutineContext, mediaControllerRepository: MediaControllerRepository, private val mediaControllerInteractor: MediaControllerInteractor, @@ -74,7 +75,7 @@ constructor( .onStart { emit(activeSessions) } } .map { getMediaControllers(it) } - .stateIn(coroutineScope, SharingStarted.Eagerly, MediaControllers(null, null)) + .stateIn(coroutineScope, SharingStarted.WhileSubscribed(), MediaControllers(null, null)) /** [MediaDeviceSessions] that contains currently active sessions. */ val activeMediaDeviceSessions: Flow<MediaDeviceSessions> = @@ -85,7 +86,11 @@ constructor( remote = it.remote?.mediaDeviceSession() ) } - .stateIn(coroutineScope, SharingStarted.Eagerly, MediaDeviceSessions(null, null)) + .stateIn( + coroutineScope, + SharingStarted.WhileSubscribed(), + MediaDeviceSessions(null, null) + ) /** Returns the default [MediaDeviceSession] from [activeMediaDeviceSessions] */ val defaultActiveMediaSession: StateFlow<Result<MediaDeviceSession?>> = @@ -100,7 +105,7 @@ constructor( } .wrapInResult() .flowOn(backgroundCoroutineContext) - .stateIn(coroutineScope, SharingStarted.Eagerly, Result.Loading()) + .stateIn(coroutineScope, SharingStarted.WhileSubscribed(), Result.Loading()) private val localMediaRepository: Flow<LocalMediaRepository> = defaultActiveMediaSession diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/spatial/ui/viewmodel/SpatialAudioViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/spatial/ui/viewmodel/SpatialAudioViewModel.kt index 6c6a1cca67f3..324579d1ee04 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/spatial/ui/viewmodel/SpatialAudioViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/spatial/ui/viewmodel/SpatialAudioViewModel.kt @@ -57,7 +57,7 @@ constructor( .toViewModel( isChecked = isEnabled is SpatialAudioEnabledModel.SpatialAudioEnabled, isHeadTrackingAvailable = - isAvailable is SpatialAudioAvailabilityModel.SpatialAudio, + isAvailable is SpatialAudioAvailabilityModel.HeadTracking, ) .copy(label = context.getString(R.string.volume_panel_spatial_audio_title)) } @@ -69,7 +69,7 @@ constructor( // head tracking availability means there are three possible states for the spatial // audio: disabled, enabled regular, enabled with head tracking. // Show popup in this case instead of a togglealbe button. - it is SpatialAudioAvailabilityModel.SpatialAudio + it is SpatialAudioAvailabilityModel.HeadTracking } .stateIn(scope, SharingStarted.Eagerly, false) diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioStreamSliderViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioStreamSliderViewModel.kt index 850162e65aa6..c18573ed1545 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioStreamSliderViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioStreamSliderViewModel.kt @@ -32,9 +32,13 @@ import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject import kotlin.math.roundToInt import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.filterNotNull +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.launch @@ -49,6 +53,7 @@ constructor( private val uiEventLogger: UiEventLogger, ) : SliderViewModel { + private val volumeChanges = MutableStateFlow<Int?>(null) private val streamsAffectedByRing = setOf( AudioManager.STREAM_RING, @@ -104,12 +109,17 @@ constructor( } .stateIn(coroutineScope, SharingStarted.Eagerly, SliderState.Empty) + init { + volumeChanges + .filterNotNull() + .onEach { audioVolumeInteractor.setVolume(audioStream, it) } + .launchIn(coroutineScope) + } + override fun onValueChanged(state: SliderState, newValue: Float) { val audioViewModel = state as? State audioViewModel ?: return - coroutineScope.launch { - audioVolumeInteractor.setVolume(audioStream, newValue.roundToInt()) - } + volumeChanges.tryEmit(newValue.roundToInt()) } override fun onValueChangeFinished() { diff --git a/packages/SystemUI/src/com/android/systemui/volume/ui/binder/VolumeDialogMenuIconBinder.kt b/packages/SystemUI/src/com/android/systemui/volume/ui/binder/VolumeDialogMenuIconBinder.kt new file mode 100644 index 000000000000..a352e286dcfe --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/volume/ui/binder/VolumeDialogMenuIconBinder.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.volume.ui.binder + +import android.animation.ValueAnimator +import android.graphics.drawable.Animatable2 +import android.widget.ImageView +import androidx.core.animation.doOnEnd +import com.android.systemui.common.shared.model.Icon +import com.android.systemui.common.ui.binder.IconViewBinder +import com.android.systemui.dagger.qualifiers.Application +import com.android.systemui.statusbar.CrossFadeHelper +import com.android.systemui.volume.ui.viewmodel.VolumeMenuIconViewModel +import javax.inject.Inject +import kotlin.coroutines.resume +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Job +import kotlinx.coroutines.flow.collectLatest +import kotlinx.coroutines.launch +import kotlinx.coroutines.suspendCancellableCoroutine + +/** Binds volume dialog menu button icon. */ +class VolumeDialogMenuIconBinder +@Inject +constructor( + @Application private val coroutineScope: CoroutineScope, + private val viewModel: VolumeMenuIconViewModel, +) { + + private var job: Job? = null + + fun bind(iconImageView: ImageView?) { + job?.cancel() + job = + iconImageView?.let { imageView -> + coroutineScope.launch { + viewModel.icon.collectLatest { icon -> + animate { CrossFadeHelper.fadeOut(imageView, it) } + IconViewBinder.bind(icon, imageView) + if (icon is Icon.Loaded && icon.drawable is Animatable2) { + icon.drawable.start() + } + animate { CrossFadeHelper.fadeIn(imageView, it) } + } + } + } + } + + private suspend fun animate(update: (value: Float) -> Unit) = + suspendCancellableCoroutine { continuation -> + val anim = ValueAnimator.ofFloat(0f, 1f) + anim.start() + anim.addUpdateListener { update(it.animatedValue as Float) } + anim.doOnEnd { continuation.resume(Unit) } + continuation.invokeOnCancellation { + anim.removeAllListeners() + anim.cancel() + } + } + + fun destroy() { + job?.cancel() + job = null + } +} diff --git a/packages/SystemUI/src/com/android/systemui/volume/ui/viewmodel/VolumeMenuIconViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/ui/viewmodel/VolumeMenuIconViewModel.kt new file mode 100644 index 000000000000..0bd3adba503a --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/volume/ui/viewmodel/VolumeMenuIconViewModel.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.volume.ui.viewmodel + +import android.annotation.SuppressLint +import android.content.Context +import com.android.systemui.common.shared.model.Icon +import com.android.systemui.dagger.qualifiers.Application +import com.android.systemui.res.R +import com.android.systemui.util.drawable.LoopedAnimatable2DrawableWrapper +import com.android.systemui.volume.panel.component.mediaoutput.domain.interactor.MediaDeviceSessionInteractor +import com.android.systemui.volume.panel.component.mediaoutput.domain.interactor.MediaOutputInteractor +import com.android.systemui.volume.panel.shared.model.filterData +import javax.inject.Inject +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.distinctUntilChangedBy +import kotlinx.coroutines.flow.flatMapLatest +import kotlinx.coroutines.flow.flowOf +import kotlinx.coroutines.flow.mapLatest + +/** View Model that provides an icon for the menu button of the volume dialog. */ +interface VolumeMenuIconViewModel { + + val icon: Flow<Icon> +} + +@OptIn(ExperimentalCoroutinesApi::class) +@SuppressLint("UseCompatLoadingForDrawables") +class AnimatedVolumeMenuIconViewModel +@Inject +constructor( + @Application private val context: Context, + private val mediaOutputInteractor: MediaOutputInteractor, + private val mediaDeviceSessionInteractor: MediaDeviceSessionInteractor, +) : VolumeMenuIconViewModel { + + override val icon: Flow<Icon> + get() = + mediaOutputInteractor.defaultActiveMediaSession + .filterData() + .flatMapLatest { session -> + if (session == null) { + flowOf(null) + } else { + mediaDeviceSessionInteractor.playbackState(session) + } + } + .distinctUntilChangedBy { it?.isActive } + .mapLatest { playbackState -> + if (playbackState?.isActive == true) { + Icon.Loaded( + LoopedAnimatable2DrawableWrapper.fromDrawable( + context.getDrawable(R.drawable.audio_bars_playing)!! + ), + null, + ) + } else { + Icon.Resource(R.drawable.horizontal_ellipsis, null) + } + } +} + +class StaticVolumeMenuIconViewModel @Inject constructor() : VolumeMenuIconViewModel { + + override val icon: Flow<Icon> = flowOf(Icon.Resource(R.drawable.horizontal_ellipsis, null)) +} diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java index e073f7cde826..ec9b5cfbdeb2 100644 --- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java +++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java @@ -58,6 +58,7 @@ import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.util.kotlin.JavaAdapter; +import com.android.wm.shell.common.desktopmode.DesktopModeTransitionSource; import com.android.wm.shell.desktopmode.DesktopMode; import com.android.wm.shell.desktopmode.DesktopModeTaskRepository; import com.android.wm.shell.onehanded.OneHanded; @@ -380,11 +381,13 @@ public final class WMShell implements mCommandQueue.addCallback(new CommandQueue.Callbacks() { @Override public void moveFocusedTaskToDesktop(int displayId) { - desktopMode.moveFocusedTaskToDesktop(displayId); + desktopMode.moveFocusedTaskToDesktop(displayId, + DesktopModeTransitionSource.KEYBOARD_SHORTCUT); } @Override public void moveFocusedTaskToFullscreen(int displayId) { - desktopMode.moveFocusedTaskToFullscreen(displayId); + desktopMode.moveFocusedTaskToFullscreen(displayId, + DesktopModeTransitionSource.KEYBOARD_SHORTCUT); } @Override public void moveFocusedTaskToStageSplit(int displayId, boolean leftOrTop) { diff --git a/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt index 5702a8c61e7a..69a6f2aed4c4 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt @@ -348,7 +348,8 @@ class ClockEventControllerTest : SysuiTestCase() { fun listenForTransitionToAodFromGone_updatesClockDozeAmountToOne() = runBlocking(IMMEDIATE) { val transitionStep = MutableStateFlow(TransitionStep()) - whenever(keyguardTransitionInteractor.transitionStepsToState(AOD)) + whenever(keyguardTransitionInteractor + .transition(Edge.create(to = AOD))) .thenReturn(transitionStep) val job = underTest.listenForAnyStateToAodTransition(this) @@ -369,7 +370,8 @@ class ClockEventControllerTest : SysuiTestCase() { fun listenForTransitionToLSFromOccluded_updatesClockDozeAmountToZero() = runBlocking(IMMEDIATE) { val transitionStep = MutableStateFlow(TransitionStep()) - whenever(keyguardTransitionInteractor.transitionStepsToState(LOCKSCREEN)) + whenever(keyguardTransitionInteractor + .transition(Edge.create(to = LOCKSCREEN))) .thenReturn(transitionStep) val job = underTest.listenForAnyStateToLockscreenTransition(this) @@ -390,7 +392,8 @@ class ClockEventControllerTest : SysuiTestCase() { fun listenForTransitionToAodFromLockscreen_neverUpdatesClockDozeAmount() = runBlocking(IMMEDIATE) { val transitionStep = MutableStateFlow(TransitionStep()) - whenever(keyguardTransitionInteractor.transitionStepsToState(AOD)) + whenever(keyguardTransitionInteractor + .transition(Edge.create(to = AOD))) .thenReturn(transitionStep) val job = underTest.listenForAnyStateToAodTransition(this) @@ -411,7 +414,8 @@ class ClockEventControllerTest : SysuiTestCase() { fun listenForAnyStateToLockscreenTransition_neverUpdatesClockDozeAmount() = runBlocking(IMMEDIATE) { val transitionStep = MutableStateFlow(TransitionStep()) - whenever(keyguardTransitionInteractor.transitionStepsToState(LOCKSCREEN)) + whenever(keyguardTransitionInteractor + .transition(Edge.create(to = LOCKSCREEN))) .thenReturn(transitionStep) val job = underTest.listenForAnyStateToLockscreenTransition(this) @@ -432,7 +436,8 @@ class ClockEventControllerTest : SysuiTestCase() { fun listenForAnyStateToDozingTransition_UpdatesClockDozeAmountToOne() = runBlocking(IMMEDIATE) { val transitionStep = MutableStateFlow(TransitionStep()) - whenever(keyguardTransitionInteractor.transitionStepsToState(DOZING)) + whenever(keyguardTransitionInteractor + .transition(Edge.create(to = DOZING))) .thenReturn(transitionStep) val job = underTest.listenForAnyStateToDozingTransition(this) diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUnfoldTransitionTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUnfoldTransitionTest.kt index 3afca5933e5d..336183d71ff1 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUnfoldTransitionTest.kt +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUnfoldTransitionTest.kt @@ -18,26 +18,25 @@ package com.android.keyguard import android.testing.AndroidTestingRunner import android.view.View -import android.view.ViewGroup import androidx.test.filters.SmallTest -import com.android.systemui.res.R import com.android.systemui.SysuiTestCase +import com.android.systemui.keyguard.ui.view.KeyguardRootView +import com.android.systemui.kosmos.Kosmos import com.android.systemui.plugins.statusbar.StatusBarStateController +import com.android.systemui.res.R +import com.android.systemui.shade.NotificationShadeWindowView import com.android.systemui.statusbar.StatusBarState.KEYGUARD import com.android.systemui.statusbar.StatusBarState.SHADE +import com.android.systemui.unfold.FakeUnfoldTransitionProvider import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener -import com.android.systemui.unfold.util.NaturalRotationUnfoldProgressProvider -import com.android.systemui.util.mockito.capture -import com.android.systemui.util.mockito.whenever +import com.android.systemui.unfold.fakeUnfoldTransitionProgressProvider 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.Captor import org.mockito.Mock -import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations +import org.mockito.kotlin.whenever /** * Translates items away/towards the hinge when the device is opened/closed. This is controlled by @@ -47,11 +46,16 @@ import org.mockito.MockitoAnnotations @RunWith(AndroidTestingRunner::class) class KeyguardUnfoldTransitionTest : SysuiTestCase() { - @Mock private lateinit var progressProvider: NaturalRotationUnfoldProgressProvider + private val kosmos = Kosmos() + + private val progressProvider: FakeUnfoldTransitionProvider = + kosmos.fakeUnfoldTransitionProgressProvider - @Captor private lateinit var progressListenerCaptor: ArgumentCaptor<TransitionProgressListener> + @Mock + private lateinit var keyguardRootView: KeyguardRootView - @Mock private lateinit var parent: ViewGroup + @Mock + private lateinit var notificationShadeWindowView: NotificationShadeWindowView @Mock private lateinit var statusBarStateController: StatusBarStateController @@ -66,13 +70,15 @@ class KeyguardUnfoldTransitionTest : SysuiTestCase() { xTranslationMax = context.resources.getDimensionPixelSize(R.dimen.keyguard_unfold_translation_x).toFloat() - underTest = KeyguardUnfoldTransition(context, statusBarStateController, progressProvider) + underTest = KeyguardUnfoldTransition( + context, keyguardRootView, notificationShadeWindowView, + statusBarStateController, progressProvider + ) - underTest.setup(parent) + underTest.setup() underTest.statusViewCentered = false - verify(progressProvider).addCallback(capture(progressListenerCaptor)) - progressListener = progressListenerCaptor.value + progressListener = progressProvider } @Test @@ -81,7 +87,9 @@ class KeyguardUnfoldTransitionTest : SysuiTestCase() { underTest.statusViewCentered = true val view = View(context) - whenever(parent.findViewById<View>(R.id.lockscreen_clock_view_large)).thenReturn(view) + whenever(keyguardRootView.findViewById<View>(R.id.lockscreen_clock_view_large)).thenReturn( + view + ) progressListener.onTransitionStarted() assertThat(view.translationX).isZero() @@ -101,7 +109,9 @@ class KeyguardUnfoldTransitionTest : SysuiTestCase() { whenever(statusBarStateController.getState()).thenReturn(SHADE) val view = View(context) - whenever(parent.findViewById<View>(R.id.lockscreen_clock_view_large)).thenReturn(view) + whenever(keyguardRootView.findViewById<View>(R.id.lockscreen_clock_view_large)).thenReturn( + view + ) progressListener.onTransitionStarted() assertThat(view.translationX).isZero() @@ -121,7 +131,10 @@ class KeyguardUnfoldTransitionTest : SysuiTestCase() { whenever(statusBarStateController.getState()).thenReturn(KEYGUARD) val view = View(context) - whenever(parent.findViewById<View>(R.id.lockscreen_clock_view_large)).thenReturn(view) + whenever( + notificationShadeWindowView + .findViewById<View>(R.id.lockscreen_clock_view_large) + ).thenReturn(view) progressListener.onTransitionStarted() assertThat(view.translationX).isZero() diff --git a/packages/SystemUI/tests/src/com/android/keyguard/LegacyLockIconViewControllerWithCoroutinesTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/LegacyLockIconViewControllerWithCoroutinesTest.kt index 25a87b8aaf60..958013908e44 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/LegacyLockIconViewControllerWithCoroutinesTest.kt +++ b/packages/SystemUI/tests/src/com/android/keyguard/LegacyLockIconViewControllerWithCoroutinesTest.kt @@ -22,10 +22,6 @@ import androidx.test.filters.SmallTest import com.android.keyguard.LockIconView.ICON_LOCK import com.android.systemui.doze.util.getBurnInOffset import com.android.systemui.flags.Flags.LOCKSCREEN_WALLPAPER_DREAM_ENABLED -import com.android.systemui.keyguard.shared.model.KeyguardState.AOD -import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN -import com.android.systemui.keyguard.shared.model.TransitionState.FINISHED -import com.android.systemui.keyguard.shared.model.TransitionStep import com.android.systemui.statusbar.StatusBarState import com.android.systemui.util.mockito.whenever import kotlinx.coroutines.Dispatchers @@ -104,7 +100,7 @@ class LegacyLockIconViewControllerWithCoroutinesTest : LegacyLockIconViewControl // WHEN dozing updates mUnderTest.mIsDozingCallback.accept(true) - mUnderTest.mDozeTransitionCallback.accept(TransitionStep(LOCKSCREEN, AOD, 1f, FINISHED)) + mUnderTest.mDozeTransitionCallback.accept(1f) // THEN the view's translation is updated to use the AoD burn-in offsets verify(mLockIconView).setTranslationY(burnInOffset.toFloat()) @@ -113,7 +109,7 @@ class LegacyLockIconViewControllerWithCoroutinesTest : LegacyLockIconViewControl // WHEN the device is no longer dozing mUnderTest.mIsDozingCallback.accept(false) - mUnderTest.mDozeTransitionCallback.accept(TransitionStep(AOD, LOCKSCREEN, 0f, FINISHED)) + mUnderTest.mDozeTransitionCallback.accept(0f) // THEN the view is updated to NO translation (no burn-in offsets anymore) verify(mLockIconView).setTranslationY(0f) diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/AccessibilityLoggerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/accessibility/AccessibilityLoggerTest.kt index deacac39b587..1ce21e77f7f3 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/AccessibilityLoggerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/AccessibilityLoggerTest.kt @@ -16,7 +16,7 @@ package com.android.systemui.accessibility -import android.testing.AndroidTestingRunner +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.internal.logging.UiEventLogger import com.android.systemui.SysuiTestCase @@ -34,7 +34,7 @@ import org.mockito.Mockito.verify import org.mockito.junit.MockitoJUnit @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) class AccessibilityLoggerTest : SysuiTestCase() { @JvmField @Rule val mockito = MockitoJUnit.rule() diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/DisplayIdIndexSupplierTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/DisplayIdIndexSupplierTest.java index 9cb4fb319fa2..cb8cfc2f5dd6 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/DisplayIdIndexSupplierTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/DisplayIdIndexSupplierTest.java @@ -20,10 +20,10 @@ import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNotNull; import android.hardware.display.DisplayManager; -import android.testing.AndroidTestingRunner; import android.view.Display; import androidx.annotation.NonNull; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; @@ -33,7 +33,7 @@ import org.junit.Test; import org.junit.runner.RunWith; @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) public class DisplayIdIndexSupplierTest extends SysuiTestCase { private DisplayIdIndexSupplier<Object> mDisplayIdIndexSupplier; diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/FullscreenMagnificationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/FullscreenMagnificationControllerTest.java index 5bc9aa43e825..cbd535ba7c2b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/FullscreenMagnificationControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/FullscreenMagnificationControllerTest.java @@ -21,8 +21,10 @@ import static android.os.Build.HW_TIMEOUT_MULTIPLIER; import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; import static com.google.common.truth.Truth.assertThat; +import static com.google.common.truth.Truth.assertWithMessage; -import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -34,8 +36,12 @@ import android.animation.ValueAnimator; import android.content.Context; import android.content.pm.ActivityInfo; import android.graphics.Rect; +import android.os.RemoteException; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; +import android.view.Display; +import android.view.IRotationWatcher; +import android.view.IWindowManager; import android.view.SurfaceControl; import android.view.SurfaceControlViewHost; import android.view.View; @@ -55,6 +61,8 @@ import org.junit.After; 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.CountDownLatch; import java.util.concurrent.TimeUnit; @@ -73,9 +81,12 @@ public class FullscreenMagnificationControllerTest extends SysuiTestCase { private ValueAnimator mShowHideBorderAnimator; private SurfaceControl.Transaction mTransaction; private TestableWindowManager mWindowManager; + @Mock + private IWindowManager mIWindowManager; @Before public void setUp() { + MockitoAnnotations.initMocks(this); getInstrumentation().runOnMainSync(() -> mSurfaceControlViewHost = spy(new SurfaceControlViewHost(mContext, mContext.getDisplay(), new InputTransferToken(), "FullscreenMagnification"))); @@ -88,9 +99,11 @@ public class FullscreenMagnificationControllerTest extends SysuiTestCase { mShowHideBorderAnimator = spy(newNullTargetObjectAnimator()); mFullscreenMagnificationController = new FullscreenMagnificationController( mContext, + mContext.getMainThreadHandler(), mContext.getMainExecutor(), mContext.getSystemService(AccessibilityManager.class), mContext.getSystemService(WindowManager.class), + mIWindowManager, scvhSupplier, mTransaction, mShowHideBorderAnimator); @@ -104,7 +117,8 @@ public class FullscreenMagnificationControllerTest extends SysuiTestCase { } @Test - public void enableFullscreenMagnification_visibleBorder() throws InterruptedException { + public void enableFullscreenMagnification_visibleBorder() + throws InterruptedException, RemoteException { CountDownLatch transactionCommittedLatch = new CountDownLatch(1); CountDownLatch animationEndLatch = new CountDownLatch(1); mTransaction.addTransactionCommittedListener( @@ -119,17 +133,21 @@ public class FullscreenMagnificationControllerTest extends SysuiTestCase { //Enable fullscreen magnification mFullscreenMagnificationController .onFullscreenMagnificationActivationChanged(true)); - assertTrue("Failed to wait for transaction committed", - transactionCommittedLatch.await(WAIT_TIMEOUT_S, TimeUnit.SECONDS)); - assertTrue("Failed to wait for animation to be finished", - animationEndLatch.await(ANIMATION_TIMEOUT_MS, TimeUnit.MILLISECONDS)); + assertWithMessage("Failed to wait for transaction committed") + .that(transactionCommittedLatch.await(WAIT_TIMEOUT_S, TimeUnit.SECONDS)) + .isTrue(); + assertWithMessage("Failed to wait for animation to be finished") + .that(animationEndLatch.await(ANIMATION_TIMEOUT_MS, TimeUnit.MILLISECONDS)) + .isTrue(); verify(mShowHideBorderAnimator).start(); + verify(mIWindowManager) + .watchRotation(any(IRotationWatcher.class), eq(Display.DEFAULT_DISPLAY)); assertThat(mSurfaceControlViewHost.getView().isVisibleToUser()).isTrue(); } @Test public void disableFullscreenMagnification_reverseAnimationAndReleaseScvh() - throws InterruptedException { + throws InterruptedException, RemoteException { CountDownLatch transactionCommittedLatch = new CountDownLatch(1); CountDownLatch enableAnimationEndLatch = new CountDownLatch(1); CountDownLatch disableAnimationEndLatch = new CountDownLatch(1); @@ -149,11 +167,12 @@ public class FullscreenMagnificationControllerTest extends SysuiTestCase { //Enable fullscreen magnification mFullscreenMagnificationController .onFullscreenMagnificationActivationChanged(true)); - assertTrue("Failed to wait for transaction committed", - transactionCommittedLatch.await(WAIT_TIMEOUT_S, TimeUnit.SECONDS)); - assertTrue("Failed to wait for enabling animation to be finished", - enableAnimationEndLatch.await( - ANIMATION_TIMEOUT_MS, TimeUnit.MILLISECONDS)); + assertWithMessage("Failed to wait for transaction committed") + .that(transactionCommittedLatch.await(WAIT_TIMEOUT_S, TimeUnit.SECONDS)) + .isTrue(); + assertWithMessage("Failed to wait for enabling animation to be finished") + .that(enableAnimationEndLatch.await(ANIMATION_TIMEOUT_MS, TimeUnit.MILLISECONDS)) + .isTrue(); verify(mShowHideBorderAnimator).start(); getInstrumentation().runOnMainSync(() -> @@ -161,11 +180,12 @@ public class FullscreenMagnificationControllerTest extends SysuiTestCase { mFullscreenMagnificationController .onFullscreenMagnificationActivationChanged(false)); - assertTrue("Failed to wait for disabling animation to be finished", - disableAnimationEndLatch.await( - ANIMATION_TIMEOUT_MS, TimeUnit.MILLISECONDS)); + assertWithMessage("Failed to wait for disabling animation to be finished") + .that(disableAnimationEndLatch.await(ANIMATION_TIMEOUT_MS, TimeUnit.MILLISECONDS)) + .isTrue(); verify(mShowHideBorderAnimator).reverse(); verify(mSurfaceControlViewHost).release(); + verify(mIWindowManager).removeRotationWatcher(any(IRotationWatcher.class)); } @Test @@ -188,10 +208,12 @@ public class FullscreenMagnificationControllerTest extends SysuiTestCase { () -> mFullscreenMagnificationController .onFullscreenMagnificationActivationChanged(true)); - assertTrue("Failed to wait for transaction committed", - transactionCommittedLatch.await(WAIT_TIMEOUT_S, TimeUnit.SECONDS)); - assertTrue("Failed to wait for animation to be finished", - animationEndLatch.await(ANIMATION_TIMEOUT_MS, TimeUnit.MILLISECONDS)); + assertWithMessage("Failed to wait for transaction committed") + .that(transactionCommittedLatch.await(WAIT_TIMEOUT_S, TimeUnit.SECONDS)) + .isTrue(); + assertWithMessage("Failed to wait for animation to be finished") + .that(animationEndLatch.await(ANIMATION_TIMEOUT_MS, TimeUnit.MILLISECONDS)) + .isTrue(); verify(mShowHideBorderAnimator).reverse(); } @@ -212,10 +234,12 @@ public class FullscreenMagnificationControllerTest extends SysuiTestCase { //Enable fullscreen magnification mFullscreenMagnificationController .onFullscreenMagnificationActivationChanged(true)); - assertTrue("Failed to wait for transaction committed", - transactionCommittedLatch.await(WAIT_TIMEOUT_S, TimeUnit.SECONDS)); - assertTrue("Failed to wait for animation to be finished", - animationEndLatch.await(ANIMATION_TIMEOUT_MS, TimeUnit.MILLISECONDS)); + assertWithMessage("Failed to wait for transaction committed") + .that(transactionCommittedLatch.await(WAIT_TIMEOUT_S, TimeUnit.SECONDS)) + .isTrue(); + assertWithMessage("Failed to wait for animation to be finished") + .that(animationEndLatch.await(ANIMATION_TIMEOUT_MS, TimeUnit.MILLISECONDS)) + .isTrue(); final Rect testWindowBounds = new Rect( mWindowManager.getCurrentWindowMetrics().getBounds()); testWindowBounds.set(testWindowBounds.left, testWindowBounds.top, diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/IMagnificationConnectionTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/IMagnificationConnectionTest.java index 3164f8e11593..361a9450399a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/IMagnificationConnectionTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/IMagnificationConnectionTest.java @@ -37,14 +37,15 @@ import android.platform.test.annotations.RequiresFlagsEnabled; import android.platform.test.flag.junit.CheckFlagsRule; import android.platform.test.flag.junit.DeviceFlagsValueProvider; import android.provider.Settings; -import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.view.Display; +import android.view.IWindowManager; import android.view.accessibility.AccessibilityManager; import android.view.accessibility.IMagnificationConnection; import android.view.accessibility.IMagnificationConnectionCallback; import android.view.accessibility.IRemoteMagnificationAnimationCallback; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.systemui.Flags; @@ -67,7 +68,7 @@ import org.mockito.MockitoAnnotations; * {@link Magnification} */ @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) @TestableLooper.RunWithLooper(setAsMainLooper = true) public class IMagnificationConnectionTest extends SysuiTestCase { @@ -99,6 +100,8 @@ public class IMagnificationConnectionTest extends SysuiTestCase { private SecureSettings mSecureSettings; @Mock private AccessibilityLogger mA11yLogger; + @Mock + private IWindowManager mIWindowManager; private IMagnificationConnection mIMagnificationConnection; private Magnification mMagnification; @@ -117,9 +120,10 @@ public class IMagnificationConnectionTest extends SysuiTestCase { mTestableLooper = TestableLooper.get(this); assertNotNull(mTestableLooper); mMagnification = new Magnification(getContext(), - mTestableLooper.getLooper(), getContext().getMainExecutor(), mCommandQueue, + mTestableLooper.getLooper(), mContext.getMainExecutor(), mCommandQueue, mModeSwitchesController, mSysUiState, mOverviewProxyService, mSecureSettings, - mDisplayTracker, getContext().getSystemService(DisplayManager.class), mA11yLogger); + mDisplayTracker, getContext().getSystemService(DisplayManager.class), + mA11yLogger, mIWindowManager); mMagnification.mWindowMagnificationControllerSupplier = new FakeWindowMagnificationControllerSupplier( mContext.getSystemService(DisplayManager.class)); diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationGestureDetectorTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationGestureDetectorTest.java index ad0217980e92..7b06dd65e7b3 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationGestureDetectorTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationGestureDetectorTest.java @@ -26,11 +26,11 @@ import static org.mockito.Mockito.verify; import android.os.Handler; import android.os.SystemClock; -import android.testing.AndroidTestingRunner; import android.view.MotionEvent; import android.view.View; import android.view.ViewConfiguration; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; @@ -45,7 +45,7 @@ import org.mockito.Mockito; import org.mockito.MockitoAnnotations; @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) public class MagnificationGestureDetectorTest extends SysuiTestCase { private static final float ACTION_DOWN_X = 100; diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationModeSwitchTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationModeSwitchTest.java index 1a885453eccb..5be1180d3bdb 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationModeSwitchTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationModeSwitchTest.java @@ -58,7 +58,6 @@ import android.graphics.Insets; import android.graphics.Rect; import android.os.Handler; import android.os.SystemClock; -import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.view.Choreographer; import android.view.MotionEvent; @@ -71,6 +70,7 @@ import android.view.accessibility.AccessibilityManager; import android.view.accessibility.AccessibilityNodeInfo; import android.widget.ImageView; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.internal.graphics.SfVsyncFrameCallbackProvider; @@ -90,7 +90,7 @@ import org.mockito.MockitoAnnotations; import java.util.List; @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) @TestableLooper.RunWithLooper(setAsMainLooper = true) public class MagnificationModeSwitchTest extends SysuiTestCase { diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationSettingsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationSettingsControllerTest.java index 9eead6a422ac..d0f8e7863537 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationSettingsControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationSettingsControllerTest.java @@ -22,9 +22,9 @@ import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.verify; import android.content.pm.ActivityInfo; -import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.internal.graphics.SfVsyncFrameCallbackProvider; @@ -40,7 +40,7 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) /** Tests the MagnificationSettingsController. */ @TestableLooper.RunWithLooper(setAsMainLooper = true) public class MagnificationSettingsControllerTest extends SysuiTestCase { diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationTest.java index bbdd8050a142..17b7e21f5e74 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationTest.java @@ -40,13 +40,14 @@ import android.content.Context; import android.graphics.Rect; import android.hardware.display.DisplayManager; import android.os.RemoteException; -import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.view.Display; +import android.view.IWindowManager; import android.view.accessibility.AccessibilityManager; import android.view.accessibility.IMagnificationConnection; import android.view.accessibility.IMagnificationConnectionCallback; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; @@ -64,7 +65,7 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) @TestableLooper.RunWithLooper public class MagnificationTest extends SysuiTestCase { @@ -93,6 +94,8 @@ public class MagnificationTest extends SysuiTestCase { private MagnificationSettingsController mMagnificationSettingsController; @Mock private AccessibilityLogger mA11yLogger; + @Mock + private IWindowManager mIWindowManager; @Before public void setUp() throws Exception { @@ -122,10 +125,10 @@ public class MagnificationTest extends SysuiTestCase { mCommandQueue = new CommandQueue(getContext(), mDisplayTracker); mMagnification = new Magnification(getContext(), - getContext().getMainThreadHandler(), getContext().getMainExecutor(), + getContext().getMainThreadHandler(), mContext.getMainExecutor(), mCommandQueue, mModeSwitchesController, mSysUiState, mOverviewProxyService, mSecureSettings, mDisplayTracker, - getContext().getSystemService(DisplayManager.class), mA11yLogger); + getContext().getSystemService(DisplayManager.class), mA11yLogger, mIWindowManager); mMagnification.mWindowMagnificationControllerSupplier = new FakeControllerSupplier( mContext.getSystemService(DisplayManager.class), mWindowMagnificationController); mMagnification.mMagnificationSettingsSupplier = new FakeSettingsSupplier( diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/MirrorWindowControlTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/MirrorWindowControlTest.java index e81613edc7ca..8f9b7c8cbc45 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/MirrorWindowControlTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/MirrorWindowControlTest.java @@ -27,12 +27,12 @@ import static org.mockito.Mockito.verify; import android.content.Context; import android.graphics.Point; -import android.testing.AndroidTestingRunner; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.view.WindowManager; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; @@ -45,7 +45,7 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) public class MirrorWindowControlTest extends SysuiTestCase { @Mock WindowManager mWindowManager; diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/ModeSwitchesControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/ModeSwitchesControllerTest.java index 3c974232990a..6e942979e0ed 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/ModeSwitchesControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/ModeSwitchesControllerTest.java @@ -21,11 +21,11 @@ import static org.mockito.Mockito.verify; import android.content.pm.ActivityInfo; import android.hardware.display.DisplayManager; import android.provider.Settings; -import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.view.Display; import android.view.View; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; @@ -39,7 +39,7 @@ import org.mockito.Mockito; import org.mockito.MockitoAnnotations; @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) /** Tests the ModeSwitchesController. */ @TestableLooper.RunWithLooper(setAsMainLooper = true) public class ModeSwitchesControllerTest extends SysuiTestCase { diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/SecureSettingsContentObserverTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/SecureSettingsContentObserverTest.java index 9c601a8670c0..9222fc2222be 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/SecureSettingsContentObserverTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/SecureSettingsContentObserverTest.java @@ -21,8 +21,8 @@ import static com.google.common.truth.Truth.assertThat; import android.app.ActivityManager; import android.content.Context; import android.provider.Settings; -import android.testing.AndroidTestingRunner; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; @@ -34,7 +34,7 @@ import org.junit.runner.RunWith; import org.mockito.Mockito; /** Test for {@link SecureSettingsContentObserver}. */ -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) @SmallTest public class SecureSettingsContentObserverTest extends SysuiTestCase { diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/SystemActionsTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/SystemActionsTest.java index c67429492180..f46b2f905f5b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/SystemActionsTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/SystemActionsTest.java @@ -28,10 +28,10 @@ import android.hardware.input.InputManager; import android.os.RemoteException; import android.telecom.TelecomManager; import android.telephony.TelephonyManager; -import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.view.KeyEvent; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; @@ -55,7 +55,7 @@ import java.util.Optional; @TestableLooper.RunWithLooper @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) public class SystemActionsTest extends SysuiTestCase { @Mock private UserTracker mUserTracker; diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java index cb42078460c2..f57003e37117 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java @@ -72,7 +72,6 @@ import android.platform.test.annotations.RequiresFlagsDisabled; import android.platform.test.flag.junit.CheckFlagsRule; import android.platform.test.flag.junit.DeviceFlagsValueProvider; import android.provider.Settings; -import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.testing.TestableResources; import android.text.TextUtils; @@ -90,6 +89,7 @@ import android.view.accessibility.AccessibilityNodeInfo; import android.view.accessibility.IRemoteMagnificationAnimationCallback; import androidx.test.InstrumentationRegistry; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.LargeTest; import com.android.internal.graphics.SfVsyncFrameCallbackProvider; @@ -125,7 +125,7 @@ import java.util.concurrent.atomic.AtomicInteger; @LargeTest @TestableLooper.RunWithLooper -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) @RequiresFlagsDisabled(Flags.FLAG_CREATE_WINDOWLESS_WINDOW_MAGNIFIER) public class WindowMagnificationControllerTest extends SysuiTestCase { @@ -1511,4 +1511,4 @@ public class WindowMagnificationControllerTest extends SysuiTestCase { }); } } -}
\ No newline at end of file +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerWindowlessMagnifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerWindowlessMagnifierTest.java index 01e4d58b68c2..e27268292763 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerWindowlessMagnifierTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerWindowlessMagnifierTest.java @@ -71,7 +71,6 @@ import android.platform.test.annotations.RequiresFlagsEnabled; import android.platform.test.flag.junit.CheckFlagsRule; import android.platform.test.flag.junit.DeviceFlagsValueProvider; import android.provider.Settings; -import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.testing.TestableResources; import android.text.TextUtils; @@ -94,6 +93,7 @@ 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.systemui.Flags; @@ -129,7 +129,7 @@ import java.util.function.Supplier; @LargeTest @TestableLooper.RunWithLooper -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) @RequiresFlagsEnabled(Flags.FLAG_CREATE_WINDOWLESS_WINDOW_MAGNIFIER) public class WindowMagnificationControllerWindowlessMagnifierTest extends SysuiTestCase { diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationFrameSizePrefsTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationFrameSizePrefsTest.java index 93c0eeaac488..ad9053a7edda 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationFrameSizePrefsTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationFrameSizePrefsTest.java @@ -23,10 +23,10 @@ import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.when; -import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.util.Size; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; @@ -37,7 +37,7 @@ import org.junit.Test; import org.junit.runner.RunWith; @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) @TestableLooper.RunWithLooper public class WindowMagnificationFrameSizePrefsTest extends SysuiTestCase { diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationSettingsTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationSettingsTest.java index 138fed298ef1..003f7e4479ba 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationSettingsTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationSettingsTest.java @@ -47,7 +47,6 @@ import android.graphics.Insets; import android.graphics.Rect; import android.os.UserHandle; import android.provider.Settings; -import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.view.View; import android.view.ViewGroup; @@ -59,6 +58,7 @@ import android.widget.CompoundButton; import android.widget.LinearLayout; import androidx.test.InstrumentationRegistry; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.internal.graphics.SfVsyncFrameCallbackProvider; @@ -77,7 +77,7 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) @TestableLooper.RunWithLooper public class WindowMagnificationSettingsTest extends SysuiTestCase { diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/data/repository/AccessibilityRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/accessibility/data/repository/AccessibilityRepositoryTest.kt index aff52f511171..c4a92bf18283 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/data/repository/AccessibilityRepositoryTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/data/repository/AccessibilityRepositoryTest.kt @@ -18,8 +18,8 @@ package com.android.systemui.accessibility.data.repository -import android.testing.AndroidTestingRunner import android.view.accessibility.AccessibilityManager +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.collectLastValue @@ -37,7 +37,7 @@ import org.mockito.Mockito.verify import org.mockito.junit.MockitoJUnit import org.mockito.junit.MockitoRule -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) @SmallTest class AccessibilityRepositoryTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuControllerTest.java index 095c945ba77b..b71739ac6434 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuControllerTest.java @@ -30,11 +30,11 @@ import android.content.ContextWrapper; import android.hardware.display.DisplayManager; import android.os.UserHandle; import android.provider.Settings; -import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.view.WindowManager; import android.view.accessibility.AccessibilityManager; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.keyguard.KeyguardUpdateMonitor; @@ -59,7 +59,7 @@ import org.mockito.junit.MockitoJUnit; import org.mockito.junit.MockitoRule; /** Test for {@link AccessibilityFloatingMenuController}. */ -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) @TestableLooper.RunWithLooper(setAsMainLooper = true) @SmallTest public class AccessibilityFloatingMenuControllerTest extends SysuiTestCase { diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityTargetAdapterTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityTargetAdapterTest.java index 630db629bf76..b08f97a646b9 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityTargetAdapterTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityTargetAdapterTest.java @@ -21,10 +21,10 @@ import static com.google.common.truth.Truth.assertThat; import static org.mockito.Mockito.when; import android.graphics.drawable.Drawable; -import android.testing.AndroidTestingRunner; import android.view.LayoutInflater; import android.view.View; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.internal.accessibility.dialog.AccessibilityTarget; @@ -43,7 +43,7 @@ import java.util.List; /** Tests for {@link AccessibilityTargetAdapter}. */ @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) public class AccessibilityTargetAdapterTest extends SysuiTestCase { @Mock private AccessibilityTarget mAccessibilityTarget; diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AnnotationLinkSpanTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AnnotationLinkSpanTest.java index 4b8758889fd4..5b2afe7443dd 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AnnotationLinkSpanTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AnnotationLinkSpanTest.java @@ -20,10 +20,10 @@ import static com.google.common.truth.Truth.assertThat; import static org.mockito.Mockito.mock; -import android.testing.AndroidTestingRunner; import android.text.SpannableStringBuilder; import android.view.View; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; @@ -37,7 +37,7 @@ import java.util.concurrent.atomic.AtomicBoolean; /** Tests for {@link AnnotationLinkSpan}. */ @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) public class AnnotationLinkSpanTest extends SysuiTestCase { private AnnotationLinkSpan.LinkInfo mLinkInfo; diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/DragToInteractAnimationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/DragToInteractAnimationControllerTest.java index abc95bc23f87..19b27003ebd1 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/DragToInteractAnimationControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/DragToInteractAnimationControllerTest.java @@ -22,11 +22,11 @@ import static org.mockito.Mockito.verify; import android.annotation.NonNull; import android.platform.test.annotations.DisableFlags; import android.platform.test.annotations.EnableFlags; -import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.view.WindowManager; import android.view.accessibility.AccessibilityManager; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.systemui.Flags; @@ -46,7 +46,7 @@ import org.mockito.junit.MockitoRule; /** Tests for {@link DragToInteractAnimationController}. */ @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) @TestableLooper.RunWithLooper public class DragToInteractAnimationControllerTest extends SysuiTestCase { private DragToInteractAnimationController mDragToInteractAnimationController; diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuEduTooltipViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuEduTooltipViewTest.java index 34a2e8719f0b..b59773700ed5 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuEduTooltipViewTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuEduTooltipViewTest.java @@ -19,11 +19,11 @@ package com.android.systemui.accessibility.floatingmenu; import static com.google.common.truth.Truth.assertThat; import android.content.res.Resources; -import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.view.WindowManager; import android.widget.TextView; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; @@ -35,7 +35,7 @@ import org.junit.runner.RunWith; /** Tests for {@link MenuEduTooltipView}. */ @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) @TestableLooper.RunWithLooper public class MenuEduTooltipViewTest extends SysuiTestCase { private MenuViewAppearance mMenuViewAppearance; diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuInfoRepositoryTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuInfoRepositoryTest.java index 1faa8aca74c7..24f3a29e64ee 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuInfoRepositoryTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuInfoRepositoryTest.java @@ -25,9 +25,9 @@ import static org.mockito.Mockito.verify; import android.content.Context; import android.content.res.Configuration; -import android.testing.AndroidTestingRunner; import android.view.accessibility.AccessibilityManager; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; @@ -47,7 +47,7 @@ import java.util.List; import java.util.Locale; /** Tests for {@link MenuInfoRepository}. */ -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) @SmallTest public class MenuInfoRepositoryTest extends SysuiTestCase { @Rule diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuItemAccessibilityDelegateTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuItemAccessibilityDelegateTest.java index 1f7d0336c590..c5509ac44046 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuItemAccessibilityDelegateTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuItemAccessibilityDelegateTest.java @@ -30,7 +30,6 @@ import static org.mockito.Mockito.verify; import android.graphics.Rect; import android.platform.test.annotations.DisableFlags; import android.platform.test.annotations.EnableFlags; -import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.view.WindowManager; import android.view.accessibility.AccessibilityManager; @@ -39,6 +38,7 @@ import android.view.accessibility.AccessibilityNodeInfo; import androidx.core.view.accessibility.AccessibilityNodeInfoCompat; import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerViewAccessibilityDelegate; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.systemui.Flags; @@ -60,7 +60,7 @@ import java.util.concurrent.atomic.AtomicBoolean; /** Tests for {@link MenuItemAccessibilityDelegate}. */ @SmallTest @TestableLooper.RunWithLooper -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) public class MenuItemAccessibilityDelegateTest extends SysuiTestCase { @Rule public MockitoRule mockito = MockitoJUnit.rule(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuListViewTouchHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuListViewTouchHandlerTest.java index 9e8c6b3395e2..4373c880d999 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuListViewTouchHandlerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuListViewTouchHandlerTest.java @@ -30,7 +30,6 @@ import static org.mockito.Mockito.verify; import android.platform.test.annotations.DisableFlags; import android.platform.test.annotations.EnableFlags; -import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.view.MotionEvent; import android.view.WindowManager; @@ -38,6 +37,7 @@ import android.view.accessibility.AccessibilityManager; import androidx.dynamicanimation.animation.DynamicAnimation; import androidx.recyclerview.widget.RecyclerView; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.internal.accessibility.dialog.AccessibilityTarget; @@ -62,7 +62,7 @@ import java.util.Collections; import java.util.List; /** Tests for {@link MenuListViewTouchHandler}. */ -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) @TestableLooper.RunWithLooper(setAsMainLooper = true) @SmallTest public class MenuListViewTouchHandlerTest extends SysuiTestCase { diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuNotificationFactoryTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuNotificationFactoryTest.java index 9dd337e43b6a..2746fef42704 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuNotificationFactoryTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuNotificationFactoryTest.java @@ -19,8 +19,8 @@ package com.android.systemui.accessibility.floatingmenu; import static com.google.common.truth.Truth.assertThat; import android.app.Notification; -import android.testing.AndroidTestingRunner; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; @@ -29,7 +29,7 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) @SmallTest public class MenuNotificationFactoryTest extends SysuiTestCase { private MenuNotificationFactory mMenuNotificationFactory; diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerControllerTest.java index 31824ecaa432..bd1a7f072832 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerControllerTest.java @@ -27,7 +27,6 @@ import static org.mockito.Mockito.when; import android.content.Context; import android.graphics.Insets; import android.graphics.Rect; -import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.view.View; import android.view.ViewGroup; @@ -36,6 +35,7 @@ import android.view.WindowManager; import android.view.WindowMetrics; import android.view.accessibility.AccessibilityManager; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; @@ -50,7 +50,7 @@ import org.mockito.junit.MockitoJUnit; import org.mockito.junit.MockitoRule; /** Tests for {@link MenuViewLayerController}. */ -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) @TestableLooper.RunWithLooper(setAsMainLooper = true) @SmallTest public class MenuViewLayerControllerTest extends SysuiTestCase { diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerTest.java index 05d75606fd10..38095c83367d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerTest.java @@ -61,7 +61,6 @@ import android.os.UserHandle; import android.platform.test.annotations.DisableFlags; import android.platform.test.annotations.EnableFlags; import android.provider.Settings; -import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.util.ArraySet; import android.view.View; @@ -72,6 +71,7 @@ import android.view.accessibility.AccessibilityManager; import androidx.dynamicanimation.animation.DynamicAnimation; import androidx.dynamicanimation.animation.SpringAnimation; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.internal.accessibility.common.ShortcutConstants; @@ -101,7 +101,7 @@ import java.util.ArrayList; import java.util.List; /** Tests for {@link MenuViewLayer}. */ -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) @TestableLooper.RunWithLooper(setAsMainLooper = true) @SmallTest public class MenuViewLayerTest extends SysuiTestCase { diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewTest.java index f6288b633ac8..103449b6b0f7 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewTest.java @@ -29,11 +29,11 @@ import android.app.UiModeManager; import android.graphics.Rect; import android.graphics.drawable.GradientDrawable; import android.platform.test.annotations.EnableFlags; -import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.view.WindowManager; import android.view.accessibility.AccessibilityManager; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.systemui.Flags; @@ -53,7 +53,7 @@ import org.mockito.junit.MockitoJUnit; import org.mockito.junit.MockitoRule; /** Tests for {@link MenuView}. */ -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) @TestableLooper.RunWithLooper(setAsMainLooper = true) @SmallTest public class MenuViewTest extends SysuiTestCase { diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/PositionTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/PositionTest.java index 05f306b1e8b3..8fb71faf71a9 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/PositionTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/PositionTest.java @@ -18,8 +18,8 @@ package com.android.systemui.accessibility.floatingmenu; import static com.google.common.truth.Truth.assertThat; -import android.testing.AndroidTestingRunner; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; @@ -29,7 +29,7 @@ import org.junit.runner.RunWith; /** Tests for {@link Position}. */ @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) public class PositionTest extends SysuiTestCase { @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/RadiiAnimatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/RadiiAnimatorTest.java index d77a80a3318d..f67e8d068203 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/RadiiAnimatorTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/RadiiAnimatorTest.java @@ -20,8 +20,8 @@ import static com.google.common.truth.Truth.assertThat; import android.os.Handler; import android.os.Looper; -import android.testing.AndroidTestingRunner; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; @@ -35,7 +35,7 @@ import java.util.concurrent.atomic.AtomicBoolean; /** Tests for {@link RadiiAnimator}. */ @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) public class RadiiAnimatorTest extends SysuiTestCase { float[] mResultRadii = new float[RadiiAnimator.RADII_COUNT]; final AtomicBoolean mAnimationStarted = new AtomicBoolean(false); diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/fontscaling/FontScalingDialogDelegateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/accessibility/fontscaling/FontScalingDialogDelegateTest.kt index e371b39faab4..0bd00fb0a0e9 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/fontscaling/FontScalingDialogDelegateTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/fontscaling/FontScalingDialogDelegateTest.kt @@ -18,12 +18,12 @@ package com.android.systemui.accessibility.fontscaling import android.content.res.Configuration import android.os.Handler import android.provider.Settings -import android.testing.AndroidTestingRunner import android.testing.TestableLooper import android.view.LayoutInflater import android.view.ViewGroup import android.widget.Button import android.widget.SeekBar +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.animation.DialogTransitionAnimator @@ -58,7 +58,7 @@ private const val OFF: Int = 0 /** Tests for [FontScalingDialogDelegate]. */ @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) @TestableLooper.RunWithLooper(setAsMainLooper = true) class FontScalingDialogDelegateTest : SysuiTestCase() { private lateinit var fontScalingDialogDelegate: FontScalingDialogDelegate diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesCheckerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesCheckerTest.java new file mode 100644 index 000000000000..51f6cdb2cb89 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesCheckerTest.java @@ -0,0 +1,116 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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.hearingaid; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.Mockito.when; + +import android.bluetooth.BluetoothDevice; +import android.bluetooth.BluetoothProfile; +import android.testing.AndroidTestingRunner; +import android.testing.TestableLooper; + +import androidx.test.filters.SmallTest; + +import com.android.settingslib.bluetooth.CachedBluetoothDevice; +import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager; +import com.android.settingslib.bluetooth.LocalBluetoothAdapter; +import com.android.settingslib.bluetooth.LocalBluetoothManager; +import com.android.systemui.SysuiTestCase; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; + +import java.util.ArrayList; +import java.util.List; + +@RunWith(AndroidTestingRunner.class) +@TestableLooper.RunWithLooper(setAsMainLooper = true) +@SmallTest +public class HearingDevicesCheckerTest extends SysuiTestCase { + @Rule + public MockitoRule mockito = MockitoJUnit.rule(); + + private final List<CachedBluetoothDevice> mCachedDevices = new ArrayList<>(); + @Mock + private LocalBluetoothManager mLocalBluetoothManager; + @Mock + private LocalBluetoothAdapter mLocalBluetoothAdapter; + @Mock + private CachedBluetoothDeviceManager mCachedBluetoothDeviceManager; + @Mock + private CachedBluetoothDevice mCachedDevice; + @Mock + private BluetoothDevice mDevice; + private HearingDevicesChecker mDevicesChecker; + + @Before + public void setUp() { + when(mLocalBluetoothManager.getBluetoothAdapter()).thenReturn(mLocalBluetoothAdapter); + when(mLocalBluetoothManager.getCachedDeviceManager()).thenReturn( + mCachedBluetoothDeviceManager); + when(mCachedBluetoothDeviceManager.getCachedDevicesCopy()).thenReturn(mCachedDevices); + when(mCachedDevice.getDevice()).thenReturn(mDevice); + when(mDevice.getMetadata(BluetoothDevice.METADATA_EXCLUSIVE_MANAGER)).thenReturn( + null); + + mDevicesChecker = new HearingDevicesChecker(mContext, mLocalBluetoothManager); + } + + @Test + public void isAnyPairedHearingDevice_bluetoothDisable_returnFalse() { + when(mLocalBluetoothAdapter.isEnabled()).thenReturn(false); + + assertThat(mDevicesChecker.isAnyPairedHearingDevice()).isFalse(); + } + + @Test + public void isAnyActiveHearingDevice_bluetoothDisable_returnFalse() { + when(mLocalBluetoothAdapter.isEnabled()).thenReturn(false); + + assertThat(mDevicesChecker.isAnyActiveHearingDevice()).isFalse(); + } + + @Test + public void isAnyPairedHearingDevice_hearingAidBonded_returnTrue() { + when(mLocalBluetoothAdapter.isEnabled()).thenReturn(true); + when(mCachedDevice.isHearingAidDevice()).thenReturn(true); + when(mCachedDevice.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED); + mCachedDevices.add(mCachedDevice); + + assertThat(mDevicesChecker.isAnyPairedHearingDevice()).isTrue(); + } + + @Test + public void isAnyActiveHearingDevice_hearingAidActiveAndConnected_returnTrue() { + when(mLocalBluetoothAdapter.isEnabled()).thenReturn(true); + when(mCachedDevice.isActiveDevice(BluetoothProfile.HEARING_AID)).thenReturn(true); + when(mCachedDevice.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED); + when(mDevice.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED); + when(mDevice.isConnected()).thenReturn(true); + when(mCachedDevice.isConnectedHearingAidDevice()).thenReturn(true); + mCachedDevices.add(mCachedDevice); + + assertThat(mDevicesChecker.isAnyActiveHearingDevice()).isTrue(); + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegateTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegateTest.java index 0db0de2bcd7e..8f7dc7cf109b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegateTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegateTest.java @@ -39,11 +39,11 @@ import android.media.AudioManager; import android.os.Handler; import android.platform.test.annotations.EnableFlags; import android.provider.Settings; -import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.view.View; import android.widget.LinearLayout; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.settingslib.bluetooth.BluetoothEventManager; @@ -78,7 +78,7 @@ import java.util.ArrayList; import java.util.List; /** Tests for {@link HearingDevicesDialogDelegate}. */ -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) @TestableLooper.RunWithLooper(setAsMainLooper = true) @SmallTest public class HearingDevicesDialogDelegateTest extends SysuiTestCase { diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogManagerTest.java index e9c742d63d81..09aa286874b9 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogManagerTest.java @@ -22,19 +22,17 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.bluetooth.BluetoothDevice; -import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; -import com.android.settingslib.bluetooth.CachedBluetoothDevice; -import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager; -import com.android.settingslib.bluetooth.LocalBluetoothAdapter; -import com.android.settingslib.bluetooth.LocalBluetoothManager; import com.android.systemui.SysuiTestCase; import com.android.systemui.animation.DialogTransitionAnimator; import com.android.systemui.animation.Expandable; import com.android.systemui.statusbar.phone.SystemUIDialog; +import com.android.systemui.util.concurrency.FakeExecutor; +import com.android.systemui.util.time.FakeSystemClock; import org.junit.Before; import org.junit.Rule; @@ -44,11 +42,8 @@ import org.mockito.Mock; import org.mockito.junit.MockitoJUnit; import org.mockito.junit.MockitoRule; -import java.util.ArrayList; -import java.util.List; - /** Tests for {@link HearingDevicesDialogManager}. */ -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) @TestableLooper.RunWithLooper(setAsMainLooper = true) @SmallTest public class HearingDevicesDialogManagerTest extends SysuiTestCase { @@ -56,7 +51,8 @@ public class HearingDevicesDialogManagerTest extends SysuiTestCase { @Rule public MockitoRule mockito = MockitoJUnit.rule(); - private final List<CachedBluetoothDevice> mCachedDevices = new ArrayList<>(); + private final FakeExecutor mMainExecutor = new FakeExecutor(new FakeSystemClock()); + private final FakeExecutor mBackgroundExecutor = new FakeExecutor(new FakeSystemClock()); @Mock private Expandable mExpandable; @Mock @@ -68,13 +64,7 @@ public class HearingDevicesDialogManagerTest extends SysuiTestCase { @Mock private SystemUIDialog mDialog; @Mock - private LocalBluetoothManager mLocalBluetoothManager; - @Mock - private LocalBluetoothAdapter mLocalBluetoothAdapter; - @Mock - private CachedBluetoothDeviceManager mCachedBluetoothDeviceManager; - @Mock - private CachedBluetoothDevice mCachedDevice; + private HearingDevicesChecker mDevicesChecker; private HearingDevicesDialogManager mManager; @@ -82,36 +72,35 @@ public class HearingDevicesDialogManagerTest extends SysuiTestCase { public void setUp() { when(mDialogFactory.create(anyBoolean())).thenReturn(mDialogDelegate); when(mDialogDelegate.createDialog()).thenReturn(mDialog); - when(mLocalBluetoothManager.getBluetoothAdapter()).thenReturn(mLocalBluetoothAdapter); - when(mLocalBluetoothManager.getCachedDeviceManager()).thenReturn( - mCachedBluetoothDeviceManager); - when(mCachedBluetoothDeviceManager.getCachedDevicesCopy()).thenReturn(mCachedDevices); mManager = new HearingDevicesDialogManager( mDialogTransitionAnimator, mDialogFactory, - mLocalBluetoothManager + mDevicesChecker, + mBackgroundExecutor, + mMainExecutor ); } @Test - public void showDialog_bluetoothDisable_showPairNewDeviceTrue() { - when(mLocalBluetoothAdapter.isEnabled()).thenReturn(false); + public void showDialog_existHearingDevice_showPairNewDeviceFalse() { + when(mDevicesChecker.isAnyPairedHearingDevice()).thenReturn(true); mManager.showDialog(mExpandable); + mBackgroundExecutor.runAllReady(); + mMainExecutor.runAllReady(); - verify(mDialogFactory).create(eq(true)); + verify(mDialogFactory).create(eq(/* showPairNewDevice= */ false)); } @Test - public void showDialog_containsHearingAid_showPairNewDeviceFalse() { - when(mLocalBluetoothAdapter.isEnabled()).thenReturn(true); - when(mCachedDevice.isHearingAidDevice()).thenReturn(true); - when(mCachedDevice.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED); - mCachedDevices.add(mCachedDevice); + public void showDialog_noHearingDevice_showPairNewDeviceTrue() { + when(mDevicesChecker.isAnyPairedHearingDevice()).thenReturn(false); mManager.showDialog(mExpandable); + mBackgroundExecutor.runAllReady(); + mMainExecutor.runAllReady(); - verify(mDialogFactory).create(eq(false)); + verify(mDialogFactory).create(eq(/* showPairNewDevice= */ true)); } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesListAdapterTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesListAdapterTest.java index d16db65334d3..9359adf96f80 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesListAdapterTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesListAdapterTest.java @@ -20,9 +20,9 @@ import static com.google.common.truth.Truth.assertThat; import static org.mockito.Mockito.when; -import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.settingslib.bluetooth.CachedBluetoothDevice; @@ -41,7 +41,7 @@ import java.util.ArrayList; import java.util.List; /** Tests for {@link HearingDevicesListAdapter}. */ -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) @TestableLooper.RunWithLooper(setAsMainLooper = true) @SmallTest public class HearingDevicesListAdapterTest extends SysuiTestCase { diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesToolItemParserTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesToolItemParserTest.java index 717292378913..17ce1ddee87a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesToolItemParserTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesToolItemParserTest.java @@ -28,9 +28,9 @@ import android.content.ComponentName; import android.content.pm.ActivityInfo; import android.content.pm.PackageManager; import android.graphics.drawable.Drawable; -import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; @@ -48,7 +48,7 @@ import java.util.List; /** * Tests for {@link HearingDevicesToolItemParser}. */ -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) @TestableLooper.RunWithLooper(setAsMainLooper = true) @SmallTest public class HearingDevicesToolItemParserTest extends SysuiTestCase { diff --git a/packages/SystemUI/tests/src/com/android/systemui/ambient/touch/InputSessionTest.java b/packages/SystemUI/tests/src/com/android/systemui/ambient/touch/InputSessionTest.java index 2f4999b1b326..8fca557c7832 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/ambient/touch/InputSessionTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/ambient/touch/InputSessionTest.java @@ -24,13 +24,13 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.platform.test.annotations.EnableFlags; -import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.view.Choreographer; import android.view.GestureDetector; import android.view.InputEvent; import android.view.MotionEvent; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.systemui.Flags; @@ -50,7 +50,7 @@ import org.mockito.MockitoAnnotations; * A test suite for exercising {@link InputSession}. */ @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) @TestableLooper.RunWithLooper() public class InputSessionTest extends SysuiTestCase { @Mock diff --git a/packages/SystemUI/tests/src/com/android/systemui/ambient/touch/TouchMonitorTest.java b/packages/SystemUI/tests/src/com/android/systemui/ambient/touch/TouchMonitorTest.java index 358e8cbd4a3c..4118c90a80a0 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/ambient/touch/TouchMonitorTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/ambient/touch/TouchMonitorTest.java @@ -36,7 +36,6 @@ import android.graphics.Region; import android.hardware.display.DisplayManager; import android.platform.test.annotations.DisableFlags; import android.platform.test.annotations.EnableFlags; -import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.view.GestureDetector; import android.view.IWindowManager; @@ -50,6 +49,7 @@ import androidx.lifecycle.Lifecycle; import androidx.lifecycle.LifecycleObserver; import androidx.lifecycle.LifecycleOwner; import androidx.lifecycle.LifecycleRegistry; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.systemui.Flags; @@ -79,7 +79,7 @@ import java.util.stream.Collectors; import java.util.stream.Stream; @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) @TestableLooper.RunWithLooper(setAsMainLooper = true) public class TouchMonitorTest extends SysuiTestCase { private KosmosJavaAdapter mKosmos; diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityTransitionAnimatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityTransitionAnimatorTest.kt index 70a544ca6d2c..9aaf2958031a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityTransitionAnimatorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityTransitionAnimatorTest.kt @@ -9,7 +9,6 @@ import android.graphics.Point import android.graphics.Rect import android.os.Looper import android.platform.test.flag.junit.SetFlagsRule -import android.testing.AndroidTestingRunner import android.testing.TestableLooper.RunWithLooper import android.view.IRemoteAnimationFinishedCallback import android.view.RemoteAnimationAdapter @@ -20,6 +19,7 @@ import android.widget.FrameLayout import android.widget.LinearLayout import android.window.RemoteTransition import android.window.TransitionFilter +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.shared.Flags @@ -49,7 +49,7 @@ import org.mockito.Spy import org.mockito.junit.MockitoJUnit @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) @RunWithLooper class ActivityTransitionAnimatorTest : SysuiTestCase() { private val transitionContainer = LinearLayout(mContext) diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/AnimatorTestRuleOrderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/AnimatorTestRuleOrderTest.kt index e3be3822fb94..37f549a0c675 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/animation/AnimatorTestRuleOrderTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/animation/AnimatorTestRuleOrderTest.kt @@ -16,9 +16,9 @@ package com.android.systemui.animation -import android.testing.AndroidTestingRunner import android.testing.TestableLooper.RunWithLooper import androidx.core.animation.doOnEnd +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.FlakyTest import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase @@ -28,7 +28,7 @@ import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) @SmallTest @RunWithLooper @FlakyTest(bugId = 302149604) diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/DialogTransitionAnimatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/DialogTransitionAnimatorTest.kt index e14762cd8792..a60fb764215e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/animation/DialogTransitionAnimatorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/animation/DialogTransitionAnimatorTest.kt @@ -5,7 +5,6 @@ import android.content.Context import android.graphics.Color import android.graphics.drawable.ColorDrawable import android.os.Bundle -import android.testing.AndroidTestingRunner import android.testing.TestableLooper import android.testing.ViewUtils import android.view.View @@ -14,6 +13,7 @@ import android.view.ViewGroup.LayoutParams.MATCH_PARENT import android.view.WindowManager import android.widget.FrameLayout import android.widget.LinearLayout +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.internal.jank.Cuj import com.android.internal.policy.DecorView @@ -39,7 +39,7 @@ import org.mockito.junit.MockitoJUnit import org.mockito.junit.MockitoRule @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) @TestableLooper.RunWithLooper class DialogTransitionAnimatorTest : SysuiTestCase() { private val kosmos = testKosmos() diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/FontInterpolatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/FontInterpolatorTest.kt index 5e1a8e1432dd..ec42b7fc4713 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/animation/FontInterpolatorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/animation/FontInterpolatorTest.kt @@ -20,14 +20,14 @@ import android.graphics.Paint import android.graphics.fonts.Font import android.graphics.fonts.FontVariationAxis import android.graphics.text.TextRunShaper -import android.testing.AndroidTestingRunner +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.google.common.truth.Truth.assertThat import org.junit.Test import org.junit.runner.RunWith -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) @SmallTest class FontInterpolatorTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/FontVariationUtilsTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/FontVariationUtilsTest.kt index 070cad7302b2..b0f81c012cca 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/animation/FontVariationUtilsTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/animation/FontVariationUtilsTest.kt @@ -1,6 +1,6 @@ package com.android.systemui.animation -import android.testing.AndroidTestingRunner +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import junit.framework.Assert @@ -12,7 +12,7 @@ private const val TAG_WDTH = "wdth" private const val TAG_OPSZ = "opsz" private const val TAG_ROND = "ROND" -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) @SmallTest class FontVariationUtilsTest : SysuiTestCase() { @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/GhostedViewTransitionAnimatorControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/GhostedViewTransitionAnimatorControllerTest.kt index 42fcd547408a..e492c63d095c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/animation/GhostedViewTransitionAnimatorControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/animation/GhostedViewTransitionAnimatorControllerTest.kt @@ -17,10 +17,10 @@ package com.android.systemui.animation import android.os.HandlerThread -import android.testing.AndroidTestingRunner import android.testing.TestableLooper import android.view.View import android.widget.FrameLayout +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.internal.jank.InteractionJankMonitor import com.android.systemui.SysuiTestCase @@ -31,7 +31,7 @@ import org.junit.Test import org.junit.runner.RunWith @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) @TestableLooper.RunWithLooper class GhostedViewTransitionAnimatorControllerTest : SysuiTestCase() { companion object { diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/TextAnimatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/TextAnimatorTest.kt index 263d3750c657..6ba171525bd1 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/animation/TextAnimatorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/animation/TextAnimatorTest.kt @@ -19,10 +19,10 @@ package com.android.systemui.animation import android.animation.AnimatorListenerAdapter import android.animation.ValueAnimator import android.graphics.Typeface -import android.testing.AndroidTestingRunner import android.text.Layout import android.text.StaticLayout import android.text.TextPaint +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.google.common.truth.Truth.assertThat @@ -38,7 +38,7 @@ import org.mockito.Mockito.times import org.mockito.Mockito.verify import org.mockito.Mockito.`when` -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) @SmallTest class TextAnimatorTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/TextInterpolatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/TextInterpolatorTest.kt index f6fcd16cfd00..cca5f3525ef6 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/animation/TextInterpolatorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/animation/TextInterpolatorTest.kt @@ -22,12 +22,12 @@ import android.graphics.Color import android.graphics.Typeface import android.graphics.fonts.Font import android.graphics.fonts.FontFamily -import android.testing.AndroidTestingRunner import android.text.Layout import android.text.StaticLayout import android.text.TextDirectionHeuristic import android.text.TextDirectionHeuristics import android.text.TextPaint +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.google.common.truth.Truth.assertThat @@ -62,7 +62,7 @@ private val END_PAINT = TextPaint(PAINT).apply { typeface = Font.Builder(VF_FONT).setFontVariationSettings("'wght' 700").build().toTypeface() } -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) @SmallTest class TextInterpolatorTest : SysuiTestCase() { lateinit var typefaceCache: TypefaceVariantCache @@ -330,4 +330,4 @@ private fun Layout.toBitmap(width: Int, height: Int) = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888).also { draw(Canvas(it)) }!! private fun TextInterpolator.toBitmap(width: Int, height: Int) = - Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888).also { draw(Canvas(it)) }
\ No newline at end of file + Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888).also { draw(Canvas(it)) } diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/ViewHierarchyAnimatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/ViewHierarchyAnimatorTest.kt index c2e6db362035..a8c3af9488f0 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/animation/ViewHierarchyAnimatorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/animation/ViewHierarchyAnimatorTest.kt @@ -1,12 +1,12 @@ package com.android.systemui.animation import android.animation.ObjectAnimator -import android.testing.AndroidTestingRunner import android.testing.TestableLooper import android.view.View import android.view.ViewGroup import android.widget.LinearLayout import android.widget.RelativeLayout +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.app.animation.Interpolators import com.android.systemui.SysuiTestCase @@ -22,7 +22,7 @@ import org.junit.Test import org.junit.runner.RunWith @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) @TestableLooper.RunWithLooper class ViewHierarchyAnimatorTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/back/BackAnimationSpecTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/back/BackAnimationSpecTest.kt index 0ed84ea2d183..4809d0e4838f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/animation/back/BackAnimationSpecTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/animation/back/BackAnimationSpecTest.kt @@ -2,6 +2,7 @@ package com.android.systemui.animation.back import android.util.DisplayMetrics import android.window.BackEvent +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.util.dpToPx @@ -9,12 +10,11 @@ import com.google.common.truth.Truth.assertThat import junit.framework.TestCase.assertEquals import org.junit.Test import org.junit.runner.RunWith -import org.junit.runners.JUnit4 private data class BackInput(val progressX: Float, val progressY: Float, val edge: Int) @SmallTest -@RunWith(JUnit4::class) +@RunWith(AndroidJUnit4::class) class BackAnimationSpecTest : SysuiTestCase() { private var displayMetrics = DisplayMetrics().apply { diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/back/BackTransformationTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/back/BackTransformationTest.kt index 44a546704953..d898d1cc0f8e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/animation/back/BackTransformationTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/animation/back/BackTransformationTest.kt @@ -1,6 +1,7 @@ package com.android.systemui.animation.back import android.view.View +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.util.mockito.mock @@ -8,13 +9,12 @@ import com.google.common.truth.Truth.assertThat import org.junit.Before import org.junit.Test import org.junit.runner.RunWith -import org.junit.runners.JUnit4 import org.mockito.Mockito.verify import org.mockito.Mockito.verifyNoMoreInteractions import org.mockito.kotlin.whenever @SmallTest -@RunWith(JUnit4::class) +@RunWith(AndroidJUnit4::class) class BackTransformationTest : SysuiTestCase() { private val targetView: View = mock() diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/back/OnBackAnimationCallbackExtensionTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/back/OnBackAnimationCallbackExtensionTest.kt index 314abda66401..9548e297e7c5 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/animation/back/OnBackAnimationCallbackExtensionTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/animation/back/OnBackAnimationCallbackExtensionTest.kt @@ -2,6 +2,7 @@ package com.android.systemui.animation.back import android.util.DisplayMetrics import android.window.BackEvent +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.util.mockito.argumentCaptor @@ -10,11 +11,10 @@ import com.android.systemui.util.mockito.mock import com.google.common.truth.Truth.assertThat import org.junit.Test import org.junit.runner.RunWith -import org.junit.runners.JUnit4 import org.mockito.Mockito.verify @SmallTest -@RunWith(JUnit4::class) +@RunWith(AndroidJUnit4::class) class OnBackAnimationCallbackExtensionTest : SysuiTestCase() { private val onBackProgress: (BackTransformation) -> Unit = mock() private val onBackStart: (BackEvent) -> Unit = mock() diff --git a/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java index 0d464cfd71f7..476d6e373df3 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java @@ -49,9 +49,9 @@ import android.media.AudioManager; import android.media.AudioRecordingConfiguration; import android.os.Looper; import android.os.UserHandle; -import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.internal.R; @@ -75,7 +75,7 @@ import java.util.List; import java.util.Map; @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) @TestableLooper.RunWithLooper public class AppOpsControllerTest extends SysuiTestCase { private static final String TEST_PACKAGE_NAME = "test"; diff --git a/packages/SystemUI/tests/src/com/android/systemui/assist/ui/DisplayUtilsTest.java b/packages/SystemUI/tests/src/com/android/systemui/assist/ui/DisplayUtilsTest.java index 4d582ab02fc4..828d36741aeb 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/assist/ui/DisplayUtilsTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/assist/ui/DisplayUtilsTest.java @@ -21,8 +21,8 @@ import static org.mockito.Mockito.when; import android.content.Context; import android.content.res.Resources; -import android.testing.AndroidTestingRunner; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; @@ -35,7 +35,7 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) public class DisplayUtilsTest extends SysuiTestCase { @Mock diff --git a/packages/SystemUI/tests/src/com/android/systemui/battery/BatteryMeterViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/battery/BatteryMeterViewTest.kt index 3c073d5e7a3b..2bd0976f30dc 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/battery/BatteryMeterViewTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/battery/BatteryMeterViewTest.kt @@ -17,9 +17,9 @@ package com.android.systemui.battery import android.platform.test.annotations.DisableFlags import android.platform.test.annotations.EnableFlags -import android.testing.AndroidTestingRunner import android.testing.TestableLooper.RunWithLooper import android.widget.ImageView +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.settingslib.flags.Flags.FLAG_NEW_STATUS_BAR_ICONS import com.android.systemui.res.R @@ -33,7 +33,7 @@ import org.junit.runner.RunWith import org.mockito.MockitoAnnotations @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) @RunWithLooper class BatteryMeterViewTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt index 7c03d7899398..6dc4b10a57da 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt @@ -19,9 +19,9 @@ package com.android.systemui.biometrics import android.graphics.Point import android.hardware.biometrics.BiometricSourceType import android.hardware.fingerprint.FingerprintSensorPropertiesInternal -import android.testing.AndroidTestingRunner import android.testing.TestableLooper.RunWithLooper import android.util.DisplayMetrics +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession import com.android.keyguard.KeyguardUpdateMonitor @@ -67,7 +67,7 @@ import javax.inject.Provider @ExperimentalCoroutinesApi @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) class AuthRippleControllerTest : SysuiTestCase() { private lateinit var staticMockSession: MockitoSession diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/BiometricNotificationDialogFactoryTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/BiometricNotificationDialogFactoryTest.java index d2c695739ea9..197cb843ba5f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/BiometricNotificationDialogFactoryTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/BiometricNotificationDialogFactoryTest.java @@ -33,9 +33,9 @@ import android.hardware.biometrics.BiometricSourceType; import android.hardware.face.FaceManager; import android.hardware.fingerprint.FingerprintManager; import android.provider.Settings; -import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; @@ -53,7 +53,7 @@ import org.mockito.junit.MockitoRule; import java.util.concurrent.ExecutionException; @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) @TestableLooper.RunWithLooper(setAsMainLooper = true) public class BiometricNotificationDialogFactoryTest extends SysuiTestCase { @Rule diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/BiometricNotificationServiceTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/BiometricNotificationServiceTest.java index a279d3ee67e4..20d9433effe2 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/BiometricNotificationServiceTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/BiometricNotificationServiceTest.java @@ -36,9 +36,9 @@ import android.hardware.face.FaceManager; import android.hardware.fingerprint.FingerprintManager; import android.os.Handler; import android.os.UserHandle; -import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.keyguard.KeyguardUpdateMonitor; @@ -58,7 +58,7 @@ import org.mockito.junit.MockitoRule; import java.util.Optional; @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) @TestableLooper.RunWithLooper(setAsMainLooper = true) public class BiometricNotificationServiceTest extends SysuiTestCase { @Rule diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsUtilsTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsUtilsTest.java index 5b6aee697fec..d26ccbcd12e0 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsUtilsTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsUtilsTest.java @@ -23,6 +23,7 @@ import android.graphics.Rect; import android.hardware.fingerprint.FingerprintSensorProperties; import android.view.Surface; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; @@ -33,11 +34,10 @@ import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; -import org.junit.runners.JUnit4; import org.mockito.junit.MockitoJUnit; import org.mockito.junit.MockitoRule; -@RunWith(JUnit4.class) +@RunWith(AndroidJUnit4.class) @SmallTest public class UdfpsUtilsTest extends SysuiTestCase { @Rule diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/BiometricStatusRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/BiometricStatusRepositoryTest.kt index b3e845f00005..d2150471744e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/BiometricStatusRepositoryTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/BiometricStatusRepositoryTest.kt @@ -29,6 +29,7 @@ import android.hardware.biometrics.BiometricSourceType import android.hardware.biometrics.events.AuthenticationAcquiredInfo import android.hardware.biometrics.events.AuthenticationStartedInfo import android.hardware.biometrics.events.AuthenticationStoppedInfo +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.biometrics.shared.model.AuthenticationReason @@ -46,7 +47,6 @@ import org.junit.Before import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith -import org.junit.runners.JUnit4 import org.mockito.Mock import org.mockito.Mockito.verify import org.mockito.junit.MockitoJUnit @@ -54,7 +54,7 @@ import org.mockito.junit.MockitoRule @OptIn(ExperimentalCoroutinesApi::class) @SmallTest -@RunWith(JUnit4::class) +@RunWith(AndroidJUnit4::class) class BiometricStatusRepositoryTest : SysuiTestCase() { @JvmField @Rule var mockitoRule: MockitoRule = MockitoJUnit.rule() @Mock private lateinit var biometricManager: BiometricManager diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/DisplayStateRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/DisplayStateRepositoryTest.kt index eae953e2031e..d9b71619992f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/DisplayStateRepositoryTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/DisplayStateRepositoryTest.kt @@ -21,6 +21,7 @@ import android.view.Display import android.view.Display.DEFAULT_DISPLAY import android.view.DisplayInfo import android.view.Surface +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.biometrics.data.repository.DisplayStateRepository @@ -42,12 +43,11 @@ import kotlinx.coroutines.test.runTest import org.junit.Before import org.junit.Test import org.junit.runner.RunWith -import org.junit.runners.JUnit4 import org.mockito.Mockito.spy @OptIn(ExperimentalCoroutinesApi::class) @SmallTest -@RunWith(JUnit4::class) +@RunWith(AndroidJUnit4::class) class DisplayStateRepositoryTest : SysuiTestCase() { private val display = mock<Display>() private val testScope = TestScope(StandardTestDispatcher()) diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/FacePropertyRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/FacePropertyRepositoryImplTest.kt index f5e96c93271a..9c114054bcfb 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/FacePropertyRepositoryImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/FacePropertyRepositoryImplTest.kt @@ -26,6 +26,7 @@ import android.hardware.camera2.CameraManager import android.hardware.face.FaceManager import android.hardware.face.FaceSensorPropertiesInternal import android.hardware.face.IFaceAuthenticatorsRegisteredCallback +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.biometrics.shared.model.LockoutMode @@ -46,7 +47,6 @@ import org.junit.Before import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith -import org.junit.runners.JUnit4 import org.mockito.ArgumentCaptor import org.mockito.Captor import org.mockito.Mock @@ -57,7 +57,7 @@ import org.mockito.junit.MockitoRule @OptIn(ExperimentalCoroutinesApi::class) @SmallTest -@RunWith(JUnit4::class) +@RunWith(AndroidJUnit4::class) class FacePropertyRepositoryImplTest : SysuiTestCase() { companion object { private const val LOGICAL_CAMERA_ID_OUTER_FRONT = "0" diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/FaceSettingsRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/FaceSettingsRepositoryImplTest.kt index 63919865d8ab..0209ab803368 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/FaceSettingsRepositoryImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/FaceSettingsRepositoryImplTest.kt @@ -19,6 +19,7 @@ package com.android.systemui.biometrics.data.repository import android.database.ContentObserver import android.os.Handler import android.provider.Settings.Secure.FACE_UNLOCK_ALWAYS_REQUIRE_CONFIRMATION +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.collectLastValue @@ -35,7 +36,6 @@ import org.junit.Before import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith -import org.junit.runners.JUnit4 import org.mockito.ArgumentMatchers.anyBoolean import org.mockito.ArgumentMatchers.anyInt import org.mockito.Mock @@ -47,7 +47,7 @@ private const val USER_ID = 8 @OptIn(ExperimentalCoroutinesApi::class) @SmallTest -@RunWith(JUnit4::class) +@RunWith(AndroidJUnit4::class) class FaceSettingsRepositoryImplTest : SysuiTestCase() { @JvmField @Rule var mockitoRule = MockitoJUnit.rule() diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/FingerprintRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/FingerprintRepositoryImplTest.kt index 7808c414de00..ff5a419faf35 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/FingerprintRepositoryImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/FingerprintRepositoryImplTest.kt @@ -23,6 +23,7 @@ import android.hardware.fingerprint.FingerprintManager import android.hardware.fingerprint.FingerprintSensorProperties import android.hardware.fingerprint.FingerprintSensorPropertiesInternal import android.hardware.fingerprint.IFingerprintAuthenticatorsRegisteredCallback +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.biometrics.shared.model.FingerprintSensorType @@ -38,7 +39,6 @@ import org.junit.Before import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith -import org.junit.runners.JUnit4 import org.mockito.ArgumentCaptor import org.mockito.Captor import org.mockito.Mock @@ -47,7 +47,7 @@ import org.mockito.junit.MockitoJUnit @OptIn(ExperimentalCoroutinesApi::class) @SmallTest -@RunWith(JUnit4::class) +@RunWith(AndroidJUnit4::class) class FingerprintRepositoryImplTest : SysuiTestCase() { @JvmField @Rule var mockitoRule = MockitoJUnit.rule() diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/PromptRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/PromptRepositoryImplTest.kt index 2682633f5dfd..22971bcf799e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/PromptRepositoryImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/PromptRepositoryImplTest.kt @@ -17,6 +17,7 @@ package com.android.systemui.biometrics.data.repository import android.hardware.biometrics.PromptInfo +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.biometrics.AuthController @@ -37,7 +38,6 @@ import org.junit.Before import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith -import org.junit.runners.JUnit4 import org.mockito.ArgumentMatchers.eq import org.mockito.Mock import org.mockito.Mockito.verify @@ -51,7 +51,7 @@ private const val OP_PACKAGE_NAME = "biometric.testapp" @OptIn(ExperimentalCoroutinesApi::class) @SmallTest -@RunWith(JUnit4::class) +@RunWith(AndroidJUnit4::class) class PromptRepositoryImplTest : SysuiTestCase() { @JvmField @Rule var mockitoRule = MockitoJUnit.rule() diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/BiometricStatusInteractorImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/BiometricStatusInteractorImplTest.kt index 4cff3e6bfd23..5d2d20ce88e9 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/BiometricStatusInteractorImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/BiometricStatusInteractorImplTest.kt @@ -19,6 +19,7 @@ package com.android.systemui.biometrics.domain.interactor import android.app.ActivityManager import android.content.ComponentName import android.hardware.biometrics.BiometricFingerprintConstants +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.app.activityTaskManager import com.android.systemui.SysuiTestCase @@ -37,13 +38,12 @@ import kotlinx.coroutines.test.runTest import org.junit.Before import org.junit.Test import org.junit.runner.RunWith -import org.junit.runners.JUnit4 import org.mockito.Mockito import org.mockito.Mockito.`when` @OptIn(ExperimentalCoroutinesApi::class) @SmallTest -@RunWith(JUnit4::class) +@RunWith(AndroidJUnit4::class) class BiometricStatusInteractorImplTest : SysuiTestCase() { private val kosmos = testKosmos() private lateinit var underTest: BiometricStatusInteractorImpl diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/CredentialInteractorImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/CredentialInteractorImplTest.kt index 8690d4eb1f3e..4856f156c4c7 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/CredentialInteractorImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/CredentialInteractorImplTest.kt @@ -4,6 +4,7 @@ import android.app.admin.DevicePolicyManager import android.app.admin.DevicePolicyResourcesManager import android.content.pm.UserInfo import android.os.UserManager +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.internal.widget.LockPatternUtils import com.android.internal.widget.LockscreenCredential @@ -25,7 +26,6 @@ import org.junit.Before import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith -import org.junit.runners.JUnit4 import org.mockito.ArgumentMatchers.anyInt import org.mockito.ArgumentMatchers.anyLong import org.mockito.Mock @@ -38,7 +38,7 @@ private const val MAX_ATTEMPTS = 5 @OptIn(ExperimentalCoroutinesApi::class) @SmallTest -@RunWith(JUnit4::class) +@RunWith(AndroidJUnit4::class) class CredentialInteractorImplTest : SysuiTestCase() { @JvmField @Rule var mockitoRule = MockitoJUnit.rule() diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/DisplayStateInteractorImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/DisplayStateInteractorImplTest.kt index 31bdde2bc895..f40b6b046187 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/DisplayStateInteractorImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/DisplayStateInteractorImplTest.kt @@ -1,5 +1,6 @@ package com.android.systemui.biometrics.domain.interactor +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.biometrics.data.repository.FakeDisplayStateRepository @@ -22,14 +23,13 @@ import org.junit.Before import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith -import org.junit.runners.JUnit4 import org.mockito.Mock import org.mockito.Mockito.verify import org.mockito.junit.MockitoJUnit @OptIn(ExperimentalCoroutinesApi::class) @SmallTest -@RunWith(JUnit4::class) +@RunWith(AndroidJUnit4::class) class DisplayStateInteractorImplTest : SysuiTestCase() { @JvmField @Rule var mockitoRule = MockitoJUnit.rule() diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/LogContextInteractorImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/LogContextInteractorImplTest.kt index 3f83ce371ffe..a58efd3224a4 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/LogContextInteractorImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/LogContextInteractorImplTest.kt @@ -19,6 +19,7 @@ package com.android.systemui.biometrics.domain.interactor import android.hardware.biometrics.AuthenticateOptions import android.hardware.biometrics.IBiometricContextListener import android.hardware.biometrics.IBiometricContextListener.FoldState +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.collectLastValue @@ -40,12 +41,11 @@ import org.junit.Before import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith -import org.junit.runners.JUnit4 import org.mockito.junit.MockitoJUnit @OptIn(ExperimentalCoroutinesApi::class) @SmallTest -@RunWith(JUnit4::class) +@RunWith(AndroidJUnit4::class) class LogContextInteractorImplTest : SysuiTestCase() { @JvmField @Rule var mockitoRule = MockitoJUnit.rule() diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/PromptCredentialInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/PromptCredentialInteractorTest.kt index c4d0d23ce9f3..5a3637668cfe 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/PromptCredentialInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/PromptCredentialInteractorTest.kt @@ -3,6 +3,7 @@ package com.android.systemui.biometrics.domain.interactor import android.hardware.biometrics.PromptContentViewWithMoreOptionsButton import android.hardware.biometrics.PromptInfo import android.hardware.biometrics.PromptVerticalListContentView +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.biometrics.data.repository.FakePromptRepository @@ -29,7 +30,6 @@ import org.junit.Before import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith -import org.junit.runners.JUnit4 import org.mockito.junit.MockitoJUnit private const val USER_ID = 22 @@ -39,7 +39,7 @@ private const val OP_PACKAGE_NAME = "biometric.testapp" @OptIn(ExperimentalCoroutinesApi::class) @SmallTest -@RunWith(JUnit4::class) +@RunWith(AndroidJUnit4::class) class PromptCredentialInteractorTest : SysuiTestCase() { @JvmField @Rule var mockitoRule = MockitoJUnit.rule() diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/PromptSelectorInteractorImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/PromptSelectorInteractorImplTest.kt index 6e78e334891b..720f2071ac73 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/PromptSelectorInteractorImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/PromptSelectorInteractorImplTest.kt @@ -22,6 +22,7 @@ import android.hardware.biometrics.BiometricManager.Authenticators import android.hardware.biometrics.PromptContentViewWithMoreOptionsButton import android.hardware.biometrics.PromptInfo import android.hardware.biometrics.PromptVerticalListContentView +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.internal.widget.LockPatternUtils import com.android.systemui.SysuiTestCase @@ -47,13 +48,12 @@ import org.junit.Before import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith -import org.junit.runners.JUnit4 import org.mockito.Mock import org.mockito.junit.MockitoJUnit @OptIn(ExperimentalCoroutinesApi::class) @SmallTest -@RunWith(JUnit4::class) +@RunWith(AndroidJUnit4::class) class PromptSelectorInteractorImplTest : SysuiTestCase() { companion object { private const val TITLE = "hey there" diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/UdfpsOverlayInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/UdfpsOverlayInteractorTest.kt index 5e7adb7671f7..3d63c5b6d0f8 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/UdfpsOverlayInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/UdfpsOverlayInteractorTest.kt @@ -19,6 +19,7 @@ package com.android.systemui.biometrics.domain.interactor import android.graphics.Rect import android.view.MotionEvent import android.view.Surface +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.biometrics.AuthController @@ -34,7 +35,6 @@ import org.junit.Before import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith -import org.junit.runners.JUnit4 import org.mockito.ArgumentCaptor import org.mockito.ArgumentMatchers.anyInt import org.mockito.Captor @@ -44,7 +44,7 @@ import org.mockito.Mockito.`when` as whenever import org.mockito.junit.MockitoJUnit @SmallTest -@RunWith(JUnit4::class) +@RunWith(AndroidJUnit4::class) class UdfpsOverlayInteractorTest : SysuiTestCase() { @JvmField @Rule var mockitoRule = MockitoJUnit.rule() diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/model/BiometricPromptRequestTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/model/BiometricPromptRequestTest.kt index d10b93534f3c..08f139c6a3af 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/model/BiometricPromptRequestTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/model/BiometricPromptRequestTest.kt @@ -4,8 +4,10 @@ import android.graphics.Bitmap import android.hardware.biometrics.PromptContentItemBulletedText import android.hardware.biometrics.PromptContentViewWithMoreOptionsButton import android.hardware.biometrics.PromptVerticalListContentView +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase +import com.android.systemui.biometrics.Utils.toBitmap import com.android.systemui.biometrics.fingerprintSensorPropertiesInternal import com.android.systemui.biometrics.promptInfo import com.android.systemui.biometrics.shared.model.BiometricModalities @@ -15,19 +17,19 @@ import com.google.common.truth.Truth.assertThat import com.google.common.util.concurrent.MoreExecutors import org.junit.Test import org.junit.runner.RunWith -import org.junit.runners.JUnit4 private const val USER_ID = 2 private const val OPERATION_ID = 8L private const val OP_PACKAGE_NAME = "biometric.testapp" @SmallTest -@RunWith(JUnit4::class) +@RunWith(AndroidJUnit4::class) class BiometricPromptRequestTest : SysuiTestCase() { @Test fun biometricRequestFromPromptInfo() { val logoRes = R.drawable.ic_cake + val logoBitmapFromRes = context.getDrawable(logoRes).toBitmap() val logoDescription = "test cake" val title = "what" val subtitle = "a" @@ -44,6 +46,7 @@ class BiometricPromptRequestTest : SysuiTestCase() { BiometricPromptRequest.Biometric( promptInfo( logoRes = logoRes, + logoBitmap = logoBitmapFromRes, logoDescription = logoDescription, title = title, subtitle = subtitle, @@ -56,7 +59,8 @@ class BiometricPromptRequestTest : SysuiTestCase() { OP_PACKAGE_NAME, ) - assertThat(request.logoRes).isEqualTo(logoRes) + assertThat(request.logoBitmap).isNotNull() + assertThat(request.logoBitmap!!.sameAs(logoBitmapFromRes)).isTrue() assertThat(request.logoDescription).isEqualTo(logoDescription) assertThat(request.title).isEqualTo(title) assertThat(request.subtitle).isEqualTo(subtitle) diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/shared/model/BiometricModalitiesTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/shared/model/BiometricModalitiesTest.kt index 74c43131b955..4d8fafc2111a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/shared/model/BiometricModalitiesTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/shared/model/BiometricModalitiesTest.kt @@ -17,6 +17,7 @@ package com.android.systemui.biometrics.shared.model import android.hardware.fingerprint.FingerprintSensorProperties +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.biometrics.faceSensorPropertiesInternal @@ -24,10 +25,9 @@ import com.android.systemui.biometrics.fingerprintSensorPropertiesInternal import com.google.common.truth.Truth.assertThat import org.junit.Test import org.junit.runner.RunWith -import org.junit.runners.JUnit4 @SmallTest -@RunWith(JUnit4::class) +@RunWith(AndroidJUnit4::class) class BiometricModalitiesTest : SysuiTestCase() { @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/udfps/BoundingBoxOverlapDetectorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/udfps/BoundingBoxOverlapDetectorTest.kt index 95b72d554896..f9bedc93e193 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/udfps/BoundingBoxOverlapDetectorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/udfps/BoundingBoxOverlapDetectorTest.kt @@ -21,12 +21,13 @@ import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.google.common.truth.Truth.assertThat import org.junit.Test +import platform.test.runner.parameterized.ParameterizedAndroidJunit4 +import platform.test.runner.parameterized.Parameters +import platform.test.runner.parameterized.Parameter import org.junit.runner.RunWith -import org.junit.runners.Parameterized -import org.junit.runners.Parameterized.Parameters @SmallTest -@RunWith(Parameterized::class) +@RunWith(ParameterizedAndroidJunit4::class) class BoundingBoxOverlapDetectorTest(val testCase: TestCase) : SysuiTestCase() { val underTest = BoundingBoxOverlapDetector(1f) diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/udfps/EllipseOverlapDetectorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/udfps/EllipseOverlapDetectorTest.kt index 317141ba42dd..33ddbf1989b3 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/udfps/EllipseOverlapDetectorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/udfps/EllipseOverlapDetectorTest.kt @@ -22,12 +22,13 @@ import com.android.systemui.SysuiTestCase import com.android.systemui.biometrics.EllipseOverlapDetectorParams import com.google.common.truth.Truth.assertThat import org.junit.Test +import platform.test.runner.parameterized.ParameterizedAndroidJunit4 +import platform.test.runner.parameterized.Parameters +import platform.test.runner.parameterized.Parameter import org.junit.runner.RunWith -import org.junit.runners.Parameterized -import org.junit.runners.Parameterized.Parameters @SmallTest -@RunWith(Parameterized::class) +@RunWith(ParameterizedAndroidJunit4::class) class EllipseOverlapDetectorTest(val testCase: TestCase) : SysuiTestCase() { val underTest = EllipseOverlapDetector( diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/udfps/NormalizedTouchDataTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/udfps/NormalizedTouchDataTest.kt index 3e5c43a33474..3863b3ccdaee 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/udfps/NormalizedTouchDataTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/udfps/NormalizedTouchDataTest.kt @@ -5,12 +5,13 @@ import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.google.common.truth.Truth.assertThat import org.junit.Test +import platform.test.runner.parameterized.ParameterizedAndroidJunit4 +import platform.test.runner.parameterized.Parameters +import platform.test.runner.parameterized.Parameter import org.junit.runner.RunWith -import org.junit.runners.Parameterized -import org.junit.runners.Parameterized.Parameters @SmallTest -@RunWith(Parameterized::class) +@RunWith(ParameterizedAndroidJunit4::class) class NormalizedTouchDataTest(val testCase: TestCase) : SysuiTestCase() { @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/udfps/SinglePointerTouchProcessorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/udfps/SinglePointerTouchProcessorTest.kt index aff93bd339ad..a4653e736745 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/udfps/SinglePointerTouchProcessorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/udfps/SinglePointerTouchProcessorTest.kt @@ -27,12 +27,13 @@ import com.android.systemui.SysuiTestCase import com.android.systemui.biometrics.shared.model.UdfpsOverlayParams import com.google.common.truth.Truth.assertThat import org.junit.Test +import platform.test.runner.parameterized.ParameterizedAndroidJunit4 +import platform.test.runner.parameterized.Parameters +import platform.test.runner.parameterized.Parameter import org.junit.runner.RunWith -import org.junit.runners.Parameterized -import org.junit.runners.Parameterized.Parameters @SmallTest -@RunWith(Parameterized::class) +@RunWith(ParameterizedAndroidJunit4::class) class SinglePointerTouchProcessorTest(val testCase: TestCase) : SysuiTestCase() { private val overlapDetector = FakeOverlapDetector() private val underTest = SinglePointerTouchProcessor(overlapDetector) diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/binder/SideFpsOverlayViewBinderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/binder/SideFpsOverlayViewBinderTest.kt index ec2b10455b90..42382540d401 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/binder/SideFpsOverlayViewBinderTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/binder/SideFpsOverlayViewBinderTest.kt @@ -32,6 +32,7 @@ import android.view.WindowManager import android.view.WindowMetrics import android.view.layoutInflater import android.view.windowManager +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.airbnb.lottie.LottieAnimationView import com.android.keyguard.keyguardUpdateMonitor @@ -61,7 +62,6 @@ import org.junit.Before import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith -import org.junit.runners.JUnit4 import org.mockito.Mock import org.mockito.Mockito import org.mockito.Mockito.any @@ -76,7 +76,7 @@ import org.mockito.junit.MockitoRule @OptIn(ExperimentalCoroutinesApi::class) @SmallTest -@RunWith(JUnit4::class) +@RunWith(AndroidJUnit4::class) @TestableLooper.RunWithLooper(setAsMainLooper = true) class SideFpsOverlayViewBinderTest : SysuiTestCase() { private val kosmos = testKosmos() diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/CredentialViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/CredentialViewModelTest.kt index 9e804c123520..e4c5cd456f03 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/CredentialViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/CredentialViewModelTest.kt @@ -1,5 +1,6 @@ package com.android.systemui.biometrics.ui.viewmodel +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.biometrics.data.repository.FakePromptRepository @@ -19,7 +20,6 @@ import kotlinx.coroutines.test.runTest import org.junit.Before import org.junit.Test import org.junit.runner.RunWith -import org.junit.runners.JUnit4 private const val USER_ID = 9 private const val REQUEST_ID = 9L @@ -27,7 +27,7 @@ private const val OPERATION_ID = 10L @OptIn(ExperimentalCoroutinesApi::class) @SmallTest -@RunWith(JUnit4::class) +@RunWith(AndroidJUnit4::class) class CredentialViewModelTest : SysuiTestCase() { private val dispatcher = UnconfinedTestDispatcher() diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/DeviceEntryUdfpsTouchOverlayViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/DeviceEntryUdfpsTouchOverlayViewModelTest.kt index 1b6aaabd4fd6..77ddd3183b00 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/DeviceEntryUdfpsTouchOverlayViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/DeviceEntryUdfpsTouchOverlayViewModelTest.kt @@ -16,6 +16,7 @@ package com.android.systemui.biometrics.ui.viewmodel +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.bouncer.data.repository.fakeKeyguardBouncerRepository @@ -34,7 +35,6 @@ import kotlinx.coroutines.test.runTest import org.junit.Before import org.junit.Test import org.junit.runner.RunWith -import org.junit.runners.JUnit4 import org.mockito.ArgumentCaptor import org.mockito.Captor import org.mockito.Mockito.verify @@ -42,7 +42,7 @@ import org.mockito.MockitoAnnotations @OptIn(ExperimentalCoroutinesApi::class) @SmallTest -@RunWith(JUnit4::class) +@RunWith(AndroidJUnit4::class) class DeviceEntryUdfpsTouchOverlayViewModelTest : SysuiTestCase() { private val kosmos = testKosmos().apply { diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptAuthStateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptAuthStateTest.kt index 278a43ea1bf1..3eb2ff301212 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptAuthStateTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptAuthStateTest.kt @@ -16,16 +16,16 @@ package com.android.systemui.biometrics.ui.viewmodel +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.biometrics.shared.model.BiometricModality import com.google.common.truth.Truth.assertThat import org.junit.Test import org.junit.runner.RunWith -import org.junit.runners.JUnit4 @SmallTest -@RunWith(JUnit4::class) +@RunWith(AndroidJUnit4::class) class PromptAuthStateTest : SysuiTestCase() { @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptHistoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptHistoryImplTest.kt index f9b590f8e018..81132d72f86c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptHistoryImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptHistoryImplTest.kt @@ -16,6 +16,7 @@ package com.android.systemui.biometrics.ui.viewmodel +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.biometrics.shared.model.BiometricModality @@ -23,10 +24,9 @@ import com.google.common.truth.Truth.assertThat import org.junit.Before import org.junit.Test import org.junit.runner.RunWith -import org.junit.runners.JUnit4 @SmallTest -@RunWith(JUnit4::class) +@RunWith(AndroidJUnit4::class) class PromptHistoryImplTest : SysuiTestCase() { private lateinit var history: PromptHistoryImpl diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt index f46cfdc280fe..cc7dec56be40 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt @@ -26,6 +26,7 @@ import android.content.pm.PackageManager.NameNotFoundException import android.content.res.Configuration import android.graphics.Bitmap import android.graphics.Point +import android.graphics.Rect import android.graphics.drawable.BitmapDrawable import android.hardware.biometrics.BiometricFingerprintConstants import android.hardware.biometrics.Flags.FLAG_CUSTOM_BIOMETRIC_PROMPT @@ -48,6 +49,7 @@ import com.android.systemui.Flags.FLAG_CONSTRAINT_BP import com.android.systemui.SysuiTestCase import com.android.systemui.biometrics.AuthController import com.android.systemui.biometrics.UdfpsUtils +import com.android.systemui.biometrics.Utils.toBitmap import com.android.systemui.biometrics.data.repository.FakeBiometricStatusRepository import com.android.systemui.biometrics.data.repository.FakeDisplayStateRepository import com.android.systemui.biometrics.data.repository.FakeFingerprintPropertyRepository @@ -89,11 +91,12 @@ import org.junit.Before import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith -import org.junit.runners.Parameterized import org.mockito.ArgumentMatchers.anyInt import org.mockito.ArgumentMatchers.eq import org.mockito.Mock import org.mockito.junit.MockitoJUnit +import platform.test.runner.parameterized.ParameterizedAndroidJunit4 +import platform.test.runner.parameterized.Parameters private const val USER_ID = 4 private const val REQUEST_ID = 4L @@ -105,7 +108,7 @@ private const val OP_PACKAGE_NAME_CAN_NOT_BE_FOUND = "can.not.be.found" @OptIn(ExperimentalCoroutinesApi::class) @SmallTest -@RunWith(Parameterized::class) +@RunWith(ParameterizedAndroidJunit4::class) internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCase() { @JvmField @Rule var mockitoRule = MockitoJUnit.rule() @@ -127,11 +130,32 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa private val defaultLogoIcon = context.getDrawable(R.drawable.ic_android) private val defaultLogoIconWithOverrides = context.getDrawable(R.drawable.ic_add) private val logoResFromApp = R.drawable.ic_cake - private val logoFromApp = context.getDrawable(logoResFromApp) + private val logoDrawableFromAppRes = context.getDrawable(logoResFromApp) private val logoBitmapFromApp = Bitmap.createBitmap(400, 400, Bitmap.Config.RGB_565) private val defaultLogoDescription = "Test Android App" private val logoDescriptionFromApp = "Test Cake App" private val packageNameForLogoWithOverrides = "should.use.overridden.logo" + /** Prompt panel size padding */ + private val smallHorizontalGuidelinePadding = + context.resources.getDimensionPixelSize( + R.dimen.biometric_prompt_land_small_horizontal_guideline_padding + ) + private val udfpsHorizontalGuidelinePadding = + context.resources.getDimensionPixelSize( + R.dimen.biometric_prompt_two_pane_udfps_horizontal_guideline_padding + ) + private val udfpsHorizontalShorterGuidelinePadding = + context.resources.getDimensionPixelSize( + R.dimen.biometric_prompt_two_pane_udfps_shorter_horizontal_guideline_padding + ) + private val mediumTopGuidelinePadding = + context.resources.getDimensionPixelSize( + R.dimen.biometric_prompt_one_pane_medium_top_guideline_padding + ) + private val mediumHorizontalGuidelinePadding = + context.resources.getDimensionPixelSize( + R.dimen.biometric_prompt_two_pane_medium_horizontal_guideline_padding + ) private lateinit var fingerprintRepository: FakeFingerprintPropertyRepository private lateinit var promptRepository: FakePromptRepository @@ -221,7 +245,7 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa context.setMockPackageManager(packageManager) val resources = context.getOrCreateTestableResources() - resources.addOverride(logoResFromApp, logoFromApp) + resources.addOverride(logoResFromApp, logoDrawableFromAppRes) resources.addOverride( R.array.biometric_dialog_package_names_for_logo_with_overrides, arrayOf(packageNameForLogoWithOverrides) @@ -383,25 +407,11 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa } if (testCase.isFaceOnly) { - val shouldRepeatAnimation by collectLastValue(iconViewModel.shouldRepeatAnimation) - val shouldPulseAnimation by collectLastValue(iconViewModel.shouldPulseAnimation) - val lastPulseLightToDark by collectLastValue(iconViewModel.lastPulseLightToDark) - - val expectedIconAsset = - if (shouldPulseAnimation!!) { - if (lastPulseLightToDark!!) { - R.drawable.face_dialog_pulse_dark_to_light - } else { - R.drawable.face_dialog_pulse_light_to_dark - } - } else { - R.drawable.face_dialog_pulse_dark_to_light - } + val expectedIconAsset = R.raw.face_dialog_authenticating assertThat(iconAsset).isEqualTo(expectedIconAsset) assertThat(iconContentDescriptionId) .isEqualTo(R.string.biometric_dialog_face_icon_description_authenticating) assertThat(shouldAnimateIconView).isEqualTo(true) - assertThat(shouldRepeatAnimation).isEqualTo(true) } if (testCase.isCoex) { @@ -423,26 +433,11 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa } } else { // implicit flow - val shouldRepeatAnimation by - collectLastValue(iconViewModel.shouldRepeatAnimation) - val shouldPulseAnimation by collectLastValue(iconViewModel.shouldPulseAnimation) - val lastPulseLightToDark by collectLastValue(iconViewModel.lastPulseLightToDark) - - val expectedIconAsset = - if (shouldPulseAnimation!!) { - if (lastPulseLightToDark!!) { - R.drawable.face_dialog_pulse_dark_to_light - } else { - R.drawable.face_dialog_pulse_light_to_dark - } - } else { - R.drawable.face_dialog_pulse_dark_to_light - } + val expectedIconAsset = R.raw.face_dialog_authenticating assertThat(iconAsset).isEqualTo(expectedIconAsset) assertThat(iconContentDescriptionId) .isEqualTo(R.string.biometric_dialog_face_icon_description_authenticating) assertThat(shouldAnimateIconView).isEqualTo(true) - assertThat(shouldRepeatAnimation).isEqualTo(true) } } } @@ -532,14 +527,9 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa } if (testCase.isFaceOnly) { - val shouldRepeatAnimation by collectLastValue(iconViewModel.shouldRepeatAnimation) - val shouldPulseAnimation by collectLastValue(iconViewModel.shouldPulseAnimation) - - assertThat(shouldPulseAnimation!!).isEqualTo(false) assertThat(iconAsset).isEqualTo(R.drawable.face_dialog_dark_to_error) assertThat(iconContentDescriptionId).isEqualTo(R.string.keyguard_face_failed) assertThat(shouldAnimateIconView).isEqualTo(true) - assertThat(shouldRepeatAnimation).isEqualTo(false) // Clear error, go to idle errorJob.join() @@ -548,7 +538,6 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa assertThat(iconContentDescriptionId) .isEqualTo(R.string.biometric_dialog_face_icon_description_idle) assertThat(shouldAnimateIconView).isEqualTo(true) - assertThat(shouldRepeatAnimation).isEqualTo(false) } if (testCase.isCoex) { @@ -631,15 +620,10 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa // If co-ex, using implicit flow (explicit flow always requires confirmation) if (testCase.isFaceOnly || testCase.isCoex) { - val shouldRepeatAnimation by collectLastValue(iconViewModel.shouldRepeatAnimation) - val shouldPulseAnimation by collectLastValue(iconViewModel.shouldPulseAnimation) - - assertThat(shouldPulseAnimation!!).isEqualTo(false) assertThat(iconAsset).isEqualTo(R.drawable.face_dialog_dark_to_checkmark) assertThat(iconContentDescriptionId) .isEqualTo(R.string.biometric_dialog_face_icon_description_authenticated) assertThat(shouldAnimateIconView).isEqualTo(true) - assertThat(shouldRepeatAnimation).isEqualTo(false) } } } @@ -661,15 +645,10 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa ) if (testCase.isFaceOnly) { - val shouldRepeatAnimation by collectLastValue(iconViewModel.shouldRepeatAnimation) - val shouldPulseAnimation by collectLastValue(iconViewModel.shouldPulseAnimation) - - assertThat(shouldPulseAnimation!!).isEqualTo(false) assertThat(iconAsset).isEqualTo(R.drawable.face_dialog_wink_from_dark) assertThat(iconContentDescriptionId) .isEqualTo(R.string.biometric_dialog_face_icon_description_authenticated) assertThat(shouldAnimateIconView).isEqualTo(true) - assertThat(shouldRepeatAnimation).isEqualTo(false) } // explicit flow because confirmation requested @@ -711,15 +690,10 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa viewModel.confirmAuthenticated() if (testCase.isFaceOnly) { - val shouldRepeatAnimation by collectLastValue(iconViewModel.shouldRepeatAnimation) - val shouldPulseAnimation by collectLastValue(iconViewModel.shouldPulseAnimation) - - assertThat(shouldPulseAnimation!!).isEqualTo(false) assertThat(iconAsset).isEqualTo(R.drawable.face_dialog_dark_to_checkmark) assertThat(iconContentDescriptionId) .isEqualTo(R.string.biometric_dialog_face_icon_description_confirmed) assertThat(shouldAnimateIconView).isEqualTo(true) - assertThat(shouldRepeatAnimation).isEqualTo(false) } // explicit flow because confirmation requested @@ -1299,7 +1273,7 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa @Test @EnableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT, FLAG_CONSTRAINT_BP) fun descriptionOverriddenByVerticalListContentView() = - runGenericTest(contentView = promptContentView, description = "test description") { + runGenericTest(description = "test description", contentView = promptContentView) { val contentView by collectLastValue(viewModel.contentView) val description by collectLastValue(viewModel.description) @@ -1311,8 +1285,8 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa @EnableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT, FLAG_CONSTRAINT_BP) fun descriptionOverriddenByContentViewWithMoreOptionsButton() = runGenericTest( - contentView = promptContentViewWithMoreOptionsButton, - description = "test description" + description = "test description", + contentView = promptContentViewWithMoreOptionsButton ) { val contentView by collectLastValue(viewModel.contentView) val description by collectLastValue(viewModel.description) @@ -1372,8 +1346,9 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa @EnableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT, FLAG_CONSTRAINT_BP) fun logo_resSetByApp() = runGenericTest(logoRes = logoResFromApp) { + val expectedBitmap = context.getDrawable(logoResFromApp).toBitmap() val logo by collectLastValue(viewModel.logo) - assertThat(logo).isEqualTo(logoFromApp) + assertThat((logo as BitmapDrawable).bitmap.sameAs(expectedBitmap)).isTrue() } @Test @@ -1416,6 +1391,142 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa } @Test + @EnableFlags(FLAG_CONSTRAINT_BP) + fun position_bottom_rotation0() = runGenericTest { + displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_0) + val position by collectLastValue(viewModel.position) + assertThat(position).isEqualTo(PromptPosition.Bottom) + } // TODO(b/335278136): Add test for no sensor landscape + + @Test + @EnableFlags(FLAG_CONSTRAINT_BP) + fun position_bottom_forceLarge() = runGenericTest { + displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_270) + viewModel.onSwitchToCredential() + val position by collectLastValue(viewModel.position) + assertThat(position).isEqualTo(PromptPosition.Bottom) + } + + @Test + @EnableFlags(FLAG_CONSTRAINT_BP) + fun position_bottom_largeScreen() = runGenericTest { + displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_270) + displayStateRepository.setIsLargeScreen(true) + val position by collectLastValue(viewModel.position) + assertThat(position).isEqualTo(PromptPosition.Bottom) + } + + @Test + @EnableFlags(FLAG_CONSTRAINT_BP) + fun position_right_rotation90() = runGenericTest { + displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_90) + val position by collectLastValue(viewModel.position) + assertThat(position).isEqualTo(PromptPosition.Right) + } + + @Test + @EnableFlags(FLAG_CONSTRAINT_BP) + fun position_left_rotation270() = runGenericTest { + displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_270) + val position by collectLastValue(viewModel.position) + assertThat(position).isEqualTo(PromptPosition.Left) + } + + @Test + @EnableFlags(FLAG_CONSTRAINT_BP) + fun position_top_rotation180() = runGenericTest { + displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_180) + val position by collectLastValue(viewModel.position) + if (testCase.modalities.hasUdfps) { + assertThat(position).isEqualTo(PromptPosition.Top) + } else { + assertThat(position).isEqualTo(PromptPosition.Bottom) + } + } + + @Test + @EnableFlags(FLAG_CONSTRAINT_BP) + fun guideline_bottom() = runGenericTest { + displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_0) + val guidelineBounds by collectLastValue(viewModel.guidelineBounds) + assertThat(guidelineBounds).isEqualTo(Rect(0, mediumTopGuidelinePadding, 0, 0)) + } // TODO(b/335278136): Add test for no sensor landscape + + @Test + @EnableFlags(FLAG_CONSTRAINT_BP) + fun guideline_right() = runGenericTest { + displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_90) + + val isSmall = testCase.shouldStartAsImplicitFlow + val guidelineBounds by collectLastValue(viewModel.guidelineBounds) + + if (isSmall) { + assertThat(guidelineBounds).isEqualTo(Rect(-smallHorizontalGuidelinePadding, 0, 0, 0)) + } else if (testCase.modalities.hasUdfps) { + assertThat(guidelineBounds).isEqualTo(Rect(udfpsHorizontalGuidelinePadding, 0, 0, 0)) + } else { + assertThat(guidelineBounds).isEqualTo(Rect(-mediumHorizontalGuidelinePadding, 0, 0, 0)) + } + } + + @Test + @EnableFlags(FLAG_CONSTRAINT_BP) + fun guideline_right_onlyShortTitle() = + runGenericTest(subtitle = "") { + displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_90) + + val isSmall = testCase.shouldStartAsImplicitFlow + val guidelineBounds by collectLastValue(viewModel.guidelineBounds) + + if (!isSmall && testCase.modalities.hasUdfps) { + assertThat(guidelineBounds) + .isEqualTo(Rect(-udfpsHorizontalShorterGuidelinePadding, 0, 0, 0)) + } + } + + @Test + @EnableFlags(FLAG_CONSTRAINT_BP) + fun guideline_left() = runGenericTest { + displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_270) + + val isSmall = testCase.shouldStartAsImplicitFlow + val guidelineBounds by collectLastValue(viewModel.guidelineBounds) + + if (isSmall) { + assertThat(guidelineBounds).isEqualTo(Rect(0, 0, -smallHorizontalGuidelinePadding, 0)) + } else if (testCase.modalities.hasUdfps) { + assertThat(guidelineBounds).isEqualTo(Rect(0, 0, udfpsHorizontalGuidelinePadding, 0)) + } else { + assertThat(guidelineBounds).isEqualTo(Rect(0, 0, -mediumHorizontalGuidelinePadding, 0)) + } + } + + @Test + @EnableFlags(FLAG_CONSTRAINT_BP) + fun guideline_left_onlyShortTitle() = + runGenericTest(subtitle = "") { + displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_270) + + val isSmall = testCase.shouldStartAsImplicitFlow + val guidelineBounds by collectLastValue(viewModel.guidelineBounds) + + if (!isSmall && testCase.modalities.hasUdfps) { + assertThat(guidelineBounds) + .isEqualTo(Rect(0, 0, -udfpsHorizontalShorterGuidelinePadding, 0)) + } + } + + @Test + @EnableFlags(FLAG_CONSTRAINT_BP) + fun guideline_top() = runGenericTest { + displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_180) + val guidelineBounds by collectLastValue(viewModel.guidelineBounds) + if (testCase.modalities.hasUdfps) { + assertThat(guidelineBounds).isEqualTo(Rect(0, 0, 0, 0)) + } + } + + @Test fun iconViewLoaded() = runGenericTest { val isIconViewLoaded by collectLastValue(viewModel.isIconViewLoaded) // TODO(b/328677869): Add test for noIcon logic. @@ -1445,9 +1556,10 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa private fun runGenericTest( doNotStart: Boolean = false, allowCredentialFallback: Boolean = false, + subtitle: String? = "s", description: String? = null, contentView: PromptContentView? = null, - logoRes: Int = -1, + logoRes: Int = 0, logoBitmap: Bitmap? = null, logoDescription: String? = null, packageName: String = OP_PACKAGE_NAME, @@ -1483,10 +1595,11 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa allowCredentialFallback = allowCredentialFallback, fingerprint = testCase.fingerprint, face = testCase.face, + subtitleFromApp = subtitle, descriptionFromApp = description, contentViewFromApp = contentView, logoResFromApp = logoRes, - logoBitmapFromApp = logoBitmap, + logoBitmapFromApp = if (logoRes != 0) logoDrawableFromAppRes.toBitmap() else logoBitmap, logoDescriptionFromApp = logoDescription, packageName = packageName, ) @@ -1526,7 +1639,7 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa companion object { @JvmStatic - @Parameterized.Parameters(name = "{0}") + @Parameters(name = "{0}") fun data(): Collection<TestCase> = singleModalityTestCases + coexTestCases private val singleModalityTestCases = @@ -1670,26 +1783,28 @@ private fun PromptSelectorInteractor.initializePrompt( face: FaceSensorPropertiesInternal? = null, requireConfirmation: Boolean = false, allowCredentialFallback: Boolean = false, + subtitleFromApp: String? = "s", descriptionFromApp: String? = null, contentViewFromApp: PromptContentView? = null, - logoResFromApp: Int = -1, + logoResFromApp: Int = 0, logoBitmapFromApp: Bitmap? = null, logoDescriptionFromApp: String? = null, packageName: String = OP_PACKAGE_NAME, ) { val info = PromptInfo().apply { - logoRes = logoResFromApp - logoBitmap = logoBitmapFromApp logoDescription = logoDescriptionFromApp title = "t" - subtitle = "s" + subtitle = subtitleFromApp description = descriptionFromApp contentView = contentViewFromApp authenticators = listOf(face, fingerprint).extractAuthenticatorTypes() isDeviceCredentialAllowed = allowCredentialFallback isConfirmationRequested = requireConfirmation } + if (logoBitmapFromApp != null) { + info.setLogo(logoResFromApp, logoBitmapFromApp) + } setPrompt( info, diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/SideFpsOverlayViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/SideFpsOverlayViewModelTest.kt index b065393de1f6..3b2cf61dde68 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/SideFpsOverlayViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/SideFpsOverlayViewModelTest.kt @@ -27,6 +27,7 @@ import android.view.DisplayInfo import android.view.WindowInsets import android.view.WindowMetrics import android.view.windowManager +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.airbnb.lottie.model.KeyPath import com.android.keyguard.keyguardUpdateMonitor @@ -62,7 +63,6 @@ import org.junit.Before import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith -import org.junit.runners.JUnit4 import org.mockito.Mock import org.mockito.Mockito.mock import org.mockito.Mockito.spy @@ -71,7 +71,7 @@ import org.mockito.junit.MockitoRule @OptIn(ExperimentalCoroutinesApi::class) @SmallTest -@RunWith(JUnit4::class) +@RunWith(AndroidJUnit4::class) class SideFpsOverlayViewModelTest : SysuiTestCase() { private val kosmos = testKosmos() @JvmField @Rule var mockitoRule: MockitoRule = MockitoJUnit.rule() diff --git a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/BroadcastDialogDelegateTest.java b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/BroadcastDialogDelegateTest.java index 49f204372730..7d4ee25ef0a5 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/BroadcastDialogDelegateTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/BroadcastDialogDelegateTest.java @@ -28,11 +28,11 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.widget.Button; import android.widget.TextView; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.internal.logging.testing.UiEventLoggerFake; @@ -58,7 +58,7 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) @TestableLooper.RunWithLooper(setAsMainLooper = true) public class BroadcastDialogDelegateTest extends SysuiTestCase { diff --git a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/AudioSharingInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/AudioSharingInteractorTest.kt index 8a1a08249856..4d7c49973605 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/AudioSharingInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/AudioSharingInteractorTest.kt @@ -16,8 +16,8 @@ package com.android.systemui.bluetooth.qsdialog -import android.testing.AndroidTestingRunner import android.testing.TestableLooper +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession import com.android.dx.mockito.inline.extended.StaticMockitoSession @@ -44,7 +44,7 @@ import org.mockito.Mock @ExperimentalCoroutinesApi @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) @TestableLooper.RunWithLooper(setAsMainLooper = true) class AudioSharingInteractorTest : SysuiTestCase() { private val testDispatcher = UnconfinedTestDispatcher() diff --git a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothAutoOnInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothAutoOnInteractorTest.kt index 4949716ad129..ac5ceb8ed266 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothAutoOnInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothAutoOnInteractorTest.kt @@ -17,7 +17,7 @@ package com.android.systemui.bluetooth.qsdialog import android.bluetooth.BluetoothAdapter -import android.testing.AndroidTestingRunner +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.settingslib.bluetooth.LocalBluetoothManager import com.android.systemui.SysuiTestCase @@ -39,7 +39,7 @@ import org.mockito.junit.MockitoJUnit import org.mockito.junit.MockitoRule @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) class BluetoothAutoOnInteractorTest : SysuiTestCase() { @get:Rule val mockitoRule: MockitoRule = MockitoJUnit.rule() private val testDispatcher = StandardTestDispatcher() diff --git a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothAutoOnRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothAutoOnRepositoryTest.kt index 85e2a8d4b48e..b7b2be48bb61 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothAutoOnRepositoryTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothAutoOnRepositoryTest.kt @@ -17,7 +17,7 @@ package com.android.systemui.bluetooth.qsdialog import android.bluetooth.BluetoothAdapter -import android.testing.AndroidTestingRunner +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.settingslib.bluetooth.BluetoothEventManager import com.android.settingslib.bluetooth.LocalBluetoothManager @@ -38,7 +38,7 @@ import org.mockito.junit.MockitoJUnit import org.mockito.junit.MockitoRule @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) class BluetoothAutoOnRepositoryTest : SysuiTestCase() { @get:Rule val mockitoRule: MockitoRule = MockitoJUnit.rule() private val testDispatcher = StandardTestDispatcher() diff --git a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothStateInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothStateInteractorTest.kt index 6fe7d86faab8..993cac721f48 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothStateInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothStateInteractorTest.kt @@ -16,8 +16,8 @@ package com.android.systemui.bluetooth.qsdialog -import android.testing.AndroidTestingRunner import android.testing.TestableLooper +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.settingslib.bluetooth.LocalBluetoothAdapter import com.android.settingslib.bluetooth.LocalBluetoothManager @@ -38,7 +38,7 @@ import org.mockito.junit.MockitoJUnit import org.mockito.junit.MockitoRule @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) @TestableLooper.RunWithLooper(setAsMainLooper = true) class BluetoothStateInteractorTest : SysuiTestCase() { @get:Rule val mockitoRule: MockitoRule = MockitoJUnit.rule() diff --git a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogDelegateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogDelegateTest.kt index 72156194b0e1..d01fac36230e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogDelegateTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogDelegateTest.kt @@ -17,7 +17,6 @@ package com.android.systemui.bluetooth.qsdialog import android.graphics.drawable.Drawable -import android.testing.AndroidTestingRunner import android.testing.TestableLooper import android.view.LayoutInflater import android.view.View @@ -27,6 +26,7 @@ import android.view.ViewGroup.LayoutParams.MATCH_PARENT import android.view.ViewGroup.LayoutParams.WRAP_CONTENT import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.internal.logging.UiEventLogger import com.android.settingslib.bluetooth.CachedBluetoothDevice @@ -57,7 +57,7 @@ import org.mockito.junit.MockitoJUnit import org.mockito.junit.MockitoRule @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) @TestableLooper.RunWithLooper(setAsMainLooper = true) class BluetoothTileDialogDelegateTest : SysuiTestCase() { companion object { diff --git a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogRepositoryTest.kt index 4aa6209fab3c..1f3dcac24726 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogRepositoryTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogRepositoryTest.kt @@ -17,8 +17,8 @@ package com.android.systemui.bluetooth.qsdialog import android.bluetooth.BluetoothAdapter -import android.testing.AndroidTestingRunner import android.testing.TestableLooper +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.settingslib.bluetooth.CachedBluetoothDevice import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager @@ -35,7 +35,7 @@ import org.mockito.junit.MockitoJUnit import org.mockito.junit.MockitoRule @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) @TestableLooper.RunWithLooper(setAsMainLooper = true) class BluetoothTileDialogRepositoryTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogViewModelTest.kt index 11f74c0b98cb..9abb85d249eb 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogViewModelTest.kt @@ -18,11 +18,11 @@ package com.android.systemui.bluetooth.qsdialog import android.bluetooth.BluetoothAdapter import android.platform.test.annotations.EnableFlags -import android.testing.AndroidTestingRunner import android.testing.TestableLooper import android.view.View import android.view.View.GONE import android.view.View.VISIBLE +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.internal.logging.UiEventLogger import com.android.settingslib.bluetooth.CachedBluetoothDevice @@ -62,7 +62,7 @@ import org.mockito.junit.MockitoJUnit import org.mockito.junit.MockitoRule @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) @TestableLooper.RunWithLooper(setAsMainLooper = true) @EnableFlags(Flags.FLAG_BLUETOOTH_QS_TILE_DIALOG_AUTO_ON_TOGGLE) class BluetoothTileDialogViewModelTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/DeviceItemActionInteractorImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/DeviceItemActionInteractorImplTest.kt deleted file mode 100644 index 762137bede27..000000000000 --- a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/DeviceItemActionInteractorImplTest.kt +++ /dev/null @@ -1,121 +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.bluetooth.qsdialog - -import android.bluetooth.BluetoothDevice -import android.testing.AndroidTestingRunner -import android.testing.TestableLooper -import androidx.test.filters.SmallTest -import com.android.settingslib.bluetooth.CachedBluetoothDevice -import com.android.systemui.SysuiTestCase -import com.android.systemui.kosmos.testDispatcher -import com.android.systemui.kosmos.testScope -import com.android.systemui.statusbar.phone.SystemUIDialog -import com.android.systemui.testKosmos -import com.android.systemui.util.mockito.whenever -import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.test.UnconfinedTestDispatcher -import kotlinx.coroutines.test.runTest -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.verify -import org.mockito.junit.MockitoJUnit -import org.mockito.junit.MockitoRule - -@SmallTest -@RunWith(AndroidTestingRunner::class) -@TestableLooper.RunWithLooper(setAsMainLooper = true) -@OptIn(ExperimentalCoroutinesApi::class) -class DeviceItemActionInteractorImplTest : SysuiTestCase() { - @get:Rule val mockitoRule: MockitoRule = MockitoJUnit.rule() - private val kosmos = testKosmos().apply { testDispatcher = UnconfinedTestDispatcher() } - private lateinit var actionInteractorImpl: DeviceItemActionInteractor - - @Mock private lateinit var dialog: SystemUIDialog - @Mock private lateinit var cachedDevice: CachedBluetoothDevice - @Mock private lateinit var device: BluetoothDevice - @Mock private lateinit var deviceItem: DeviceItem - - @Before - fun setUp() { - actionInteractorImpl = kosmos.deviceItemActionInteractor - whenever(deviceItem.cachedBluetoothDevice).thenReturn(cachedDevice) - whenever(cachedDevice.address).thenReturn("ADDRESS") - whenever(cachedDevice.device).thenReturn(device) - } - - @Test - fun testOnClick_connectedMedia_setActive() { - with(kosmos) { - testScope.runTest { - whenever(deviceItem.type) - .thenReturn(DeviceItemType.AVAILABLE_MEDIA_BLUETOOTH_DEVICE) - actionInteractorImpl.onClick(deviceItem, dialog) - verify(cachedDevice).setActive() - verify(bluetoothTileDialogLogger) - .logDeviceClick( - cachedDevice.address, - DeviceItemType.AVAILABLE_MEDIA_BLUETOOTH_DEVICE - ) - } - } - } - - @Test - fun testOnClick_activeMedia_disconnect() { - with(kosmos) { - testScope.runTest { - whenever(deviceItem.type).thenReturn(DeviceItemType.ACTIVE_MEDIA_BLUETOOTH_DEVICE) - actionInteractorImpl.onClick(deviceItem, dialog) - verify(cachedDevice).disconnect() - verify(bluetoothTileDialogLogger) - .logDeviceClick( - cachedDevice.address, - DeviceItemType.ACTIVE_MEDIA_BLUETOOTH_DEVICE - ) - } - } - } - - @Test - fun testOnClick_connectedOtherDevice_disconnect() { - with(kosmos) { - testScope.runTest { - whenever(deviceItem.type).thenReturn(DeviceItemType.CONNECTED_BLUETOOTH_DEVICE) - actionInteractorImpl.onClick(deviceItem, dialog) - verify(cachedDevice).disconnect() - verify(bluetoothTileDialogLogger) - .logDeviceClick(cachedDevice.address, DeviceItemType.CONNECTED_BLUETOOTH_DEVICE) - } - } - } - - @Test - fun testOnClick_saved_connect() { - with(kosmos) { - testScope.runTest { - whenever(deviceItem.type).thenReturn(DeviceItemType.SAVED_BLUETOOTH_DEVICE) - actionInteractorImpl.onClick(deviceItem, dialog) - verify(cachedDevice).connect() - verify(bluetoothTileDialogLogger) - .logDeviceClick(cachedDevice.address, DeviceItemType.SAVED_BLUETOOTH_DEVICE) - } - } - } -} diff --git a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/DeviceItemActionInteractorKosmos.kt b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/DeviceItemActionInteractorKosmos.kt index e8e37bc81866..5ff46346b386 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/DeviceItemActionInteractorKosmos.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/DeviceItemActionInteractorKosmos.kt @@ -13,19 +13,28 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.android.systemui.bluetooth.qsdialog import com.android.internal.logging.uiEventLogger +import com.android.settingslib.bluetooth.LocalBluetoothManager +import com.android.systemui.animation.DialogTransitionAnimator import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.testDispatcher +import com.android.systemui.plugins.activityStarter import com.android.systemui.util.mockito.mock val Kosmos.bluetoothTileDialogLogger: BluetoothTileDialogLogger by Kosmos.Fixture { mock {} } +val Kosmos.localBluetoothManager: LocalBluetoothManager by Kosmos.Fixture { mock {} } + +val Kosmos.dialogTransitionAnimator: DialogTransitionAnimator by Kosmos.Fixture { mock {} } + val Kosmos.deviceItemActionInteractor: DeviceItemActionInteractor by Kosmos.Fixture { - DeviceItemActionInteractorImpl( + DeviceItemActionInteractor( + activityStarter, + dialogTransitionAnimator, + localBluetoothManager, testDispatcher, bluetoothTileDialogLogger, uiEventLogger, diff --git a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/DeviceItemActionInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/DeviceItemActionInteractorTest.kt new file mode 100644 index 000000000000..82465065c1e1 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/DeviceItemActionInteractorTest.kt @@ -0,0 +1,459 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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.bluetooth.qsdialog + +import android.bluetooth.BluetoothDevice +import android.testing.AndroidTestingRunner +import android.testing.TestableLooper +import androidx.test.filters.SmallTest +import com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession +import com.android.dx.mockito.inline.extended.StaticMockitoSession +import com.android.settingslib.bluetooth.BluetoothUtils +import com.android.settingslib.bluetooth.CachedBluetoothDevice +import com.android.settingslib.bluetooth.LeAudioProfile +import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastAssistant +import com.android.settingslib.bluetooth.LocalBluetoothProfileManager +import com.android.systemui.SysuiTestCase +import com.android.systemui.kosmos.testDispatcher +import com.android.systemui.kosmos.testScope +import com.android.systemui.plugins.activityStarter +import com.android.systemui.statusbar.phone.SystemUIDialog +import com.android.systemui.testKosmos +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.UnconfinedTestDispatcher +import kotlinx.coroutines.test.runTest +import org.junit.After +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.ArgumentMatchers +import org.mockito.Mock +import org.mockito.Mockito +import org.mockito.Mockito.verify +import org.mockito.junit.MockitoJUnit +import org.mockito.junit.MockitoRule +import org.mockito.kotlin.whenever + +@SmallTest +@RunWith(AndroidTestingRunner::class) +@TestableLooper.RunWithLooper(setAsMainLooper = true) +@OptIn(ExperimentalCoroutinesApi::class) +class DeviceItemActionInteractorTest : SysuiTestCase() { + @get:Rule val mockitoRule: MockitoRule = MockitoJUnit.rule() + private val kosmos = testKosmos().apply { testDispatcher = UnconfinedTestDispatcher() } + private lateinit var actionInteractorImpl: DeviceItemActionInteractor + private lateinit var mockitoSession: StaticMockitoSession + private lateinit var activeMediaDeviceItem: DeviceItem + private lateinit var notConnectedDeviceItem: DeviceItem + private lateinit var connectedMediaDeviceItem: DeviceItem + private lateinit var connectedOtherDeviceItem: DeviceItem + @Mock private lateinit var dialog: SystemUIDialog + @Mock private lateinit var profileManager: LocalBluetoothProfileManager + @Mock private lateinit var leAudioProfile: LeAudioProfile + @Mock private lateinit var assistantProfile: LocalBluetoothLeBroadcastAssistant + @Mock private lateinit var bluetoothDevice: BluetoothDevice + @Mock private lateinit var bluetoothDeviceGroupId2: BluetoothDevice + @Mock private lateinit var cachedBluetoothDevice: CachedBluetoothDevice + + @Before + fun setUp() { + mockitoSession = + mockitoSession().initMocks(this).mockStatic(BluetoothUtils::class.java).startMocking() + activeMediaDeviceItem = + DeviceItem( + type = DeviceItemType.ACTIVE_MEDIA_BLUETOOTH_DEVICE, + cachedBluetoothDevice = cachedBluetoothDevice, + deviceName = DEVICE_NAME, + connectionSummary = DEVICE_CONNECTION_SUMMARY, + iconWithDescription = null, + background = null + ) + notConnectedDeviceItem = + DeviceItem( + type = DeviceItemType.SAVED_BLUETOOTH_DEVICE, + cachedBluetoothDevice = cachedBluetoothDevice, + deviceName = DEVICE_NAME, + connectionSummary = DEVICE_CONNECTION_SUMMARY, + iconWithDescription = null, + background = null + ) + connectedMediaDeviceItem = + DeviceItem( + type = DeviceItemType.AVAILABLE_MEDIA_BLUETOOTH_DEVICE, + cachedBluetoothDevice = cachedBluetoothDevice, + deviceName = DEVICE_NAME, + connectionSummary = DEVICE_CONNECTION_SUMMARY, + iconWithDescription = null, + background = null + ) + connectedOtherDeviceItem = + DeviceItem( + type = DeviceItemType.CONNECTED_BLUETOOTH_DEVICE, + cachedBluetoothDevice = cachedBluetoothDevice, + deviceName = DEVICE_NAME, + connectionSummary = DEVICE_CONNECTION_SUMMARY, + iconWithDescription = null, + background = null + ) + actionInteractorImpl = kosmos.deviceItemActionInteractor + } + + @After + fun tearDown() { + mockitoSession.finishMocking() + } + + @Test + fun testOnClick_connectedMedia_setActive() { + with(kosmos) { + testScope.runTest { + whenever(cachedBluetoothDevice.address).thenReturn(DEVICE_ADDRESS) + whenever(BluetoothUtils.isAudioSharingEnabled()).thenReturn(false) + actionInteractorImpl.onClick(connectedMediaDeviceItem, dialog) + verify(cachedBluetoothDevice).setActive() + verify(bluetoothTileDialogLogger) + .logDeviceClick( + cachedBluetoothDevice.address, + DeviceItemType.AVAILABLE_MEDIA_BLUETOOTH_DEVICE + ) + } + } + } + + @Test + fun testOnClick_activeMedia_disconnect() { + with(kosmos) { + testScope.runTest { + whenever(cachedBluetoothDevice.address).thenReturn(DEVICE_ADDRESS) + whenever(BluetoothUtils.isAudioSharingEnabled()).thenReturn(false) + actionInteractorImpl.onClick(activeMediaDeviceItem, dialog) + verify(cachedBluetoothDevice).disconnect() + verify(bluetoothTileDialogLogger) + .logDeviceClick( + cachedBluetoothDevice.address, + DeviceItemType.ACTIVE_MEDIA_BLUETOOTH_DEVICE + ) + } + } + } + + @Test + fun testOnClick_connectedOtherDevice_disconnect() { + with(kosmos) { + testScope.runTest { + whenever(cachedBluetoothDevice.address).thenReturn(DEVICE_ADDRESS) + whenever(BluetoothUtils.isAudioSharingEnabled()).thenReturn(false) + actionInteractorImpl.onClick(connectedOtherDeviceItem, dialog) + verify(cachedBluetoothDevice).disconnect() + verify(bluetoothTileDialogLogger) + .logDeviceClick( + cachedBluetoothDevice.address, + DeviceItemType.CONNECTED_BLUETOOTH_DEVICE + ) + } + } + } + + @Test + fun testOnClick_saved_connect() { + with(kosmos) { + testScope.runTest { + whenever(cachedBluetoothDevice.address).thenReturn(DEVICE_ADDRESS) + whenever(BluetoothUtils.isAudioSharingEnabled()).thenReturn(false) + actionInteractorImpl.onClick(notConnectedDeviceItem, dialog) + verify(cachedBluetoothDevice).connect() + verify(bluetoothTileDialogLogger) + .logDeviceClick( + cachedBluetoothDevice.address, + DeviceItemType.SAVED_BLUETOOTH_DEVICE + ) + } + } + } + + @Test + fun testOnClick_audioSharingDisabled_shouldNotLaunchSettings() { + with(kosmos) { + testScope.runTest { + whenever(cachedBluetoothDevice.address).thenReturn(DEVICE_ADDRESS) + whenever(BluetoothUtils.isAudioSharingEnabled()).thenReturn(false) + + actionInteractorImpl.onClick(connectedMediaDeviceItem, dialog) + verify(activityStarter, Mockito.never()) + .postStartActivityDismissingKeyguard( + ArgumentMatchers.any(), + ArgumentMatchers.anyInt(), + ArgumentMatchers.any() + ) + } + } + } + + @Test + fun testOnClick_inAudioSharing_clickedDeviceHasSource_shouldNotLaunchSettings() { + with(kosmos) { + testScope.runTest { + whenever(cachedBluetoothDevice.address).thenReturn(DEVICE_ADDRESS) + whenever(cachedBluetoothDevice.connectableProfiles) + .thenReturn(listOf(leAudioProfile)) + + whenever(BluetoothUtils.isAudioSharingEnabled()).thenReturn(true) + whenever(localBluetoothManager.profileManager).thenReturn(profileManager) + whenever(profileManager.leAudioProfile).thenReturn(leAudioProfile) + whenever(profileManager.leAudioBroadcastAssistantProfile) + .thenReturn(assistantProfile) + + whenever(BluetoothUtils.isBroadcasting(ArgumentMatchers.any())).thenReturn(true) + whenever( + BluetoothUtils.hasConnectedBroadcastSource( + ArgumentMatchers.any(), + ArgumentMatchers.any() + ) + ) + .thenReturn(true) + + actionInteractorImpl.onClick(connectedMediaDeviceItem, dialog) + verify(activityStarter, Mockito.never()) + .postStartActivityDismissingKeyguard( + ArgumentMatchers.any(), + ArgumentMatchers.anyInt(), + ArgumentMatchers.any() + ) + } + } + } + + @Test + fun testOnClick_inAudioSharing_clickedDeviceNoSource_shouldLaunchSettings() { + with(kosmos) { + testScope.runTest { + whenever(cachedBluetoothDevice.address).thenReturn(DEVICE_ADDRESS) + whenever(cachedBluetoothDevice.device).thenReturn(bluetoothDevice) + whenever(cachedBluetoothDevice.connectableProfiles) + .thenReturn(listOf(leAudioProfile)) + + whenever(BluetoothUtils.isAudioSharingEnabled()).thenReturn(true) + whenever(localBluetoothManager.profileManager).thenReturn(profileManager) + whenever(profileManager.leAudioProfile).thenReturn(leAudioProfile) + whenever(profileManager.leAudioBroadcastAssistantProfile) + .thenReturn(assistantProfile) + + whenever(BluetoothUtils.isBroadcasting(ArgumentMatchers.any())).thenReturn(true) + whenever( + BluetoothUtils.hasConnectedBroadcastSource( + ArgumentMatchers.any(), + ArgumentMatchers.any() + ) + ) + .thenReturn(false) + + actionInteractorImpl.onClick(connectedMediaDeviceItem, dialog) + verify(activityStarter) + .postStartActivityDismissingKeyguard( + ArgumentMatchers.any(), + ArgumentMatchers.anyInt(), + ArgumentMatchers.any() + ) + } + } + } + + @Test + fun testOnClick_noConnectedLeDevice_shouldNotLaunchSettings() { + with(kosmos) { + testScope.runTest { + whenever(cachedBluetoothDevice.address).thenReturn(DEVICE_ADDRESS) + + whenever(BluetoothUtils.isAudioSharingEnabled()).thenReturn(true) + whenever(localBluetoothManager.profileManager).thenReturn(profileManager) + whenever(profileManager.leAudioProfile).thenReturn(leAudioProfile) + whenever(profileManager.leAudioBroadcastAssistantProfile) + .thenReturn(assistantProfile) + + actionInteractorImpl.onClick(notConnectedDeviceItem, dialog) + verify(activityStarter, Mockito.never()) + .postStartActivityDismissingKeyguard( + ArgumentMatchers.any(), + ArgumentMatchers.anyInt(), + ArgumentMatchers.any() + ) + } + } + } + + @Test + fun testOnClick_hasOneConnectedLeDevice_clickedNonLe_shouldNotLaunchSettings() { + with(kosmos) { + testScope.runTest { + whenever(cachedBluetoothDevice.address).thenReturn(DEVICE_ADDRESS) + + whenever(BluetoothUtils.isAudioSharingEnabled()).thenReturn(true) + whenever(localBluetoothManager.profileManager).thenReturn(profileManager) + whenever(profileManager.leAudioProfile).thenReturn(leAudioProfile) + whenever(profileManager.leAudioBroadcastAssistantProfile) + .thenReturn(assistantProfile) + + whenever( + assistantProfile.getDevicesMatchingConnectionStates(ArgumentMatchers.any()) + ) + .thenReturn(listOf(bluetoothDevice)) + + actionInteractorImpl.onClick(notConnectedDeviceItem, dialog) + verify(activityStarter, Mockito.never()) + .postStartActivityDismissingKeyguard( + ArgumentMatchers.any(), + ArgumentMatchers.anyInt(), + ArgumentMatchers.any() + ) + } + } + } + + @Test + fun testOnClick_hasOneConnectedLeDevice_clickedLe_shouldLaunchSettings() { + with(kosmos) { + testScope.runTest { + whenever(cachedBluetoothDevice.device).thenReturn(bluetoothDevice) + whenever(cachedBluetoothDevice.address).thenReturn(DEVICE_ADDRESS) + whenever(cachedBluetoothDevice.profiles).thenReturn(listOf(leAudioProfile)) + whenever(leAudioProfile.isEnabled(ArgumentMatchers.any())).thenReturn(true) + + whenever(BluetoothUtils.isAudioSharingEnabled()).thenReturn(true) + whenever(localBluetoothManager.profileManager).thenReturn(profileManager) + whenever(profileManager.leAudioProfile).thenReturn(leAudioProfile) + whenever(profileManager.leAudioBroadcastAssistantProfile) + .thenReturn(assistantProfile) + + whenever( + assistantProfile.getDevicesMatchingConnectionStates(ArgumentMatchers.any()) + ) + .thenReturn(listOf(bluetoothDevice)) + + actionInteractorImpl.onClick(notConnectedDeviceItem, dialog) + verify(activityStarter) + .postStartActivityDismissingKeyguard( + ArgumentMatchers.any(), + ArgumentMatchers.anyInt(), + ArgumentMatchers.any() + ) + } + } + } + + @Test + fun testOnClick_hasOneConnectedLeDevice_clickedConnectedLe_shouldNotLaunchSettings() { + with(kosmos) { + testScope.runTest { + whenever(cachedBluetoothDevice.address).thenReturn(DEVICE_ADDRESS) + + whenever(BluetoothUtils.isAudioSharingEnabled()).thenReturn(true) + whenever(localBluetoothManager.profileManager).thenReturn(profileManager) + whenever(profileManager.leAudioProfile).thenReturn(leAudioProfile) + whenever(profileManager.leAudioBroadcastAssistantProfile) + .thenReturn(assistantProfile) + + whenever( + assistantProfile.getDevicesMatchingConnectionStates(ArgumentMatchers.any()) + ) + .thenReturn(listOf(bluetoothDevice)) + + actionInteractorImpl.onClick(connectedMediaDeviceItem, dialog) + verify(activityStarter, Mockito.never()) + .postStartActivityDismissingKeyguard( + ArgumentMatchers.any(), + ArgumentMatchers.anyInt(), + ArgumentMatchers.any() + ) + } + } + } + + @Test + fun testOnClick_hasTwoConnectedLeDevice_clickedNotConnectedLe_shouldNotLaunchSettings() { + with(kosmos) { + testScope.runTest { + whenever(cachedBluetoothDevice.address).thenReturn(DEVICE_ADDRESS) + + whenever(BluetoothUtils.isAudioSharingEnabled()).thenReturn(true) + whenever(localBluetoothManager.profileManager).thenReturn(profileManager) + whenever(profileManager.leAudioProfile).thenReturn(leAudioProfile) + whenever(profileManager.leAudioBroadcastAssistantProfile) + .thenReturn(assistantProfile) + + whenever( + assistantProfile.getDevicesMatchingConnectionStates(ArgumentMatchers.any()) + ) + .thenReturn(listOf(bluetoothDevice, bluetoothDeviceGroupId2)) + whenever(leAudioProfile.getGroupId(ArgumentMatchers.any())).thenAnswer { + val device = it.arguments.first() as BluetoothDevice + if (device == bluetoothDevice) GROUP_ID_1 else GROUP_ID_2 + } + + actionInteractorImpl.onClick(notConnectedDeviceItem, dialog) + verify(activityStarter, Mockito.never()) + .postStartActivityDismissingKeyguard( + ArgumentMatchers.any(), + ArgumentMatchers.anyInt(), + ArgumentMatchers.any() + ) + } + } + } + + @Test + fun testOnClick_hasTwoConnectedLeDevice_clickedConnectedLe_shouldLaunchSettings() { + with(kosmos) { + testScope.runTest { + whenever(cachedBluetoothDevice.device).thenReturn(bluetoothDevice) + whenever(cachedBluetoothDevice.address).thenReturn(DEVICE_ADDRESS) + whenever(cachedBluetoothDevice.profiles).thenReturn(listOf(leAudioProfile)) + whenever(leAudioProfile.isEnabled(ArgumentMatchers.any())).thenReturn(true) + + whenever(BluetoothUtils.isAudioSharingEnabled()).thenReturn(true) + whenever(localBluetoothManager.profileManager).thenReturn(profileManager) + whenever(profileManager.leAudioProfile).thenReturn(leAudioProfile) + whenever(profileManager.leAudioBroadcastAssistantProfile) + .thenReturn(assistantProfile) + + whenever( + assistantProfile.getDevicesMatchingConnectionStates(ArgumentMatchers.any()) + ) + .thenReturn(listOf(bluetoothDevice, bluetoothDeviceGroupId2)) + whenever(leAudioProfile.getGroupId(ArgumentMatchers.any())).thenAnswer { + val device = it.arguments.first() as BluetoothDevice + if (device == bluetoothDevice) GROUP_ID_1 else GROUP_ID_2 + } + + actionInteractorImpl.onClick(connectedMediaDeviceItem, dialog) + verify(activityStarter) + .postStartActivityDismissingKeyguard( + ArgumentMatchers.any(), + ArgumentMatchers.anyInt(), + ArgumentMatchers.any() + ) + } + } + } + + private companion object { + const val DEVICE_NAME = "device" + const val DEVICE_CONNECTION_SUMMARY = "active" + const val DEVICE_ADDRESS = "address" + const val GROUP_ID_1 = 1 + const val GROUP_ID_2 = 2 + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/DeviceItemFactoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/DeviceItemFactoryTest.kt index 4bcd9a9b3f1c..a27ccc67d584 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/DeviceItemFactoryTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/DeviceItemFactoryTest.kt @@ -22,8 +22,8 @@ import android.content.pm.PackageManager import android.media.AudioManager import android.platform.test.annotations.DisableFlags import android.platform.test.annotations.EnableFlags -import android.testing.AndroidTestingRunner import android.testing.TestableLooper +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.settingslib.bluetooth.CachedBluetoothDevice import com.android.settingslib.flags.Flags @@ -39,7 +39,7 @@ import org.mockito.junit.MockitoJUnit import org.mockito.junit.MockitoRule @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) @TestableLooper.RunWithLooper(setAsMainLooper = true) class DeviceItemFactoryTest : SysuiTestCase() { @get:Rule val mockitoRule: MockitoRule = MockitoJUnit.rule() diff --git a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/DeviceItemInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/DeviceItemInteractorTest.kt index 2b4f9503f371..7f7abaf9b689 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/DeviceItemInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/DeviceItemInteractorTest.kt @@ -20,8 +20,8 @@ import android.bluetooth.BluetoothAdapter import android.bluetooth.BluetoothDevice import android.content.Context import android.media.AudioManager -import android.testing.AndroidTestingRunner import android.testing.TestableLooper +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.settingslib.bluetooth.CachedBluetoothDevice import com.android.settingslib.bluetooth.LocalBluetoothManager @@ -43,7 +43,7 @@ import org.mockito.junit.MockitoJUnit import org.mockito.junit.MockitoRule @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) @TestableLooper.RunWithLooper(setAsMainLooper = true) class DeviceItemInteractorTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/helper/BouncerSceneLayoutTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/helper/BouncerSceneLayoutTest.kt index ca9582240b93..923687b9375d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/helper/BouncerSceneLayoutTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/helper/BouncerSceneLayoutTest.kt @@ -25,11 +25,13 @@ import com.android.systemui.bouncer.ui.helper.BouncerSceneLayout.STANDARD_BOUNCE import com.google.common.truth.Truth.assertThat import java.util.Locale import org.junit.Test +import platform.test.runner.parameterized.ParameterizedAndroidJunit4 +import platform.test.runner.parameterized.Parameter +import platform.test.runner.parameterized.Parameters import org.junit.runner.RunWith -import org.junit.runners.Parameterized @SmallTest -@RunWith(Parameterized::class) +@RunWith(ParameterizedAndroidJunit4::class) class BouncerSceneLayoutTest : SysuiTestCase() { data object Phone : @@ -79,7 +81,7 @@ class BouncerSceneLayoutTest : SysuiTestCase() { companion object { @JvmStatic - @Parameterized.Parameters(name = "{0}") + @Parameters(name = "{0}") fun testCases() = listOf( Phone to @@ -158,7 +160,7 @@ class BouncerSceneLayoutTest : SysuiTestCase() { } } - @Parameterized.Parameter @JvmField var testCase: TestCase? = null + @Parameter @JvmField var testCase: TestCase? = null @Test fun calculateLayout() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/broadcast/BroadcastSenderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/broadcast/BroadcastSenderTest.kt index 8e8172757cbe..1e9f8558d73c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/broadcast/BroadcastSenderTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/broadcast/BroadcastSenderTest.kt @@ -20,7 +20,7 @@ import android.content.Context import android.content.Intent import android.os.Bundle import android.os.UserHandle -import android.testing.AndroidTestingRunner +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.util.concurrency.FakeExecutor @@ -34,7 +34,7 @@ import org.mockito.Mock import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) @SmallTest class BroadcastSenderTest : SysuiTestCase() { @@ -138,4 +138,4 @@ class BroadcastSenderTest : SysuiTestCase() { verification.invoke() assertThat(wakeLock.isHeld).isFalse() } -}
\ No newline at end of file +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/broadcast/PendingRemovalStoreTest.kt b/packages/SystemUI/tests/src/com/android/systemui/broadcast/PendingRemovalStoreTest.kt index 43d2cb8be2d6..c693ecc7252f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/broadcast/PendingRemovalStoreTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/broadcast/PendingRemovalStoreTest.kt @@ -2,7 +2,7 @@ package com.android.systemui.broadcast import android.content.BroadcastReceiver import android.os.UserHandle -import android.testing.AndroidTestingRunner +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.broadcast.logging.BroadcastDispatcherLogger @@ -14,7 +14,7 @@ import org.mockito.Mock import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) @SmallTest class PendingRemovalStoreTest : SysuiTestCase() { @@ -78,4 +78,4 @@ class PendingRemovalStoreTest : SysuiTestCase() { assertThat(store.isPendingRemoval(receiverOne, user)).isTrue() assertThat(store.isPendingRemoval(receiverTwo, user)).isFalse() } -}
\ No newline at end of file +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/broadcast/UserBroadcastDispatcherTest.kt b/packages/SystemUI/tests/src/com/android/systemui/broadcast/UserBroadcastDispatcherTest.kt index 582f30110a5f..d878352183bd 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/broadcast/UserBroadcastDispatcherTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/broadcast/UserBroadcastDispatcherTest.kt @@ -21,8 +21,8 @@ import android.content.Context import android.content.IntentFilter import android.os.Handler import android.os.UserHandle +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest -import android.testing.AndroidTestingRunner import android.testing.TestableLooper import com.android.systemui.SysuiTestCase import com.android.systemui.broadcast.logging.BroadcastDispatcherLogger @@ -43,7 +43,7 @@ import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations import java.util.concurrent.Executor -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) @TestableLooper.RunWithLooper @SmallTest class UserBroadcastDispatcherTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/camera/CameraGestureHelperTest.kt b/packages/SystemUI/tests/src/com/android/systemui/camera/CameraGestureHelperTest.kt index 669795bc91a8..bea0db66555d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/camera/CameraGestureHelperTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/camera/CameraGestureHelperTest.kt @@ -24,6 +24,7 @@ import android.content.Intent import android.content.pm.ActivityInfo import android.content.pm.PackageManager import android.content.pm.ResolveInfo +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.ActivityIntentHelper import com.android.systemui.SysuiTestCase @@ -41,7 +42,6 @@ import com.google.common.util.concurrent.MoreExecutors import org.junit.Before import org.junit.Test import org.junit.runner.RunWith -import org.junit.runners.JUnit4 import org.mockito.Mock import org.mockito.Mockito.any import org.mockito.Mockito.anyInt @@ -50,7 +50,7 @@ import org.mockito.MockitoAnnotations import org.mockito.Mockito.`when` as whenever @SmallTest -@RunWith(JUnit4::class) +@RunWith(AndroidJUnit4::class) class CameraGestureHelperTest : SysuiTestCase() { @Mock diff --git a/packages/SystemUI/tests/src/com/android/systemui/camera/CameraIntentsTest.kt b/packages/SystemUI/tests/src/com/android/systemui/camera/CameraIntentsTest.kt index 1e522fc9941a..34940246a7cb 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/camera/CameraIntentsTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/camera/CameraIntentsTest.kt @@ -17,8 +17,8 @@ package com.android.systemui.camera import android.content.Intent +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest -import android.testing.AndroidTestingRunner import com.android.systemui.SysuiTestCase import org.junit.Assert.assertFalse import org.junit.Assert.assertTrue @@ -26,7 +26,7 @@ import org.junit.Test import org.junit.runner.RunWith @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) class CameraIntentsTest : SysuiTestCase() { companion object { val VALID_SECURE_INTENT = Intent(CameraIntents.DEFAULT_SECURE_CAMERA_INTENT_ACTION) diff --git a/packages/SystemUI/tests/src/com/android/systemui/charging/WiredChargingRippleControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/charging/WiredChargingRippleControllerTest.kt index 11756d5b8ea2..034bab855faf 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/charging/WiredChargingRippleControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/charging/WiredChargingRippleControllerTest.kt @@ -17,11 +17,11 @@ package com.android.systemui.charging import android.graphics.Rect -import android.testing.AndroidTestingRunner import android.view.Surface import android.view.View import android.view.WindowManager import android.view.WindowMetrics +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.internal.logging.UiEventLogger import com.android.systemui.res.R @@ -49,7 +49,7 @@ import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) class WiredChargingRippleControllerTest : SysuiTestCase() { private lateinit var controller: WiredChargingRippleController @Mock private lateinit var commandRegistry: CommandRegistry diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/BrightLineClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/BrightLineClassifierTest.java index 6afbde09610e..88bfcf00423f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/classifier/BrightLineClassifierTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/BrightLineClassifierTest.java @@ -29,10 +29,10 @@ import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import android.testing.AndroidTestingRunner; import android.view.MotionEvent; import android.view.accessibility.AccessibilityManager; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.internal.logging.MetricsLogger; @@ -58,7 +58,7 @@ import java.util.List; import java.util.Set; @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) public class BrightLineClassifierTest extends SysuiTestCase { private BrightLineFalsingManager mBrightLineFalsingManager; @Mock diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/BrightLineFalsingManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/BrightLineFalsingManagerTest.java index 6e00b70b5410..ec8cc4d493d0 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/classifier/BrightLineFalsingManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/BrightLineFalsingManagerTest.java @@ -25,10 +25,10 @@ import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import android.testing.AndroidTestingRunner; import android.view.MotionEvent; import android.view.accessibility.AccessibilityManager; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.internal.logging.MetricsLogger; @@ -53,7 +53,7 @@ import java.util.List; import java.util.Set; @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) public class BrightLineFalsingManagerTest extends SysuiTestCase { private BrightLineFalsingManager mBrightLineFalsingManager; @Mock diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/DiagonalClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/DiagonalClassifierTest.java index 14dcd58e40a2..8e1be4160498 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/classifier/DiagonalClassifierTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/DiagonalClassifierTest.java @@ -24,8 +24,8 @@ import static com.google.common.truth.Truth.assertThat; import static org.mockito.Mockito.when; -import android.testing.AndroidTestingRunner; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.systemui.util.DeviceConfigProxyFake; @@ -38,7 +38,7 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) public class DiagonalClassifierTest extends ClassifierTest { // Next variable is not actually five, but is very close. 5 degrees is currently the value diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/DistanceClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/DistanceClassifierTest.java index ab6d5b771d5a..cbfeceeb88d3 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/classifier/DistanceClassifierTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/DistanceClassifierTest.java @@ -21,8 +21,8 @@ import static com.android.systemui.classifier.Classifier.QS_SWIPE_SIDE; import static com.google.common.truth.Truth.assertThat; -import android.testing.AndroidTestingRunner; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.systemui.util.DeviceConfigProxyFake; @@ -33,7 +33,7 @@ import org.junit.Test; import org.junit.runner.RunWith; @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) public class DistanceClassifierTest extends ClassifierTest { private FalsingDataProvider mDataProvider; diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/DoubleTapClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/DoubleTapClassifierTest.java index 2ceee6dc56d8..9289867cbfe2 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/classifier/DoubleTapClassifierTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/DoubleTapClassifierTest.java @@ -22,9 +22,9 @@ import static org.mockito.ArgumentMatchers.anyDouble; import static org.mockito.ArgumentMatchers.anyList; import static org.mockito.Mockito.when; -import android.testing.AndroidTestingRunner; import android.view.MotionEvent; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import org.junit.After; @@ -38,7 +38,7 @@ import java.util.ArrayList; import java.util.List; @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) public class DoubleTapClassifierTest extends ClassifierTest { private static final int TOUCH_SLOP = 100; diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingA11yDelegateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingA11yDelegateTest.kt index 2c904e7e3735..8e4bec3f2e50 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingA11yDelegateTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingA11yDelegateTest.kt @@ -16,10 +16,10 @@ package com.android.systemui.classifier -import android.testing.AndroidTestingRunner import android.view.View import android.view.accessibility.AccessibilityNodeInfo.ACTION_CLICK import android.view.accessibility.AccessibilityNodeInfo.ACTION_LONG_CLICK +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import org.junit.Before @@ -31,7 +31,7 @@ import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) class FalsingA11yDelegateTest : SysuiTestCase() { @Mock lateinit var falsingCollector: FalsingCollector @Mock lateinit var view: View diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingCollectorImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingCollectorImplTest.java index 5361cef2ac64..5d0bfd7d3d87 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingCollectorImplTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingCollectorImplTest.java @@ -25,11 +25,11 @@ import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.view.KeyEvent; import android.view.MotionEvent; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.keyguard.KeyguardUpdateMonitor; @@ -66,7 +66,7 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) @TestableLooper.RunWithLooper(setAsMainLooper = true) public class FalsingCollectorImplTest extends SysuiTestCase { diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingDataProviderTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingDataProviderTest.java index 057b0a158cab..49c6239d2541 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingDataProviderTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingDataProviderTest.java @@ -25,11 +25,11 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.hardware.devicestate.DeviceStateManager.FoldStateListener; -import android.testing.AndroidTestingRunner; import android.util.DisplayMetrics; import android.view.KeyEvent; import android.view.MotionEvent; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.systemui.classifier.FalsingDataProvider.GestureFinalizedListener; @@ -46,7 +46,7 @@ import org.mockito.MockitoAnnotations; import java.util.List; @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) public class FalsingDataProviderTest extends ClassifierTest { private FalsingDataProvider mDataProvider; diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/HistoryTrackerTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/HistoryTrackerTest.java index 38355c74a4a7..8e19a1f84d72 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/classifier/HistoryTrackerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/HistoryTrackerTest.java @@ -18,8 +18,8 @@ package com.android.systemui.classifier; import static com.google.common.truth.Truth.assertThat; -import android.testing.AndroidTestingRunner; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; @@ -33,7 +33,7 @@ import org.mockito.MockitoAnnotations; import java.util.Collections; @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) public class HistoryTrackerTest extends SysuiTestCase { private FakeSystemClock mSystemClock = new FakeSystemClock(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/PointerCountClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/PointerCountClassifierTest.java index b8ea062eedc8..352a25c49e8b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/classifier/PointerCountClassifierTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/PointerCountClassifierTest.java @@ -23,9 +23,9 @@ import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.anyDouble; import static org.mockito.ArgumentMatchers.anyInt; -import android.testing.AndroidTestingRunner; import android.view.MotionEvent; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import org.junit.After; @@ -34,7 +34,7 @@ import org.junit.Test; import org.junit.runner.RunWith; @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) public class PointerCountClassifierTest extends ClassifierTest { private FalsingClassifier mClassifier; diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/ProximityClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/ProximityClassifierTest.java index 1c3922a57368..f965a11c2aa9 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/classifier/ProximityClassifierTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/ProximityClassifierTest.java @@ -24,9 +24,9 @@ import static com.google.common.truth.Truth.assertThat; import static org.mockito.Mockito.when; -import android.testing.AndroidTestingRunner; import android.view.MotionEvent; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.systemui.plugins.FalsingManager; @@ -40,7 +40,7 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) public class ProximityClassifierTest extends ClassifierTest { private static final long NS_PER_MS = 1000000; diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/SingleTapClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/SingleTapClassifierTest.java index e3c800e441e0..65e90888ecb2 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/classifier/SingleTapClassifierTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/SingleTapClassifierTest.java @@ -20,9 +20,9 @@ import static com.google.common.truth.Truth.assertThat; import static org.mockito.Mockito.when; -import android.testing.AndroidTestingRunner; import android.view.MotionEvent; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import org.junit.After; @@ -36,7 +36,7 @@ import java.util.ArrayList; import java.util.List; @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) public class SingleTapClassifierTest extends ClassifierTest { private static final int TOUCH_SLOP = 100; diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/TimeLimitedInputEventBufferTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/TimeLimitedInputEventBufferTest.java index ad7afa3c593f..9a27f386d519 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/classifier/TimeLimitedInputEventBufferTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/TimeLimitedInputEventBufferTest.java @@ -19,11 +19,11 @@ package com.android.systemui.classifier; import static org.hamcrest.CoreMatchers.is; import static org.junit.Assert.assertThat; -import android.testing.AndroidTestingRunner; import android.view.InputEvent; import android.view.KeyEvent; import android.view.MotionEvent; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; @@ -35,7 +35,7 @@ import org.junit.runner.RunWith; import org.mockito.MockitoAnnotations; @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) public class TimeLimitedInputEventBufferTest extends SysuiTestCase { private static final long MAX_AGE_MS = 100; diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/TypeClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/TypeClassifierTest.java index 588edb770047..80c44e2537ec 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/classifier/TypeClassifierTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/TypeClassifierTest.java @@ -33,8 +33,8 @@ import static com.google.common.truth.Truth.assertThat; import static org.mockito.Mockito.when; -import android.testing.AndroidTestingRunner; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import org.junit.Before; @@ -44,7 +44,7 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) public class TypeClassifierTest extends ClassifierTest { @Mock diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/ZigZagClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/ZigZagClassifierTest.java index ae2b8bbb4ce6..1fe726863f7f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/classifier/ZigZagClassifierTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/ZigZagClassifierTest.java @@ -20,8 +20,8 @@ import static com.android.systemui.classifier.Classifier.BRIGHTNESS_SLIDER; import static com.google.common.truth.Truth.assertThat; -import android.testing.AndroidTestingRunner; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.systemui.util.DeviceConfigProxyFake; @@ -34,7 +34,7 @@ import org.junit.runner.RunWith; import java.util.Random; @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) public class ZigZagClassifierTest extends ClassifierTest { private FalsingClassifier mClassifier; diff --git a/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardModelTest.kt index c0dada4725b8..5d76e325bd3a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardModelTest.kt @@ -22,8 +22,8 @@ import android.content.Context import android.graphics.Bitmap import android.net.Uri import android.os.PersistableBundle +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest -import androidx.test.runner.AndroidJUnit4 import com.android.systemui.SysuiTestCase import com.android.systemui.util.mockito.whenever import java.io.IOException diff --git a/packages/SystemUI/tests/src/com/android/systemui/common/coroutine/CoroutineResultTest.kt b/packages/SystemUI/tests/src/com/android/systemui/common/coroutine/CoroutineResultTest.kt index d552c9d922ff..de07cda21e75 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/common/coroutine/CoroutineResultTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/common/coroutine/CoroutineResultTest.kt @@ -14,7 +14,7 @@ package com.android.systemui.common.coroutine -import android.testing.AndroidTestingRunner +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.google.common.truth.Truth.assertThat @@ -28,7 +28,7 @@ import org.junit.runner.RunWith /** atest SystemUITests:CoroutineResultTest */ @OptIn(ExperimentalCoroutinesApi::class) @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) class CoroutineResultTest : SysuiTestCase() { @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/common/ui/view/LongPressHandlingViewInteractionHandlerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/common/ui/view/LongPressHandlingViewInteractionHandlerTest.kt index 2f4fc96ebf6c..bb400f274fbe 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/common/ui/view/LongPressHandlingViewInteractionHandlerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/common/ui/view/LongPressHandlingViewInteractionHandlerTest.kt @@ -18,6 +18,7 @@ package com.android.systemui.common.ui.view import android.view.ViewConfiguration +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.common.ui.view.LongPressHandlingViewInteractionHandler.MotionEventModel @@ -33,7 +34,6 @@ import kotlinx.coroutines.test.runTest import org.junit.Before import org.junit.Test import org.junit.runner.RunWith -import org.junit.runners.JUnit4 import org.mockito.Mock import org.mockito.Mockito.never import org.mockito.Mockito.verify @@ -41,7 +41,7 @@ import org.mockito.MockitoAnnotations @OptIn(ExperimentalCoroutinesApi::class) @SmallTest -@RunWith(JUnit4::class) +@RunWith(AndroidJUnit4::class) class LongPressHandlingViewInteractionHandlerTest : SysuiTestCase() { @Mock private lateinit var postDelayed: (Runnable, Long) -> DisposableHandle diff --git a/packages/SystemUI/tests/src/com/android/systemui/common/ui/view/SeekBarWithIconButtonsViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/common/ui/view/SeekBarWithIconButtonsViewTest.java index 4c4205eaa8bb..cecb5251b6e2 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/common/ui/view/SeekBarWithIconButtonsViewTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/common/ui/view/SeekBarWithIconButtonsViewTest.java @@ -25,12 +25,12 @@ import static org.mockito.Mockito.never; import static org.mockito.Mockito.reset; import static org.mockito.Mockito.verify; -import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.view.ViewGroup; import android.widget.ImageView; import android.widget.SeekBar; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; @@ -49,7 +49,7 @@ import org.mockito.MockitoAnnotations; * Tests for {@link SeekBarWithIconButtonsView} */ @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) @TestableLooper.RunWithLooper public class SeekBarWithIconButtonsViewTest extends SysuiTestCase { diff --git a/packages/SystemUI/tests/src/com/android/systemui/complication/ComplicationCollectionLiveDataTest.java b/packages/SystemUI/tests/src/com/android/systemui/complication/ComplicationCollectionLiveDataTest.java index 288f3b651a3c..ed214749d6a7 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/complication/ComplicationCollectionLiveDataTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/complication/ComplicationCollectionLiveDataTest.java @@ -21,10 +21,10 @@ import static com.google.common.truth.Truth.assertThat; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; -import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import androidx.lifecycle.Observer; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; @@ -48,7 +48,7 @@ import java.util.Collection; import java.util.HashSet; @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) @TestableLooper.RunWithLooper(setAsMainLooper = true) public class ComplicationCollectionLiveDataTest extends SysuiTestCase { diff --git a/packages/SystemUI/tests/src/com/android/systemui/complication/ComplicationHostViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/complication/ComplicationHostViewControllerTest.java index c43df17f498c..dd3f991e60b7 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/complication/ComplicationHostViewControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/complication/ComplicationHostViewControllerTest.java @@ -24,13 +24,13 @@ import static org.mockito.Mockito.when; import android.os.UserHandle; import android.provider.Settings; -import android.testing.AndroidTestingRunner; import android.view.View; import androidx.constraintlayout.widget.ConstraintLayout; import androidx.lifecycle.LifecycleOwner; import androidx.lifecycle.LiveData; import androidx.lifecycle.Observer; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; @@ -51,7 +51,7 @@ import java.util.Collections; import java.util.HashSet; @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) public class ComplicationHostViewControllerTest extends SysuiTestCase { @Mock ConstraintLayout mComplicationHostView; diff --git a/packages/SystemUI/tests/src/com/android/systemui/complication/ComplicationLayoutEngineTest.java b/packages/SystemUI/tests/src/com/android/systemui/complication/ComplicationLayoutEngineTest.java index baaeee1c3bd2..383e0fab73ff 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/complication/ComplicationLayoutEngineTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/complication/ComplicationLayoutEngineTest.java @@ -22,10 +22,10 @@ import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import android.testing.AndroidTestingRunner; import android.view.View; import androidx.constraintlayout.widget.ConstraintLayout; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; @@ -48,7 +48,7 @@ import java.util.function.Consumer; import java.util.stream.Collectors; @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) public class ComplicationLayoutEngineTest extends SysuiTestCase { @Mock ConstraintLayout mLayout; diff --git a/packages/SystemUI/tests/src/com/android/systemui/complication/ComplicationLayoutParamsTest.java b/packages/SystemUI/tests/src/com/android/systemui/complication/ComplicationLayoutParamsTest.java index a23e9e40959a..12cb8a61e0d8 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/complication/ComplicationLayoutParamsTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/complication/ComplicationLayoutParamsTest.java @@ -21,8 +21,8 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; -import android.testing.AndroidTestingRunner; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; @@ -36,7 +36,7 @@ import java.util.HashSet; import java.util.function.Consumer; @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) public class ComplicationLayoutParamsTest extends SysuiTestCase { /** * Ensures ComplicationLayoutParams cannot be constructed with improper position or direction. diff --git a/packages/SystemUI/tests/src/com/android/systemui/complication/ComplicationTypesUpdaterTest.java b/packages/SystemUI/tests/src/com/android/systemui/complication/ComplicationTypesUpdaterTest.java index 8cd23b27e4eb..d728517e2000 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/complication/ComplicationTypesUpdaterTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/complication/ComplicationTypesUpdaterTest.java @@ -25,8 +25,8 @@ import android.content.Context; import android.database.ContentObserver; import android.os.UserHandle; import android.provider.Settings; -import android.testing.AndroidTestingRunner; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.settingslib.dream.DreamBackend; @@ -50,7 +50,7 @@ import java.util.Arrays; import java.util.HashSet; @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) public class ComplicationTypesUpdaterTest extends SysuiTestCase { @Mock private Context mContext; diff --git a/packages/SystemUI/tests/src/com/android/systemui/complication/ComplicationUtilsTest.java b/packages/SystemUI/tests/src/com/android/systemui/complication/ComplicationUtilsTest.java index e23e1f4c0578..1e802337a9e7 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/complication/ComplicationUtilsTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/complication/ComplicationUtilsTest.java @@ -29,8 +29,8 @@ import static com.android.systemui.complication.ComplicationUtils.convertComplic import static com.google.common.truth.Truth.assertThat; -import android.testing.AndroidTestingRunner; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.settingslib.dream.DreamBackend; @@ -45,7 +45,7 @@ import java.util.HashSet; import java.util.Set; @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) public class ComplicationUtilsTest extends SysuiTestCase { @Test public void testConvertComplicationType() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/complication/ComplicationViewModelTransformerTest.java b/packages/SystemUI/tests/src/com/android/systemui/complication/ComplicationViewModelTransformerTest.java index 09675e28f5da..98b119ae75c4 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/complication/ComplicationViewModelTransformerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/complication/ComplicationViewModelTransformerTest.java @@ -21,9 +21,9 @@ import static org.junit.Assert.assertNotEquals; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import android.testing.AndroidTestingRunner; import androidx.lifecycle.ViewModel; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; @@ -38,7 +38,7 @@ import org.mockito.Mockito; import org.mockito.MockitoAnnotations; @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) public class ComplicationViewModelTransformerTest extends SysuiTestCase { @Mock ComplicationViewModelComponent.Factory mFactory; diff --git a/packages/SystemUI/tests/src/com/android/systemui/complication/DreamClockTimeComplicationTest.java b/packages/SystemUI/tests/src/com/android/systemui/complication/DreamClockTimeComplicationTest.java index b9aa4c65be92..22ab4994f026 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/complication/DreamClockTimeComplicationTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/complication/DreamClockTimeComplicationTest.java @@ -23,9 +23,9 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.content.Context; -import android.testing.AndroidTestingRunner; import android.view.View; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.internal.logging.UiEventLogger; @@ -42,7 +42,7 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) public class DreamClockTimeComplicationTest extends SysuiTestCase { @SuppressWarnings("HidingField") @Mock diff --git a/packages/SystemUI/tests/src/com/android/systemui/complication/DreamHomeControlsComplicationTest.java b/packages/SystemUI/tests/src/com/android/systemui/complication/DreamHomeControlsComplicationTest.java index 18bd960b30a5..ddf69b5b964c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/complication/DreamHomeControlsComplicationTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/complication/DreamHomeControlsComplicationTest.java @@ -29,9 +29,9 @@ import static org.mockito.Mockito.when; import android.content.ComponentName; import android.content.res.Resources; -import android.testing.AndroidTestingRunner; import android.view.View; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.internal.logging.UiEventLogger; @@ -62,7 +62,7 @@ import java.util.List; import java.util.Optional; @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) public class DreamHomeControlsComplicationTest extends SysuiTestCase { @Mock private DreamHomeControlsComplication mComplication; diff --git a/packages/SystemUI/tests/src/com/android/systemui/complication/DreamMediaEntryComplicationTest.java b/packages/SystemUI/tests/src/com/android/systemui/complication/DreamMediaEntryComplicationTest.java index 05b4a41d1b40..3a856a05ac02 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/complication/DreamMediaEntryComplicationTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/complication/DreamMediaEntryComplicationTest.java @@ -26,10 +26,10 @@ import static org.mockito.Mockito.when; import android.app.PendingIntent; import android.content.Intent; -import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.view.View; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.systemui.ActivityIntentHelper; @@ -51,7 +51,7 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) @TestableLooper.RunWithLooper public class DreamMediaEntryComplicationTest extends SysuiTestCase { @Mock diff --git a/packages/SystemUI/tests/src/com/android/systemui/complication/SmartSpaceComplicationTest.java b/packages/SystemUI/tests/src/com/android/systemui/complication/SmartSpaceComplicationTest.java index 87de8657f593..6c354ef0966c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/complication/SmartSpaceComplicationTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/complication/SmartSpaceComplicationTest.java @@ -24,9 +24,9 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.app.smartspace.SmartspaceTarget; -import android.testing.AndroidTestingRunner; import android.view.View; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; @@ -52,7 +52,7 @@ import java.util.HashSet; import java.util.Set; @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) public class SmartSpaceComplicationTest extends SysuiTestCase { @Mock diff --git a/packages/SystemUI/tests/src/com/android/systemui/compose/ComposeInitializerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/compose/ComposeInitializerTest.kt index 03e4f9ae1685..c2fe009c13bb 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/compose/ComposeInitializerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/compose/ComposeInitializerTest.kt @@ -17,10 +17,10 @@ package com.android.systemui.compose import android.content.Context -import android.testing.AndroidTestingRunner import android.testing.ViewUtils import android.widget.FrameLayout import androidx.compose.ui.platform.ComposeView +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.google.common.truth.Truth.assertThat @@ -28,7 +28,7 @@ import org.junit.Test import org.junit.runner.RunWith @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) class ComposeInitializerTest : SysuiTestCase() { @Test fun testCanAddComposeViewInInitializedWindow() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/CustomIconCacheTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/CustomIconCacheTest.kt index 4d0f2ed47495..28e0cffc4f78 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/controls/CustomIconCacheTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/controls/CustomIconCacheTest.kt @@ -18,7 +18,7 @@ package com.android.systemui.controls import android.content.ComponentName import android.graphics.drawable.Icon -import android.testing.AndroidTestingRunner +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import org.junit.Assert.assertNull @@ -30,7 +30,7 @@ import org.mockito.Mock import org.mockito.MockitoAnnotations @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) class CustomIconCacheTest : SysuiTestCase() { companion object { @@ -98,4 +98,4 @@ class CustomIconCacheTest : SysuiTestCase() { assertNull(customIconCache.retrieve(TEST_COMPONENT1, CONTROL_ID_1)) } -}
\ No newline at end of file +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/AuxiliaryPersistenceWrapperTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/AuxiliaryPersistenceWrapperTest.kt index 129fe9a36a0d..8d6e3a04ee50 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/AuxiliaryPersistenceWrapperTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/AuxiliaryPersistenceWrapperTest.kt @@ -17,7 +17,7 @@ package com.android.systemui.controls.controller import android.content.ComponentName -import android.testing.AndroidTestingRunner +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import org.junit.Assert.assertEquals @@ -37,7 +37,7 @@ import org.mockito.MockitoAnnotations import java.io.File @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) class AuxiliaryPersistenceWrapperTest : SysuiTestCase() { companion object { @@ -128,4 +128,4 @@ class AuxiliaryPersistenceWrapperTest : SysuiTestCase() { verify(persistenceWrapper, never()).storeFavorites(ArgumentMatchers.anyList()) } -}
\ No newline at end of file +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlActionCoordinatorImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlActionCoordinatorImplTest.kt index 6cc3ef19ef3b..928514657257 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlActionCoordinatorImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlActionCoordinatorImplTest.kt @@ -16,8 +16,8 @@ package com.android.systemui.controls.ui +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest -import android.testing.AndroidTestingRunner import android.view.HapticFeedbackConstants import com.android.systemui.SysuiTestCase import com.android.systemui.broadcast.BroadcastSender @@ -48,7 +48,7 @@ import org.mockito.MockitoAnnotations import java.util.Optional @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) class ControlActionCoordinatorImplTest : SysuiTestCase() { @Mock private lateinit var vibratorHelper: VibratorHelper diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsBindingControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsBindingControllerImplTest.kt index 724c9d1dfc42..ed0c7ee54f13 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsBindingControllerImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsBindingControllerImplTest.kt @@ -24,7 +24,7 @@ import android.service.controls.Control import android.service.controls.DeviceTypes import android.service.controls.IControlsSubscriber import android.service.controls.IControlsSubscription -import android.testing.AndroidTestingRunner +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.settings.UserTracker @@ -49,7 +49,7 @@ import org.mockito.Mockito.`when` import org.mockito.MockitoAnnotations @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) class ControlsBindingControllerImplTest : SysuiTestCase() { companion object { diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsControllerImplTest.kt index de455f6374f1..cf385e79483b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsControllerImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsControllerImplTest.kt @@ -26,7 +26,7 @@ import android.os.UserHandle import android.service.controls.Control import android.service.controls.DeviceTypes import android.service.controls.actions.ControlAction -import android.testing.AndroidTestingRunner +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.backup.BackupHelper @@ -74,7 +74,7 @@ import java.util.Optional import java.util.function.Consumer @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) class ControlsControllerImplTest : SysuiTestCase() { private val kosmos = testKosmos() diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsFavoritePersistenceWrapperTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsFavoritePersistenceWrapperTest.kt index 690b9a7248be..afa5ceccb6cb 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsFavoritePersistenceWrapperTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsFavoritePersistenceWrapperTest.kt @@ -18,7 +18,7 @@ package com.android.systemui.controls.controller import android.content.ComponentName import android.service.controls.DeviceTypes -import android.testing.AndroidTestingRunner +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.util.concurrency.FakeExecutor @@ -32,7 +32,7 @@ import org.junit.runner.RunWith import java.io.File @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) class ControlsFavoritePersistenceWrapperTest : SysuiTestCase() { private lateinit var file: File diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManagerTest.kt index b5d34768dc9f..f9c2c6b791f1 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManagerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManagerTest.kt @@ -26,7 +26,7 @@ import android.service.controls.IControlsProvider import android.service.controls.IControlsSubscriber import android.service.controls.actions.ControlAction import android.service.controls.actions.ControlActionWrapper -import android.testing.AndroidTestingRunner +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.util.concurrency.FakeExecutor @@ -57,7 +57,7 @@ import org.mockito.Mockito.`when` import org.mockito.MockitoAnnotations @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) class ControlsProviderLifecycleManagerTest : SysuiTestCase() { @Mock diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsTileResourceConfigurationImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsTileResourceConfigurationImplTest.kt index 581e88b52c0f..e04ce45592b1 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsTileResourceConfigurationImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsTileResourceConfigurationImplTest.kt @@ -16,7 +16,7 @@ package com.android.systemui.controls.controller -import android.testing.AndroidTestingRunner +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.res.R import com.android.systemui.SysuiTestCase @@ -25,7 +25,7 @@ import org.junit.Assert.assertEquals import org.junit.Test import org.junit.runner.RunWith -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) @SmallTest class ControlsTileResourceConfigurationImplTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/DeletionJobServiceTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/DeletionJobServiceTest.kt index 228374671ad4..b6ea62e48f0f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/DeletionJobServiceTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/DeletionJobServiceTest.kt @@ -19,7 +19,7 @@ package com.android.systemui.controls.controller import android.app.job.JobParameters import android.content.Context import android.os.PersistableBundle -import android.testing.AndroidTestingRunner +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.controls.controller.AuxiliaryPersistenceWrapper.DeletionJobService.Companion.USER @@ -37,7 +37,7 @@ import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) class DeletionJobServiceTest : SysuiTestCase() { @Mock private lateinit var context: Context diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/PackageUpdateMonitorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/PackageUpdateMonitorTest.kt index 85d6211bdb8c..282ea5ccb5c2 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/PackageUpdateMonitorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/PackageUpdateMonitorTest.kt @@ -20,7 +20,7 @@ import android.content.Context import android.content.pm.PackageManager import android.os.Handler import android.os.UserHandle -import android.testing.AndroidTestingRunner +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.util.mockito.any @@ -39,7 +39,7 @@ import org.mockito.Mockito.`when` as whenever import org.mockito.MockitoAnnotations @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) class PackageUpdateMonitorTest : SysuiTestCase() { @Mock private lateinit var context: Context diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ServiceWrapperTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ServiceWrapperTest.kt index 789d6dfebf72..b5c6c538ec9e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ServiceWrapperTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ServiceWrapperTest.kt @@ -23,7 +23,7 @@ import android.service.controls.IControlsSubscriber import android.service.controls.IControlsSubscription import android.service.controls.actions.ControlAction import android.service.controls.actions.ControlActionWrapper -import android.testing.AndroidTestingRunner +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import org.junit.Assert.assertEquals @@ -42,7 +42,7 @@ import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) class ServiceWrapperTest : SysuiTestCase() { @Mock diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/StatefulControlSubscriberTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/StatefulControlSubscriberTest.kt index 267520ef7f1d..7d197f75b5f9 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/StatefulControlSubscriberTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/StatefulControlSubscriberTest.kt @@ -20,7 +20,7 @@ import android.content.ComponentName import android.os.Binder import android.service.controls.Control import android.service.controls.IControlsSubscription -import android.testing.AndroidTestingRunner +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.util.concurrency.FakeExecutor @@ -36,7 +36,7 @@ import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) class StatefulControlSubscriberTest : SysuiTestCase() { @Mock diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/dagger/ControlsComponentTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/dagger/ControlsComponentTest.kt index 54f66dc957d9..844cc1f1f8fa 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/controls/dagger/ControlsComponentTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/controls/dagger/ControlsComponentTest.kt @@ -16,7 +16,7 @@ package com.android.systemui.controls.dagger -import android.testing.AndroidTestingRunner +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.internal.widget.LockPatternUtils import com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_NOT_REQUIRED @@ -45,7 +45,7 @@ import org.mockito.Mockito.`when` import org.mockito.MockitoAnnotations @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) class ControlsComponentTest : SysuiTestCase() { @Mock private lateinit var controller: ControlsController diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/management/AllModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/management/AllModelTest.kt index 4ea96169e8a5..5528f6523111 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/controls/management/AllModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/controls/management/AllModelTest.kt @@ -19,7 +19,7 @@ package com.android.systemui.controls.management import android.app.PendingIntent import android.content.ComponentName import android.service.controls.Control -import android.testing.AndroidTestingRunner +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.controls.ControlStatus @@ -37,7 +37,7 @@ import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) class AllModelTest : SysuiTestCase() { companion object { diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/management/AppAdapterTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/management/AppAdapterTest.kt index 226ef3b85706..56c7c854b69d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/controls/management/AppAdapterTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/controls/management/AppAdapterTest.kt @@ -18,10 +18,10 @@ package com.android.systemui.controls.management import android.content.ComponentName import android.content.res.Resources -import android.testing.AndroidTestingRunner 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.settingslib.core.lifecycle.Lifecycle import com.android.systemui.SysuiTestCase @@ -45,7 +45,7 @@ import org.mockito.MockitoAnnotations import java.text.Collator @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) @TestableLooper.RunWithLooper(setAsMainLooper = true) class AppAdapterTest : SysuiTestCase() { private val fakeSystemClock = FakeSystemClock() diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsEditingActivityTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsEditingActivityTest.kt index 2a4524b971a4..39e1e1d8bb57 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsEditingActivityTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsEditingActivityTest.kt @@ -3,12 +3,12 @@ package com.android.systemui.controls.management import android.content.ComponentName import android.content.Intent import android.os.Bundle -import android.testing.AndroidTestingRunner import android.testing.TestableLooper import android.view.View import android.widget.Button import android.window.OnBackInvokedCallback import android.window.OnBackInvokedDispatcher +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import androidx.test.rule.ActivityTestRule import com.android.systemui.res.R @@ -33,7 +33,7 @@ import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) @TestableLooper.RunWithLooper class ControlsEditingActivityTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsFavoritingActivityTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsFavoritingActivityTest.kt index 88d36af71958..f5616d45a3da 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsFavoritingActivityTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsFavoritingActivityTest.kt @@ -4,12 +4,12 @@ import android.content.ComponentName import android.content.Intent import android.os.Bundle import android.service.controls.Control -import android.testing.AndroidTestingRunner import android.testing.TestableLooper import android.view.View import android.widget.Button import android.window.OnBackInvokedCallback import android.window.OnBackInvokedDispatcher +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.FlakyTest import androidx.test.filters.SmallTest import androidx.test.rule.ActivityTestRule @@ -45,7 +45,7 @@ import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) @TestableLooper.RunWithLooper class ControlsFavoritingActivityTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsListingControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsListingControllerImplTest.kt index 6361e94bd0ef..e4f0910f99fd 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsListingControllerImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsListingControllerImplTest.kt @@ -28,7 +28,7 @@ import android.content.pm.ServiceInfo import android.os.Bundle import android.os.UserHandle import android.service.controls.ControlsProviderService -import android.testing.AndroidTestingRunner +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.settingslib.applications.ServiceListing import com.android.systemui.res.R @@ -65,7 +65,7 @@ import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) class ControlsListingControllerImplTest : SysuiTestCase() { companion object { diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsProviderSelectorActivityTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsProviderSelectorActivityTest.kt index d17495f21a68..7698520cdf2c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsProviderSelectorActivityTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsProviderSelectorActivityTest.kt @@ -23,10 +23,10 @@ import android.content.pm.ApplicationInfo import android.content.pm.ServiceInfo import android.graphics.drawable.Drawable import android.os.Bundle -import android.testing.AndroidTestingRunner import android.testing.TestableLooper import android.window.OnBackInvokedCallback import android.window.OnBackInvokedDispatcher +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import androidx.test.rule.ActivityTestRule import com.android.systemui.SysuiTestCase @@ -65,7 +65,7 @@ import org.mockito.Mockito.verifyNoMoreInteractions import org.mockito.MockitoAnnotations @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) @TestableLooper.RunWithLooper class ControlsProviderSelectorActivityTest : SysuiTestCase() { @Main private val executor: Executor = MoreExecutors.directExecutor() diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsRequestDialogTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsRequestDialogTest.kt index ca970bb41d56..5008927fc939 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsRequestDialogTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsRequestDialogTest.kt @@ -25,9 +25,9 @@ import android.os.UserHandle import android.service.controls.Control import android.service.controls.ControlsProviderService import android.service.controls.DeviceTypes -import android.testing.AndroidTestingRunner import android.testing.TestableLooper import androidx.lifecycle.Lifecycle +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.MediumTest import androidx.test.rule.ActivityTestRule import com.android.systemui.SysuiTestCase @@ -53,7 +53,7 @@ import org.mockito.MockitoAnnotations import java.util.concurrent.Executor @MediumTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) @TestableLooper.RunWithLooper class ControlsRequestDialogTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsRequestReceiverTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsRequestReceiverTest.kt index ae77d1f590e3..c49867a30dc9 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsRequestReceiverTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsRequestReceiverTest.kt @@ -30,7 +30,7 @@ import android.os.Parcelable import android.os.UserHandle import android.service.controls.Control import android.service.controls.ControlsProviderService -import android.testing.AndroidTestingRunner +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import org.junit.Assert.assertEquals @@ -49,7 +49,7 @@ import org.mockito.Mockito.`when` import org.mockito.MockitoAnnotations @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) class ControlsRequestReceiverTest : SysuiTestCase() { @Mock @@ -266,4 +266,4 @@ class ControlsRequestReceiverTest : SysuiTestCase() { } } } -}
\ No newline at end of file +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/management/FavoritesModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/management/FavoritesModelTest.kt index f0003ed603ab..281addc053f9 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/controls/management/FavoritesModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/controls/management/FavoritesModelTest.kt @@ -17,8 +17,8 @@ package com.android.systemui.controls.management import android.content.ComponentName -import android.testing.AndroidTestingRunner import androidx.recyclerview.widget.RecyclerView +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.controls.ControlInterface @@ -43,7 +43,7 @@ import org.mockito.Mockito.verifyNoMoreInteractions import org.mockito.MockitoAnnotations @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) class FavoritesModelTest : SysuiTestCase() { companion object { @@ -299,4 +299,4 @@ class FavoritesModelTest : SysuiTestCase() { } private fun getDividerPosition(): Int = model.elements.indexOf(dividerWrapper) -}
\ No newline at end of file +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/management/PanelConfirmationDialogFactoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/management/PanelConfirmationDialogFactoryTest.kt index 7f0ea9a7a6d0..d8aac101e84f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/controls/management/PanelConfirmationDialogFactoryTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/controls/management/PanelConfirmationDialogFactoryTest.kt @@ -19,7 +19,7 @@ package com.android.systemui.controls.management import android.content.Context import android.content.DialogInterface -import android.testing.AndroidTestingRunner +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.res.R @@ -39,7 +39,7 @@ import org.mockito.MockitoAnnotations import org.mockito.Mockito.`when` as whenever @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) class PanelConfirmationDialogFactoryTest : SysuiTestCase() { @Mock private lateinit var mockDialog : SystemUIDialog diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/panels/AuthorizedPanelsRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/panels/AuthorizedPanelsRepositoryImplTest.kt index 18ce4a8e1b7a..fd4c6810a7fc 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/controls/panels/AuthorizedPanelsRepositoryImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/controls/panels/AuthorizedPanelsRepositoryImplTest.kt @@ -19,7 +19,7 @@ package com.android.systemui.controls.panels import android.content.SharedPreferences import android.content.pm.UserInfo -import android.testing.AndroidTestingRunner +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.collectLastValue @@ -37,7 +37,7 @@ import org.junit.Before import org.junit.Test import org.junit.runner.RunWith -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) @SmallTest class AuthorizedPanelsRepositoryImplTest : SysuiTestCase() { val kosmos = testKosmos() diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/panels/SelectedComponentRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/panels/SelectedComponentRepositoryTest.kt index a7e7ba97b5e5..86e3481ff263 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/controls/panels/SelectedComponentRepositoryTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/controls/panels/SelectedComponentRepositoryTest.kt @@ -19,7 +19,7 @@ package com.android.systemui.controls.panels import android.content.ComponentName import android.content.SharedPreferences import android.os.UserHandle -import android.testing.AndroidTestingRunner +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.collectLastValue @@ -43,7 +43,7 @@ import org.mockito.Mock import org.mockito.MockitoAnnotations @ExperimentalCoroutinesApi -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) @SmallTest class SelectedComponentRepositoryTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/settings/ControlsSettingsDialogManagerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/settings/ControlsSettingsDialogManagerImplTest.kt index 154c3734558d..aee334f82f63 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/controls/settings/ControlsSettingsDialogManagerImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/controls/settings/ControlsSettingsDialogManagerImplTest.kt @@ -22,8 +22,8 @@ import android.content.SharedPreferences import android.database.ContentObserver import android.provider.Settings.Secure.LOCKSCREEN_ALLOW_TRIVIAL_CONTROLS import android.provider.Settings.Secure.LOCKSCREEN_SHOW_CONTROLS -import android.testing.AndroidTestingRunner import android.testing.TestableLooper +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.controls.settings.ControlsSettingsDialogManager.Companion.PREFS_SETTINGS_DIALOG_ATTEMPTS @@ -52,7 +52,7 @@ import org.mockito.Mockito.`when` import org.mockito.MockitoAnnotations @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) @TestableLooper.RunWithLooper class ControlsSettingsDialogManagerImplTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/settings/ControlsSettingsRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/settings/ControlsSettingsRepositoryImplTest.kt index b904ac14e707..3bdd5cf8cfe7 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/controls/settings/ControlsSettingsRepositoryImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/controls/settings/ControlsSettingsRepositoryImplTest.kt @@ -19,6 +19,7 @@ package com.android.systemui.controls.settings import android.content.pm.UserInfo 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.user.data.repository.FakeUserRepository @@ -33,10 +34,9 @@ import kotlinx.coroutines.test.runTest import org.junit.Before import org.junit.Test import org.junit.runner.RunWith -import org.junit.runners.JUnit4 @SmallTest -@RunWith(JUnit4::class) +@RunWith(AndroidJUnit4::class) @OptIn(ExperimentalCoroutinesApi::class) class ControlsSettingsRepositoryImplTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/start/ControlsStartableTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/start/ControlsStartableTest.kt index c44429bbbf9e..9e8914a1119f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/controls/start/ControlsStartableTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/controls/start/ControlsStartableTest.kt @@ -26,7 +26,7 @@ import android.content.pm.ApplicationInfo import android.content.pm.ServiceInfo import android.os.UserHandle import android.os.UserManager -import android.testing.AndroidTestingRunner +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.broadcast.BroadcastDispatcher @@ -75,7 +75,7 @@ import org.mockito.MockitoAnnotations @OptIn(ExperimentalCoroutinesApi::class) @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) class ControlsStartableTest : SysuiTestCase() { private val kosmos = testKosmos() diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/CanUseIconPredicateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/CanUseIconPredicateTest.kt index bfdb9231a9f8..193ce21dcfa0 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/CanUseIconPredicateTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/CanUseIconPredicateTest.kt @@ -21,7 +21,7 @@ import android.graphics.Bitmap import android.graphics.drawable.Icon import android.net.Uri import android.os.UserHandle -import android.testing.AndroidTestingRunner +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.google.common.truth.Truth.assertThat @@ -29,7 +29,7 @@ import org.junit.Test import org.junit.runner.RunWith @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) class CanUseIconPredicateTest : SysuiTestCase() { private companion object { diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlViewHolderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlViewHolderTest.kt index 101b8ed4f6f0..4b30fa5dd161 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlViewHolderTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlViewHolderTest.kt @@ -24,11 +24,11 @@ import android.graphics.drawable.Icon import android.service.controls.Control import android.service.controls.DeviceTypes import android.service.controls.templates.ControlTemplate -import android.testing.AndroidTestingRunner import android.testing.TestableLooper import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.res.R import com.android.systemui.SysuiTestCase @@ -44,7 +44,7 @@ import org.junit.runner.RunWith import org.mockito.Mockito.mock @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) @TestableLooper.RunWithLooper class ControlViewHolderTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlsActivityTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlsActivityTest.kt index e279d28de499..03aa622977be 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlsActivityTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlsActivityTest.kt @@ -19,8 +19,8 @@ package com.android.systemui.controls.ui import android.content.Intent import android.content.res.Configuration import android.service.dreams.IDreamManager -import android.testing.AndroidTestingRunner import android.testing.TestableLooper +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import androidx.test.rule.ActivityTestRule import com.android.systemui.SysuiTestCase @@ -39,7 +39,7 @@ import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) @TestableLooper.RunWithLooper class ControlsActivityTest : SysuiTestCase() { @Mock private lateinit var uiController: ControlsUiController diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlsDialogsFactoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlsDialogsFactoryTest.kt index 38c6a0e236ed..ca33f16b10ac 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlsDialogsFactoryTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlsDialogsFactoryTest.kt @@ -18,7 +18,7 @@ package com.android.systemui.controls.ui import android.content.Context -import android.testing.AndroidTestingRunner +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.res.R import com.android.systemui.SysuiTestCase @@ -36,7 +36,7 @@ import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) class ControlsDialogsFactoryTest : SysuiTestCase() { private companion object { diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlsPopupMenuTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlsPopupMenuTest.kt index 48e3962717c1..66303ebb0def 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlsPopupMenuTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlsPopupMenuTest.kt @@ -19,12 +19,12 @@ package com.android.systemui.controls.ui import android.app.Activity import android.graphics.Color import android.graphics.drawable.ShapeDrawable -import android.testing.AndroidTestingRunner import android.util.DisplayMetrics import android.view.View import android.view.ViewGroup import android.widget.PopupWindow.OnDismissListener import androidx.test.ext.junit.rules.ActivityScenarioRule +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.res.R import com.android.systemui.SysuiTestCase @@ -43,7 +43,7 @@ import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) open class ControlsPopupMenuTest : SysuiTestCase() { private companion object { diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlsUiControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlsUiControllerImplTest.kt index 8f3813d49b8f..20890a7780c8 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlsUiControllerImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlsUiControllerImplTest.kt @@ -26,13 +26,13 @@ import android.graphics.drawable.Drawable import android.os.UserHandle import android.service.controls.ControlsProviderService import android.service.controls.flags.Flags.FLAG_HOME_PANEL_DREAM -import android.testing.AndroidTestingRunner import android.testing.TestableLooper import android.util.AttributeSet import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.widget.FrameLayout +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.controls.ControlsMetricsLogger @@ -83,7 +83,7 @@ import org.mockito.Mockito.`when` import org.mockito.MockitoAnnotations @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) @TestableLooper.RunWithLooper class ControlsUiControllerImplTest : SysuiTestCase() { private val kosmos = testKosmos() diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/DetailDialogTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/DetailDialogTest.kt index 677108cab291..10b3ce31a895 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/DetailDialogTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/DetailDialogTest.kt @@ -19,9 +19,9 @@ package com.android.systemui.controls.ui import android.app.ActivityOptions import android.app.PendingIntent import android.content.Context -import android.testing.AndroidTestingRunner import android.testing.TestableLooper import androidx.test.ext.junit.rules.ActivityScenarioRule +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.activity.EmptyTestActivity @@ -44,7 +44,7 @@ import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) @TestableLooper.RunWithLooper class DetailDialogTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/OverflowMenuAdapterTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/OverflowMenuAdapterTest.kt index 483ab3bae6f1..6092b8c5eb65 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/OverflowMenuAdapterTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/OverflowMenuAdapterTest.kt @@ -17,7 +17,7 @@ package com.android.systemui.controls.ui -import android.testing.AndroidTestingRunner +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.google.common.truth.Truth.assertThat @@ -25,7 +25,7 @@ import org.junit.Test import org.junit.runner.RunWith @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) class OverflowMenuAdapterTest : SysuiTestCase() { @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/PanelTaskViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/PanelTaskViewControllerTest.kt index 021facc51dba..de2d8529adff 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/PanelTaskViewControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/PanelTaskViewControllerTest.kt @@ -25,8 +25,8 @@ import android.content.Intent import android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK import android.content.Intent.FLAG_ACTIVITY_NEW_TASK import android.graphics.Rect -import android.testing.AndroidTestingRunner import android.testing.TestableLooper +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.util.boundsOnScreen @@ -49,7 +49,7 @@ import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) @TestableLooper.RunWithLooper class PanelTaskViewControllerTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/SelectionItemTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/SelectionItemTest.kt index 57176f0eabe0..45798072ed73 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/SelectionItemTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/SelectionItemTest.kt @@ -1,7 +1,7 @@ package com.android.systemui.controls.ui import android.content.ComponentName -import android.testing.AndroidTestingRunner +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.controls.controller.StructureInfo @@ -11,7 +11,7 @@ import org.junit.Test import org.junit.runner.RunWith @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) class SelectionItemTest : SysuiTestCase() { @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ToggleRangeTemplateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ToggleRangeTemplateTest.kt index 31e09549a2b5..9f4836ad1d9b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ToggleRangeTemplateTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ToggleRangeTemplateTest.kt @@ -17,7 +17,7 @@ package com.android.systemui.controls.ui import android.service.controls.templates.RangeTemplate -import android.testing.AndroidTestingRunner +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import org.junit.Assert.assertEquals @@ -25,7 +25,7 @@ import org.junit.Test import org.junit.runner.RunWith @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) class ToggleRangeTemplateTest : SysuiTestCase() { @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/coroutines/FlowTest.kt b/packages/SystemUI/tests/src/com/android/systemui/coroutines/FlowTest.kt index 1e4753e5b4a5..23da3f1d3ac0 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/coroutines/FlowTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/coroutines/FlowTest.kt @@ -1,6 +1,6 @@ package com.android.systemui.coroutines -import android.testing.AndroidTestingRunner +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.google.common.truth.Truth.assertThat @@ -12,7 +12,7 @@ import org.junit.runner.RunWith @OptIn(ExperimentalCoroutinesApi::class) @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) class FlowTest : SysuiTestCase() { @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/decor/CutoutDecorProviderFactoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/decor/CutoutDecorProviderFactoryTest.kt index 1040ec453a7c..f029847161d1 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/decor/CutoutDecorProviderFactoryTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/decor/CutoutDecorProviderFactoryTest.kt @@ -18,7 +18,6 @@ package com.android.systemui.decor import android.graphics.Insets import android.graphics.Rect -import android.testing.AndroidTestingRunner import android.testing.TestableResources import android.util.RotationUtils import android.util.Size @@ -27,6 +26,7 @@ import android.view.DisplayCutout import android.view.DisplayCutout.BOUNDS_POSITION_LENGTH import android.view.DisplayInfo import android.view.Surface +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.util.mockito.any @@ -38,7 +38,7 @@ import org.mockito.Mock import org.mockito.Mockito.doAnswer import org.mockito.MockitoAnnotations -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) @SmallTest class CutoutDecorProviderFactoryTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/decor/OverlayWindowTest.kt b/packages/SystemUI/tests/src/com/android/systemui/decor/OverlayWindowTest.kt index a1cffc1419d5..69fab5675e7f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/decor/OverlayWindowTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/decor/OverlayWindowTest.kt @@ -17,11 +17,11 @@ package com.android.systemui.decor import android.graphics.Color -import android.testing.AndroidTestingRunner import android.testing.TestableLooper.RunWithLooper import android.view.DisplayCutout import android.view.Surface import android.view.View +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.res.R import com.android.systemui.SysuiTestCase @@ -34,7 +34,7 @@ import org.mockito.Mockito.spy import org.mockito.Mockito.times import org.mockito.Mockito.verify -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) @RunWithLooper(setAsMainLooper = true) @SmallTest class OverlayWindowTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/decor/PrivacyDotDecorProviderFactoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/decor/PrivacyDotDecorProviderFactoryTest.kt index e4ddc37c4f3c..6d6c6efff13a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/decor/PrivacyDotDecorProviderFactoryTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/decor/PrivacyDotDecorProviderFactoryTest.kt @@ -17,8 +17,8 @@ package com.android.systemui.decor import android.content.res.Resources -import android.testing.AndroidTestingRunner import android.view.DisplayCutout +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.res.R import com.android.systemui.SysuiTestCase @@ -30,7 +30,7 @@ import org.mockito.Mock import org.mockito.Mockito.spy import org.mockito.Mockito.`when` as whenever -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) @SmallTest class PrivacyDotDecorProviderFactoryTest : SysuiTestCase() { private lateinit var mPrivacyDotDecorProviderFactory: PrivacyDotDecorProviderFactory @@ -83,4 +83,4 @@ class PrivacyDotDecorProviderFactoryTest : SysuiTestCase() { and it.alignedBounds.contains(DisplayCutout.BOUNDS_POSITION_RIGHT)) }) } -}
\ No newline at end of file +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/decor/RoundedCornerDecorProviderFactoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/decor/RoundedCornerDecorProviderFactoryTest.kt index d1d48803c486..4da988a27cd5 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/decor/RoundedCornerDecorProviderFactoryTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/decor/RoundedCornerDecorProviderFactoryTest.kt @@ -16,9 +16,9 @@ package com.android.systemui.decor -import android.testing.AndroidTestingRunner import android.util.Size import android.view.DisplayCutout +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.res.R import com.android.systemui.SysuiTestCase @@ -30,7 +30,7 @@ import org.mockito.Mock import org.mockito.Mockito import org.mockito.Mockito.spy -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) @SmallTest class RoundedCornerDecorProviderFactoryTest : SysuiTestCase() { @@ -139,4 +139,4 @@ class RoundedCornerDecorProviderFactoryTest : SysuiTestCase() { }) } } -}
\ No newline at end of file +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/decor/RoundedCornerResDelegateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/decor/RoundedCornerResDelegateTest.kt index 2bff7d22785a..9d440c353d04 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/decor/RoundedCornerResDelegateTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/decor/RoundedCornerResDelegateTest.kt @@ -18,9 +18,9 @@ package com.android.systemui.decor import android.content.res.TypedArray import android.graphics.drawable.Drawable -import android.testing.AndroidTestingRunner import android.util.Size import androidx.annotation.DrawableRes +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.internal.R as InternalR import com.android.systemui.res.R as SystemUIR @@ -33,7 +33,7 @@ import org.junit.runner.RunWith import org.mockito.Mock import org.mockito.MockitoAnnotations -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) @SmallTest class RoundedCornerResDelegateTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/demomode/DemoModeControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/demomode/DemoModeControllerTest.kt index 6c2e13691fba..4793a52f3497 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/demomode/DemoModeControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/demomode/DemoModeControllerTest.kt @@ -18,8 +18,8 @@ package com.android.systemui.demomode import android.content.Intent import android.os.Bundle -import android.testing.AndroidTestingRunner import android.testing.TestableLooper +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.demomode.DemoMode.ACTION_DEMO @@ -40,7 +40,7 @@ import org.mockito.MockitoAnnotations @Suppress("EXPERIMENTAL_IS_NOT_ENABLED") @OptIn(ExperimentalCoroutinesApi::class) -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) @TestableLooper.RunWithLooper @SmallTest class DemoModeControllerTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/deviceentry/data/repository/FaceWakeUpTriggersConfigTest.kt b/packages/SystemUI/tests/src/com/android/systemui/deviceentry/data/repository/FaceWakeUpTriggersConfigTest.kt index e9b4bbb5bb1a..6b0de92711b9 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/deviceentry/data/repository/FaceWakeUpTriggersConfigTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/deviceentry/data/repository/FaceWakeUpTriggersConfigTest.kt @@ -17,7 +17,7 @@ package com.android.systemui.deviceentry.data.repository import android.os.PowerManager -import android.testing.AndroidTestingRunner +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.dump.DumpManager @@ -30,7 +30,7 @@ import org.junit.runner.RunWith import org.mockito.Mock import org.mockito.MockitoAnnotations -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) @SmallTest class FaceWakeUpTriggersConfigTest : SysuiTestCase() { @Mock lateinit var globalSettings: GlobalSettings diff --git a/packages/SystemUI/tests/src/com/android/systemui/devicepolicy/DevicePolicyManagerExtTest.kt b/packages/SystemUI/tests/src/com/android/systemui/devicepolicy/DevicePolicyManagerExtTest.kt index 820329119f8d..64ff5f73bd4d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/devicepolicy/DevicePolicyManagerExtTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/devicepolicy/DevicePolicyManagerExtTest.kt @@ -22,6 +22,7 @@ import android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_FEATURES_ALL import android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_FEATURES_NONE import android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_SECURE_CAMERA import android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_SHORTCUTS_ALL +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.settings.UserTracker @@ -31,13 +32,12 @@ import com.google.common.truth.Truth.assertThat import org.junit.Before import org.junit.Test import org.junit.runner.RunWith -import org.junit.runners.JUnit4 import org.mockito.ArgumentMatchers.anyInt import org.mockito.Mock import org.mockito.MockitoAnnotations @SmallTest -@RunWith(JUnit4::class) +@RunWith(AndroidJUnit4::class) class DevicePolicyManagerExtTest : SysuiTestCase() { @Mock lateinit var devicePolicyManager: DevicePolicyManager diff --git a/packages/SystemUI/tests/src/com/android/systemui/display/data/repository/DeviceStateRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/display/data/repository/DeviceStateRepositoryTest.kt index c79cbab87576..3f5b9a35d3a5 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/display/data/repository/DeviceStateRepositoryTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/display/data/repository/DeviceStateRepositoryTest.kt @@ -17,8 +17,8 @@ package com.android.systemui.display.data.repository import android.hardware.devicestate.DeviceStateManager -import android.testing.AndroidTestingRunner import android.testing.TestableLooper +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.internal.R import com.android.systemui.SysuiTestCase @@ -41,7 +41,7 @@ import org.junit.runner.RunWith import org.mockito.Mockito.never import org.mockito.Mockito.verify -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) @TestableLooper.RunWithLooper @OptIn(ExperimentalCoroutinesApi::class) @SmallTest diff --git a/packages/SystemUI/tests/src/com/android/systemui/display/data/repository/DisplayRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/display/data/repository/DisplayRepositoryTest.kt index 68d49c78c567..01868ae3e5da 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/display/data/repository/DisplayRepositoryTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/display/data/repository/DisplayRepositoryTest.kt @@ -18,11 +18,11 @@ package com.android.systemui.display.data.repository import android.hardware.display.DisplayManager import android.os.Looper -import android.testing.AndroidTestingRunner import android.testing.TestableLooper import android.view.Display import android.view.Display.TYPE_EXTERNAL import android.view.Display.TYPE_INTERNAL +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.FlowValue @@ -46,7 +46,7 @@ import org.mockito.Mockito.never import org.mockito.Mockito.times import org.mockito.Mockito.verify -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) @TestableLooper.RunWithLooper @OptIn(ExperimentalCoroutinesApi::class) @SmallTest diff --git a/packages/SystemUI/tests/src/com/android/systemui/display/domain/interactor/ConnectedDisplayInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/display/domain/interactor/ConnectedDisplayInteractorTest.kt index 37c740968261..fd9964f938c8 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/display/domain/interactor/ConnectedDisplayInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/display/domain/interactor/ConnectedDisplayInteractorTest.kt @@ -19,12 +19,12 @@ package com.android.systemui.display.domain.interactor import android.companion.virtual.VirtualDeviceManager import android.companion.virtual.flags.Flags.FLAG_INTERACTIVE_SCREEN_MIRROR import android.platform.test.annotations.EnableFlags -import android.testing.AndroidTestingRunner import android.testing.TestableLooper import android.view.Display import android.view.Display.TYPE_EXTERNAL import android.view.Display.TYPE_INTERNAL import android.view.Display.TYPE_VIRTUAL +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.FlowValue @@ -53,7 +53,7 @@ import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mockito.anyInt -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) @TestableLooper.RunWithLooper @OptIn(ExperimentalCoroutinesApi::class) @SmallTest diff --git a/packages/SystemUI/tests/src/com/android/systemui/display/ui/view/MirroringConfirmationDialogDelegateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/display/ui/view/MirroringConfirmationDialogDelegateTest.kt index d118cc718aed..8105bc8ae6a0 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/display/ui/view/MirroringConfirmationDialogDelegateTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/display/ui/view/MirroringConfirmationDialogDelegateTest.kt @@ -18,13 +18,13 @@ package com.android.systemui.display.ui.view import android.app.Dialog import android.graphics.Insets -import android.testing.AndroidTestingRunner import android.testing.TestableLooper import android.view.LayoutInflater import android.view.View import android.view.Window import android.view.WindowInsets import android.view.WindowInsetsAnimation +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.app.animation.Interpolators import com.android.systemui.SysuiTestCase @@ -42,7 +42,7 @@ import org.mockito.Mockito.never import org.mockito.Mockito.verify @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) @TestableLooper.RunWithLooper(setAsMainLooper = true) class MirroringConfirmationDialogDelegateTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeDockHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeDockHandlerTest.java index 6d2df19b997d..8c125f833b41 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeDockHandlerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeDockHandlerTest.java @@ -28,9 +28,9 @@ import static org.mockito.Mockito.when; import android.app.ActivityManager; import android.hardware.display.AmbientDisplayConfiguration; -import android.testing.AndroidTestingRunner; import android.testing.TestableLooper.RunWithLooper; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; @@ -46,7 +46,7 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) @RunWithLooper public class DozeDockHandlerTest extends SysuiTestCase { @Mock private DozeMachine mMachine; diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java index 27fd3b13d55a..aa5edae72684 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java @@ -45,8 +45,8 @@ import android.content.Intent; import android.os.PowerManager; import android.os.UserHandle; import android.provider.Settings; -import android.testing.AndroidTestingRunner; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; @@ -71,7 +71,7 @@ import org.mockito.MockitoAnnotations; import java.util.Optional; @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) public class DozeScreenBrightnessTest extends SysuiTestCase { private static final int DEFAULT_BRIGHTNESS = 10; @@ -583,4 +583,4 @@ public class DozeScreenBrightnessTest extends SysuiTestCase { private void waitForSensorManager() { mFakeExecutor.runAllReady(); } -}
\ No newline at end of file +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java index 3cc04511d57b..9c127b85b38e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java @@ -42,10 +42,10 @@ import android.content.res.Resources; import android.database.ContentObserver; import android.hardware.Sensor; import android.hardware.display.AmbientDisplayConfiguration; -import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.testing.TestableLooper.RunWithLooper; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; @@ -75,7 +75,7 @@ import java.util.ArrayList; import java.util.List; import java.util.function.Consumer; -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) @RunWithLooper @SmallTest public class DozeSensorsTest extends SysuiTestCase { diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSuppressorTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSuppressorTest.java index 92941f946286..fad52e090c69 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSuppressorTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSuppressorTest.java @@ -38,9 +38,9 @@ import static org.mockito.Mockito.when; import android.app.ActivityManager; import android.hardware.display.AmbientDisplayConfiguration; -import android.testing.AndroidTestingRunner; import android.testing.UiThreadTest; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; @@ -60,7 +60,7 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) @UiThreadTest public class DozeSuppressorTest extends SysuiTestCase { 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 3a6b07539a43..3d1a0d0cef3c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java @@ -21,12 +21,14 @@ import static com.android.systemui.doze.DozeMachine.State.INITIALIZED; import static com.android.systemui.doze.DozeMachine.State.UNINITIALIZED; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; 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.clearInvocations; import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; @@ -35,14 +37,16 @@ import static org.mockito.Mockito.when; import android.app.StatusBarManager; import android.hardware.Sensor; import android.hardware.display.AmbientDisplayConfiguration; -import android.testing.AndroidTestingRunner; +import android.platform.test.annotations.EnableFlags; import android.testing.TestableLooper.RunWithLooper; import android.view.Display; +import androidx.test.ext.junit.runners.AndroidJUnit4; 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; @@ -71,11 +75,12 @@ 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(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) @RunWithLooper(setAsMainLooper = true) public class DozeTriggersTest extends SysuiTestCase { @@ -85,6 +90,7 @@ public class DozeTriggersTest extends SysuiTestCase { private DozeHost mHost; @Mock private BroadcastDispatcher mBroadcastDispatcher; + private final AmbientDisplayConfiguration mConfig = DozeConfigurationUtil.createMockConfig(); @Mock private DockManager mDockManager; @Mock @@ -105,6 +111,8 @@ public class DozeTriggersTest extends SysuiTestCase { private SelectedUserInteractor mSelectedUserInteractor; @Mock private SessionTracker mSessionTracker; + @Captor + private ArgumentCaptor<DozeHost.Callback> mHostCallbackCaptor; private DozeTriggers mTriggers; private FakeSensorManager mSensors; @@ -116,7 +124,7 @@ public class DozeTriggersTest extends SysuiTestCase { public void setUp() throws Exception { MockitoAnnotations.initMocks(this); setupDozeTriggers( - DozeConfigurationUtil.createMockConfig(), + mConfig, DozeConfigurationUtil.createMockParameters()); } @@ -174,10 +182,69 @@ public class DozeTriggersTest extends SysuiTestCase { } @Test + public void testOnNotification_startsPulseRequest() { + // GIVEN device is dozing + Runnable pulseSuppressListener = mock(Runnable.class); + when(mMachine.getState()).thenReturn(DozeMachine.State.DOZE); + doAnswer(invocation -> null).when(mHost).addCallback(mHostCallbackCaptor.capture()); + mTriggers.transitionTo(UNINITIALIZED, DozeMachine.State.INITIALIZED); + mTriggers.transitionTo(DozeMachine.State.INITIALIZED, DozeMachine.State.DOZE); + clearInvocations(mMachine); + + // WHEN receive an alerting notification + mHostCallbackCaptor.getValue().onNotificationAlerted(pulseSuppressListener); + + // THEN entering to pulse + verify(mHost).setPulsePending(true); + // AND suppress listeners are NOT notified + verify(pulseSuppressListener, never()).run(); + } + + @Test + public void testOnNotification_cannotPulse_notificationSuppressed() { + // GIVEN device is dozing + Runnable pulseSuppressListener = mock(Runnable.class); + when(mMachine.getState()).thenReturn(DozeMachine.State.DOZE); + doAnswer(invocation -> null).when(mHost).addCallback(mHostCallbackCaptor.capture()); + mTriggers.transitionTo(UNINITIALIZED, DozeMachine.State.INITIALIZED); + mTriggers.transitionTo(DozeMachine.State.INITIALIZED, DozeMachine.State.DOZE); + clearInvocations(mMachine); + // AND pulsing is disabled + when(mConfig.pulseOnNotificationEnabled(anyInt())).thenReturn(false); + + // WHEN receive an alerting notification + mHostCallbackCaptor.getValue().onNotificationAlerted(pulseSuppressListener); + + // THEN NOT starting pulse + verify(mHost, never()).setPulsePending(anyBoolean()); + // AND the notification is suppressed + verify(pulseSuppressListener).run(); + } + + @Test + @EnableFlags(Flags.FLAG_NOTIFICATION_PULSING_FIX) + public void testOnNotification_alreadyPulsing_notificationNotSuppressed() { + // GIVEN device is pulsing + Runnable pulseSuppressListener = mock(Runnable.class); + when(mMachine.getState()).thenReturn(DozeMachine.State.DOZE_PULSING); + doAnswer(invocation -> null).when(mHost).addCallback(mHostCallbackCaptor.capture()); + mTriggers.transitionTo(UNINITIALIZED, DozeMachine.State.INITIALIZED); + mTriggers.transitionTo(DozeMachine.State.INITIALIZED, DozeMachine.State.DOZE_PULSING); + clearInvocations(mMachine); + + // WHEN receive an alerting notification + mHostCallbackCaptor.getValue().onNotificationAlerted(pulseSuppressListener); + + // THEN entering to pulse + verify(mHost, never()).setPulsePending(anyBoolean()); + // AND suppress listeners are NOT notified + verify(pulseSuppressListener, never()).run(); + } + + @Test public void testOnNotification_noPulseIfPulseIsNotPendingAnymore() { when(mMachine.getState()).thenReturn(DozeMachine.State.DOZE); - ArgumentCaptor<DozeHost.Callback> captor = ArgumentCaptor.forClass(DozeHost.Callback.class); - doAnswer(invocation -> null).when(mHost).addCallback(captor.capture()); + doAnswer(invocation -> null).when(mHost).addCallback(mHostCallbackCaptor.capture()); mTriggers.transitionTo(UNINITIALIZED, DozeMachine.State.INITIALIZED); mTriggers.transitionTo(DozeMachine.State.INITIALIZED, DozeMachine.State.DOZE); @@ -189,7 +256,7 @@ public class DozeTriggersTest extends SysuiTestCase { // WHEN prox check returns FAR mProximitySensor.setLastEvent(new ThresholdSensorEvent(false, 2)); - captor.getValue().onNotificationAlerted(null /* pulseSuppressedListener */); + mHostCallbackCaptor.getValue().onNotificationAlerted(null /* pulseSuppressedListener */); mProximitySensor.alertListeners(); // THEN don't request pulse because the pending pulse was abandoned early diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeUiTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeUiTest.java index e7caf000ef67..69e74d841919 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeUiTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeUiTest.java @@ -85,7 +85,7 @@ public class DozeUiTest extends SysuiTestCase { mHandler = mHandlerThread.getThreadHandler(); mFakeExecutor = new FakeExecutor(new FakeSystemClock()); mDozeUi = new DozeUi(mContext, mAlarmManager, mWakeLock, mHost, mHandler, - mDozeParameters, mFakeExecutor, mDozeLog); + mHandler, mDozeParameters, mFakeExecutor, mDozeLog); mDozeUi.setDozeMachine(mMachine); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeWallpaperStateTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeWallpaperStateTest.java index f07edf331f13..4253c766e62f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeWallpaperStateTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeWallpaperStateTest.java @@ -26,6 +26,7 @@ import static org.mockito.Mockito.when; import android.app.IWallpaperManager; import android.os.RemoteException; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; @@ -36,11 +37,10 @@ import com.android.systemui.statusbar.phone.DozeParameters; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; -import org.junit.runners.JUnit4; import org.mockito.Mock; import org.mockito.MockitoAnnotations; -@RunWith(JUnit4.class) +@RunWith(AndroidJUnit4.class) @SmallTest public class DozeWallpaperStateTest extends SysuiTestCase { diff --git a/packages/SystemUI/tests/src/com/android/systemui/flags/FakeFeatureFlagsTest.kt b/packages/SystemUI/tests/src/com/android/systemui/flags/FakeFeatureFlagsTest.kt index 2bd2bff80951..771ecaf967a2 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/flags/FakeFeatureFlagsTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/flags/FakeFeatureFlagsTest.kt @@ -16,7 +16,7 @@ package com.android.systemui.flags -import android.testing.AndroidTestingRunner +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.google.common.truth.Truth.assertThat @@ -25,7 +25,7 @@ import org.junit.Test import org.junit.runner.RunWith @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) class FakeFeatureFlagsTest : SysuiTestCase() { private val unreleasedFlag = UnreleasedFlag("-1000", "test") diff --git a/packages/SystemUI/tests/src/com/android/systemui/flags/FlagDependenciesTest.kt b/packages/SystemUI/tests/src/com/android/systemui/flags/FlagDependenciesTest.kt index 91da88e480d4..0ae59bbf47fe 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/flags/FlagDependenciesTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/flags/FlagDependenciesTest.kt @@ -16,8 +16,8 @@ package com.android.systemui.flags -import android.testing.AndroidTestingRunner import android.util.Log +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import java.io.PrintWriter @@ -25,7 +25,7 @@ import kotlin.test.fail import org.junit.Test import org.junit.runner.RunWith -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) @SmallTest class FlagDependenciesTest : SysuiTestCase() { @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/flags/ServerFlagReaderImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/flags/ServerFlagReaderImplTest.kt index 2daa86bf5c14..d1082bdca76b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/flags/ServerFlagReaderImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/flags/ServerFlagReaderImplTest.kt @@ -16,7 +16,7 @@ package com.android.systemui.flags -import android.testing.AndroidTestingRunner +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.util.DeviceConfigProxyFake @@ -33,7 +33,7 @@ import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) class ServerFlagReaderImplTest : SysuiTestCase() { private val NAMESPACE = "test" diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java index 6b1d39a6b278..03afcb77d88f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java @@ -1240,6 +1240,7 @@ public class KeyguardViewMediatorTest extends SysuiTestCase { mViewMediator.start(); mViewMediator.registerCentralSurfaces(mCentralSurfaces, null, null, null, null); + mViewMediator.onBootCompleted(); } private void captureKeyguardStateControllerCallback() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractorTest.kt index 7c92ede89541..42ab25fcd9bd 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractorTest.kt @@ -48,6 +48,7 @@ import com.android.systemui.keyguard.util.KeyguardTransitionRepositorySpySubject import com.android.systemui.kosmos.testScope import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAsleepForTest import com.android.systemui.power.domain.interactor.powerInteractor +import com.android.systemui.statusbar.domain.interactor.keyguardOcclusionInteractor import com.android.systemui.testKosmos import kotlin.test.Test import kotlinx.coroutines.ExperimentalCoroutinesApi @@ -78,6 +79,9 @@ class FromDreamingTransitionInteractorTest : SysuiTestCase() { fun setup() { underTest.start() + kosmos.fakeKeyguardRepository.setDreaming(true) + kosmos.keyguardOcclusionInteractor.setWmNotifiedShowWhenLockedActivityOnTop(true) + // Transition to DOZING and set the power interactor asleep. powerInteractor.setAsleepForTest() runBlocking { diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractorTest.kt index 88fe4ce51a5d..af76b088787e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractorTest.kt @@ -16,12 +16,18 @@ package com.android.systemui.keyguard.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.internal.widget.LockPatternUtils +import com.android.systemui.Flags import com.android.systemui.SysuiTestCase import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository +import com.android.systemui.keyguard.data.repository.fakeBiometricSettingsRepository import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository +import com.android.systemui.keyguard.shared.model.AuthenticationFlags import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.shared.model.TransitionState import com.android.systemui.keyguard.shared.model.TransitionStep @@ -81,6 +87,7 @@ class FromGoneTransitionInteractorTest : SysuiTestCase() { } @Test + @DisableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR) fun testTransitionsToLockscreen_ifFinishedInGone() = testScope.runTest { keyguardTransitionRepository.sendTransitionSteps( @@ -92,7 +99,34 @@ class FromGoneTransitionInteractorTest : SysuiTestCase() { kosmos.fakeKeyguardRepository.setKeyguardShowing(true) runCurrent() - // We're in the middle of a LOCKSCREEN -> GONE transition. + // We're in the middle of a GONE -> LOCKSCREEN transition. + assertThat(keyguardTransitionRepository) + .startedTransition( + to = KeyguardState.LOCKSCREEN, + ) + } + + @Test + @EnableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR) + fun testTransitionsToLockscreen_ifFinishedInGone_wmRefactor() = + testScope.runTest { + keyguardTransitionRepository.sendTransitionSteps( + from = KeyguardState.LOCKSCREEN, + to = KeyguardState.GONE, + testScope, + ) + reset(keyguardTransitionRepository) + + // Trigger lockdown. + kosmos.fakeBiometricSettingsRepository.setAuthenticationFlags( + AuthenticationFlags( + 0, + LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN + ) + ) + runCurrent() + + // We're in the middle of a GONE -> LOCKSCREEN transition. assertThat(keyguardTransitionRepository) .startedTransition( to = KeyguardState.LOCKSCREEN, diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/InWindowLauncherUnlockAnimationInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/InWindowLauncherUnlockAnimationInteractorTest.kt index c782e9d2d98d..459e41d7dbc0 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/InWindowLauncherUnlockAnimationInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/InWindowLauncherUnlockAnimationInteractorTest.kt @@ -32,6 +32,7 @@ import com.android.systemui.keyguard.shared.model.TransitionState import com.android.systemui.keyguard.shared.model.TransitionStep import com.android.systemui.keyguard.util.mockTopActivityClassName import com.android.systemui.shared.system.ActivityManagerWrapper +import com.android.systemui.user.domain.UserDomainLayerModule import dagger.BindsInstance import dagger.Component import junit.framework.Assert.assertEquals @@ -443,6 +444,7 @@ class InWindowLauncherUnlockAnimationInteractorTest : SysuiTestCase() { [ SysUITestModule::class, BiometricsDomainLayerModule::class, + UserDomainLayerModule::class, ] ) interface TestComponent { diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt index 86a976f30898..246cfbfbea68 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt @@ -30,7 +30,6 @@ import com.android.systemui.bouncer.data.repository.fakeKeyguardBouncerRepositor import com.android.systemui.communal.domain.interactor.communalInteractor import com.android.systemui.communal.domain.interactor.setCommunalAvailable import com.android.systemui.communal.shared.model.CommunalScenes -import com.android.systemui.deviceentry.data.repository.fakeDeviceEntryRepository import com.android.systemui.dock.fakeDockManager import com.android.systemui.flags.BrokenWithSceneContainer import com.android.systemui.flags.DisableSceneContainer @@ -608,31 +607,6 @@ class KeyguardTransitionScenariosTest(flags: FlagsParameterization?) : SysuiTest /** This handles security method NONE and screen off with lock timeout */ @Test - fun dozingToGoneWithKeyguardNotShowing() = - testScope.runTest { - // GIVEN a prior transition has run to DOZING - runTransitionAndSetWakefulness(KeyguardState.LOCKSCREEN, KeyguardState.DOZING) - runCurrent() - - // WHEN the device wakes up without a keyguard - keyguardRepository.setKeyguardShowing(false) - keyguardRepository.setKeyguardDismissible(true) - kosmos.fakeDeviceEntryRepository.setLockscreenEnabled(false) - powerInteractor.setAwakeForTest() - advanceTimeBy(60L) - - assertThat(transitionRepository) - .startedTransition( - to = KeyguardState.GONE, - from = KeyguardState.DOZING, - animatorAssertion = { it.isNotNull() } - ) - - coroutineContext.cancelChildren() - } - - /** This handles security method NONE and screen off with lock timeout */ - @Test @DisableSceneContainer fun dreamingToGoneWithKeyguardNotShowing() = testScope.runTest { diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/binder/InWindowLauncherUnlockAnimationManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/binder/InWindowLauncherUnlockAnimationManagerTest.kt index 33e9b363915c..c7f44164e582 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/binder/InWindowLauncherUnlockAnimationManagerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/binder/InWindowLauncherUnlockAnimationManagerTest.kt @@ -24,6 +24,7 @@ import com.android.systemui.biometrics.domain.BiometricsDomainLayerModule import com.android.systemui.dagger.SysUISingleton import com.android.systemui.keyguard.ui.view.InWindowLauncherUnlockAnimationManager import com.android.systemui.shared.system.smartspace.ILauncherUnlockAnimationController +import com.android.systemui.user.domain.UserDomainLayerModule import com.android.systemui.util.mockito.any import dagger.BindsInstance import dagger.Component @@ -120,6 +121,7 @@ class InWindowLauncherUnlockAnimationManagerTest : SysuiTestCase() { [ SysUITestModule::class, BiometricsDomainLayerModule::class, + UserDomainLayerModule::class, ] ) interface TestComponent { diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/binder/WindowManagerLockscreenVisibilityManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/binder/WindowManagerLockscreenVisibilityManagerTest.kt index 1f132989b169..4e1b12f70da5 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/binder/WindowManagerLockscreenVisibilityManagerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/binder/WindowManagerLockscreenVisibilityManagerTest.kt @@ -100,9 +100,11 @@ class WindowManagerLockscreenVisibilityManagerTest : SysuiTestCase() { } @Test - fun testAodVisible_noLockscreenShownCallYet_defaultsToShowLockscreen() { + fun testAodVisible_noLockscreenShownCallYet_doesNotShowLockscreenUntilLater() { underTest.setAodVisible(false) + verifyNoMoreInteractions(activityTaskManagerService) + underTest.setLockscreenShown(true) verify(activityTaskManagerService).setLockScreenShown(true, false) verifyNoMoreInteractions(activityTaskManagerService) } diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSectionTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSectionTest.kt index 7d4f03453fa7..201ee88cdd80 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSectionTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSectionTest.kt @@ -18,6 +18,7 @@ package com.android.systemui.keyguard.ui.view.layout.sections import android.view.View +import android.widget.LinearLayout import androidx.constraintlayout.widget.ConstraintLayout import androidx.constraintlayout.widget.ConstraintSet import androidx.constraintlayout.widget.ConstraintSet.GONE @@ -58,7 +59,7 @@ class SmartspaceSectionTest : SysuiTestCase() { private val smartspaceView = View(mContext).also { it.id = sharedR.id.bc_smartspace_view } private val weatherView = View(mContext).also { it.id = sharedR.id.weather_smartspace_view } - private val dateView = View(mContext).also { it.id = sharedR.id.date_smartspace_view } + private val dateView = LinearLayout(mContext).also { it.id = sharedR.id.date_smartspace_view } private lateinit var constraintLayout: ConstraintLayout private lateinit var constraintSet: ConstraintSet @@ -109,7 +110,7 @@ class SmartspaceSectionTest : SysuiTestCase() { whenever(keyguardSmartspaceViewModel.isDateWeatherDecoupled).thenReturn(true) underTest.addViews(constraintLayout) assert(smartspaceView.parent == constraintLayout) - assert(weatherView.parent == constraintLayout) + assertThat(weatherView.parent).isEqualTo(dateView) assert(dateView.parent == constraintLayout) } @@ -127,7 +128,7 @@ class SmartspaceSectionTest : SysuiTestCase() { whenever(keyguardSmartspaceViewModel.isDateWeatherDecoupled).thenReturn(true) underTest.addViews(constraintLayout) underTest.applyConstraints(constraintSet) - assertWeatherSmartspaceConstrains(constraintSet) + assertThat(weatherView.parent).isEqualTo(dateView) val smartspaceConstraints = constraintSet.getConstraint(smartspaceView.id) assertThat(smartspaceConstraints.layout.topToBottom).isEqualTo(dateView.id) @@ -141,7 +142,6 @@ class SmartspaceSectionTest : SysuiTestCase() { hasCustomWeatherDataDisplay.value = true underTest.addViews(constraintLayout) underTest.applyConstraints(constraintSet) - assertWeatherSmartspaceConstrains(constraintSet) val dateConstraints = constraintSet.getConstraint(dateView.id) assertThat(dateConstraints.layout.bottomToTop).isEqualTo(smartspaceView.id) @@ -168,12 +168,4 @@ class SmartspaceSectionTest : SysuiTestCase() { assertThat(constraintSet.getVisibility(weatherView.id)).isEqualTo(GONE) assertThat(constraintSet.getVisibility(dateView.id)).isEqualTo(GONE) } - - private fun assertWeatherSmartspaceConstrains(cs: ConstraintSet) { - val weatherConstraints = cs.getConstraint(weatherView.id) - assertThat(weatherConstraints.layout.topToTop).isEqualTo(dateView.id) - assertThat(weatherConstraints.layout.bottomToBottom).isEqualTo(dateView.id) - assertThat(weatherConstraints.layout.startToEnd).isEqualTo(dateView.id) - assertThat(weatherConstraints.layout.startMargin).isEqualTo(4) - } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataFilterImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataFilterImplTest.kt index 8471fe1ed2d2..064cf09959ca 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataFilterImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataFilterImplTest.kt @@ -663,6 +663,7 @@ class MediaDataFilterImplTest : SysuiTestCase() { true ) mediaDataFilter.onMediaDataLoaded(KEY, null, dataCurrent) + repository.setOrderedMedia() assertThat(currentMedia).containsExactly(controlCommonModel) verify(listener) @@ -706,6 +707,7 @@ class MediaDataFilterImplTest : SysuiTestCase() { true ) mediaDataFilter.onMediaDataLoaded(KEY, null, dataCurrent) + repository.setOrderedMedia() assertThat(currentMedia).containsExactly(controlCommonModel) verify(listener) .onMediaDataLoaded(eq(KEY), eq(null), eq(dataCurrent), eq(true), eq(0), eq(false)) @@ -760,6 +762,7 @@ class MediaDataFilterImplTest : SysuiTestCase() { ) mediaDataFilter.onMediaDataLoaded(KEY, null, dataCurrent) + repository.setOrderedMedia() assertThat(currentMedia).containsExactly(controlCommonModel) verify(listener) @@ -834,6 +837,7 @@ class MediaDataFilterImplTest : SysuiTestCase() { ) val dataCurrent = dataMain.copy(active = false, lastActive = clock.elapsedRealtime()) mediaDataFilter.onMediaDataLoaded(KEY, null, dataCurrent) + repository.setOrderedMedia() assertThat(currentMedia).containsExactly(controlCommonModel) verify(listener) @@ -922,6 +926,7 @@ class MediaDataFilterImplTest : SysuiTestCase() { // If there is media that was recently played but inactive val dataCurrent = dataMain.copy(active = false, lastActive = clock.elapsedRealtime()) mediaDataFilter.onMediaDataLoaded(KEY, null, dataCurrent) + repository.setOrderedMedia() verify(listener) .onMediaDataLoaded(eq(KEY), eq(null), eq(dataCurrent), eq(true), eq(0), eq(false)) @@ -986,6 +991,7 @@ class MediaDataFilterImplTest : SysuiTestCase() { // WHEN we have media that was recently played, but not currently active val dataCurrent = dataMain.copy(active = false, lastActive = clock.elapsedRealtime()) mediaDataFilter.onMediaDataLoaded(KEY, null, dataCurrent) + repository.setOrderedMedia() verify(listener) .onMediaDataLoaded(eq(KEY), eq(null), eq(dataCurrent), eq(true), eq(0), eq(false)) @@ -1039,6 +1045,7 @@ class MediaDataFilterImplTest : SysuiTestCase() { // WHEN we have media that was recently played, but not currently active val dataCurrent = dataMain.copy(active = false, lastActive = clock.elapsedRealtime()) mediaDataFilter.onMediaDataLoaded(KEY, null, dataCurrent) + repository.setOrderedMedia() verify(listener) .onMediaDataLoaded(eq(KEY), eq(null), eq(dataCurrent), eq(true), eq(0), eq(false)) 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 18b4c48d063e..3b541cd98f4e 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 @@ -242,7 +242,6 @@ class MediaDataProcessorTest : SysuiTestCase() { mediaCarouselInteractor = MediaCarouselInteractor( applicationScope = testScope.backgroundScope, - mediaDataRepository = mediaDataRepository, mediaDataProcessor = mediaDataProcessor, mediaTimeoutListener = mediaTimeoutListener, mediaResumeListener = mediaResumeListener, diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManagerTest.kt index 42bd46f323b5..514273042685 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManagerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManagerTest.kt @@ -21,6 +21,7 @@ import android.bluetooth.BluetoothLeBroadcastMetadata import android.content.pm.ApplicationInfo import android.content.pm.PackageManager import android.graphics.drawable.Drawable +import android.graphics.drawable.TestStubDrawable import android.media.MediaRoute2Info import android.media.MediaRouter2Manager import android.media.RoutingSessionInfo @@ -30,6 +31,7 @@ import android.media.session.MediaSession import android.platform.test.annotations.DisableFlags import android.platform.test.annotations.EnableFlags import android.platform.test.annotations.RequiresFlagsDisabled +import android.platform.test.annotations.RequiresFlagsEnabled import android.platform.test.flag.junit.DeviceFlagsValueProvider import android.testing.TestableLooper import androidx.test.ext.junit.runners.AndroidJUnit4 @@ -89,6 +91,11 @@ private const val NORMAL_APP_NAME = "NORMAL_APP_NAME" @RunWith(AndroidJUnit4::class) @TestableLooper.RunWithLooper public class MediaDeviceManagerTest : SysuiTestCase() { + + private companion object { + val OTHER_DEVICE_ICON_STUB = TestStubDrawable() + } + @get:Rule val checkFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule() private lateinit var manager: MediaDeviceManager @@ -155,6 +162,11 @@ public class MediaDeviceManagerTest : SysuiTestCase() { MediaTestUtils.emptyMediaData.copy(packageName = PACKAGE, token = session.sessionToken) whenever(controllerFactory.create(session.sessionToken)).thenReturn(controller) setupLeAudioConfiguration(false) + + context.orCreateTestableResources.addOverride( + R.drawable.ic_media_home_devices, + OTHER_DEVICE_ICON_STUB + ) } @After @@ -414,6 +426,7 @@ public class MediaDeviceManagerTest : SysuiTestCase() { assertThat(data.name).isEqualTo(REMOTE_DEVICE_NAME) } + @RequiresFlagsDisabled(FLAG_USE_PLAYBACK_INFO_FOR_ROUTING_CONTROLS) @Test fun onMediaDataLoaded_withRemotePlaybackInfo_noMatchingRoutingSession_setsDisabledDevice() { // GIVEN that MR2Manager returns null for routing session @@ -429,6 +442,24 @@ public class MediaDeviceManagerTest : SysuiTestCase() { assertThat(data.name).isNull() } + @RequiresFlagsEnabled(FLAG_USE_PLAYBACK_INFO_FOR_ROUTING_CONTROLS) + @Test + fun onMediaDataLoaded_withRemotePlaybackInfo_noMatchingRoutingSession_returnsOtherDevice() { + // GIVEN that MR2Manager returns null for routing session + whenever(playbackInfo.playbackType).thenReturn(PlaybackInfo.PLAYBACK_TYPE_REMOTE) + whenever(mr2.getRoutingSessionForMediaController(any())).thenReturn(null) + // WHEN a notification is added + manager.onMediaDataLoaded(KEY, null, mediaData) + fakeBgExecutor.runAllReady() + fakeFgExecutor.runAllReady() + // THEN the device is disabled and name and icon are set to "OTHER DEVICE". + val data = captureDeviceData(KEY) + assertThat(data.enabled).isFalse() + assertThat(data.name).isEqualTo(context.getString(R.string.media_seamless_other_device)) + assertThat(data.icon).isEqualTo(OTHER_DEVICE_ICON_STUB) + } + + @RequiresFlagsDisabled(FLAG_USE_PLAYBACK_INFO_FOR_ROUTING_CONTROLS) @Test fun onSelectedDeviceStateChanged_withRemotePlaybackInfo_noMatchingRoutingSession_setsDisabledDevice() { // GIVEN a notif is added @@ -449,7 +480,30 @@ public class MediaDeviceManagerTest : SysuiTestCase() { assertThat(data.enabled).isFalse() assertThat(data.name).isNull() } + @RequiresFlagsEnabled(FLAG_USE_PLAYBACK_INFO_FOR_ROUTING_CONTROLS) + @Test + fun onSelectedDeviceStateChanged_withRemotePlaybackInfo_noMatchingRoutingSession_returnOtherDevice() { + // GIVEN a notif is added + manager.onMediaDataLoaded(KEY, null, mediaData) + fakeBgExecutor.runAllReady() + fakeFgExecutor.runAllReady() + reset(listener) + // AND MR2Manager returns null for routing session + whenever(playbackInfo.playbackType).thenReturn(PlaybackInfo.PLAYBACK_TYPE_REMOTE) + whenever(mr2.getRoutingSessionForMediaController(any())).thenReturn(null) + // WHEN the selected device changes state + val deviceCallback = captureCallback() + deviceCallback.onSelectedDeviceStateChanged(device, 1) + fakeBgExecutor.runAllReady() + fakeFgExecutor.runAllReady() + // THEN the device is disabled and name and icon are set to "OTHER DEVICE". + val data = captureDeviceData(KEY) + assertThat(data.enabled).isFalse() + assertThat(data.name).isEqualTo(context.getString(R.string.media_seamless_other_device)) + assertThat(data.icon).isEqualTo(OTHER_DEVICE_ICON_STUB) + } + @RequiresFlagsDisabled(FLAG_USE_PLAYBACK_INFO_FOR_ROUTING_CONTROLS) @Test fun onDeviceListUpdate_withRemotePlaybackInfo_noMatchingRoutingSession_setsDisabledDevice() { // GIVEN a notif is added @@ -471,6 +525,29 @@ public class MediaDeviceManagerTest : SysuiTestCase() { assertThat(data.name).isNull() } + @RequiresFlagsEnabled(FLAG_USE_PLAYBACK_INFO_FOR_ROUTING_CONTROLS) + @Test + fun onDeviceListUpdate_withRemotePlaybackInfo_noMatchingRoutingSession_returnsOtherDevice() { + // GIVEN a notif is added + manager.onMediaDataLoaded(KEY, null, mediaData) + fakeBgExecutor.runAllReady() + fakeFgExecutor.runAllReady() + reset(listener) + // GIVEN that MR2Manager returns null for routing session + whenever(playbackInfo.playbackType).thenReturn(PlaybackInfo.PLAYBACK_TYPE_REMOTE) + whenever(mr2.getRoutingSessionForMediaController(any())).thenReturn(null) + // WHEN the selected device changes state + val deviceCallback = captureCallback() + deviceCallback.onDeviceListUpdate(mutableListOf(device)) + fakeBgExecutor.runAllReady() + fakeFgExecutor.runAllReady() + // THEN device is disabled and name and icon are set to "OTHER DEVICE". + val data = captureDeviceData(KEY) + assertThat(data.enabled).isFalse() + assertThat(data.name).isEqualTo(context.getString(R.string.media_seamless_other_device)) + assertThat(data.icon).isEqualTo(OTHER_DEVICE_ICON_STUB) + } + // With the flag enabled, MediaDeviceManager no longer gathers device name information directly. @RequiresFlagsDisabled(FLAG_USE_PLAYBACK_INFO_FOR_ROUTING_CONTROLS) @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaCarouselControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaCarouselControllerTest.kt index f7b3f2ea2804..ccf926a535e4 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaCarouselControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaCarouselControllerTest.kt @@ -40,7 +40,6 @@ import com.android.systemui.flags.fakeFeatureFlagsClassic import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor import com.android.systemui.keyguard.shared.model.KeyguardState -import com.android.systemui.kosmos.applicationCoroutineScope import com.android.systemui.kosmos.testDispatcher import com.android.systemui.kosmos.testScope import com.android.systemui.media.controls.MediaTestUtils @@ -159,7 +158,6 @@ class MediaCarouselControllerTest : SysuiTestCase() { testDispatcher = UnconfinedTestDispatcher() mediaCarouselController = MediaCarouselController( - applicationScope = kosmos.applicationCoroutineScope, context = context, mediaControlPanelFactory = mediaControlPanelFactory, visualStabilityProvider = visualStabilityProvider, @@ -195,11 +193,12 @@ class MediaCarouselControllerTest : SysuiTestCase() { whenever(panel.mediaViewController).thenReturn(mediaViewController) whenever(mediaFlags.isPersistentSsCardEnabled()).thenReturn(false) MediaPlayerData.clear() + FakeExecutor.exhaustExecutors(bgExecutor) verify(globalSettings) - .registerContentObserverSync( - eq(Settings.Global.getUriFor(Settings.Global.ANIMATOR_DURATION_SCALE)), - capture(settingsObserverCaptor) - ) + .registerContentObserverSync( + eq(Settings.Global.getUriFor(Settings.Global.ANIMATOR_DURATION_SCALE)), + capture(settingsObserverCaptor) + ) } @After @@ -895,10 +894,7 @@ class MediaCarouselControllerTest : SysuiTestCase() { mediaCarouselController.updateHostVisibility = { updatedVisibility = true } mediaCarouselController.mediaCarousel = mediaCarousel - val settingsJob = - mediaCarouselController.listenForLockscreenSettingChanges( - kosmos.applicationCoroutineScope - ) + val settingsJob = mediaCarouselController.listenForLockscreenSettingChanges(this) secureSettings.putBool(Settings.Secure.MEDIA_CONTROLS_LOCK_SCREEN, false) val keyguardJob = mediaCarouselController.listenForAnyStateToLockscreenTransition(this) @@ -925,10 +921,7 @@ class MediaCarouselControllerTest : SysuiTestCase() { mediaCarouselController.updateHostVisibility = { updatedVisibility = true } mediaCarouselController.mediaCarousel = mediaCarousel - val settingsJob = - mediaCarouselController.listenForLockscreenSettingChanges( - kosmos.applicationCoroutineScope - ) + val settingsJob = mediaCarouselController.listenForLockscreenSettingChanges(this) secureSettings.putBool(Settings.Secure.MEDIA_CONTROLS_LOCK_SCREEN, true) val keyguardJob = mediaCarouselController.listenForAnyStateToLockscreenTransition(this) diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManagerTest.kt index bba01bd0b78b..6c350cb4a5b0 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManagerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManagerTest.kt @@ -25,6 +25,8 @@ import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.keyguard.KeyguardViewController import com.android.systemui.SysuiTestCase +import com.android.systemui.communal.data.repository.fakeCommunalSceneRepository +import com.android.systemui.communal.shared.model.CommunalScenes import com.android.systemui.communal.ui.viewmodel.communalTransitionViewModel import com.android.systemui.controls.controller.ControlsControllerImplTest.Companion.eq import com.android.systemui.dreams.DreamOverlayStateController @@ -498,6 +500,8 @@ class MediaHierarchyManagerTest : SysuiTestCase() { to = KeyguardState.GLANCEABLE_HUB, testScope = testScope, ) + kosmos.fakeCommunalSceneRepository.changeScene(CommunalScenes.Communal) + runCurrent() mediaHierarchyManager.qsExpansion = 0f mediaHierarchyManager.setTransitionToFullShadeAmount(123f) @@ -542,6 +546,8 @@ class MediaHierarchyManagerTest : SysuiTestCase() { to = KeyguardState.GLANCEABLE_HUB, testScope = testScope, ) + kosmos.fakeCommunalSceneRepository.changeScene(CommunalScenes.Communal) + runCurrent() verify(mediaCarouselController) .onDesiredLocationChanged( eq(MediaHierarchyManager.LOCATION_COMMUNAL_HUB), @@ -557,6 +563,8 @@ class MediaHierarchyManagerTest : SysuiTestCase() { to = KeyguardState.LOCKSCREEN, testScope = testScope, ) + kosmos.fakeCommunalSceneRepository.changeScene(CommunalScenes.Blank) + runCurrent() verify(mediaCarouselController) .onDesiredLocationChanged( eq(MediaHierarchyManager.LOCATION_QQS), @@ -579,6 +587,8 @@ class MediaHierarchyManagerTest : SysuiTestCase() { to = KeyguardState.GLANCEABLE_HUB, testScope = testScope, ) + kosmos.fakeCommunalSceneRepository.changeScene(CommunalScenes.Communal) + runCurrent() verify(mediaCarouselController) .onDesiredLocationChanged( eq(MediaHierarchyManager.LOCATION_COMMUNAL_HUB), @@ -600,6 +610,8 @@ class MediaHierarchyManagerTest : SysuiTestCase() { to = KeyguardState.GLANCEABLE_HUB, testScope = testScope, ) + kosmos.fakeCommunalSceneRepository.changeScene(CommunalScenes.Communal) + runCurrent() verify(mediaCarouselController) .onDesiredLocationChanged( eq(MediaHierarchyManager.LOCATION_COMMUNAL_HUB), @@ -635,6 +647,8 @@ class MediaHierarchyManagerTest : SysuiTestCase() { to = KeyguardState.GLANCEABLE_HUB, testScope = testScope, ) + kosmos.fakeCommunalSceneRepository.changeScene(CommunalScenes.Communal) + runCurrent() // Mock the behavior for dreaming that pulling down shade will immediately set QS as // expanded expandQS() diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java index ec02c6445b57..411ff91ebc2f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java @@ -113,8 +113,8 @@ public class MediaOutputAdapterTest extends SysuiTestCase { LocalMediaManager.MediaDeviceState.STATE_DISCONNECTED); mMediaDevices.add(mMediaDevice1); mMediaDevices.add(mMediaDevice2); - mMediaItems.add(new MediaItem(mMediaDevice1)); - mMediaItems.add(new MediaItem(mMediaDevice2)); + mMediaItems.add(MediaItem.createDeviceMediaItem(mMediaDevice1)); + mMediaItems.add(MediaItem.createDeviceMediaItem(mMediaDevice2)); mMediaOutputAdapter = new MediaOutputAdapter(mMediaOutputController); mMediaOutputAdapter.updateItems(); @@ -146,7 +146,8 @@ public class MediaOutputAdapterTest extends SysuiTestCase { mMediaOutputAdapter.updateItems(); mViewHolder = (MediaOutputAdapter.MediaDeviceViewHolder) mMediaOutputAdapter .onCreateViewHolder(new LinearLayout(mContext), 0); - mMediaItems.add(new MediaItem()); + mMediaItems.add(MediaItem.createPairNewDeviceMediaItem()); + mMediaItems.add(MediaItem.createPairNewDeviceMediaItem()); mMediaOutputAdapter.updateItems(); mMediaOutputAdapter.onBindViewHolder(mViewHolder, 2); @@ -589,7 +590,7 @@ public class MediaOutputAdapterTest extends SysuiTestCase { mMediaOutputAdapter.updateItems(); mViewHolder = (MediaOutputAdapter.MediaDeviceViewHolder) mMediaOutputAdapter .onCreateViewHolder(new LinearLayout(mContext), 0); - mMediaItems.add(new MediaItem()); + mMediaItems.add(MediaItem.createPairNewDeviceMediaItem()); mMediaOutputAdapter.updateItems(); mMediaOutputAdapter.onBindViewHolder(mViewHolder, 2); mViewHolder.mContainerLayout.performClick(); @@ -725,7 +726,7 @@ public class MediaOutputAdapterTest extends SysuiTestCase { public void updateItems_controllerItemsUpdated_notUpdatesInAdapterUntilUpdateItems() { mMediaOutputAdapter.updateItems(); List<MediaItem> updatedList = new ArrayList<>(); - updatedList.add(new MediaItem()); + updatedList.add(MediaItem.createPairNewDeviceMediaItem()); when(mMediaOutputController.getMediaItemList()).thenReturn(updatedList); assertThat(mMediaOutputAdapter.getItemCount()).isEqualTo(mMediaItems.size()); diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/muteawait/MediaMuteAwaitConnectionManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/muteawait/MediaMuteAwaitConnectionManagerTest.kt index 53e9dc814161..2a8967edf13d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/muteawait/MediaMuteAwaitConnectionManagerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/muteawait/MediaMuteAwaitConnectionManagerTest.kt @@ -62,7 +62,7 @@ class MediaMuteAwaitConnectionManagerTest : SysuiTestCase() { MockitoAnnotations.initMocks(this) context.addMockSystemService(Context.AUDIO_SERVICE, audioManager) icon = context.getDrawable(R.drawable.ic_cake)!! - whenever(deviceIconUtil.getIconFromAudioDeviceType(any(), any())).thenReturn(icon) + whenever(deviceIconUtil.getIconFromAudioDeviceType(any())).thenReturn(icon) muteAwaitConnectionManager = MediaMuteAwaitConnectionManager( FakeExecutor(FakeSystemClock()), diff --git a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/data/ShellRecentTaskListProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/data/ShellRecentTaskListProviderTest.kt index 2f61579d53c3..721162099169 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/data/ShellRecentTaskListProviderTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/data/ShellRecentTaskListProviderTest.kt @@ -2,6 +2,7 @@ package com.android.systemui.mediaprojection.appselector.data import android.app.ActivityManager.RecentTaskInfo import android.content.pm.UserInfo +import android.graphics.Rect import android.os.UserManager import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest @@ -14,8 +15,10 @@ import com.android.systemui.settings.UserTracker 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.common.split.SplitScreenConstants.SNAP_TO_50_50 import com.android.wm.shell.recents.RecentTasks import com.android.wm.shell.util.GroupedRecentTaskInfo +import com.android.wm.shell.util.SplitBounds import com.google.common.truth.Truth.assertThat import java.util.Optional import java.util.function.Consumer @@ -101,6 +104,17 @@ class ShellRecentTaskListProviderTest : SysuiTestCase() { } @Test + fun loadRecentTasks_singleTaskPair_returnsTasksAsForeground() { + givenRecentTasks( + createTaskPair(taskId1 = 2, taskId2 = 3, isVisible = true), + ) + + val result = runBlocking { recentTaskListProvider.loadRecentTasks() } + + assertThat(result[0].isForegroundTask).isTrue() + } + + @Test fun loadRecentTasks_multipleTasks_returnsSecondVisibleTaskAsForegroundTask() { givenRecentTasks( createSingleTask(taskId = 1), @@ -144,6 +158,21 @@ class ShellRecentTaskListProviderTest : SysuiTestCase() { } @Test + fun loadRecentTasks_firstTaskIsGroupedAndVisible_marksBothGroupedTasksAsForeground() { + givenRecentTasks( + createTaskPair(taskId1 = 1, taskId2 = 2, isVisible = true), + createSingleTask(taskId = 3), + createSingleTask(taskId = 4), + ) + + val result = runBlocking { recentTaskListProvider.loadRecentTasks() } + + assertThat(result.map { it.isForegroundTask }) + .containsExactly(true, true, false, false) + .inOrder() + } + + @Test fun loadRecentTasks_secondTaskIsGroupedAndInvisible_marksBothGroupedTasksAsNotForeground() { givenRecentTasks( createSingleTask(taskId = 1), @@ -159,6 +188,21 @@ class ShellRecentTaskListProviderTest : SysuiTestCase() { } @Test + fun loadRecentTasks_firstTaskIsGroupedAndInvisible_marksBothGroupedTasksAsNotForeground() { + givenRecentTasks( + createTaskPair(taskId1 = 1, taskId2 = 2, isVisible = false), + createSingleTask(taskId = 3), + createSingleTask(taskId = 4), + ) + + val result = runBlocking { recentTaskListProvider.loadRecentTasks() } + + assertThat(result.map { it.isForegroundTask }) + .containsExactly(false, false, false, false) + .inOrder() + } + + @Test fun loadRecentTasks_assignsCorrectUserType() { givenRecentTasks( createSingleTask(taskId = 1, userId = 10, userType = STANDARD), @@ -224,7 +268,7 @@ class ShellRecentTaskListProviderTest : SysuiTestCase() { GroupedRecentTaskInfo.forSplitTasks( createTaskInfo(taskId1, userId1, isVisible), createTaskInfo(taskId2, userId2, isVisible), - null + SplitBounds(Rect(), Rect(), taskId1, taskId2, SNAP_TO_50_50) ) private fun createTaskInfo(taskId: Int, userId: Int, isVisible: Boolean = false) = diff --git a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/data/repository/MediaProjectionManagerRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/data/repository/MediaProjectionManagerRepositoryTest.kt index b7fefc0a202f..c0d411b12496 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/data/repository/MediaProjectionManagerRepositoryTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/data/repository/MediaProjectionManagerRepositoryTest.kt @@ -16,7 +16,9 @@ package com.android.systemui.mediaprojection.data.repository +import android.media.projection.MediaProjectionInfo import android.os.Binder +import android.os.UserHandle import android.view.ContentRecordingSession import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest @@ -26,6 +28,7 @@ import com.android.systemui.kosmos.testScope import com.android.systemui.mediaprojection.data.model.MediaProjectionState import com.android.systemui.mediaprojection.taskswitcher.FakeActivityTaskManager.Companion.createTask import com.android.systemui.mediaprojection.taskswitcher.FakeActivityTaskManager.Companion.createToken +import com.android.systemui.mediaprojection.taskswitcher.FakeMediaProjectionManager.Companion.createDisplaySession import com.android.systemui.mediaprojection.taskswitcher.fakeActivityTaskManager import com.android.systemui.mediaprojection.taskswitcher.fakeMediaProjectionManager import com.android.systemui.mediaprojection.taskswitcher.taskSwitcherKosmos @@ -33,6 +36,7 @@ import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.test.runTest import org.junit.Test import org.junit.runner.RunWith +import org.mockito.kotlin.verify @RunWith(AndroidJUnit4::class) @SmallTest @@ -55,7 +59,8 @@ class MediaProjectionManagerRepositoryTest : SysuiTestCase() { fakeActivityTaskManager.addRunningTasks(task) repo.switchProjectedTask(task) - assertThat(state).isEqualTo(MediaProjectionState.SingleTask(task)) + assertThat(state).isInstanceOf(MediaProjectionState.Projecting.SingleTask::class.java) + assertThat((state as MediaProjectionState.Projecting.SingleTask).task).isEqualTo(task) } @Test @@ -97,7 +102,7 @@ class MediaProjectionManagerRepositoryTest : SysuiTestCase() { session = ContentRecordingSession.createDisplaySession(/* displayToMirror= */ 123) ) - assertThat(state).isEqualTo(MediaProjectionState.EntireScreen) + assertThat(state).isInstanceOf(MediaProjectionState.Projecting.EntireScreen::class.java) } @Test @@ -110,7 +115,27 @@ class MediaProjectionManagerRepositoryTest : SysuiTestCase() { session = ContentRecordingSession.createTaskSession(taskWindowContainerToken) ) - assertThat(state).isEqualTo(MediaProjectionState.EntireScreen) + assertThat(state).isInstanceOf(MediaProjectionState.Projecting.EntireScreen::class.java) + } + + @Test + fun mediaProjectionState_entireScreen_hasHostPackage() = + testScope.runTest { + val state by collectLastValue(repo.mediaProjectionState) + + val info = + MediaProjectionInfo( + /* packageName= */ "com.media.projection.repository.test", + /* handle= */ UserHandle.getUserHandleForUid(UserHandle.myUserId()), + /* launchCookie = */ null, + ) + fakeMediaProjectionManager.dispatchOnSessionSet( + info = info, + session = createDisplaySession(), + ) + + assertThat((state as MediaProjectionState.Projecting.EntireScreen).hostPackage) + .isEqualTo("com.media.projection.repository.test") } @Test @@ -125,6 +150,39 @@ class MediaProjectionManagerRepositoryTest : SysuiTestCase() { session = ContentRecordingSession.createTaskSession(token.asBinder()) ) - assertThat(state).isEqualTo(MediaProjectionState.SingleTask(task)) + assertThat(state).isInstanceOf(MediaProjectionState.Projecting.SingleTask::class.java) + assertThat((state as MediaProjectionState.Projecting.SingleTask).task).isEqualTo(task) + } + + @Test + fun mediaProjectionState_singleTask_hasHostPackage() = + testScope.runTest { + val state by collectLastValue(repo.mediaProjectionState) + + val token = createToken() + val task = createTask(taskId = 1, token = token) + fakeActivityTaskManager.addRunningTasks(task) + + val info = + MediaProjectionInfo( + /* packageName= */ "com.media.projection.repository.test", + /* handle= */ UserHandle.getUserHandleForUid(UserHandle.myUserId()), + /* launchCookie = */ null, + ) + fakeMediaProjectionManager.dispatchOnSessionSet( + info = info, + session = ContentRecordingSession.createTaskSession(token.asBinder()) + ) + + assertThat((state as MediaProjectionState.Projecting.SingleTask).hostPackage) + .isEqualTo("com.media.projection.repository.test") + } + + @Test + fun stopProjecting_invokesManager() = + testScope.runTest { + repo.stopProjecting() + + verify(fakeMediaProjectionManager.mediaProjectionManager).stopActiveProjection() } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleSpaceWidgetManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleSpaceWidgetManagerTest.java index 56a2adcef699..16a022f720ae 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleSpaceWidgetManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleSpaceWidgetManagerTest.java @@ -140,8 +140,6 @@ import java.util.List; import java.util.Map; import java.util.Optional; import java.util.Set; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; @SmallTest @@ -1620,60 +1618,6 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase { verify(mAppWidgetManager, times(1)).setWidgetPreview(any(), anyInt(), any()); } - @Test - @EnableFlags({ - android.appwidget.flags.Flags.FLAG_GENERATED_PREVIEWS, - android.appwidget.flags.Flags.FLAG_DRAW_DATA_PARCEL - }) - public void testUpdateGeneratedPreviewWithDataParcel_userLocked() throws InterruptedException { - when(mUserManager.isUserUnlocked(mUserTracker.getUserHandle())).thenReturn(false); - - mManager.updateGeneratedPreviewForUser(mUserTracker.getUserHandle()); - assertThat(waitForBackgroundJob()).isTrue(); - verify(mAppWidgetManager, times(0)).setWidgetPreview(any(), anyInt(), any()); - } - - @Test - @EnableFlags({ - android.appwidget.flags.Flags.FLAG_GENERATED_PREVIEWS, - android.appwidget.flags.Flags.FLAG_DRAW_DATA_PARCEL - }) - public void testUpdateGeneratedPreviewWithDataParcel_userUnlocked() - throws InterruptedException { - when(mUserManager.isUserUnlocked(mUserTracker.getUserHandle())).thenReturn(true); - when(mAppWidgetManager.setWidgetPreview(any(), anyInt(), any())).thenReturn(true); - - mManager.updateGeneratedPreviewForUser(mUserTracker.getUserHandle()); - assertThat(waitForBackgroundJob()).isTrue(); - verify(mAppWidgetManager, times(1)).setWidgetPreview(any(), anyInt(), any()); - } - - @Test - @EnableFlags({ - android.appwidget.flags.Flags.FLAG_GENERATED_PREVIEWS, - android.appwidget.flags.Flags.FLAG_DRAW_DATA_PARCEL - }) - public void testUpdateGeneratedPreviewWithDataParcel_doesNotSetTwice() - throws InterruptedException { - when(mUserManager.isUserUnlocked(mUserTracker.getUserHandle())).thenReturn(true); - when(mAppWidgetManager.setWidgetPreview(any(), anyInt(), any())).thenReturn(true); - - mManager.updateGeneratedPreviewForUser(mUserTracker.getUserHandle()); - mManager.updateGeneratedPreviewForUser(mUserTracker.getUserHandle()); - assertThat(waitForBackgroundJob()).isTrue(); - verify(mAppWidgetManager, times(1)).setWidgetPreview(any(), anyInt(), any()); - } - - private boolean waitForBackgroundJob() throws InterruptedException { - final CountDownLatch latch = new CountDownLatch(1); - mFakeExecutor.execute(latch::countDown); - mFakeExecutor.runAllReady(); - mFakeExecutor.advanceClockToNext(); - mFakeExecutor.runAllReady(); - return latch.await(30000, TimeUnit.MILLISECONDS); - - } - private void setFinalField(String fieldName, int value) { try { Field field = NotificationManager.Policy.class.getDeclaredField(fieldName); diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.kt index 2d282dcd1514..8aaa121640a3 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.kt @@ -14,6 +14,7 @@ package com.android.systemui.qs import android.graphics.Rect +import android.platform.test.flag.junit.FlagsParameterization import android.testing.TestableContext import android.testing.TestableLooper import android.testing.TestableLooper.RunWithLooper @@ -25,15 +26,16 @@ import android.view.ViewGroup.LayoutParams.MATCH_PARENT import android.view.accessibility.AccessibilityNodeInfo import android.widget.FrameLayout import android.widget.LinearLayout -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.flags.DisableSceneContainer +import com.android.systemui.flags.parameterizeSceneContainerFlag import com.android.systemui.plugins.qs.QSTile import com.android.systemui.plugins.qs.QSTileView import com.android.systemui.qs.QSPanelControllerBase.TileRecord import com.android.systemui.qs.logging.QSLogger import com.android.systemui.qs.tileimpl.QSTileViewImpl +import com.android.systemui.res.R import com.google.common.truth.Truth.assertThat import org.junit.After import org.junit.Before @@ -44,11 +46,17 @@ import org.mockito.Mockito.mock import org.mockito.Mockito.never import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations +import platform.test.runner.parameterized.ParameterizedAndroidJunit4 +import platform.test.runner.parameterized.Parameters -@RunWith(AndroidJUnit4::class) +@RunWith(ParameterizedAndroidJunit4::class) @RunWithLooper @SmallTest -class QSPanelTest : SysuiTestCase() { +class QSPanelTest(flags: FlagsParameterization) : SysuiTestCase() { + + init { + mSetFlagsRule.setFlagsParameterization(flags) + } @Mock private lateinit var qsLogger: QSLogger @@ -57,9 +65,8 @@ class QSPanelTest : SysuiTestCase() { private lateinit var footer: View - private val themedContext = TestableContext( - ContextThemeWrapper(context, R.style.Theme_SystemUI_QuickSettings) - ) + private val themedContext = + TestableContext(ContextThemeWrapper(context, R.style.Theme_SystemUI_QuickSettings)) @Before @Throws(Exception::class) @@ -106,38 +113,8 @@ class QSPanelTest : SysuiTestCase() { } @Test - fun testTilesFooterVisibleRTLLandscapeMedia() { - qsPanel.layoutDirection = View.LAYOUT_DIRECTION_RTL - // We need at least a tile so the layout has a height - qsPanel.tileLayout?.addTile( - QSPanelControllerBase.TileRecord( - mock(QSTile::class.java), - QSTileViewImpl(themedContext) - ) - ) - - val mediaView = FrameLayout(themedContext) - mediaView.addView(View(themedContext), MATCH_PARENT, 800) - - qsPanel.setUsingHorizontalLayout(/* horizontal */ true, mediaView, /* force */ true) - qsPanel.measure( - /* width */ View.MeasureSpec.makeMeasureSpec(3000, View.MeasureSpec.EXACTLY), - /* height */ View.MeasureSpec.makeMeasureSpec(1000, View.MeasureSpec.EXACTLY) - ) - qsPanel.layout(0, 0, qsPanel.measuredWidth, qsPanel.measuredHeight) - - val tiles = qsPanel.tileLayout as View - // Tiles are effectively to the right of media - assertThat(mediaView isLeftOf tiles) - assertThat(tiles.isVisibleToUser).isTrue() - - assertThat(mediaView isLeftOf footer) - assertThat(footer.isVisibleToUser).isTrue() - } - - @Test + @DisableSceneContainer fun testTilesFooterVisibleLandscapeMedia() { - qsPanel.layoutDirection = View.LAYOUT_DIRECTION_LTR // We need at least a tile so the layout has a height qsPanel.tileLayout?.addTile( QSPanelControllerBase.TileRecord( @@ -158,10 +135,10 @@ class QSPanelTest : SysuiTestCase() { val tiles = qsPanel.tileLayout as View // Tiles are effectively to the left of media - assertThat(tiles isLeftOf mediaView) + assertThat(tiles isLeftOf mediaView).isTrue() assertThat(tiles.isVisibleToUser).isTrue() - assertThat(footer isLeftOf mediaView) + assertThat(footer isLeftOf mediaView).isTrue() assertThat(footer.isVisibleToUser).isTrue() } @@ -169,8 +146,8 @@ class QSPanelTest : SysuiTestCase() { fun testBottomPadding() { val padding = 10 themedContext.orCreateTestableResources.addOverride( - R.dimen.qs_panel_padding_bottom, - padding + R.dimen.qs_panel_padding_bottom, + padding ) qsPanel.updatePadding() assertThat(qsPanel.paddingBottom).isEqualTo(padding) @@ -182,8 +159,8 @@ class QSPanelTest : SysuiTestCase() { val paddingCombined = 100 themedContext.orCreateTestableResources.addOverride(R.dimen.qs_panel_padding_top, padding) themedContext.orCreateTestableResources.addOverride( - R.dimen.qs_panel_padding_top, - paddingCombined + R.dimen.qs_panel_padding_top, + paddingCombined ) qsPanel.updatePadding() @@ -220,7 +197,8 @@ class QSPanelTest : SysuiTestCase() { } @Test - fun initializedWithNoMedia_tileLayoutParentIsAlwaysQsPanel() { + @DisableSceneContainer + fun initializedWithNoMedia_sceneContainerDisabled_tileLayoutParentIsAlwaysQsPanel() { lateinit var panel: QSPanel lateinit var tileLayout: View testableLooper.runWithLooper { @@ -249,6 +227,7 @@ class QSPanelTest : SysuiTestCase() { } @Test + @DisableSceneContainer fun initializeWithNoMedia_mediaNeverAttached() { lateinit var panel: QSPanel testableLooper.runWithLooper { @@ -288,6 +267,10 @@ class QSPanelTest : SysuiTestCase() { assertThat(qsPanel.tileLayout!!.maxColumns).isEqualTo(2) } + companion object { + @Parameters(name = "{0}") @JvmStatic fun getParams() = parameterizeSceneContainerFlag() + } + private infix fun View.isLeftOf(other: View): Boolean { val rect = Rect() getBoundsOnScreen(rect) diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileViewImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileViewImplTest.kt index 661830848682..415cc7c99b2c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileViewImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileViewImplTest.kt @@ -19,8 +19,6 @@ package com.android.systemui.qs.tileimpl import android.content.Context import android.graphics.Rect import android.graphics.drawable.Drawable -import android.platform.test.annotations.DisableFlags -import android.platform.test.annotations.EnableFlags import android.service.quicksettings.Tile import android.testing.TestableLooper import android.text.TextUtils @@ -30,13 +28,12 @@ import android.view.accessibility.AccessibilityNodeInfo import android.widget.TextView import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest -import com.android.systemui.Flags.FLAG_QUICK_SETTINGS_VISUAL_HAPTICS_LONGPRESS -import com.android.systemui.res.R import com.android.systemui.SysuiTestCase import com.android.systemui.haptics.qs.QSLongPressEffect import com.android.systemui.haptics.qs.qsLongPressEffect import com.android.systemui.plugins.qs.QSTile import com.android.systemui.qs.qsTileFactory +import com.android.systemui.res.R import com.android.systemui.testKosmos import com.google.common.truth.Truth.assertThat import org.junit.Before @@ -50,8 +47,7 @@ import org.mockito.MockitoAnnotations @TestableLooper.RunWithLooper(setAsMainLooper = true) class QSTileViewImplTest : SysuiTestCase() { - @Mock - private lateinit var customDrawable: Drawable + @Mock private lateinit var customDrawable: Drawable private lateinit var tileView: FakeTileView private lateinit var customDrawableView: View @@ -134,9 +130,8 @@ class QSTileViewImplTest : SysuiTestCase() { tileView.changeState(state) - assertThat(state.secondaryLabel as CharSequence).isEqualTo( - context.getString(R.string.tile_unavailable) - ) + assertThat(state.secondaryLabel as CharSequence) + .isEqualTo(context.getString(R.string.tile_unavailable)) } @Test @@ -147,9 +142,8 @@ class QSTileViewImplTest : SysuiTestCase() { tileView.changeState(state) - assertThat(state.secondaryLabel as CharSequence).isEqualTo( - context.getString(R.string.switch_bar_off) - ) + assertThat(state.secondaryLabel as CharSequence) + .isEqualTo(context.getString(R.string.switch_bar_off)) } @Test @@ -160,9 +154,8 @@ class QSTileViewImplTest : SysuiTestCase() { tileView.changeState(state) - assertThat(state.secondaryLabel as CharSequence).isEqualTo( - context.getString(R.string.switch_bar_on) - ) + assertThat(state.secondaryLabel as CharSequence) + .isEqualTo(context.getString(R.string.switch_bar_on)) } @Test @@ -240,11 +233,10 @@ class QSTileViewImplTest : SysuiTestCase() { val offString = "${spec}_off" val onString = "${spec}_on" - context.orCreateTestableResources.addOverride(R.array.tile_states_internet, arrayOf( - unavailableString, - offString, - onString - )) + context.orCreateTestableResources.addOverride( + R.array.tile_states_internet, + arrayOf(unavailableString, offString, onString) + ) // State UNAVAILABLE state.secondaryLabel = "" @@ -346,11 +338,10 @@ class QSTileViewImplTest : SysuiTestCase() { @Test fun testDisabledByPolicy_secondaryLabelText() { val testA11yLabel = "TEST_LABEL" - context.orCreateTestableResources - .addOverride( - R.string.accessibility_tile_disabled_by_policy_action_description, - testA11yLabel - ) + context.orCreateTestableResources.addOverride( + R.string.accessibility_tile_disabled_by_policy_action_description, + testA11yLabel + ) val stateDisabledByPolicy = QSTile.State() stateDisabledByPolicy.state = Tile.STATE_INACTIVE @@ -361,10 +352,11 @@ class QSTileViewImplTest : SysuiTestCase() { val info = AccessibilityNodeInfo(tileView) tileView.onInitializeAccessibilityNodeInfo(info) assertThat( - info.actionList.find { - it.id == AccessibilityNodeInfo.AccessibilityAction.ACTION_CLICK.id - }?.label - ).isEqualTo(testA11yLabel) + info.actionList + .find { it.id == AccessibilityNodeInfo.AccessibilityAction.ACTION_CLICK.id } + ?.label + ) + .isEqualTo(testA11yLabel) } @Test @@ -379,11 +371,10 @@ class QSTileViewImplTest : SysuiTestCase() { val offString = "${spec}_off" val onString = "${spec}_on" - context.orCreateTestableResources.addOverride(R.array.tile_states_internet, arrayOf( - unavailableString, - offString, - onString - )) + context.orCreateTestableResources.addOverride( + R.array.tile_states_internet, + arrayOf(unavailableString, offString, onString) + ) tileView.changeState(state) assertThat(tileView.stateDescription?.contains(unavailableString)).isTrue() @@ -416,7 +407,7 @@ class QSTileViewImplTest : SysuiTestCase() { } @Test - fun onStateChange_fromLongPress_to_noLongPress_unBoundsTile() { + fun onStateChange_fromLongPress_to_noLongPress_clearsResources() { // GIVEN a state that no longer handles long-press val state = QSTile.State() state.handlesLongClick = false @@ -424,12 +415,12 @@ class QSTileViewImplTest : SysuiTestCase() { // WHEN the state changes tileView.changeState(state) - // THEN the view binder no longer binds the view to the long-press effect - assertThat(tileView.isLongPressEffectBound).isFalse() + // THEN the long-press effect resources are not set + assertThat(tileView.areLongPressEffectPropertiesSet).isFalse() } @Test - fun onStateChange_fromNoLongPress_to_longPress_bindsTile() { + fun onStateChange_fromNoLongPress_to_longPress_setsProperties() { // GIVEN that the tile has changed to a state that does not handle long-press val state = QSTile.State() state.handlesLongClick = false @@ -439,12 +430,12 @@ class QSTileViewImplTest : SysuiTestCase() { state.handlesLongClick = true tileView.changeState(state) - // THEN the view is bounded to the long-press effect - assertThat(tileView.isLongPressEffectBound).isTrue() + // THEN the long-press effect resources are set + assertThat(tileView.areLongPressEffectPropertiesSet).isTrue() } @Test - fun onStateChange_withoutLongPressEffect_fromLongPress_to_noLongPress_neverBindsEffect() { + fun onStateChange_withoutLongPressEffect_fromLongPress_to_noLongPress_neverSetsProperties() { // GIVEN a tile where the long-press effect is null tileView = FakeTileView(context, false, null) @@ -455,13 +446,13 @@ class QSTileViewImplTest : SysuiTestCase() { // WHEN the state changes tileView.changeState(state) - // THEN the view binder does not bind the view and no effect is initialized - assertThat(tileView.isLongPressEffectBound).isFalse() + // THEN the effect properties are not set and the effect is not initialized + assertThat(tileView.areLongPressEffectPropertiesSet).isFalse() assertThat(tileView.isLongPressEffectInitialized).isFalse() } @Test - fun onStateChange_withoutLongPressEffect_fromNoLongPress_to_longPress_neverBindsEffect() { + fun onStateChange_withoutLongPressEffect_fromNoLongPress_to_longPress_neverSetsProperties() { // GIVEN a tile where the long-press effect is null tileView = FakeTileView(context, false, null) @@ -474,8 +465,8 @@ class QSTileViewImplTest : SysuiTestCase() { state.handlesLongClick = true tileView.changeState(state) - // THEN the view binder does not bind the view and no effect is initialized - assertThat(tileView.isLongPressEffectBound).isFalse() + // THEN the effect properties are not set and the effect is not initialized + assertThat(tileView.areLongPressEffectPropertiesSet).isFalse() assertThat(tileView.isLongPressEffectInitialized).isFalse() } @@ -494,14 +485,15 @@ class QSTileViewImplTest : SysuiTestCase() { // THE animation padding corresponds to the tile's growth due to the effect val padding = tileView.getPaddingForLaunchAnimation() - assertThat(padding).isEqualTo( - Rect( - -deltaWidth.toInt() / 2, - -deltaHeight.toInt() / 2, - deltaWidth.toInt() / 2, - deltaHeight.toInt() / 2, + assertThat(padding) + .isEqualTo( + Rect( + -deltaWidth.toInt() / 2, + -deltaHeight.toInt() / 2, + deltaWidth.toInt() / 2, + deltaHeight.toInt() / 2, + ) ) - ) } @Test @@ -541,7 +533,6 @@ class QSTileViewImplTest : SysuiTestCase() { } @Test - @EnableFlags(FLAG_QUICK_SETTINGS_VISUAL_HAPTICS_LONGPRESS) fun onInit_withLongPressEffect_longPressEffectHasTileAndExpandable() { val tile = kosmos.qsTileFactory.createTile("Test Tile") tileView.init(tile) @@ -551,8 +542,8 @@ class QSTileViewImplTest : SysuiTestCase() { } @Test - @DisableFlags(FLAG_QUICK_SETTINGS_VISUAL_HAPTICS_LONGPRESS) fun onInit_withoutLongPressEffect_longPressEffectDoesNotHaveTileAndExpandable() { + tileView = FakeTileView(context, false, null) val tile = kosmos.qsTileFactory.createTile("Test Tile") tileView.init(tile) @@ -564,19 +555,21 @@ class QSTileViewImplTest : SysuiTestCase() { context: Context, collapsed: Boolean, private val longPressEffect: QSLongPressEffect?, - ) : QSTileViewImpl( + ) : + QSTileViewImpl( ContextThemeWrapper(context, R.style.Theme_SystemUI_QuickSettings), collapsed, longPressEffect, - ) { + ) { var constantLongPressEffectDuration = 500 - val isTileAddedToLongPress: Boolean get() = longPressEffect?.qsTile != null + val isExpandableAddedToLongPress: Boolean get() = longPressEffect?.expandable != null override fun getLongPressEffectDuration(): Int = constantLongPressEffectDuration + fun changeState(state: QSTile.State) { handleStateChanged(state) } diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/HearingDevicesTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/HearingDevicesTileTest.java index 59ee0b843043..76c8cf081262 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/HearingDevicesTileTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/HearingDevicesTileTest.java @@ -28,6 +28,7 @@ import android.os.Handler; import android.platform.test.annotations.DisableFlags; import android.platform.test.annotations.EnableFlags; import android.provider.Settings; +import android.service.quicksettings.Tile; import android.testing.TestableLooper; import android.view.View; @@ -37,14 +38,18 @@ import androidx.test.filters.SmallTest; import com.android.internal.logging.MetricsLogger; import com.android.systemui.Flags; import com.android.systemui.SysuiTestCase; +import com.android.systemui.accessibility.hearingaid.HearingDevicesChecker; import com.android.systemui.accessibility.hearingaid.HearingDevicesDialogManager; import com.android.systemui.animation.Expandable; import com.android.systemui.classifier.FalsingManagerFake; import com.android.systemui.plugins.ActivityStarter; +import com.android.systemui.plugins.qs.QSTile.BooleanState; 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.res.R; +import com.android.systemui.statusbar.policy.BluetoothController; import org.junit.After; import org.junit.Before; @@ -78,7 +83,11 @@ public class HearingDevicesTileTest extends SysuiTestCase { @Mock private QSLogger mQSLogger; @Mock + private HearingDevicesChecker mDevicesChecker; + @Mock HearingDevicesDialogManager mHearingDevicesDialogManager; + @Mock + BluetoothController mBluetoothController; private TestableLooper mTestableLooper; private HearingDevicesTile mTile; @@ -98,7 +107,9 @@ public class HearingDevicesTileTest extends SysuiTestCase { mStatusBarStateController, mActivityStarter, mQSLogger, - mHearingDevicesDialogManager); + mHearingDevicesDialogManager, + mDevicesChecker, + mBluetoothController); mTile.initialize(); mTestableLooper.processAllMessages(); @@ -142,4 +153,41 @@ public class HearingDevicesTileTest extends SysuiTestCase { verify(mHearingDevicesDialogManager).showDialog(expandable); } + + @Test + public void handleUpdateState_activeHearingDevice_stateActiveConnectedLabel() { + when(mDevicesChecker.isAnyActiveHearingDevice()).thenReturn(true); + when(mDevicesChecker.isAnyPairedHearingDevice()).thenReturn(true); + + BooleanState activeState = new BooleanState(); + mTile.handleUpdateState(activeState, null); + + assertThat(activeState.state).isEqualTo(Tile.STATE_ACTIVE); + assertThat(activeState.secondaryLabel.toString()).isEqualTo( + mContext.getString(R.string.quick_settings_hearing_devices_connected)); + } + + @Test + public void handleUpdateState_bondedInactiveHearingDevice_stateInactiveDisconnectedLabel() { + when(mDevicesChecker.isAnyActiveHearingDevice()).thenReturn(false); + when(mDevicesChecker.isAnyPairedHearingDevice()).thenReturn(true); + + BooleanState disconnectedState = new BooleanState(); + mTile.handleUpdateState(disconnectedState, null); + + assertThat(disconnectedState.state).isEqualTo(Tile.STATE_INACTIVE); + assertThat(disconnectedState.secondaryLabel.toString()).isEqualTo( + mContext.getString(R.string.quick_settings_hearing_devices_disconnected)); + } + + @Test + public void handleUpdateState_noHearingDevice_stateInactive() { + when(mDevicesChecker.isAnyActiveHearingDevice()).thenReturn(false); + when(mDevicesChecker.isAnyPairedHearingDevice()).thenReturn(false); + + BooleanState inactiveState = new BooleanState(); + mTile.handleUpdateState(inactiveState, null); + + assertThat(inactiveState.state).isEqualTo(Tile.STATE_INACTIVE); + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/RecordIssueTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/RecordIssueTileTest.kt index df59e572bd3b..73548baad377 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/RecordIssueTileTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/RecordIssueTileTest.kt @@ -72,13 +72,13 @@ class RecordIssueTileTest : SysuiTestCase() { @Mock private lateinit var dialogLauncherAnimator: DialogTransitionAnimator @Mock private lateinit var panelInteractor: PanelInteractor @Mock private lateinit var userContextProvider: UserContextProvider + @Mock private lateinit var issueRecordingState: IssueRecordingState @Mock private lateinit var traceurMessageSender: TraceurMessageSender @Mock private lateinit var delegateFactory: RecordIssueDialogDelegate.Factory @Mock private lateinit var dialogDelegate: RecordIssueDialogDelegate @Mock private lateinit var dialog: SystemUIDialog private lateinit var testableLooper: TestableLooper - private val issueRecordingState = IssueRecordingState() private lateinit var tile: RecordIssueTile @Before @@ -114,7 +114,7 @@ class RecordIssueTileTest : SysuiTestCase() { @Test fun qsTileUi_shouldLookCorrect_whenInactive() { - issueRecordingState.isRecording = false + whenever(issueRecordingState.isRecording).thenReturn(false) val testState = tile.newTileState() tile.handleUpdateState(testState, null) @@ -126,7 +126,7 @@ class RecordIssueTileTest : SysuiTestCase() { @Test fun qsTileUi_shouldLookCorrect_whenRecording() { - issueRecordingState.isRecording = true + whenever(issueRecordingState.isRecording).thenReturn(true) val testState = tile.newTileState() tile.handleUpdateState(testState, null) @@ -137,7 +137,7 @@ class RecordIssueTileTest : SysuiTestCase() { @Test fun inActiveQsTile_switchesToActive_whenClicked() { - issueRecordingState.isRecording = false + whenever(issueRecordingState.isRecording).thenReturn(false) val testState = tile.newTileState() tile.handleUpdateState(testState, null) @@ -147,7 +147,7 @@ class RecordIssueTileTest : SysuiTestCase() { @Test fun activeQsTile_switchesToInActive_whenClicked() { - issueRecordingState.isRecording = true + whenever(issueRecordingState.isRecording).thenReturn(true) val testState = tile.newTileState() tile.handleUpdateState(testState, null) @@ -157,7 +157,7 @@ class RecordIssueTileTest : SysuiTestCase() { @Test fun showPrompt_shouldUseKeyguardDismissUtil_ToShowDialog() { - issueRecordingState.isRecording = false + whenever(issueRecordingState.isRecording).thenReturn(false) tile.handleClick(null) testableLooper.processAllMessages() 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 fc74586e7643..6e6e31177564 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/recents/OverviewProxyServiceTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/recents/OverviewProxyServiceTest.kt @@ -241,7 +241,6 @@ class OverviewProxyServiceTest : SysuiTestCase() { statusBarWinController, sysUiState, mock(), - mock(), userTracker, wakefulnessLifecycle, uiEventLogger, 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 ca606505af68..503c52f89b0a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/recordissue/RecordIssueDialogDelegateTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/recordissue/RecordIssueDialogDelegateTest.kt @@ -17,7 +17,6 @@ package com.android.systemui.recordissue import android.app.Dialog -import android.content.Context import android.content.SharedPreferences import android.os.UserHandle import android.testing.TestableLooper @@ -35,9 +34,8 @@ import com.android.systemui.mediaprojection.SessionCreationSource import com.android.systemui.mediaprojection.devicepolicy.ScreenCaptureDevicePolicyResolver import com.android.systemui.mediaprojection.devicepolicy.ScreenCaptureDisabledDialogDelegate import com.android.systemui.model.SysUiState -import com.android.systemui.qs.tiles.RecordIssueTile +import com.android.systemui.recordissue.IssueRecordingState.Companion.ISSUE_TYPE_NOT_SET import com.android.systemui.res.R -import com.android.systemui.settings.UserFileManager import com.android.systemui.settings.UserTracker import com.android.systemui.statusbar.phone.SystemUIDialog import com.android.systemui.statusbar.phone.SystemUIDialogManager @@ -72,7 +70,7 @@ class RecordIssueDialogDelegateTest : SysuiTestCase() { @Mock private lateinit var dprLazy: dagger.Lazy<ScreenCaptureDevicePolicyResolver> @Mock private lateinit var mediaProjectionMetricsLogger: MediaProjectionMetricsLogger @Mock private lateinit var userTracker: UserTracker - @Mock private lateinit var userFileManager: UserFileManager + @Mock private lateinit var state: IssueRecordingState @Mock private lateinit var sharedPreferences: SharedPreferences @Mock private lateinit var screenCaptureDisabledDialogDelegate: ScreenCaptureDisabledDialogDelegate @@ -90,7 +88,6 @@ class RecordIssueDialogDelegateTest : SysuiTestCase() { private lateinit var dialog: SystemUIDialog private lateinit var factory: SystemUIDialog.Factory private lateinit var latch: CountDownLatch - private var issueRecordingState = IssueRecordingState() @Before fun setup() { @@ -99,14 +96,7 @@ class RecordIssueDialogDelegateTest : SysuiTestCase() { whenever(sysuiState.setFlag(anyLong(), anyBoolean())).thenReturn(sysuiState) whenever(screenCaptureDisabledDialogDelegate.createSysUIDialog()) .thenReturn(screenCaptureDisabledDialog) - whenever( - userFileManager.getSharedPreferences( - eq(RecordIssueTile.TILE_SPEC), - eq(Context.MODE_PRIVATE), - anyInt() - ) - ) - .thenReturn(sharedPreferences) + whenever(state.issueTypeRes).thenReturn(ISSUE_TYPE_NOT_SET) factory = spy( @@ -129,9 +119,8 @@ class RecordIssueDialogDelegateTest : SysuiTestCase() { mainExecutor, dprLazy, mediaProjectionMetricsLogger, - userFileManager, screenCaptureDisabledDialogDelegate, - issueRecordingState, + state, traceurMessageSender ) { latch.countDown() @@ -190,8 +179,7 @@ class RecordIssueDialogDelegateTest : SysuiTestCase() { whenever(devicePolicyResolver.isScreenCaptureCompletelyDisabled(any<UserHandle>())) .thenReturn(false) whenever(flags.isEnabled(Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING)).thenReturn(true) - whenever(sharedPreferences.getBoolean(HAS_APPROVED_SCREEN_RECORDING, false)) - .thenReturn(false) + whenever(state.hasUserApprovedScreenRecording).thenReturn(false) val screenRecordSwitch = dialog.requireViewById<Switch>(R.id.screenrecord_switch) screenRecordSwitch.isChecked = true diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/data/repository/ScreenRecordRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/data/repository/ScreenRecordRepositoryTest.kt index b77a15b43bea..61ea437f10dd 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/data/repository/ScreenRecordRepositoryTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/data/repository/ScreenRecordRepositoryTest.kt @@ -119,4 +119,12 @@ class ScreenRecordRepositoryTest : SysuiTestCase() { assertThat(lastModel).isEqualTo(isRecording) } + + @Test + fun stopRecording_invokesController() = + testScope.runTest { + underTest.stopRecording() + + verify(recordingController).stopRecording() + } } 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 new file mode 100644 index 000000000000..4945ace3b88c --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/policy/PolicyRequestProcessorTest.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.screenshot.policy + +import android.content.ComponentName +import android.graphics.Insets +import android.graphics.Rect +import android.os.UserHandle +import android.view.Display.DEFAULT_DISPLAY +import android.view.WindowManager.ScreenshotSource.SCREENSHOT_KEY_CHORD +import android.view.WindowManager.TAKE_SCREENSHOT_FULLSCREEN +import com.android.systemui.screenshot.ImageCapture +import com.android.systemui.screenshot.ScreenshotData +import com.android.systemui.screenshot.data.model.DisplayContentScenarios.ActivityNames.FILES +import com.android.systemui.screenshot.data.model.DisplayContentScenarios.TaskSpec +import com.android.systemui.screenshot.data.model.DisplayContentScenarios.singleFullScreen +import com.android.systemui.screenshot.data.repository.DisplayContentRepository +import com.android.systemui.screenshot.policy.TestUserIds.PERSONAL +import com.android.systemui.screenshot.policy.TestUserIds.WORK +import com.google.common.truth.Truth.assertWithMessage +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.runBlocking +import org.junit.Test + +class PolicyRequestProcessorTest { + + val imageCapture = object : ImageCapture { + override fun captureDisplay(displayId: Int, crop: Rect?) = null + override suspend fun captureTask(taskId: Int) = null + } + + /** Tests behavior when no policies are applied */ + @Test + fun testProcess_defaultOwner_whenNoPolicyApplied() { + val fullScreenWork = DisplayContentRepository { + singleFullScreen(TaskSpec(taskId = 1001, name = FILES, userId = WORK)) + } + + val request = + ScreenshotData(TAKE_SCREENSHOT_FULLSCREEN, + SCREENSHOT_KEY_CHORD, + null, + topComponent = null, + screenBounds = Rect(0, 0, 1, 1), + taskId = -1, + insets = Insets.NONE, + bitmap = null, + displayId = DEFAULT_DISPLAY) + + /* Create a policy request processor with no capture policies */ + val requestProcessor = + PolicyRequestProcessor(Dispatchers.Unconfined, + imageCapture, + policies = emptyList(), + defaultOwner = UserHandle.of(PERSONAL), + defaultComponent = ComponentName("default", "Component"), + displayTasks = fullScreenWork) + + val result = runBlocking { requestProcessor.process(request) } + + assertWithMessage( + "With no policy, the screenshot should be assigned to the default user" + ).that(result.userHandle).isEqualTo(UserHandle.of(PERSONAL)) + + assertWithMessage("The topComponent of the screenshot").that(result.topComponent) + .isEqualTo(ComponentName.unflattenFromString(FILES)) + } +}
\ No newline at end of file diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt index 4a867a8ecf22..74a299910b18 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt @@ -70,6 +70,7 @@ import com.android.systemui.statusbar.phone.DozeServiceHost import com.android.systemui.statusbar.phone.PhoneStatusBarViewController import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager import com.android.systemui.statusbar.window.StatusBarWindowStateController +import com.android.systemui.unfold.SysUIUnfoldComponent import com.android.systemui.unfold.UnfoldTransitionProgressProvider import com.android.systemui.user.domain.interactor.SelectedUserInteractor import com.android.systemui.util.mockito.any @@ -85,6 +86,7 @@ import org.junit.Assert.assertEquals import org.junit.Before 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.anyFloat @@ -132,6 +134,8 @@ class NotificationShadeWindowViewControllerTest(flags: FlagsParameterization) : private lateinit var mLockscreenHostedDreamGestureListener: LockscreenHostedDreamGestureListener @Mock private lateinit var notificationInsetsController: NotificationInsetsController @Mock private lateinit var mGlanceableHubContainerController: GlanceableHubContainerController + @Mock(answer = Answers.RETURNS_DEEP_STUBS) + private lateinit var sysUiUnfoldComponent: SysUIUnfoldComponent @Mock lateinit var keyguardBouncerComponentFactory: KeyguardBouncerComponent.Factory @Mock lateinit var keyguardBouncerComponent: KeyguardBouncerComponent @Mock lateinit var keyguardSecurityContainerController: KeyguardSecurityContainerController @@ -209,6 +213,7 @@ class NotificationShadeWindowViewControllerTest(flags: FlagsParameterization) : dozeScrimController, notificationShadeWindowController, unfoldTransitionProgressProvider, + Optional.of(sysUiUnfoldComponent), keyguardUnlockAnimationController, notificationInsetsController, ambientState, @@ -519,6 +524,46 @@ class NotificationShadeWindowViewControllerTest(flags: FlagsParameterization) : } @Test + fun handleExternalTouch_intercepted_sendsOnTouch() { + // Accept dispatch and also intercept. + whenever(view.dispatchTouchEvent(any())).thenReturn(true) + whenever(view.onInterceptTouchEvent(any())).thenReturn(true) + + underTest.handleExternalTouch(DOWN_EVENT) + underTest.handleExternalTouch(MOVE_EVENT) + + // Once intercepted, both events are sent to the view. + verify(view).onTouchEvent(DOWN_EVENT) + verify(view).onTouchEvent(MOVE_EVENT) + } + + @Test + fun handleExternalTouch_notDispatched_interceptNotCalled() { + // Don't accept dispatch + whenever(view.dispatchTouchEvent(any())).thenReturn(false) + + underTest.handleExternalTouch(DOWN_EVENT) + + // Interception is not offered. + verify(view, never()).onInterceptTouchEvent(any()) + } + + @Test + fun handleExternalTouch_notIntercepted_onTouchNotSent() { + // Accept dispatch, but don't dispatch + whenever(view.dispatchTouchEvent(any())).thenReturn(true) + whenever(view.onInterceptTouchEvent(any())).thenReturn(false) + + underTest.handleExternalTouch(DOWN_EVENT) + underTest.handleExternalTouch(MOVE_EVENT) + + // Interception offered for both events, but onTouchEvent is never called. + verify(view).onInterceptTouchEvent(DOWN_EVENT) + verify(view).onInterceptTouchEvent(MOVE_EVENT) + verify(view, never()).onTouchEvent(any()) + } + + @Test fun testGetKeyguardMessageArea() = testScope.runTest { underTest.keyguardMessageArea diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt index e83a46bb56a0..31bd12f9a168 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt @@ -58,6 +58,7 @@ import com.android.systemui.statusbar.phone.DozeScrimController import com.android.systemui.statusbar.phone.DozeServiceHost import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager import com.android.systemui.statusbar.window.StatusBarWindowStateController +import com.android.systemui.unfold.SysUIUnfoldComponent import com.android.systemui.unfold.UnfoldTransitionProgressProvider import com.android.systemui.util.mockito.any import com.android.systemui.util.mockito.mock @@ -112,6 +113,7 @@ class NotificationShadeWindowViewTest : SysuiTestCase() { @Mock private lateinit var shadeLogger: ShadeLogger @Mock private lateinit var dumpManager: DumpManager @Mock private lateinit var pulsingGestureListener: PulsingGestureListener + @Mock private lateinit var sysUiUnfoldComponent: SysUIUnfoldComponent @Mock private lateinit var mLockscreenHostedDreamGestureListener: LockscreenHostedDreamGestureListener @Mock private lateinit var keyguardBouncerComponentFactory: KeyguardBouncerComponent.Factory @@ -181,6 +183,7 @@ class NotificationShadeWindowViewTest : SysuiTestCase() { dozeScrimController, notificationShadeWindowController, unfoldTransitionProgressProvider, + Optional.of(sysUiUnfoldComponent), keyguardUnlockAnimationController, notificationInsetsController, ambientState, diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/animation/UnfoldConstantTranslateAnimatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shared/animation/UnfoldConstantTranslateAnimatorTest.kt index a05a23bb8bb1..293dc048af1d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shared/animation/UnfoldConstantTranslateAnimatorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/shared/animation/UnfoldConstantTranslateAnimatorTest.kt @@ -27,6 +27,9 @@ import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mock +import org.mockito.Mockito.anyFloat +import org.mockito.Mockito.spy +import org.mockito.Mockito.verify import org.mockito.Mockito.`when` as whenever import org.mockito.MockitoAnnotations @@ -78,6 +81,22 @@ class UnfoldConstantTranslateAnimatorTest : SysuiTestCase() { } @Test + fun initMultipleTimes_onTransition_translationIsSetOnlyOnce() { + animator.init(parent, MAX_TRANSLATION) + animator.init(parent, MAX_TRANSLATION) + animator.init(parent, MAX_TRANSLATION) + + // GIVEN one view with a matching id + val view = spy(View(context)) + whenever(parent.findViewById<View>(START_VIEW_ID)).thenReturn(view) + progressProvider.onTransitionStarted() + + // WHEN the transition progresses, translation is updated once + progressProvider.onTransitionProgress(.5f) + verify(view).translationX = anyFloat() + } + + @Test fun onTransition_oneMovesStartWithRTL() { // GIVEN one view with a matching id val view = View(context) diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyboardShortcutsTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyboardShortcutsTest.java index 2b3f13986113..6ad8b8bc3637 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyboardShortcutsTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyboardShortcutsTest.java @@ -18,12 +18,16 @@ package com.android.systemui.statusbar; import static com.google.common.truth.Truth.assertThat; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.any; import static org.mockito.Mockito.anyInt; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import static java.util.Collections.singletonList; + +import android.annotation.Nullable; import android.app.Dialog; import android.graphics.drawable.Icon; import android.os.Handler; @@ -44,11 +48,13 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; import org.mockito.Mock; +import org.mockito.invocation.InvocationOnMock; import org.mockito.junit.MockitoJUnit; import org.mockito.junit.MockitoRule; +import org.mockito.stubbing.Answer; import java.util.Arrays; -import java.util.Collections; +import java.util.List; @SmallTest @RunWith(AndroidJUnit4.class) @@ -56,7 +62,7 @@ public class KeyboardShortcutsTest extends SysuiTestCase { @Rule public MockitoRule mockito = MockitoJUnit.rule(); - private static int DEVICE_ID = 1; + private static final int DEVICE_ID = 1; private KeyboardShortcuts mKeyboardShortcuts; @Mock private Dialog mDialog; @@ -66,26 +72,35 @@ public class KeyboardShortcutsTest extends SysuiTestCase { @Before public void setUp() { mKeyboardShortcuts = new KeyboardShortcuts(mContext, mWindowManager); - mKeyboardShortcuts.sInstance = mKeyboardShortcuts; + KeyboardShortcuts.sInstance = mKeyboardShortcuts; mKeyboardShortcuts.mKeyboardShortcutsDialog = mDialog; mKeyboardShortcuts.mContext = mContext; mKeyboardShortcuts.mBackgroundHandler = mHandler; + when(mHandler.post(any())) + .thenAnswer( + new Answer<>() { + @Override + public Object answer(InvocationOnMock invocation) { + ((Runnable) invocation.getArgument(0)).run(); + return null; + } + }); } @Test public void toggle_isShowingTrue_instanceShouldBeNull() { when(mDialog.isShowing()).thenReturn(true); - mKeyboardShortcuts.toggle(mContext, DEVICE_ID); + KeyboardShortcuts.toggle(mContext, DEVICE_ID); - assertThat(mKeyboardShortcuts.sInstance).isNull(); + assertThat(KeyboardShortcuts.sInstance).isNull(); } @Test public void toggle_isShowingFalse_showKeyboardShortcuts() { when(mDialog.isShowing()).thenReturn(false); - mKeyboardShortcuts.toggle(mContext, DEVICE_ID); + KeyboardShortcuts.toggle(mContext, DEVICE_ID); verify(mWindowManager).requestAppKeyboardShortcuts(any(), anyInt()); verify(mWindowManager).requestImeKeyboardShortcuts(any(), anyInt()); @@ -95,7 +110,7 @@ public class KeyboardShortcutsTest extends SysuiTestCase { public void sanitiseShortcuts_clearsIcons() { KeyboardShortcutGroup group = createKeyboardShortcutGroupForIconTests(); - KeyboardShortcuts.sanitiseShortcuts(Collections.singletonList(group)); + KeyboardShortcuts.sanitiseShortcuts(singletonList(group)); verify(group.getItems().get(0)).clearIcon(); verify(group.getItems().get(1)).clearIcon(); @@ -106,7 +121,7 @@ public class KeyboardShortcutsTest extends SysuiTestCase { KeyboardShortcutGroup group = createKeyboardShortcutGroupForIconTests(); group.setPackageName(null); - KeyboardShortcuts.sanitiseShortcuts(Collections.singletonList(group)); + KeyboardShortcuts.sanitiseShortcuts(singletonList(group)); verify(group.getItems().get(0)).clearIcon(); verify(group.getItems().get(1)).clearIcon(); @@ -116,16 +131,9 @@ public class KeyboardShortcutsTest extends SysuiTestCase { @EnableFlags(Flags.FLAG_VALIDATE_KEYBOARD_SHORTCUT_HELPER_ICON_URI) public void requestAppKeyboardShortcuts_callback_sanitisesIcons() { KeyboardShortcutGroup group = createKeyboardShortcutGroupForIconTests(); + KeyboardShortcuts.toggle(mContext, DEVICE_ID); - mKeyboardShortcuts.toggle(mContext, DEVICE_ID); - - ArgumentCaptor<WindowManager.KeyboardShortcutsReceiver> callbackCaptor = - ArgumentCaptor.forClass(WindowManager.KeyboardShortcutsReceiver.class); - ArgumentCaptor<Runnable> handlerRunnableCaptor = ArgumentCaptor.forClass(Runnable.class); - verify(mWindowManager).requestAppKeyboardShortcuts(callbackCaptor.capture(), anyInt()); - callbackCaptor.getValue().onKeyboardShortcutsReceived(Collections.singletonList(group)); - verify(mHandler).post(handlerRunnableCaptor.capture()); - handlerRunnableCaptor.getValue().run(); + emitAppShortcuts(singletonList(group), DEVICE_ID); verify(group.getItems().get(0)).clearIcon(); verify(group.getItems().get(1)).clearIcon(); @@ -135,20 +143,38 @@ public class KeyboardShortcutsTest extends SysuiTestCase { @EnableFlags(Flags.FLAG_VALIDATE_KEYBOARD_SHORTCUT_HELPER_ICON_URI) public void requestImeKeyboardShortcuts_callback_sanitisesIcons() { KeyboardShortcutGroup group = createKeyboardShortcutGroupForIconTests(); + KeyboardShortcuts.toggle(mContext, DEVICE_ID); - mKeyboardShortcuts.toggle(mContext, DEVICE_ID); - - ArgumentCaptor<WindowManager.KeyboardShortcutsReceiver> callbackCaptor = - ArgumentCaptor.forClass(WindowManager.KeyboardShortcutsReceiver.class); - ArgumentCaptor<Runnable> handlerRunnableCaptor = ArgumentCaptor.forClass(Runnable.class); - verify(mWindowManager).requestImeKeyboardShortcuts(callbackCaptor.capture(), anyInt()); - callbackCaptor.getValue().onKeyboardShortcutsReceived(Collections.singletonList(group)); - verify(mHandler).post(handlerRunnableCaptor.capture()); - handlerRunnableCaptor.getValue().run(); + emitImeShortcuts(singletonList(group), DEVICE_ID); verify(group.getItems().get(0)).clearIcon(); verify(group.getItems().get(1)).clearIcon(); + } + + @Test + public void onImeAndAppShortcutsReceived_appShortcutsNull_doesNotCrash() { + KeyboardShortcutGroup group = createKeyboardShortcutGroupForIconTests(); + KeyboardShortcuts.toggle(mContext, DEVICE_ID); + + emitImeShortcuts(singletonList(group), DEVICE_ID); + emitAppShortcuts(/* groups= */ null, DEVICE_ID); + } + + @Test + public void onImeAndAppShortcutsReceived_imeShortcutsNull_doesNotCrash() { + KeyboardShortcutGroup group = createKeyboardShortcutGroupForIconTests(); + KeyboardShortcuts.toggle(mContext, DEVICE_ID); + emitAppShortcuts(singletonList(group), DEVICE_ID); + emitImeShortcuts(/* groups= */ null, DEVICE_ID); + } + + @Test + public void onImeAndAppShortcutsReceived_bothNull_doesNotCrash() { + KeyboardShortcuts.toggle(mContext, DEVICE_ID); + + emitImeShortcuts(/* groups= */ null, DEVICE_ID); + emitAppShortcuts(/* groups= */ null, DEVICE_ID); } private KeyboardShortcutGroup createKeyboardShortcutGroupForIconTests() { @@ -159,9 +185,23 @@ public class KeyboardShortcutsTest extends SysuiTestCase { when(info1.getIcon()).thenReturn(icon); when(info2.getIcon()).thenReturn(icon); - KeyboardShortcutGroup group = new KeyboardShortcutGroup("label", - Arrays.asList(new KeyboardShortcutInfo[]{ info1, info2})); + KeyboardShortcutGroup group = + new KeyboardShortcutGroup("label", Arrays.asList(info1, info2)); group.setPackageName("com.example"); return group; } + + private void emitImeShortcuts(@Nullable List<KeyboardShortcutGroup> groups, int deviceId) { + ArgumentCaptor<WindowManager.KeyboardShortcutsReceiver> callbackCaptor = + ArgumentCaptor.forClass(WindowManager.KeyboardShortcutsReceiver.class); + verify(mWindowManager).requestImeKeyboardShortcuts(callbackCaptor.capture(), eq(deviceId)); + callbackCaptor.getValue().onKeyboardShortcutsReceived(groups); + } + + private void emitAppShortcuts(@Nullable List<KeyboardShortcutGroup> groups, int deviceId) { + ArgumentCaptor<WindowManager.KeyboardShortcutsReceiver> callbackCaptor = + ArgumentCaptor.forClass(WindowManager.KeyboardShortcutsReceiver.class); + verify(mWindowManager).requestAppKeyboardShortcuts(callbackCaptor.capture(), eq(deviceId)); + callbackCaptor.getValue().onKeyboardShortcutsReceived(groups); + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarIconViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarIconViewTest.java index b6ee46ddafeb..50131cb06631 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarIconViewTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarIconViewTest.java @@ -97,7 +97,8 @@ public class StatusBarIconViewTest extends SysuiTestCase { mIconView = new StatusBarIconView(mContext, "test_slot", null); mStatusBarIcon = new StatusBarIcon(UserHandle.ALL, "mockPackage", - Icon.createWithResource(mContext, R.drawable.ic_android), 0, 0, ""); + Icon.createWithResource(mContext, R.drawable.ic_android), 0, 0, "", + StatusBarIcon.Type.SystemIcon); } @Test @@ -138,7 +139,7 @@ public class StatusBarIconViewTest extends SysuiTestCase { Bitmap largeBitmap = Bitmap.createBitmap(6000, 6000, Bitmap.Config.ARGB_8888); Icon icon = Icon.createWithBitmap(largeBitmap); StatusBarIcon largeIcon = new StatusBarIcon(UserHandle.ALL, "mockPackage", - icon, 0, 0, ""); + icon, 0, 0, "", StatusBarIcon.Type.SystemIcon); assertTrue(mIconView.set(largeIcon)); // The view should downscale the bitmap. @@ -152,7 +153,7 @@ public class StatusBarIconViewTest extends SysuiTestCase { Bitmap bitmap = Bitmap.createBitmap(60, 60, Bitmap.Config.ARGB_8888); Icon icon = Icon.createWithBitmap(bitmap); StatusBarIcon largeIcon = new StatusBarIcon(UserHandle.ALL, "mockPackage", - icon, 0, 0, ""); + icon, 0, 0, "", StatusBarIcon.Type.SystemIcon); mIconView.setNotification(getMockSbn()); mIconView.getIcon(largeIcon); // no crash? good @@ -172,7 +173,7 @@ public class StatusBarIconViewTest extends SysuiTestCase { Bitmap bitmap = Bitmap.createBitmap(60, 60, Bitmap.Config.ARGB_8888); Icon icon = Icon.createWithBitmap(bitmap); StatusBarIcon largeIcon = new StatusBarIcon(UserHandle.ALL, "mockPackage", - icon, 0, 0, ""); + icon, 0, 0, "", StatusBarIcon.Type.SystemIcon); mIconView.getIcon(largeIcon); // No crash? good } @@ -430,7 +431,7 @@ public class StatusBarIconViewTest extends SysuiTestCase { width, height, Bitmap.Config.ARGB_8888); Icon icon = Icon.createWithBitmap(bitmap); mStatusBarIcon = new StatusBarIcon(UserHandle.ALL, "mockPackage", - icon, 0, 0, ""); + icon, 0, 0, "", StatusBarIcon.Type.SystemIcon); // Since we only want to verify icon scale logic here, we directly use // {@link StatusBarIconView#setImageDrawable(Drawable)} to set the image drawable // to iconView instead of call {@link StatusBarIconView#set(StatusBarIcon)}. It's to prevent diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/domain/interactor/OngoingActivityChipInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/domain/interactor/OngoingActivityChipInteractorTest.kt new file mode 100644 index 000000000000..abb6e2b02103 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/domain/interactor/OngoingActivityChipInteractorTest.kt @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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.chips.domain.interactor + +import android.view.View +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.animation.DialogTransitionAnimator +import com.android.systemui.res.R +import com.android.systemui.statusbar.chips.domain.interactor.OngoingActivityChipInteractor.Companion.createDialogLaunchOnClickListener +import com.android.systemui.statusbar.chips.ui.view.ChipBackgroundContainer +import com.android.systemui.statusbar.phone.SystemUIDialog +import kotlin.test.Test +import org.mockito.ArgumentMatchers.anyBoolean +import org.mockito.kotlin.eq +import org.mockito.kotlin.mock +import org.mockito.kotlin.verify +import org.mockito.kotlin.whenever + +@SmallTest +class OngoingActivityChipInteractorTest : SysuiTestCase() { + private val mockSystemUIDialog = mock<SystemUIDialog>() + private val dialogDelegate = SystemUIDialog.Delegate { mockSystemUIDialog } + private val dialogTransitionAnimator = mock<DialogTransitionAnimator>() + + private val chipBackgroundView = mock<ChipBackgroundContainer>() + private val chipView = + mock<View>().apply { + whenever( + this.requireViewById<ChipBackgroundContainer>( + R.id.ongoing_activity_chip_background + ) + ) + .thenReturn(chipBackgroundView) + } + + @Test + fun createDialogLaunchOnClickListener_showsDialogOnClick() { + val clickListener = + createDialogLaunchOnClickListener(dialogDelegate, dialogTransitionAnimator) + + // Dialogs must be created on the main thread + context.mainExecutor.execute { + clickListener.onClick(chipView) + verify(dialogTransitionAnimator) + .showFromView( + eq(mockSystemUIDialog), + eq(chipBackgroundView), + eq(null), + anyBoolean(), + ) + } + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/mediaprojection/domain/interactor/MediaProjectionChipInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/mediaprojection/domain/interactor/MediaProjectionChipInteractorTest.kt index 0f33b9dfc077..a4505a99cb77 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/mediaprojection/domain/interactor/MediaProjectionChipInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/mediaprojection/domain/interactor/MediaProjectionChipInteractorTest.kt @@ -16,8 +16,15 @@ package com.android.systemui.statusbar.chips.mediaprojection.domain.interactor +import android.Manifest +import android.content.Intent +import android.content.packageManager +import android.content.pm.PackageManager +import android.content.pm.ResolveInfo +import android.view.View import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase +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 @@ -27,11 +34,24 @@ import com.android.systemui.mediaprojection.data.repository.fakeMediaProjectionR import com.android.systemui.mediaprojection.taskswitcher.FakeActivityTaskManager.Companion.createTask import com.android.systemui.res.R import com.android.systemui.statusbar.chips.domain.model.OngoingActivityChipModel -import com.android.systemui.statusbar.chips.ui.viewmodel.mediaProjectionChipInteractor +import com.android.systemui.statusbar.chips.mediaprojection.ui.view.EndCastToOtherDeviceDialogDelegate +import com.android.systemui.statusbar.chips.mediaprojection.ui.view.EndShareToAppDialogDelegate +import com.android.systemui.statusbar.chips.ui.view.ChipBackgroundContainer +import com.android.systemui.statusbar.phone.SystemUIDialog +import com.android.systemui.statusbar.phone.mockSystemUIDialogFactory import com.android.systemui.util.time.fakeSystemClock import com.google.common.truth.Truth.assertThat import kotlin.test.Test import kotlinx.coroutines.test.runTest +import org.junit.Before +import org.mockito.ArgumentMatchers.anyBoolean +import org.mockito.ArgumentMatchers.anyInt +import org.mockito.Mockito.doAnswer +import org.mockito.kotlin.any +import org.mockito.kotlin.eq +import org.mockito.kotlin.mock +import org.mockito.kotlin.verify +import org.mockito.kotlin.whenever @SmallTest class MediaProjectionChipInteractorTest : SysuiTestCase() { @@ -40,6 +60,30 @@ class MediaProjectionChipInteractorTest : SysuiTestCase() { private val mediaProjectionRepo = kosmos.fakeMediaProjectionRepository private val systemClock = kosmos.fakeSystemClock + private val mockCastDialog = mock<SystemUIDialog>() + private val mockShareDialog = mock<SystemUIDialog>() + + private val chipBackgroundView = mock<ChipBackgroundContainer>() + private val chipView = + mock<View>().apply { + whenever( + this.requireViewById<ChipBackgroundContainer>( + R.id.ongoing_activity_chip_background + ) + ) + .thenReturn(chipBackgroundView) + } + + @Before + fun setUp() { + setUpPackageManagerForMediaProjection(kosmos) + + whenever(kosmos.mockSystemUIDialogFactory.create(any<EndCastToOtherDeviceDialogDelegate>())) + .thenReturn(mockCastDialog) + whenever(kosmos.mockSystemUIDialogFactory.create(any<EndShareToAppDialogDelegate>())) + .thenReturn(mockShareDialog) + } + private val underTest = kosmos.mediaProjectionChipInteractor @Test @@ -53,12 +97,15 @@ class MediaProjectionChipInteractorTest : SysuiTestCase() { } @Test - fun chip_singleTaskState_isShownWithIcon() = + fun chip_singleTaskState_otherDevicesPackage_castToOtherDeviceChipShown() = testScope.runTest { val latest by collectLastValue(underTest.chip) mediaProjectionRepo.mediaProjectionState.value = - MediaProjectionState.SingleTask(createTask(taskId = 1)) + MediaProjectionState.Projecting.SingleTask( + CAST_TO_OTHER_DEVICES_PACKAGE, + createTask(taskId = 1) + ) assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown::class.java) val icon = (latest as OngoingActivityChipModel.Shown).icon @@ -66,11 +113,12 @@ class MediaProjectionChipInteractorTest : SysuiTestCase() { } @Test - fun chip_entireScreenState_isShownWithIcon() = + fun chip_entireScreenState_otherDevicesPackage_castToOtherDeviceChipShown() = testScope.runTest { val latest by collectLastValue(underTest.chip) - mediaProjectionRepo.mediaProjectionState.value = MediaProjectionState.EntireScreen + mediaProjectionRepo.mediaProjectionState.value = + MediaProjectionState.Projecting.EntireScreen(CAST_TO_OTHER_DEVICES_PACKAGE) assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown::class.java) val icon = (latest as OngoingActivityChipModel.Shown).icon @@ -78,12 +126,39 @@ class MediaProjectionChipInteractorTest : SysuiTestCase() { } @Test + fun chip_singleTaskState_normalPackage_shareToAppChipShown() = + testScope.runTest { + val latest by collectLastValue(underTest.chip) + + mediaProjectionRepo.mediaProjectionState.value = + MediaProjectionState.Projecting.SingleTask(NORMAL_PACKAGE, createTask(taskId = 1)) + + assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown::class.java) + val icon = (latest as OngoingActivityChipModel.Shown).icon + assertThat((icon as Icon.Resource).res).isEqualTo(R.drawable.ic_screenshot_share) + } + + @Test + fun chip_entireScreenState_normalPackage_shareToAppChipShown() = + testScope.runTest { + val latest by collectLastValue(underTest.chip) + + mediaProjectionRepo.mediaProjectionState.value = + MediaProjectionState.Projecting.EntireScreen(NORMAL_PACKAGE) + + assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown::class.java) + val icon = (latest as OngoingActivityChipModel.Shown).icon + assertThat((icon as Icon.Resource).res).isEqualTo(R.drawable.ic_screenshot_share) + } + + @Test fun chip_timeResetsOnEachNewShare() = testScope.runTest { val latest by collectLastValue(underTest.chip) systemClock.setElapsedRealtime(1234) - mediaProjectionRepo.mediaProjectionState.value = MediaProjectionState.EntireScreen + mediaProjectionRepo.mediaProjectionState.value = + MediaProjectionState.Projecting.EntireScreen(NORMAL_PACKAGE) assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown::class.java) assertThat((latest as OngoingActivityChipModel.Shown).startTimeMs).isEqualTo(1234) @@ -92,9 +167,99 @@ class MediaProjectionChipInteractorTest : SysuiTestCase() { assertThat(latest).isInstanceOf(OngoingActivityChipModel.Hidden::class.java) systemClock.setElapsedRealtime(5678) - mediaProjectionRepo.mediaProjectionState.value = MediaProjectionState.EntireScreen + mediaProjectionRepo.mediaProjectionState.value = + MediaProjectionState.Projecting.SingleTask( + CAST_TO_OTHER_DEVICES_PACKAGE, + createTask(taskId = 1) + ) assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown::class.java) assertThat((latest as OngoingActivityChipModel.Shown).startTimeMs).isEqualTo(5678) } + + @Test + fun chip_castToOtherDevice_clickListenerShowsCastDialog() = + testScope.runTest { + val latest by collectLastValue(underTest.chip) + mediaProjectionRepo.mediaProjectionState.value = + MediaProjectionState.Projecting.EntireScreen(CAST_TO_OTHER_DEVICES_PACKAGE) + + val clickListener = ((latest as OngoingActivityChipModel.Shown).onClickListener) + + // Dialogs must be created on the main thread + context.mainExecutor.execute { + clickListener.onClick(chipView) + verify(kosmos.mockDialogTransitionAnimator) + .showFromView( + eq(mockCastDialog), + eq(chipBackgroundView), + eq(null), + anyBoolean(), + ) + } + } + + @Test + fun chip_shareToApp_clickListenerShowsShareDialog() = + testScope.runTest { + val latest by collectLastValue(underTest.chip) + mediaProjectionRepo.mediaProjectionState.value = + MediaProjectionState.Projecting.EntireScreen(NORMAL_PACKAGE) + + val clickListener = ((latest as OngoingActivityChipModel.Shown).onClickListener) + + // Dialogs must be created on the main thread + context.mainExecutor.execute { + clickListener.onClick(chipView) + verify(kosmos.mockDialogTransitionAnimator) + .showFromView( + eq(mockShareDialog), + eq(chipBackgroundView), + eq(null), + anyBoolean(), + ) + } + } + + companion object { + const val CAST_TO_OTHER_DEVICES_PACKAGE = "other.devices.package" + const val NORMAL_PACKAGE = "some.normal.package" + + /** + * Sets up [kosmos.packageManager] so that [CAST_TO_OTHER_DEVICES_PACKAGE] is marked as a + * package that casts to other devices, and [NORMAL_PACKAGE] is *not* marked as casting to + * other devices. + */ + fun setUpPackageManagerForMediaProjection(kosmos: Kosmos) { + kosmos.packageManager.apply { + whenever( + this.checkPermission( + Manifest.permission.REMOTE_DISPLAY_PROVIDER, + CAST_TO_OTHER_DEVICES_PACKAGE + ) + ) + .thenReturn(PackageManager.PERMISSION_GRANTED) + whenever( + this.checkPermission( + Manifest.permission.REMOTE_DISPLAY_PROVIDER, + NORMAL_PACKAGE + ) + ) + .thenReturn(PackageManager.PERMISSION_DENIED) + + doAnswer { + // See Utils.isHeadlessRemoteDisplayProvider + if ( + (it.arguments[0] as Intent).`package` == CAST_TO_OTHER_DEVICES_PACKAGE + ) { + emptyList() + } else { + listOf(mock<ResolveInfo>()) + } + } + .whenever(this) + .queryIntentActivities(any(), anyInt()) + } + } + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/mediaprojection/ui/view/EndCastToOtherDeviceDialogDelegateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/mediaprojection/ui/view/EndCastToOtherDeviceDialogDelegateTest.kt new file mode 100644 index 000000000000..9a2f545fa67e --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/mediaprojection/ui/view/EndCastToOtherDeviceDialogDelegateTest.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.chips.mediaprojection.ui.view + +import android.content.DialogInterface +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.mediaprojection.data.repository.fakeMediaProjectionRepository +import com.android.systemui.res.R +import com.android.systemui.statusbar.chips.mediaprojection.domain.interactor.mediaProjectionChipInteractor +import com.android.systemui.statusbar.phone.SystemUIDialog +import com.android.systemui.statusbar.phone.mockSystemUIDialogFactory +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.mockito.kotlin.argumentCaptor +import org.mockito.kotlin.eq +import org.mockito.kotlin.mock +import org.mockito.kotlin.verify +import org.mockito.kotlin.whenever + +@SmallTest +@OptIn(ExperimentalCoroutinesApi::class) +class EndCastToOtherDeviceDialogDelegateTest : SysuiTestCase() { + private val kosmos = Kosmos() + private val sysuiDialog = mock<SystemUIDialog>() + private val sysuiDialogFactory = kosmos.mockSystemUIDialogFactory + private val underTest = + EndCastToOtherDeviceDialogDelegate( + sysuiDialogFactory, + kosmos.mediaProjectionChipInteractor, + ) + + @Before + fun setUp() { + whenever(sysuiDialogFactory.create(eq(underTest), eq(context))).thenReturn(sysuiDialog) + } + + @Test + fun icon() { + underTest.beforeCreate(sysuiDialog, /* savedInstanceState= */ null) + + verify(sysuiDialog).setIcon(R.drawable.ic_cast_connected) + } + + @Test + fun title() { + underTest.beforeCreate(sysuiDialog, /* savedInstanceState= */ null) + + verify(sysuiDialog).setTitle(R.string.cast_to_other_device_stop_dialog_title) + } + + @Test + fun message() { + underTest.beforeCreate(sysuiDialog, /* savedInstanceState= */ null) + + verify(sysuiDialog).setMessage(R.string.cast_to_other_device_stop_dialog_message) + } + + @Test + fun negativeButton() { + underTest.beforeCreate(sysuiDialog, /* savedInstanceState= */ null) + + verify(sysuiDialog).setNegativeButton(R.string.close_dialog_button, null) + } + + @Test + fun positiveButton() = + kosmos.testScope.runTest { + underTest.beforeCreate(sysuiDialog, /* savedInstanceState= */ null) + + val clickListener = argumentCaptor<DialogInterface.OnClickListener>() + + // Verify the button has the right text + verify(sysuiDialog) + .setPositiveButton( + eq(R.string.cast_to_other_device_stop_dialog_button), + clickListener.capture() + ) + + // Verify that clicking the button stops the recording + assertThat(kosmos.fakeMediaProjectionRepository.stopProjectingInvoked).isFalse() + + clickListener.firstValue.onClick(mock<DialogInterface>(), 0) + runCurrent() + + assertThat(kosmos.fakeMediaProjectionRepository.stopProjectingInvoked).isTrue() + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/mediaprojection/ui/view/EndShareToAppDialogDelegateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/mediaprojection/ui/view/EndShareToAppDialogDelegateTest.kt new file mode 100644 index 000000000000..1d6e8669274d --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/mediaprojection/ui/view/EndShareToAppDialogDelegateTest.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.chips.mediaprojection.ui.view + +import android.content.DialogInterface +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.mediaprojection.data.repository.fakeMediaProjectionRepository +import com.android.systemui.res.R +import com.android.systemui.statusbar.chips.mediaprojection.domain.interactor.mediaProjectionChipInteractor +import com.android.systemui.statusbar.phone.SystemUIDialog +import com.android.systemui.statusbar.phone.mockSystemUIDialogFactory +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.mockito.kotlin.argumentCaptor +import org.mockito.kotlin.eq +import org.mockito.kotlin.mock +import org.mockito.kotlin.verify +import org.mockito.kotlin.whenever + +@SmallTest +@OptIn(ExperimentalCoroutinesApi::class) +class EndShareToAppDialogDelegateTest : SysuiTestCase() { + private val kosmos = Kosmos() + private val sysuiDialog = mock<SystemUIDialog>() + private val sysuiDialogFactory = kosmos.mockSystemUIDialogFactory + private val underTest = + EndShareToAppDialogDelegate( + sysuiDialogFactory, + kosmos.mediaProjectionChipInteractor, + ) + + @Before + fun setUp() { + whenever(sysuiDialogFactory.create(eq(underTest), eq(context))).thenReturn(sysuiDialog) + } + + @Test + fun icon() { + underTest.beforeCreate(sysuiDialog, /* savedInstanceState= */ null) + + verify(sysuiDialog).setIcon(R.drawable.ic_screenshot_share) + } + + @Test + fun title() { + underTest.beforeCreate(sysuiDialog, /* savedInstanceState= */ null) + + verify(sysuiDialog).setTitle(R.string.share_to_app_stop_dialog_title) + } + + @Test + fun message() { + underTest.beforeCreate(sysuiDialog, /* savedInstanceState= */ null) + + verify(sysuiDialog).setMessage(R.string.share_to_app_stop_dialog_message) + } + + @Test + fun negativeButton() { + underTest.beforeCreate(sysuiDialog, /* savedInstanceState= */ null) + + verify(sysuiDialog).setNegativeButton(R.string.close_dialog_button, null) + } + + @Test + fun positiveButton() = + kosmos.testScope.runTest { + underTest.beforeCreate(sysuiDialog, /* savedInstanceState= */ null) + + val clickListener = argumentCaptor<DialogInterface.OnClickListener>() + + // Verify the button has the right text + verify(sysuiDialog) + .setPositiveButton( + eq(R.string.share_to_app_stop_dialog_button), + clickListener.capture() + ) + + // Verify that clicking the button stops the recording + assertThat(kosmos.fakeMediaProjectionRepository.stopProjectingInvoked).isFalse() + + clickListener.firstValue.onClick(mock<DialogInterface>(), 0) + runCurrent() + + assertThat(kosmos.fakeMediaProjectionRepository.stopProjectingInvoked).isTrue() + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/screenrecord/domain/interactor/ScreenRecordChipInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/screenrecord/domain/interactor/ScreenRecordChipInteractorTest.kt index 25efaf10fce7..f6c3adb364ce 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/screenrecord/domain/interactor/ScreenRecordChipInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/screenrecord/domain/interactor/ScreenRecordChipInteractorTest.kt @@ -16,8 +16,10 @@ package com.android.systemui.statusbar.chips.screenrecord.domain.interactor +import android.view.View import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase +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 @@ -26,11 +28,21 @@ import com.android.systemui.res.R import com.android.systemui.screenrecord.data.model.ScreenRecordModel import com.android.systemui.screenrecord.data.repository.screenRecordRepository import com.android.systemui.statusbar.chips.domain.model.OngoingActivityChipModel +import com.android.systemui.statusbar.chips.ui.view.ChipBackgroundContainer import com.android.systemui.statusbar.chips.ui.viewmodel.screenRecordChipInteractor +import com.android.systemui.statusbar.phone.SystemUIDialog +import com.android.systemui.statusbar.phone.mockSystemUIDialogFactory import com.android.systemui.util.time.fakeSystemClock import com.google.common.truth.Truth.assertThat import kotlin.test.Test import kotlinx.coroutines.test.runTest +import org.junit.Before +import org.mockito.ArgumentMatchers.anyBoolean +import org.mockito.kotlin.any +import org.mockito.kotlin.eq +import org.mockito.kotlin.mock +import org.mockito.kotlin.verify +import org.mockito.kotlin.whenever @SmallTest class ScreenRecordChipInteractorTest : SysuiTestCase() { @@ -38,9 +50,27 @@ class ScreenRecordChipInteractorTest : SysuiTestCase() { private val testScope = kosmos.testScope private val screenRecordRepo = kosmos.screenRecordRepository private val systemClock = kosmos.fakeSystemClock + private val mockSystemUIDialog = mock<SystemUIDialog>() + + private val chipBackgroundView = mock<ChipBackgroundContainer>() + private val chipView = + mock<View>().apply { + whenever( + this.requireViewById<ChipBackgroundContainer>( + R.id.ongoing_activity_chip_background + ) + ) + .thenReturn(chipBackgroundView) + } private val underTest = kosmos.screenRecordChipInteractor + @Before + fun setUp() { + whenever(kosmos.mockSystemUIDialogFactory.create(any<SystemUIDialog.Delegate>())) + .thenReturn(mockSystemUIDialog) + } + @Test fun chip_doingNothingState_isHidden() = testScope.runTest { @@ -70,7 +100,7 @@ class ScreenRecordChipInteractorTest : SysuiTestCase() { assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown::class.java) val icon = (latest as OngoingActivityChipModel.Shown).icon - assertThat((icon as Icon.Resource).res).isEqualTo(R.drawable.stat_sys_screen_record) + assertThat((icon as Icon.Resource).res).isEqualTo(R.drawable.ic_screenrecord) } @Test @@ -93,4 +123,25 @@ class ScreenRecordChipInteractorTest : SysuiTestCase() { assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown::class.java) assertThat((latest as OngoingActivityChipModel.Shown).startTimeMs).isEqualTo(5678) } + + @Test + fun chip_clickListenerShowsDialog() = + testScope.runTest { + val latest by collectLastValue(underTest.chip) + screenRecordRepo.screenRecordState.value = ScreenRecordModel.Recording + + val clickListener = ((latest as OngoingActivityChipModel.Shown).onClickListener) + + // Dialogs must be created on the main thread + context.mainExecutor.execute { + clickListener.onClick(chipView) + verify(kosmos.mockDialogTransitionAnimator) + .showFromView( + eq(mockSystemUIDialog), + eq(chipBackgroundView), + eq(null), + anyBoolean(), + ) + } + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/screenrecord/ui/view/EndScreenRecordingDialogDelegateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/screenrecord/ui/view/EndScreenRecordingDialogDelegateTest.kt new file mode 100644 index 000000000000..bca6763a4989 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/screenrecord/ui/view/EndScreenRecordingDialogDelegateTest.kt @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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.chips.screenrecord.ui.view + +import android.content.DialogInterface +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.res.R +import com.android.systemui.screenrecord.data.repository.screenRecordRepository +import com.android.systemui.statusbar.chips.ui.viewmodel.screenRecordChipInteractor +import com.android.systemui.statusbar.phone.SystemUIDialog +import com.android.systemui.statusbar.phone.mockSystemUIDialogFactory +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.mockito.kotlin.argumentCaptor +import org.mockito.kotlin.eq +import org.mockito.kotlin.mock +import org.mockito.kotlin.verify +import org.mockito.kotlin.whenever + +@SmallTest +@OptIn(ExperimentalCoroutinesApi::class) +class EndScreenRecordingDialogDelegateTest : SysuiTestCase() { + private val kosmos = Kosmos() + + private val sysuiDialog = mock<SystemUIDialog>() + private val sysuiDialogFactory = kosmos.mockSystemUIDialogFactory + + private val underTest = + EndScreenRecordingDialogDelegate( + sysuiDialogFactory, + kosmos.screenRecordChipInteractor, + ) + + @Before + fun setUp() { + whenever(sysuiDialogFactory.create(eq(underTest), eq(context))).thenReturn(sysuiDialog) + } + + @Test + fun icon() { + underTest.beforeCreate(sysuiDialog, /* savedInstanceState= */ null) + + verify(sysuiDialog).setIcon(R.drawable.ic_screenrecord) + } + + @Test + fun title() { + underTest.beforeCreate(sysuiDialog, /* savedInstanceState= */ null) + + verify(sysuiDialog).setTitle(R.string.screenrecord_stop_dialog_title) + } + + @Test + fun message() { + underTest.beforeCreate(sysuiDialog, /* savedInstanceState= */ null) + + verify(sysuiDialog).setMessage(R.string.screenrecord_stop_dialog_message) + } + + @Test + fun negativeButton() { + underTest.beforeCreate(sysuiDialog, /* savedInstanceState= */ null) + + verify(sysuiDialog).setNegativeButton(R.string.close_dialog_button, null) + } + + @Test + fun positiveButton() = + kosmos.testScope.runTest { + underTest.beforeCreate(sysuiDialog, /* savedInstanceState= */ null) + + val clickListener = argumentCaptor<DialogInterface.OnClickListener>() + + // Verify the button has the right text + verify(sysuiDialog) + .setPositiveButton( + eq(R.string.screenrecord_stop_dialog_button), + clickListener.capture() + ) + + // Verify that clicking the button stops the recording + assertThat(kosmos.screenRecordRepository.stopRecordingInvoked).isFalse() + + clickListener.firstValue.onClick(mock<DialogInterface>(), 0) + runCurrent() + + assertThat(kosmos.screenRecordRepository.stopRecordingInvoked).isTrue() + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelTest.kt index 121229c321b6..67129639efaf 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelTest.kt @@ -30,8 +30,11 @@ import com.android.systemui.res.R import com.android.systemui.screenrecord.data.model.ScreenRecordModel import com.android.systemui.screenrecord.data.repository.screenRecordRepository import com.android.systemui.statusbar.chips.domain.model.OngoingActivityChipModel +import com.android.systemui.statusbar.chips.mediaprojection.domain.interactor.MediaProjectionChipInteractorTest.Companion.NORMAL_PACKAGE +import com.android.systemui.statusbar.chips.mediaprojection.domain.interactor.MediaProjectionChipInteractorTest.Companion.setUpPackageManagerForMediaProjection import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.test.runTest +import org.junit.Before import org.junit.Test @SmallTest @@ -46,6 +49,11 @@ class OngoingActivityChipsViewModelTest : SysuiTestCase() { private val underTest = kosmos.ongoingActivityChipsViewModel + @Before + fun setUp() { + setUpPackageManagerForMediaProjection(kosmos) + } + @Test fun chip_allHidden_hidden() = testScope.runTest { @@ -91,7 +99,8 @@ class OngoingActivityChipsViewModelTest : SysuiTestCase() { fun chip_screenRecordShowAndMediaProjectionShow_screenRecordShown() = testScope.runTest { screenRecordState.value = ScreenRecordModel.Recording - mediaProjectionState.value = MediaProjectionState.EntireScreen + mediaProjectionState.value = + MediaProjectionState.Projecting.EntireScreen(NORMAL_PACKAGE) callState.value = OngoingActivityChipModel.Hidden val latest by collectLastValue(underTest.chip) @@ -103,7 +112,8 @@ class OngoingActivityChipsViewModelTest : SysuiTestCase() { fun chip_mediaProjectionShowAndCallShow_mediaProjectionShown() = testScope.runTest { screenRecordState.value = ScreenRecordModel.DoingNothing - mediaProjectionState.value = MediaProjectionState.EntireScreen + mediaProjectionState.value = + MediaProjectionState.Projecting.EntireScreen(NORMAL_PACKAGE) val callChip = OngoingActivityChipModel.Shown( Icon.Resource(R.drawable.ic_call, ContentDescription.Loaded("icon")), @@ -113,7 +123,7 @@ class OngoingActivityChipsViewModelTest : SysuiTestCase() { val latest by collectLastValue(underTest.chip) - assertIsMediaProjectionChip(latest) + assertIsShareToAppChip(latest) } @Test @@ -152,10 +162,14 @@ class OngoingActivityChipsViewModelTest : SysuiTestCase() { assertThat(latest).isEqualTo(callChip) // WHEN the higher priority media projection chip is added - mediaProjectionState.value = MediaProjectionState.SingleTask(createTask(taskId = 1)) + mediaProjectionState.value = + MediaProjectionState.Projecting.SingleTask( + NORMAL_PACKAGE, + createTask(taskId = 1), + ) // THEN the higher priority media projection chip is used - assertIsMediaProjectionChip(latest) + assertIsShareToAppChip(latest) // WHEN the higher priority screen record chip is added screenRecordState.value = ScreenRecordModel.Recording @@ -169,7 +183,8 @@ class OngoingActivityChipsViewModelTest : SysuiTestCase() { testScope.runTest { // WHEN all chips are active screenRecordState.value = ScreenRecordModel.Recording - mediaProjectionState.value = MediaProjectionState.EntireScreen + mediaProjectionState.value = + MediaProjectionState.Projecting.EntireScreen(NORMAL_PACKAGE) val callChip = OngoingActivityChipModel.Shown( @@ -187,7 +202,7 @@ class OngoingActivityChipsViewModelTest : SysuiTestCase() { screenRecordState.value = ScreenRecordModel.DoingNothing // THEN the lower priority media projection is used - assertIsMediaProjectionChip(latest) + assertIsShareToAppChip(latest) // WHEN the higher priority media projection is removed mediaProjectionState.value = MediaProjectionState.NotProjecting @@ -200,13 +215,13 @@ class OngoingActivityChipsViewModelTest : SysuiTestCase() { fun assertIsScreenRecordChip(latest: OngoingActivityChipModel?) { assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown::class.java) val icon = (latest as OngoingActivityChipModel.Shown).icon - assertThat((icon as Icon.Resource).res).isEqualTo(R.drawable.stat_sys_screen_record) + assertThat((icon as Icon.Resource).res).isEqualTo(R.drawable.ic_screenrecord) } - fun assertIsMediaProjectionChip(latest: OngoingActivityChipModel?) { + fun assertIsShareToAppChip(latest: OngoingActivityChipModel?) { assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown::class.java) val icon = (latest as OngoingActivityChipModel.Shown).icon - assertThat((icon as Icon.Resource).res).isEqualTo(R.drawable.ic_cast_connected) + assertThat((icon as Icon.Resource).res).isEqualTo(R.drawable.ic_screenshot_share) } } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt index 068e166de5bb..066ca1c17d30 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt @@ -762,6 +762,7 @@ class LockscreenSmartspaceControllerTest : SysuiTestCase() { // THEN the existing session is reused and views are registered verify(smartspaceManager, never()).createSmartspaceSession(any()) verify(smartspaceView2).setUiSurface(BcSmartspaceDataPlugin.UI_SURFACE_LOCK_SCREEN_AOD) + verify(smartspaceView2).setTimeChangedDelegate(any()) verify(smartspaceView2).registerDataProvider(plugin) verify(smartspaceView2).registerConfigProvider(configPlugin) } @@ -836,6 +837,7 @@ class LockscreenSmartspaceControllerTest : SysuiTestCase() { verify(dateSmartspaceView).setUiSurface( BcSmartspaceDataPlugin.UI_SURFACE_LOCK_SCREEN_AOD) + verify(dateSmartspaceView).setTimeChangedDelegate(any()) verify(dateSmartspaceView).registerDataProvider(datePlugin) verify(dateSmartspaceView).setPrimaryTextColor(anyInt()) @@ -848,6 +850,7 @@ class LockscreenSmartspaceControllerTest : SysuiTestCase() { verify(weatherSmartspaceView).setUiSurface( BcSmartspaceDataPlugin.UI_SURFACE_LOCK_SCREEN_AOD) + verify(weatherSmartspaceView).setTimeChangedDelegate(any()) verify(weatherSmartspaceView).registerDataProvider(weatherPlugin) verify(weatherSmartspaceView).setPrimaryTextColor(anyInt()) @@ -859,6 +862,7 @@ class LockscreenSmartspaceControllerTest : SysuiTestCase() { controller.stateChangeListener.onViewAttachedToWindow(view) verify(smartspaceView).setUiSurface(BcSmartspaceDataPlugin.UI_SURFACE_LOCK_SCREEN_AOD) + verify(smartspaceView).setTimeChangedDelegate(any()) verify(smartspaceView).registerDataProvider(plugin) verify(smartspaceView).registerConfigProvider(configPlugin) verify(smartspaceSession) @@ -984,6 +988,10 @@ class LockscreenSmartspaceControllerTest : SysuiTestCase() { override fun setUiSurface(uiSurface: String) { } + override fun setTimeChangedDelegate( + delegate: BcSmartspaceDataPlugin.TimeChangedDelegate? + ) {} + override fun setDozeAmount(amount: Float) { } @@ -1012,6 +1020,10 @@ class LockscreenSmartspaceControllerTest : SysuiTestCase() { override fun setUiSurface(uiSurface: String) { } + override fun setTimeChangedDelegate( + delegate: BcSmartspaceDataPlugin.TimeChangedDelegate? + ) {} + override fun setDozeAmount(amount: Float) { } @@ -1036,6 +1048,10 @@ class LockscreenSmartspaceControllerTest : SysuiTestCase() { override fun setUiSurface(uiSurface: String) { } + override fun setTimeChangedDelegate( + delegate: BcSmartspaceDataPlugin.TimeChangedDelegate? + ) {} + override fun setDozeAmount(amount: Float) { } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationSectionsFeatureManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationSectionsFeatureManagerTest.kt index 9e733be6665c..acb005f6e72f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationSectionsFeatureManagerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationSectionsFeatureManagerTest.kt @@ -19,71 +19,86 @@ package com.android.systemui.statusbar.notification import android.platform.test.annotations.DisableFlags import android.provider.DeviceConfig import android.provider.Settings - import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.dx.mockito.inline.extended.ExtendedMockito - import com.android.internal.config.sysui.SystemUiDeviceConfigFlags.NOTIFICATIONS_USE_PEOPLE_FILTERING import com.android.systemui.SysuiTestCase +import com.android.systemui.statusbar.notification.shared.NotificationMinimalismPrototype import com.android.systemui.statusbar.notification.shared.PriorityPeopleSection import com.android.systemui.util.DeviceConfigProxyFake import com.android.systemui.util.Utils import com.android.systemui.util.mockito.any - import org.junit.After import org.junit.Assert.assertFalse import org.junit.Assert.assertTrue import org.junit.Before import org.junit.Test import org.junit.runner.RunWith -import org.mockito.Mockito.`when` import org.mockito.MockitoSession +import org.mockito.kotlin.whenever import org.mockito.quality.Strictness @RunWith(AndroidJUnit4::class) @SmallTest -@DisableFlags(PriorityPeopleSection.FLAG_NAME) // this class has no logic with the flag enabled +// this class has no testable logic with either of these flags enabled +@DisableFlags(PriorityPeopleSection.FLAG_NAME, NotificationMinimalismPrototype.V2.FLAG_NAME) class NotificationSectionsFeatureManagerTest : SysuiTestCase() { - var manager: NotificationSectionsFeatureManager? = null - val proxyFake = DeviceConfigProxyFake() + lateinit var manager: NotificationSectionsFeatureManager + private val proxyFake = DeviceConfigProxyFake() private lateinit var staticMockSession: MockitoSession @Before - public fun setup() { + fun setup() { manager = NotificationSectionsFeatureManager(proxyFake, mContext) - manager!!.clearCache() - staticMockSession = ExtendedMockito.mockitoSession() - .mockStatic<Utils>(Utils::class.java) - .strictness(Strictness.LENIENT) - .startMocking() - `when`(Utils.useQsMediaPlayer(any())).thenReturn(false) - Settings.Global.putInt(context.getContentResolver(), - Settings.Global.SHOW_MEDIA_ON_QUICK_SETTINGS, 0) + manager.clearCache() + staticMockSession = + ExtendedMockito.mockitoSession() + .mockStatic(Utils::class.java) + .strictness(Strictness.LENIENT) + .startMocking() + whenever(Utils.useQsMediaPlayer(any())).thenReturn(false) + Settings.Global.putInt( + context.getContentResolver(), + Settings.Global.SHOW_MEDIA_ON_QUICK_SETTINGS, + 0 + ) } @After - public fun teardown() { + fun teardown() { staticMockSession.finishMocking() } @Test - public fun testPeopleFilteringOff_newInterruptionModelOn() { + fun testPeopleFilteringOff_newInterruptionModelOn() { proxyFake.setProperty( - DeviceConfig.NAMESPACE_SYSTEMUI, NOTIFICATIONS_USE_PEOPLE_FILTERING, "false", false) + DeviceConfig.NAMESPACE_SYSTEMUI, + NOTIFICATIONS_USE_PEOPLE_FILTERING, + "false", + false + ) - assertFalse("People filtering should be disabled", manager!!.isFilteringEnabled()) - assertTrue("Expecting 2 buckets when people filtering is disabled", - manager!!.getNumberOfBuckets() == 2) + assertFalse("People filtering should be disabled", manager.isFilteringEnabled()) + assertTrue( + "Expecting 2 buckets when people filtering is disabled", + manager.getNumberOfBuckets() == 2 + ) } @Test - public fun testPeopleFilteringOn_newInterruptionModelOn() { + fun testPeopleFilteringOn_newInterruptionModelOn() { proxyFake.setProperty( - DeviceConfig.NAMESPACE_SYSTEMUI, NOTIFICATIONS_USE_PEOPLE_FILTERING, "true", false) + DeviceConfig.NAMESPACE_SYSTEMUI, + NOTIFICATIONS_USE_PEOPLE_FILTERING, + "true", + false + ) - assertTrue("People filtering should be enabled", manager!!.isFilteringEnabled()) - assertTrue("Expecting 5 buckets when people filtering is enabled", - manager!!.getNumberOfBuckets() == 5) + assertTrue("People filtering should be enabled", manager.isFilteringEnabled()) + assertTrue( + "Expecting 5 buckets when people filtering is enabled", + manager.getNumberOfBuckets() == 5 + ) } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/domain/interactor/NotificationIconsInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/domain/interactor/NotificationIconsInteractorTest.kt index c9f2addfd39b..26f53707aba1 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/domain/interactor/NotificationIconsInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/domain/interactor/NotificationIconsInteractorTest.kt @@ -39,6 +39,7 @@ import com.android.systemui.statusbar.notification.shared.byIsRowDismissed import com.android.systemui.statusbar.notification.shared.byIsSilent import com.android.systemui.statusbar.notification.shared.byIsSuppressedFromStatusBar import com.android.systemui.statusbar.notification.shared.byKey +import com.android.systemui.user.domain.UserDomainLayerModule import com.android.systemui.util.mockito.eq import com.android.systemui.util.mockito.mock import com.android.systemui.util.mockito.whenever @@ -156,7 +157,14 @@ class AlwaysOnDisplayNotificationIconsInteractorTest : SysuiTestCase() { private val bubbles: Bubbles = mock() - @Component(modules = [SysUITestModule::class, BiometricsDomainLayerModule::class]) + @Component( + modules = + [ + SysUITestModule::class, + BiometricsDomainLayerModule::class, + UserDomainLayerModule::class, + ] + ) @SysUISingleton interface TestComponent : SysUITestComponent<AlwaysOnDisplayNotificationIconsInteractor> { diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java index 0d3ab865669e..b278f1a48b3d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java @@ -41,6 +41,7 @@ import android.os.AsyncTask; import android.os.CancellationSignal; import android.os.Handler; import android.os.Looper; +import android.platform.test.annotations.DisableFlags; import android.testing.TestableLooper; import android.testing.TestableLooper.RunWithLooper; import android.util.TypedValue; @@ -60,6 +61,7 @@ import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.BindParams; import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationCallback; import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag; +import com.android.systemui.statusbar.notification.row.shared.NotificationRowContentBinderRefactor; import com.android.systemui.statusbar.policy.InflatedSmartReplyState; import com.android.systemui.statusbar.policy.InflatedSmartReplyViewHolder; import com.android.systemui.statusbar.policy.SmartReplyStateInflater; @@ -81,6 +83,7 @@ import java.util.concurrent.TimeUnit; @SmallTest @RunWith(AndroidJUnit4.class) @RunWithLooper(setAsMainLooper = true) +@DisableFlags(NotificationRowContentBinderRefactor.FLAG_NAME) public class NotificationContentInflaterTest extends SysuiTestCase { private NotificationContentInflater mNotificationInflater; diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImplTest.kt new file mode 100644 index 000000000000..e6cba1c39c85 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImplTest.kt @@ -0,0 +1,572 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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 android.app.Notification +import android.content.Context +import android.os.AsyncTask +import android.os.Build +import android.os.CancellationSignal +import android.platform.test.annotations.EnableFlags +import android.testing.TestableLooper.RunWithLooper +import android.util.TypedValue +import android.view.View +import android.view.ViewGroup +import android.widget.RemoteViews +import android.widget.TextView +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.res.R +import com.android.systemui.statusbar.notification.ConversationNotificationProcessor +import com.android.systemui.statusbar.notification.collection.NotificationEntry +import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.BindParams +import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationCallback +import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag +import com.android.systemui.statusbar.notification.row.shared.HeadsUpStatusBarModel +import com.android.systemui.statusbar.notification.row.shared.NewRemoteViews +import com.android.systemui.statusbar.notification.row.shared.NotificationContentModel +import com.android.systemui.statusbar.notification.row.shared.NotificationRowContentBinderRefactor +import com.android.systemui.statusbar.policy.InflatedSmartReplyState +import com.android.systemui.statusbar.policy.InflatedSmartReplyViewHolder +import com.android.systemui.statusbar.policy.SmartReplyStateInflater +import com.android.systemui.util.concurrency.mockExecutorHandler +import java.util.concurrent.CountDownLatch +import java.util.concurrent.Executor +import java.util.concurrent.TimeUnit +import org.junit.Assert +import org.junit.Before +import org.junit.Ignore +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.kotlin.any +import org.mockito.kotlin.eq +import org.mockito.kotlin.mock +import org.mockito.kotlin.spy +import org.mockito.kotlin.times +import org.mockito.kotlin.verify +import org.mockito.kotlin.whenever + +@SmallTest +@RunWith(AndroidJUnit4::class) +@RunWithLooper +@EnableFlags(NotificationRowContentBinderRefactor.FLAG_NAME) +class NotificationRowContentBinderImplTest : SysuiTestCase() { + private lateinit var mNotificationInflater: NotificationRowContentBinderImpl + private lateinit var mBuilder: Notification.Builder + private lateinit var mRow: ExpandableNotificationRow + private lateinit var mHelper: NotificationTestHelper + + private var mCache: NotifRemoteViewCache = mock() + private var mConversationNotificationProcessor: ConversationNotificationProcessor = mock() + private var mInflatedSmartReplyState: InflatedSmartReplyState = mock() + private var mInflatedSmartReplies: InflatedSmartReplyViewHolder = mock() + private var mNotifLayoutInflaterFactoryProvider: NotifLayoutInflaterFactory.Provider = mock() + private var mHeadsUpStyleProvider: HeadsUpStyleProvider = mock() + private var mNotifLayoutInflaterFactory: NotifLayoutInflaterFactory = mock() + private val mSmartReplyStateInflater: SmartReplyStateInflater = + object : SmartReplyStateInflater { + override fun inflateSmartReplyViewHolder( + sysuiContext: Context, + notifPackageContext: Context, + entry: NotificationEntry, + existingSmartReplyState: InflatedSmartReplyState?, + newSmartReplyState: InflatedSmartReplyState + ): InflatedSmartReplyViewHolder { + return mInflatedSmartReplies + } + + override fun inflateSmartReplyState(entry: NotificationEntry): InflatedSmartReplyState { + return mInflatedSmartReplyState + } + } + + @Before + fun setUp() { + allowTestableLooperAsMainThread() + mBuilder = + Notification.Builder(mContext, "no-id") + .setSmallIcon(R.drawable.ic_person) + .setContentTitle("Title") + .setContentText("Text") + .setStyle(Notification.BigTextStyle().bigText("big text")) + mHelper = NotificationTestHelper(mContext, mDependency) + val row = mHelper.createRow(mBuilder.build()) + mRow = spy(row) + whenever(mNotifLayoutInflaterFactoryProvider.provide(any(), any())) + .thenReturn(mNotifLayoutInflaterFactory) + mNotificationInflater = + NotificationRowContentBinderImpl( + mCache, + mock(), + mConversationNotificationProcessor, + mock(), + mSmartReplyStateInflater, + mNotifLayoutInflaterFactoryProvider, + mHeadsUpStyleProvider, + mock() + ) + } + + @Test + fun testIncreasedHeadsUpBeingUsed() { + val params = BindParams() + params.usesIncreasedHeadsUpHeight = true + val builder = spy(mBuilder) + mNotificationInflater.inflateNotificationViews( + mRow.entry, + mRow, + params, + true /* inflateSynchronously */, + NotificationRowContentBinder.FLAG_CONTENT_VIEW_ALL, + builder, + mContext, + mSmartReplyStateInflater + ) + verify(builder).createHeadsUpContentView(true) + } + + @Test + fun testIncreasedHeightBeingUsed() { + val params = BindParams() + params.usesIncreasedHeight = true + val builder = spy(mBuilder) + mNotificationInflater.inflateNotificationViews( + mRow.entry, + mRow, + params, + true /* inflateSynchronously */, + NotificationRowContentBinder.FLAG_CONTENT_VIEW_ALL, + builder, + mContext, + mSmartReplyStateInflater + ) + verify(builder).createContentView(true) + } + + @Test + fun testInflationCallsUpdated() { + inflateAndWait( + mNotificationInflater, + NotificationRowContentBinder.FLAG_CONTENT_VIEW_ALL, + mRow + ) + verify(mRow).onNotificationUpdated() + } + + @Test + fun testInflationOnlyInflatesSetFlags() { + inflateAndWait( + mNotificationInflater, + NotificationRowContentBinder.FLAG_CONTENT_VIEW_HEADS_UP, + mRow + ) + Assert.assertNotNull(mRow.privateLayout.headsUpChild) + verify(mRow).onNotificationUpdated() + } + + @Test + fun testInflationThrowsErrorDoesntCallUpdated() { + mRow.privateLayout.removeAllViews() + mRow.entry.sbn.notification.contentView = + RemoteViews(mContext.packageName, R.layout.status_bar) + inflateAndWait( + true /* expectingException */, + mNotificationInflater, + NotificationRowContentBinder.FLAG_CONTENT_VIEW_ALL, + mRow + ) + Assert.assertTrue(mRow.privateLayout.childCount == 0) + verify(mRow, times(0)).onNotificationUpdated() + } + + @Test + fun testAsyncTaskRemoved() { + mRow.entry.abortTask() + inflateAndWait( + mNotificationInflater, + NotificationRowContentBinder.FLAG_CONTENT_VIEW_ALL, + mRow + ) + verify(mRow).onNotificationUpdated() + } + + @Test + fun testRemovedNotInflated() { + mRow.setRemoved() + mNotificationInflater.setInflateSynchronously(true) + mNotificationInflater.bindContent( + mRow.entry, + mRow, + NotificationRowContentBinder.FLAG_CONTENT_VIEW_ALL, + BindParams(), + false /* forceInflate */, + null /* callback */ + ) + Assert.assertNull(mRow.entry.runningTask) + } + + @Test + @Ignore("b/345418902") + fun testInflationIsRetriedIfAsyncFails() { + val headsUpStatusBarModel = HeadsUpStatusBarModel("private", "public") + val result = + NotificationRowContentBinderImpl.InflationProgress( + packageContext = mContext, + remoteViews = NewRemoteViews(), + contentModel = NotificationContentModel(headsUpStatusBarModel) + ) + val countDownLatch = CountDownLatch(1) + NotificationRowContentBinderImpl.applyRemoteView( + AsyncTask.SERIAL_EXECUTOR, + inflateSynchronously = false, + isMinimized = false, + result = result, + reInflateFlags = NotificationRowContentBinder.FLAG_CONTENT_VIEW_EXPANDED, + inflationId = 0, + remoteViewCache = mock(), + entry = mRow.entry, + row = mRow, + isNewView = true, /* isNewView */ + remoteViewClickHandler = { _, _, _ -> true }, + callback = + object : InflationCallback { + override fun handleInflationException(entry: NotificationEntry, e: Exception) { + countDownLatch.countDown() + throw RuntimeException("No Exception expected") + } + + override fun onAsyncInflationFinished(entry: NotificationEntry) { + countDownLatch.countDown() + } + }, + parentLayout = mRow.privateLayout, + existingView = null, + existingWrapper = null, + runningInflations = HashMap(), + applyCallback = + object : NotificationRowContentBinderImpl.ApplyCallback() { + override fun setResultView(v: View) {} + + override val remoteView: RemoteViews + get() = + AsyncFailRemoteView( + mContext.packageName, + com.android.systemui.tests.R.layout.custom_view_dark + ) + }, + logger = mock(), + ) + Assert.assertTrue(countDownLatch.await(500, TimeUnit.MILLISECONDS)) + } + + @Test + fun doesntReapplyDisallowedRemoteView() { + mBuilder.setStyle(Notification.MediaStyle()) + val mediaView = mBuilder.createContentView() + mBuilder.setStyle(Notification.DecoratedCustomViewStyle()) + mBuilder.setCustomContentView( + RemoteViews(context.packageName, com.android.systemui.tests.R.layout.custom_view_dark) + ) + val decoratedMediaView = mBuilder.createContentView() + Assert.assertFalse( + "The decorated media style doesn't allow a view to be reapplied!", + NotificationRowContentBinderImpl.canReapplyRemoteView(mediaView, decoratedMediaView) + ) + } + + @Test + @Ignore("b/345418902") + fun testUsesSameViewWhenCachedPossibleToReuse() { + // GIVEN a cached view. + val contractedRemoteView = mBuilder.createContentView() + whenever( + mCache.hasCachedView( + mRow.entry, + NotificationRowContentBinder.FLAG_CONTENT_VIEW_CONTRACTED + ) + ) + .thenReturn(true) + whenever( + mCache.getCachedView( + mRow.entry, + NotificationRowContentBinder.FLAG_CONTENT_VIEW_CONTRACTED + ) + ) + .thenReturn(contractedRemoteView) + + // GIVEN existing bound view with same layout id. + val view = contractedRemoteView.apply(mContext, null /* parent */) + mRow.privateLayout.setContractedChild(view) + + // WHEN inflater inflates + inflateAndWait( + mNotificationInflater, + NotificationRowContentBinder.FLAG_CONTENT_VIEW_CONTRACTED, + mRow + ) + + // THEN the view should be re-used + Assert.assertEquals( + "Binder inflated a new view even though the old one was cached and usable.", + view, + mRow.privateLayout.contractedChild + ) + } + + @Test + fun testInflatesNewViewWhenCachedNotPossibleToReuse() { + // GIVEN a cached remote view. + val contractedRemoteView = mBuilder.createHeadsUpContentView() + whenever( + mCache.hasCachedView( + mRow.entry, + NotificationRowContentBinder.FLAG_CONTENT_VIEW_CONTRACTED + ) + ) + .thenReturn(true) + whenever( + mCache.getCachedView( + mRow.entry, + NotificationRowContentBinder.FLAG_CONTENT_VIEW_CONTRACTED + ) + ) + .thenReturn(contractedRemoteView) + + // GIVEN existing bound view with different layout id. + val view: View = TextView(mContext) + mRow.privateLayout.setContractedChild(view) + + // WHEN inflater inflates + inflateAndWait( + mNotificationInflater, + NotificationRowContentBinder.FLAG_CONTENT_VIEW_CONTRACTED, + mRow + ) + + // THEN the view should be a new view + Assert.assertNotEquals( + "Binder (somehow) used the same view when inflating.", + view, + mRow.privateLayout.contractedChild + ) + } + + @Test + fun testInflationCachesCreatedRemoteView() { + // WHEN inflater inflates + inflateAndWait( + mNotificationInflater, + NotificationRowContentBinder.FLAG_CONTENT_VIEW_CONTRACTED, + mRow + ) + + // THEN inflater informs cache of the new remote view + verify(mCache) + .putCachedView( + eq(mRow.entry), + eq(NotificationRowContentBinder.FLAG_CONTENT_VIEW_CONTRACTED), + any() + ) + } + + @Test + fun testUnbindRemovesCachedRemoteView() { + // WHEN inflated unbinds content + mNotificationInflater.unbindContent( + mRow.entry, + mRow, + NotificationRowContentBinder.FLAG_CONTENT_VIEW_HEADS_UP + ) + + // THEN inflated informs cache to remove remote view + verify(mCache) + .removeCachedView( + eq(mRow.entry), + eq(NotificationRowContentBinder.FLAG_CONTENT_VIEW_HEADS_UP) + ) + } + + @Test + fun testNotificationViewHeightTooSmallFailsValidation() { + val validationError = + getValidationError( + measuredHeightDp = 5f, + targetSdk = Build.VERSION_CODES.R, + contentView = mock(), + ) + Assert.assertNotNull(validationError) + } + + @Test + fun testNotificationViewHeightPassesForNewerSDK() { + val validationError = + getValidationError( + measuredHeightDp = 5f, + targetSdk = Build.VERSION_CODES.S, + contentView = mock(), + ) + Assert.assertNull(validationError) + } + + @Test + fun testNotificationViewHeightPassesForTemplatedViews() { + val validationError = + getValidationError( + measuredHeightDp = 5f, + targetSdk = Build.VERSION_CODES.R, + contentView = null, + ) + Assert.assertNull(validationError) + } + + @Test + fun testNotificationViewPassesValidation() { + val validationError = + getValidationError( + measuredHeightDp = 20f, + targetSdk = Build.VERSION_CODES.R, + contentView = mock(), + ) + Assert.assertNull(validationError) + } + + private fun getValidationError( + measuredHeightDp: Float, + targetSdk: Int, + contentView: RemoteViews? + ): String? { + val view: View = mock() + whenever(view.measuredHeight) + .thenReturn( + TypedValue.applyDimension( + TypedValue.COMPLEX_UNIT_SP, + measuredHeightDp, + mContext.resources.displayMetrics + ) + .toInt() + ) + mRow.entry.targetSdk = targetSdk + mRow.entry.sbn.notification.contentView = contentView + return NotificationRowContentBinderImpl.isValidView(view, mRow.entry, mContext.resources) + } + + @Test + fun testInvalidNotificationDoesNotInvokeCallback() { + mRow.privateLayout.removeAllViews() + mRow.entry.sbn.notification.contentView = + RemoteViews( + mContext.packageName, + com.android.systemui.tests.R.layout.invalid_notification_height + ) + inflateAndWait( + true, + mNotificationInflater, + NotificationRowContentBinder.FLAG_CONTENT_VIEW_ALL, + mRow + ) + Assert.assertEquals(0, mRow.privateLayout.childCount.toLong()) + verify(mRow, times(0)).onNotificationUpdated() + } + + private class ExceptionHolder { + var mException: Exception? = null + + fun setException(exception: Exception?) { + mException = exception + } + } + + private class AsyncFailRemoteView(packageName: String?, layoutId: Int) : + RemoteViews(packageName, layoutId) { + var mHandler = mockExecutorHandler { p0 -> p0.run() } + + override fun apply(context: Context, parent: ViewGroup): View { + return super.apply(context, parent) + } + + override fun applyAsync( + context: Context, + parent: ViewGroup, + executor: Executor, + listener: OnViewAppliedListener, + handler: InteractionHandler? + ): CancellationSignal { + mHandler.post { listener.onError(RuntimeException("Failed to inflate async")) } + return CancellationSignal() + } + + override fun applyAsync( + context: Context, + parent: ViewGroup, + executor: Executor, + listener: OnViewAppliedListener + ): CancellationSignal { + return applyAsync(context, parent, executor, listener, null) + } + } + + companion object { + private fun inflateAndWait( + inflater: NotificationRowContentBinderImpl, + @InflationFlag contentToInflate: Int, + row: ExpandableNotificationRow + ) { + inflateAndWait(false /* expectingException */, inflater, contentToInflate, row) + } + + private fun inflateAndWait( + expectingException: Boolean, + inflater: NotificationRowContentBinderImpl, + @InflationFlag contentToInflate: Int, + row: ExpandableNotificationRow, + ) { + val countDownLatch = CountDownLatch(1) + val exceptionHolder = ExceptionHolder() + inflater.setInflateSynchronously(true) + val callback: InflationCallback = + object : InflationCallback { + override fun handleInflationException(entry: NotificationEntry, e: Exception) { + if (!expectingException) { + exceptionHolder.setException(e) + } + countDownLatch.countDown() + } + + override fun onAsyncInflationFinished(entry: NotificationEntry) { + if (expectingException) { + exceptionHolder.setException( + RuntimeException( + "Inflation finished even though there should be an error" + ) + ) + } + countDownLatch.countDown() + } + } + inflater.bindContent( + row.entry, + row, + contentToInflate, + BindParams(), + false /* forceInflate */, + callback /* callback */ + ) + Assert.assertTrue(countDownLatch.await(500, TimeUnit.MILLISECONDS)) + exceptionHolder.mException?.let { throw it } + } + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java index 65941adf280e..21d586b1b5fc 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java @@ -84,6 +84,7 @@ import com.android.systemui.statusbar.notification.people.PeopleNotificationIden import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow.ExpandableNotificationRowLogger; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow.OnExpandClickListener; import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag; +import com.android.systemui.statusbar.notification.row.shared.NotificationRowContentBinderRefactor; import com.android.systemui.statusbar.notification.stack.NotificationChildrenContainerLogger; import com.android.systemui.statusbar.phone.KeyguardBypassController; import com.android.systemui.statusbar.policy.HeadsUpManager; @@ -192,16 +193,27 @@ public class NotificationTestHelper { mBgCoroutineContext, mMainCoroutineContext); - NotificationRowContentBinder contentBinder = new NotificationContentInflater( - mock(NotifRemoteViewCache.class), - mock(NotificationRemoteInputManager.class), - mock(ConversationNotificationProcessor.class), - mock(MediaFeatureFlag.class), - mock(Executor.class), - new MockSmartReplyInflater(), - mock(NotifLayoutInflaterFactory.Provider.class), - mock(HeadsUpStyleProvider.class), - mock(NotificationRowContentBinderLogger.class)); + NotificationRowContentBinder contentBinder = + NotificationRowContentBinderRefactor.isEnabled() + ? new NotificationRowContentBinderImpl( + mock(NotifRemoteViewCache.class), + mock(NotificationRemoteInputManager.class), + mock(ConversationNotificationProcessor.class), + mock(Executor.class), + new MockSmartReplyInflater(), + mock(NotifLayoutInflaterFactory.Provider.class), + mock(HeadsUpStyleProvider.class), + mock(NotificationRowContentBinderLogger.class)) + : new NotificationContentInflater( + mock(NotifRemoteViewCache.class), + mock(NotificationRemoteInputManager.class), + mock(ConversationNotificationProcessor.class), + mock(MediaFeatureFlag.class), + mock(Executor.class), + new MockSmartReplyInflater(), + mock(NotifLayoutInflaterFactory.Provider.class), + mock(HeadsUpStyleProvider.class), + mock(NotificationRowContentBinderLogger.class)); contentBinder.setInflateSynchronously(true); mBindStage = new RowContentBindStage(contentBinder, mock(NotifInflationErrorManager.class), diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java index f3d640758cf3..53e643e00be4 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java @@ -223,7 +223,8 @@ public class KeyguardStatusBarViewControllerTest extends SysuiTestCase { mFakeExecutor, mBackgroundExecutor, mLogger, - mStatusOverlayHoverListenerFactory + mStatusOverlayHoverListenerFactory, + mKosmos.getCommunalSceneInteractor() ); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ui/StatusBarIconControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ui/StatusBarIconControllerImplTest.kt index 617c553d773d..7e523fbd8747 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ui/StatusBarIconControllerImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ui/StatusBarIconControllerImplTest.kt @@ -86,6 +86,7 @@ class StatusBarIconControllerImplTest : SysuiTestCase() { /* iconLevel= */ 0, /* number= */ 0, "contentDescription", + StatusBarIcon.Type.SystemIcon, ) underTest.setIconFromTile(slotName, externalIcon) @@ -114,6 +115,7 @@ class StatusBarIconControllerImplTest : SysuiTestCase() { /* iconLevel= */ 0, /* number= */ 0, "contentDescription", + StatusBarIcon.Type.SystemIcon, ) commandQueueCallbacks.setIcon(slotName, externalIcon) @@ -240,6 +242,7 @@ class StatusBarIconControllerImplTest : SysuiTestCase() { /* iconLevel= */ 0, /* number= */ 0, "externalDescription", + StatusBarIcon.Type.SystemIcon, ) underTest.setIconFromTile(slotName, startingExternalIcon) @@ -278,6 +281,7 @@ class StatusBarIconControllerImplTest : SysuiTestCase() { /* iconLevel= */ 0, /* number= */ 0, "externalDescription", + StatusBarIcon.Type.SystemIcon, ) underTest.setIconFromTile(slotName, startingExternalIcon) @@ -290,6 +294,7 @@ class StatusBarIconControllerImplTest : SysuiTestCase() { /* iconLevel= */ 0, /* number= */ 0, "newExternalDescription", + StatusBarIcon.Type.SystemIcon, ) underTest.setIconFromTile(slotName, newExternalIcon) @@ -325,6 +330,7 @@ class StatusBarIconControllerImplTest : SysuiTestCase() { /* iconLevel= */ 0, /* number= */ 0, "externalDescription", + StatusBarIcon.Type.SystemIcon, ) commandQueueCallbacks.setIcon(slotName, startingExternalIcon) @@ -337,6 +343,7 @@ class StatusBarIconControllerImplTest : SysuiTestCase() { /* iconLevel= */ 0, /* number= */ 0, "newExternalDescription", + StatusBarIcon.Type.SystemIcon, ) commandQueueCallbacks.setIcon(slotName, newExternalIcon) @@ -404,6 +411,7 @@ class StatusBarIconControllerImplTest : SysuiTestCase() { /* iconLevel= */ 0, /* number= */ 0, "contentDescription", + StatusBarIcon.Type.SystemIcon, ) } } 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 d24d87c6f57a..890a2e40c94c 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 @@ -34,6 +34,7 @@ import android.telephony.satellite.SatelliteManager.SATELLITE_MODEM_STATE_UNAVAI import android.telephony.satellite.SatelliteManager.SATELLITE_MODEM_STATE_UNKNOWN import android.telephony.satellite.SatelliteManager.SatelliteException import android.telephony.satellite.SatelliteModemStateCallback +import android.telephony.satellite.SatelliteProvisionStateCallback import android.telephony.satellite.SatelliteSupportedStateCallback import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase @@ -326,6 +327,98 @@ class DeviceBasedSatelliteRepositoryImplTest : SysuiTestCase() { } @Test + fun satelliteProvisioned_notSupported_defaultFalse() = + testScope.runTest { + // GIVEN satellite is not supported + setUpRepo( + uptime = MIN_UPTIME, + satMan = satelliteManager, + satelliteSupported = false, + ) + + assertThat(underTest.isSatelliteProvisioned.value).isFalse() + } + + @Test + fun satelliteProvisioned_supported_defaultFalse() = + testScope.runTest { + // GIVEN satellite is supported + setUpRepo( + uptime = MIN_UPTIME, + satMan = satelliteManager, + satelliteSupported = true, + ) + + // THEN default provisioned state is false + assertThat(underTest.isSatelliteProvisioned.value).isFalse() + } + + @Test + fun satelliteProvisioned_supported_tracksCallback() = + testScope.runTest { + // GIVEN satellite is not supported + setUpRepo( + uptime = MIN_UPTIME, + satMan = satelliteManager, + satelliteSupported = true, + ) + + val provisioned by collectLastValue(underTest.isSatelliteProvisioned) + runCurrent() + + val callback = + withArgCaptor<SatelliteProvisionStateCallback> { + verify(satelliteManager).registerForProvisionStateChanged(any(), capture()) + } + + // WHEN provisioning state changes + callback.onSatelliteProvisionStateChanged(true) + + // THEN the value is reflected in the repo + assertThat(provisioned).isTrue() + } + + @Test + fun satelliteProvisioned_supported_tracksCallback_reRegistersOnCrash() = + testScope.runTest { + // GIVEN satellite is supported + setUpRepo( + uptime = MIN_UPTIME, + satMan = satelliteManager, + satelliteSupported = true, + ) + + val provisioned by collectLastValue(underTest.isSatelliteProvisioned) + + runCurrent() + + val callback = + withArgCaptor<SatelliteProvisionStateCallback> { + verify(satelliteManager).registerForProvisionStateChanged(any(), capture()) + } + val telephonyCallback = + MobileTelephonyHelpers.getTelephonyCallbackForType< + TelephonyCallback.RadioPowerStateListener + >( + telephonyManager + ) + + // GIVEN satellite is currently provisioned + callback.onSatelliteProvisionStateChanged(true) + + assertThat(provisioned).isTrue() + + // WHEN a crash event happens (detected by radio state change) + telephonyCallback.onRadioPowerStateChanged(TelephonyManager.RADIO_POWER_ON) + runCurrent() + telephonyCallback.onRadioPowerStateChanged(TelephonyManager.RADIO_POWER_OFF) + runCurrent() + + // THEN listeners are re-registered + verify(satelliteManager, times(2)).registerForProvisionStateChanged(any(), any()) + } + + @Test fun satelliteNotSupported_listenersAreNotRegistered() = testScope.runTest { // GIVEN satellite is not supported diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/FakeDeviceBasedSatelliteRepository.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/FakeDeviceBasedSatelliteRepository.kt index 5fa2d33c9de0..55460bd5b801 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/FakeDeviceBasedSatelliteRepository.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/FakeDeviceBasedSatelliteRepository.kt @@ -21,6 +21,8 @@ import com.android.systemui.statusbar.pipeline.satellite.shared.model.SatelliteC import kotlinx.coroutines.flow.MutableStateFlow class FakeDeviceBasedSatelliteRepository() : DeviceBasedSatelliteRepository { + override val isSatelliteProvisioned = MutableStateFlow(true) + override val connectionState = MutableStateFlow(Off) override val signalStrength = MutableStateFlow(0) diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/domain/interactor/DeviceBasedSatelliteInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/domain/interactor/DeviceBasedSatelliteInteractorTest.kt index d303976612c1..2e5ebb3e3194 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/domain/interactor/DeviceBasedSatelliteInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/domain/interactor/DeviceBasedSatelliteInteractorTest.kt @@ -31,8 +31,6 @@ import com.android.systemui.statusbar.pipeline.shared.data.repository.FakeConnec import com.android.systemui.statusbar.pipeline.wifi.data.repository.FakeWifiRepository import com.android.systemui.statusbar.pipeline.wifi.domain.interactor.WifiInteractorImpl import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel -import com.android.systemui.statusbar.policy.data.repository.FakeDeviceProvisioningRepository -import com.android.systemui.statusbar.policy.domain.interactor.DeviceProvisioningInteractor import com.android.systemui.util.mockito.mock import com.google.common.truth.Truth.assertThat import kotlin.test.Test @@ -55,9 +53,6 @@ class DeviceBasedSatelliteInteractorTest : SysuiTestCase() { ) private val repo = FakeDeviceBasedSatelliteRepository() - private val deviceProvisionedRepository = FakeDeviceProvisioningRepository() - private val deviceProvisioningInteractor = - DeviceProvisioningInteractor(deviceProvisionedRepository) private val connectivityRepository = FakeConnectivityRepository() private val wifiRepository = FakeWifiRepository() private val wifiInteractor = @@ -69,7 +64,6 @@ class DeviceBasedSatelliteInteractorTest : SysuiTestCase() { DeviceBasedSatelliteInteractor( repo, iconsInteractor, - deviceProvisioningInteractor, wifiInteractor, testScope.backgroundScope, FakeLogBuffer.Factory.create(), @@ -113,7 +107,6 @@ class DeviceBasedSatelliteInteractorTest : SysuiTestCase() { DeviceBasedSatelliteInteractor( repo, iconsInteractor, - deviceProvisioningInteractor, wifiInteractor, testScope.backgroundScope, FakeLogBuffer.Factory.create(), @@ -162,7 +155,6 @@ class DeviceBasedSatelliteInteractorTest : SysuiTestCase() { DeviceBasedSatelliteInteractor( repo, iconsInteractor, - deviceProvisioningInteractor, wifiInteractor, testScope.backgroundScope, FakeLogBuffer.Factory.create(), @@ -219,7 +211,6 @@ class DeviceBasedSatelliteInteractorTest : SysuiTestCase() { DeviceBasedSatelliteInteractor( repo, iconsInteractor, - deviceProvisioningInteractor, wifiInteractor, testScope.backgroundScope, FakeLogBuffer.Factory.create(), @@ -538,7 +529,6 @@ class DeviceBasedSatelliteInteractorTest : SysuiTestCase() { DeviceBasedSatelliteInteractor( repo, iconsInteractor, - deviceProvisioningInteractor, wifiInteractor, testScope.backgroundScope, FakeLogBuffer.Factory.create(), diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModelTest.kt index 43b95688729c..c39e301155fe 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModelTest.kt @@ -32,8 +32,6 @@ import com.android.systemui.statusbar.pipeline.shared.data.repository.FakeConnec import com.android.systemui.statusbar.pipeline.wifi.data.repository.FakeWifiRepository import com.android.systemui.statusbar.pipeline.wifi.domain.interactor.WifiInteractorImpl import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel -import com.android.systemui.statusbar.policy.data.repository.FakeDeviceProvisioningRepository -import com.android.systemui.statusbar.policy.domain.interactor.DeviceProvisioningInteractor import com.android.systemui.util.mockito.mock import com.google.common.truth.Truth.assertThat import kotlin.test.Test @@ -55,9 +53,6 @@ class DeviceBasedSatelliteViewModelTest : SysuiTestCase() { private val mobileIconsInteractor = FakeMobileIconsInteractor(FakeMobileMappingsProxy(), mock()) - private val deviceProvisionedRepository = FakeDeviceProvisioningRepository() - private val deviceProvisioningInteractor = - DeviceProvisioningInteractor(deviceProvisionedRepository) private val connectivityRepository = FakeConnectivityRepository() private val wifiRepository = FakeWifiRepository() private val wifiInteractor = @@ -72,7 +67,6 @@ class DeviceBasedSatelliteViewModelTest : SysuiTestCase() { DeviceBasedSatelliteInteractor( repo, mobileIconsInteractor, - deviceProvisioningInteractor, wifiInteractor, testScope.backgroundScope, FakeLogBuffer.Factory.create(), @@ -252,14 +246,14 @@ class DeviceBasedSatelliteViewModelTest : SysuiTestCase() { // GIVEN apm is disabled airplaneModeRepository.setIsAirplaneMode(false) - // GIVEN device is not provisioned - deviceProvisionedRepository.setDeviceProvisioned(false) + // GIVEN satellite is not provisioned + repo.isSatelliteProvisioned.value = false // THEN icon is null because the device is not provisioned assertThat(latest).isNull() - // GIVEN device becomes provisioned - deviceProvisionedRepository.setDeviceProvisioned(true) + // GIVEN satellite becomes provisioned + repo.isSatelliteProvisioned.value = true // Wait for delay to be completed advanceTimeBy(10.seconds) @@ -285,8 +279,8 @@ class DeviceBasedSatelliteViewModelTest : SysuiTestCase() { // GIVEN apm is disabled airplaneModeRepository.setIsAirplaneMode(false) - // GIVEN device is provisioned - deviceProvisionedRepository.setDeviceProvisioned(true) + // GIVEN satellite is provisioned + repo.isSatelliteProvisioned.value = true // GIVEN wifi network is active wifiRepository.setWifiNetwork(WifiNetworkModel.Active(networkId = 0, level = 1)) @@ -474,14 +468,14 @@ class DeviceBasedSatelliteViewModelTest : SysuiTestCase() { // GIVEN apm is disabled airplaneModeRepository.setIsAirplaneMode(false) - // GIVEN device is not provisioned - deviceProvisionedRepository.setDeviceProvisioned(false) + // GIVEN satellite is not provisioned + repo.isSatelliteProvisioned.value = false // THEN carrier text is null because the device is not provisioned assertThat(latest).isNull() - // GIVEN device becomes provisioned - deviceProvisionedRepository.setDeviceProvisioned(true) + // GIVEN satellite becomes provisioned + repo.isSatelliteProvisioned.value = true // Wait for delay to be completed advanceTimeBy(10.seconds) @@ -508,8 +502,8 @@ class DeviceBasedSatelliteViewModelTest : SysuiTestCase() { // GIVEN apm is disabled airplaneModeRepository.setIsAirplaneMode(false) - // GIVEN device is provisioned - deviceProvisionedRepository.setDeviceProvisioned(true) + // GIVEN satellite is provisioned + repo.isSatelliteProvisioned.value = true // GIVEN wifi network is active wifiRepository.setWifiNetwork(WifiNetworkModel.Active(networkId = 0, level = 1)) diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModelImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModelImplTest.kt index c9fe44918757..cdb2b883078a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModelImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModelImplTest.kt @@ -37,8 +37,10 @@ import com.android.systemui.mediaprojection.data.repository.fakeMediaProjectionR import com.android.systemui.screenrecord.data.model.ScreenRecordModel import com.android.systemui.screenrecord.data.repository.screenRecordRepository import com.android.systemui.statusbar.chips.domain.model.OngoingActivityChipModel -import com.android.systemui.statusbar.chips.ui.viewmodel.OngoingActivityChipsViewModelTest.Companion.assertIsMediaProjectionChip +import com.android.systemui.statusbar.chips.mediaprojection.domain.interactor.MediaProjectionChipInteractorTest.Companion.NORMAL_PACKAGE +import com.android.systemui.statusbar.chips.mediaprojection.domain.interactor.MediaProjectionChipInteractorTest.Companion.setUpPackageManagerForMediaProjection import com.android.systemui.statusbar.chips.ui.viewmodel.OngoingActivityChipsViewModelTest.Companion.assertIsScreenRecordChip +import com.android.systemui.statusbar.chips.ui.viewmodel.OngoingActivityChipsViewModelTest.Companion.assertIsShareToAppChip import com.android.systemui.statusbar.chips.ui.viewmodel.ongoingActivityChipsViewModel import com.android.systemui.statusbar.data.model.StatusBarMode import com.android.systemui.statusbar.data.repository.FakeStatusBarModeRepository.Companion.DISPLAY_ID @@ -55,6 +57,7 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.test.UnconfinedTestDispatcher import kotlinx.coroutines.test.runTest +import org.junit.Before import org.junit.Test @SmallTest @@ -77,6 +80,11 @@ class CollapsedStatusBarViewModelImplTest : SysuiTestCase() { kosmos.applicationCoroutineScope, ) + @Before + fun setUp() { + setUpPackageManagerForMediaProjection(kosmos) + } + @Test fun isTransitioningFromLockscreenToOccluded_started_isTrue() = testScope.runTest { @@ -405,9 +413,9 @@ class CollapsedStatusBarViewModelImplTest : SysuiTestCase() { assertThat(latest).isEqualTo(OngoingActivityChipModel.Hidden) kosmos.fakeMediaProjectionRepository.mediaProjectionState.value = - MediaProjectionState.EntireScreen + MediaProjectionState.Projecting.EntireScreen(NORMAL_PACKAGE) - assertIsMediaProjectionChip(latest) + assertIsShareToAppChip(latest) } private fun activeNotificationsStore(notifications: List<ActiveNotificationModel>) = diff --git a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java index 05d07e5256b8..fac6a4c22178 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java @@ -87,6 +87,8 @@ import com.android.systemui.util.settings.FakeSettings; import com.android.systemui.util.settings.SecureSettings; import com.android.systemui.util.time.FakeSystemClock; import com.android.systemui.volume.domain.interactor.VolumePanelNavigationInteractor; +import com.android.systemui.volume.panel.shared.flag.VolumePanelFlag; +import com.android.systemui.volume.ui.binder.VolumeDialogMenuIconBinder; import com.android.systemui.volume.ui.navigation.VolumeNavigator; import dagger.Lazy; @@ -118,7 +120,6 @@ public class VolumeDialogImplTest extends SysuiTestCase { View mDrawerNormal; ViewGroup mDialogRowsView; CaptionsToggleImageButton mODICaptionsIcon; - private TestableLooper mTestableLooper; private ConfigurationController mConfigurationController; private int mOriginalOrientation; @@ -148,6 +149,10 @@ public class VolumeDialogImplTest extends SysuiTestCase { private VolumePanelNavigationInteractor mVolumePanelNavigationInteractor; @Mock private VolumeNavigator mVolumeNavigator; + @Mock + private VolumeDialogMenuIconBinder mVolumeDialogMenuIconBinder; + @Mock + private VolumePanelFlag mVolumePanelFlag; private final CsdWarningDialog.Factory mCsdWarningDialogFactory = new CsdWarningDialog.Factory() { @@ -208,9 +213,11 @@ public class VolumeDialogImplTest extends SysuiTestCase { mCsdWarningDialogFactory, mPostureController, mTestableLooper.getLooper(), + mVolumePanelFlag, mDumpManager, mLazySecureSettings, mVibratorHelper, + mVolumeDialogMenuIconBinder, new FakeSystemClock()); mDialog.init(0, null); State state = createShellState(); 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 0f89dcc42c82..fabb9b770a11 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java @@ -169,6 +169,7 @@ import com.android.wm.shell.bubbles.BubbleViewInfoTask; import com.android.wm.shell.bubbles.BubbleViewProvider; import com.android.wm.shell.bubbles.Bubbles; import com.android.wm.shell.bubbles.StackEducationView; +import com.android.wm.shell.bubbles.bar.BubbleBarLayerView; import com.android.wm.shell.bubbles.properties.BubbleProperties; import com.android.wm.shell.common.DisplayController; import com.android.wm.shell.common.FloatingContentCoordinator; @@ -2109,6 +2110,33 @@ public class BubblesTest extends SysuiTestCase { } @Test + public void registerBubbleBarListener_switchToBarWhileExpanded() { + mBubbleProperties.mIsBubbleBarEnabled = true; + mPositioner.setIsLargeScreen(true); + + mEntryListener.onEntryAdded(mRow); + mBubbleController.updateBubble(mBubbleEntry); + BubbleStackView stackView = mBubbleController.getStackView(); + spyOn(stackView); + + mBubbleData.setExpanded(true); + + assertStackMode(); + assertThat(mBubbleData.isExpanded()).isTrue(); + assertThat(stackView.isExpanded()).isTrue(); + + FakeBubbleStateListener bubbleStateListener = new FakeBubbleStateListener(); + mBubbleController.registerBubbleStateListener(bubbleStateListener); + + BubbleBarLayerView layerView = mBubbleController.getLayerView(); + spyOn(layerView); + + assertBarMode(); + assertThat(mBubbleData.isExpanded()).isTrue(); + assertThat(layerView.isExpanded()).isTrue(); + } + + @Test public void switchBetweenBarAndStack_noBubbles_shouldBeIgnored() { mBubbleProperties.mIsBubbleBarEnabled = false; mPositioner.setIsLargeScreen(true); diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/animation/DialogTransitionAnimatorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/animation/DialogTransitionAnimatorKosmos.kt index 5a092f38f929..976a19ce6265 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/animation/DialogTransitionAnimatorKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/animation/DialogTransitionAnimatorKosmos.kt @@ -16,16 +16,19 @@ package com.android.systemui.animation +import android.content.applicationContext import com.android.systemui.jank.interactionJankMonitor import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.Kosmos.Fixture -import com.android.systemui.kosmos.testCase +import com.android.systemui.util.mockito.mock val Kosmos.dialogTransitionAnimator by Fixture { fakeDialogTransitionAnimator( // The main thread is checked in a bunch of places inside the different transitions // animators, so we have to pass the real main executor here. - mainExecutor = testCase.context.mainExecutor, + mainExecutor = applicationContext.mainExecutor, interactionJankMonitor = interactionJankMonitor, ) } + +val Kosmos.mockDialogTransitionAnimator by Fixture { mock<DialogTransitionAnimator>() } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalSceneRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalSceneRepository.kt index a7bf87def5be..d280be216f5e 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalSceneRepository.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalSceneRepository.kt @@ -6,6 +6,7 @@ import com.android.compose.animation.scene.TransitionKey import com.android.systemui.communal.shared.model.CommunalScenes import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.delay import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharingStarted @@ -13,20 +14,25 @@ import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.stateIn +import kotlinx.coroutines.launch /** Fake implementation of [CommunalSceneRepository]. */ @OptIn(ExperimentalCoroutinesApi::class) class FakeCommunalSceneRepository( - applicationScope: CoroutineScope, + private val applicationScope: CoroutineScope, override val currentScene: MutableStateFlow<SceneKey> = MutableStateFlow(CommunalScenes.Default), ) : CommunalSceneRepository { + override fun changeScene(toScene: SceneKey, transitionKey: TransitionKey?) = - snapToScene(toScene) + snapToScene(toScene, 0) - override fun snapToScene(toScene: SceneKey) { - this.currentScene.value = toScene - this._transitionState.value = flowOf(ObservableTransitionState.Idle(toScene)) + override fun snapToScene(toScene: SceneKey, delayMillis: Long) { + applicationScope.launch { + delay(delayMillis) + currentScene.value = toScene + _transitionState.value = flowOf(ObservableTransitionState.Idle(toScene)) + } } private val defaultTransitionState = ObservableTransitionState.Idle(CommunalScenes.Default) diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/ui/viewmodel/CommunalTransitionViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/ui/viewmodel/CommunalTransitionViewModelKosmos.kt index e3c218df2c53..3f3840810946 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/ui/viewmodel/CommunalTransitionViewModelKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/ui/viewmodel/CommunalTransitionViewModelKosmos.kt @@ -17,6 +17,7 @@ package com.android.systemui.communal.ui.viewmodel import com.android.systemui.communal.domain.interactor.communalInteractor +import com.android.systemui.communal.domain.interactor.communalSceneInteractor import com.android.systemui.communal.util.communalColors import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor import com.android.systemui.keyguard.ui.viewmodel.dreamingToGlanceableHubTransitionViewModel @@ -37,6 +38,7 @@ val Kosmos.communalTransitionViewModel by dreamToGlanceableHubTransitionViewModel = dreamingToGlanceableHubTransitionViewModel, glanceableHubToDreamTransitionViewModel = glanceableHubToDreamingTransitionViewModel, communalInteractor = communalInteractor, + communalSceneInteractor = communalSceneInteractor, keyguardTransitionInteractor = keyguardTransitionInteractor, communalColors = communalColors, ) diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/haptics/qs/QSLongPressEffectKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/haptics/qs/QSLongPressEffectKosmos.kt index 24603ef200d9..eff99e047f45 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/haptics/qs/QSLongPressEffectKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/haptics/qs/QSLongPressEffectKosmos.kt @@ -17,8 +17,8 @@ package com.android.systemui.haptics.qs import com.android.systemui.haptics.vibratorHelper -import com.android.systemui.keyguard.domain.interactor.keyguardInteractor import com.android.systemui.kosmos.Kosmos +import com.android.systemui.statusbar.policy.keyguardStateController val Kosmos.qsLongPressEffect by - Kosmos.Fixture { QSLongPressEffect(vibratorHelper, keyguardInteractor) } + Kosmos.Fixture { QSLongPressEffect(vibratorHelper, keyguardStateController) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt index 22b8c8dbdfbd..2d100f0dd5a8 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt @@ -80,7 +80,7 @@ class FakeKeyguardRepository @Inject constructor() : KeyguardRepository { override val isAodAvailable: StateFlow<Boolean> = _isAodAvailable private val _isDreaming = MutableStateFlow(false) - override val isDreaming: Flow<Boolean> = _isDreaming + override val isDreaming: MutableStateFlow<Boolean> = _isDreaming private val _isDreamingWithOverlay = MutableStateFlow(false) override val isDreamingWithOverlay: Flow<Boolean> = _isDreamingWithOverlay @@ -204,7 +204,7 @@ class FakeKeyguardRepository @Inject constructor() : KeyguardRepository { _isAodAvailable.value = value } - fun setDreaming(isDreaming: Boolean) { + override fun setDreaming(isDreaming: Boolean) { _isDreaming.value = isDreaming } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractorKosmos.kt index 162fd9029bb0..28bd43982eba 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractorKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractorKosmos.kt @@ -16,7 +16,6 @@ package com.android.systemui.keyguard.domain.interactor -import com.android.systemui.flags.featureFlagsClassic import com.android.systemui.keyguard.data.repository.keyguardTransitionRepository import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.applicationCoroutineScope @@ -34,7 +33,6 @@ var Kosmos.fromLockscreenTransitionInteractor by bgDispatcher = testDispatcher, mainDispatcher = testDispatcher, keyguardInteractor = keyguardInteractor, - flags = featureFlagsClassic, shadeRepository = shadeRepository, powerInteractor = powerInteractor, glanceableHubTransitions = glanceableHubTransitions, diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractorKosmos.kt index 98babffb50d3..d72b9c10d3d8 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractorKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractorKosmos.kt @@ -18,7 +18,6 @@ package com.android.systemui.keyguard.domain.interactor import com.android.keyguard.keyguardSecurityModel import com.android.systemui.communal.domain.interactor.communalInteractor -import com.android.systemui.flags.featureFlagsClassic import com.android.systemui.keyguard.data.repository.keyguardTransitionRepository import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.applicationCoroutineScope @@ -37,7 +36,6 @@ var Kosmos.fromPrimaryBouncerTransitionInteractor by mainDispatcher = testDispatcher, keyguardInteractor = keyguardInteractor, communalInteractor = communalInteractor, - flags = featureFlagsClassic, keyguardSecurityModel = keyguardSecurityModel, selectedUserInteractor = selectedUserInteractor, powerInteractor = powerInteractor, 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 02842cc3f56b..b5ca964d6968 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 @@ -24,6 +24,7 @@ import com.android.systemui.flags.FakeFeatureFlags import com.android.systemui.keyguard.data.repository.FakeCommandQueue import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository import com.android.systemui.keyguard.shared.model.KeyguardState +import com.android.systemui.keyguard.shared.model.TransitionStep import com.android.systemui.power.domain.interactor.PowerInteractor import com.android.systemui.power.domain.interactor.PowerInteractorFactory import com.android.systemui.scene.domain.interactor.SceneInteractor @@ -34,6 +35,7 @@ import com.android.systemui.util.mockito.mock import com.android.systemui.util.mockito.whenever import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.MutableSharedFlow +import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.test.TestScope /** @@ -60,9 +62,11 @@ object KeyguardInteractorFactory { ): WithDependencies { // Mock these until they are replaced by kosmos val currentKeyguardStateFlow = MutableSharedFlow<KeyguardState>() + val transitionStateFlow = MutableStateFlow(TransitionStep()) val keyguardTransitionInteractor = mock<KeyguardTransitionInteractor>().also { whenever(it.currentKeyguardState).thenReturn(currentKeyguardStateFlow) + whenever(it.transitionState).thenReturn(transitionStateFlow) } val configurationDimensionFlow = MutableSharedFlow<ConfigurationBasedDimensions>() configurationDimensionFlow.tryEmit( diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModelKosmos.kt index 6d46694db650..3c62b44ed2c4 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModelKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModelKosmos.kt @@ -22,6 +22,7 @@ import com.android.systemui.keyguard.domain.interactor.keyguardClockInteractor import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.applicationCoroutineScope import com.android.systemui.shade.domain.interactor.shadeInteractor +import com.android.systemui.statusbar.notification.icon.ui.viewmodel.notificationIconContainerAlwaysOnDisplayViewModel import com.android.systemui.statusbar.notification.stack.domain.interactor.notificationsKeyguardInteractor import com.android.systemui.statusbar.ui.systemBarUtilsProxy @@ -30,6 +31,7 @@ val Kosmos.keyguardClockViewModel by KeyguardClockViewModel( keyguardClockInteractor = keyguardClockInteractor, applicationScope = applicationCoroutineScope, + aodNotificationIconViewModel = notificationIconContainerAlwaysOnDisplayViewModel, notifsKeyguardInteractor = notificationsKeyguardInteractor, shadeInteractor = shadeInteractor, systemBarUtils = systemBarUtilsProxy, 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 6d2d04a70538..67f844340096 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 @@ -29,6 +29,7 @@ import com.android.systemui.common.ui.data.repository.fakeConfigurationRepositor import com.android.systemui.common.ui.domain.interactor.configurationInteractor import com.android.systemui.communal.data.repository.fakeCommunalSceneRepository import com.android.systemui.communal.domain.interactor.communalInteractor +import com.android.systemui.communal.domain.interactor.communalSceneInteractor import com.android.systemui.communal.ui.viewmodel.communalTransitionViewModel import com.android.systemui.concurrency.fakeExecutor import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor @@ -62,6 +63,8 @@ import com.android.systemui.statusbar.notification.stack.domain.interactor.heads import com.android.systemui.statusbar.notification.stack.domain.interactor.sharedNotificationContainerInteractor import com.android.systemui.statusbar.phone.scrimController import com.android.systemui.statusbar.pipeline.mobile.data.repository.fakeMobileConnectionsRepository +import com.android.systemui.statusbar.pipeline.wifi.data.repository.fakeWifiRepository +import com.android.systemui.statusbar.pipeline.wifi.domain.interactor.wifiInteractor import com.android.systemui.statusbar.policy.data.repository.fakeDeviceProvisioningRepository import com.android.systemui.statusbar.policy.domain.interactor.deviceProvisioningInteractor import com.android.systemui.util.time.systemClock @@ -111,6 +114,7 @@ class KosmosJavaAdapter() { val deviceEntryUdfpsInteractor by lazy { kosmos.deviceEntryUdfpsInteractor } val deviceUnlockedInteractor by lazy { kosmos.deviceUnlockedInteractor } val communalInteractor by lazy { kosmos.communalInteractor } + val communalSceneInteractor by lazy { kosmos.communalSceneInteractor } val sceneContainerPlugin by lazy { kosmos.sceneContainerPlugin } val deviceProvisioningInteractor by lazy { kosmos.deviceProvisioningInteractor } val fakeDeviceProvisioningRepository by lazy { kosmos.fakeDeviceProvisioningRepository } @@ -130,6 +134,8 @@ class KosmosJavaAdapter() { val shadeController by lazy { kosmos.shadeController } val shadeRepository by lazy { kosmos.shadeRepository } val shadeInteractor by lazy { kosmos.shadeInteractor } + val wifiInteractor by lazy { kosmos.wifiInteractor } + val fakeWifiRepository by lazy { kosmos.fakeWifiRepository } val ongoingActivityChipsViewModel by lazy { kosmos.ongoingActivityChipsViewModel } val scrimController by lazy { kosmos.scrimController } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaCarouselInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaCarouselInteractorKosmos.kt index e5e2affdc49a..ca1b3f571a90 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaCarouselInteractorKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaCarouselInteractorKosmos.kt @@ -18,7 +18,6 @@ package com.android.systemui.media.controls.domain.pipeline.interactor import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.applicationCoroutineScope -import com.android.systemui.media.controls.data.repository.mediaDataRepository import com.android.systemui.media.controls.data.repository.mediaFilterRepository import com.android.systemui.media.controls.domain.pipeline.mediaDataCombineLatest import com.android.systemui.media.controls.domain.pipeline.mediaDataFilter @@ -33,7 +32,6 @@ val Kosmos.mediaCarouselInteractor by Kosmos.Fixture { MediaCarouselInteractor( applicationScope = applicationCoroutineScope, - mediaDataRepository = mediaDataRepository, mediaDataProcessor = mediaDataProcessor, mediaTimeoutListener = mediaTimeoutListener, mediaResumeListener = mediaResumeListener, diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/mediaprojection/data/repository/FakeMediaProjectionRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/mediaprojection/data/repository/FakeMediaProjectionRepository.kt index c4365c9093d2..d631f926176d 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/mediaprojection/data/repository/FakeMediaProjectionRepository.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/mediaprojection/data/repository/FakeMediaProjectionRepository.kt @@ -25,4 +25,10 @@ class FakeMediaProjectionRepository : MediaProjectionRepository { override val mediaProjectionState: MutableStateFlow<MediaProjectionState> = MutableStateFlow(MediaProjectionState.NotProjecting) + + var stopProjectingInvoked = false + + override suspend fun stopProjecting() { + stopProjectingInvoked = true + } } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/interactor/SceneContainerStartableKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/interactor/SceneContainerStartableKosmos.kt index d82286fd3569..cf18c0e295ea 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/interactor/SceneContainerStartableKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/interactor/SceneContainerStartableKosmos.kt @@ -26,6 +26,7 @@ import com.android.systemui.deviceentry.domain.interactor.deviceEntryFaceAuthInt import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor import com.android.systemui.deviceentry.domain.interactor.deviceUnlockedInteractor 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 @@ -67,5 +68,6 @@ val Kosmos.sceneContainerStartable by Fixture { uiEventLogger = uiEventLogger, sceneBackInteractor = sceneBackInteractor, shadeSessionStorage = shadeSessionStorage, + windowMgrLockscreenVisInteractor = windowManagerLockscreenVisibilityInteractor, ) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/interactor/SceneInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/interactor/SceneInteractorKosmos.kt index ef7aa6308491..066736c1e036 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/interactor/SceneInteractorKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/interactor/SceneInteractorKosmos.kt @@ -20,6 +20,7 @@ import com.android.systemui.deviceentry.domain.interactor.deviceUnlockedInteract import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.applicationCoroutineScope import com.android.systemui.scene.data.repository.sceneContainerRepository +import com.android.systemui.scene.domain.resolver.sceneFamilyResolvers import com.android.systemui.scene.shared.logger.sceneLogger val Kosmos.sceneInteractor by @@ -28,6 +29,7 @@ val Kosmos.sceneInteractor by applicationScope = applicationCoroutineScope, repository = sceneContainerRepository, logger = sceneLogger, + sceneFamilyResolvers = { sceneFamilyResolvers }, deviceUnlockedInteractor = deviceUnlockedInteractor, ) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/resolver/SceneResolverKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/resolver/SceneResolverKosmos.kt new file mode 100644 index 000000000000..6be19393ca2b --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/resolver/SceneResolverKosmos.kt @@ -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. + */ + +@file:OptIn(ExperimentalCoroutinesApi::class) + +package com.android.systemui.scene.domain.resolver + +import com.android.compose.animation.scene.SceneKey +import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.kosmos.applicationCoroutineScope +import com.android.systemui.scene.shared.model.SceneFamilies +import com.android.systemui.shade.domain.interactor.shadeInteractor +import kotlinx.coroutines.ExperimentalCoroutinesApi + +val Kosmos.sceneFamilyResolvers: Map<SceneKey, SceneResolver> + get() = + mapOf( + SceneFamilies.Home to homeSceneFamilyResolver, + SceneFamilies.NotifShade to notifShadeSceneFamilyResolver, + SceneFamilies.QuickSettings to quickSettingsSceneFamilyResolver, + ) + +val Kosmos.homeSceneFamilyResolver by + Kosmos.Fixture { + HomeSceneFamilyResolver( + applicationScope = applicationCoroutineScope, + deviceEntryInteractor = deviceEntryInteractor, + ) + } + +val Kosmos.notifShadeSceneFamilyResolver by + Kosmos.Fixture { + NotifShadeSceneFamilyResolver( + applicationScope = applicationCoroutineScope, + shadeInteractor = shadeInteractor, + ) + } + +val Kosmos.quickSettingsSceneFamilyResolver by + Kosmos.Fixture { + QuickSettingsSceneFamilyResolver( + applicationScope = applicationCoroutineScope, + shadeInteractor = shadeInteractor, + ) + } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/screenrecord/data/repository/FakeScreenRecordRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/screenrecord/data/repository/FakeScreenRecordRepository.kt index fb0e3687bb1a..30b4763118a7 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/screenrecord/data/repository/FakeScreenRecordRepository.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/screenrecord/data/repository/FakeScreenRecordRepository.kt @@ -22,4 +22,10 @@ import kotlinx.coroutines.flow.MutableStateFlow class FakeScreenRecordRepository : ScreenRecordRepository { override val screenRecordState: MutableStateFlow<ScreenRecordModel> = MutableStateFlow(ScreenRecordModel.DoingNothing) + + var stopRecordingInvoked = false + + override suspend fun stopRecording() { + stopRecordingInvoked = true + } } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ShadeControllerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ShadeControllerKosmos.kt index d08855f190ed..0bc4d54b41c8 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ShadeControllerKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ShadeControllerKosmos.kt @@ -20,12 +20,9 @@ package com.android.systemui.shade import com.android.systemui.assist.AssistManager import com.android.systemui.concurrency.fakeExecutor -import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor -import com.android.systemui.deviceentry.domain.interactor.deviceUnlockedInteractor import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.applicationCoroutineScope import com.android.systemui.kosmos.testDispatcher -import com.android.systemui.log.LogBuffer import com.android.systemui.plugins.statusbar.statusBarStateController import com.android.systemui.scene.domain.interactor.sceneInteractor import com.android.systemui.scene.shared.flag.SceneContainerFlag @@ -52,15 +49,13 @@ val Kosmos.shadeControllerSceneImpl by shadeInteractor = shadeInteractor, sceneInteractor = sceneInteractor, notificationStackScrollLayout = mock<NotificationStackScrollLayout>(), - deviceEntryInteractor = deviceEntryInteractor, - touchLog = mock<LogBuffer>(), vibratorHelper = mock<VibratorHelper>(), commandQueue = mock<CommandQueue>(), statusBarKeyguardViewManager = mock<StatusBarKeyguardViewManager>(), notificationShadeWindowController = mock<NotificationShadeWindowController>(), - assistManagerLazy = { mock<AssistManager>() }, - deviceUnlockedInteractor = deviceUnlockedInteractor, - ) + ) { + mock<AssistManager>() + } } val Kosmos.shadeControllerImpl by diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/interactor/ShadeLockscreenInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/interactor/ShadeLockscreenInteractorKosmos.kt index 0a3a2ee91773..bcea9839ad82 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/interactor/ShadeLockscreenInteractorKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/interactor/ShadeLockscreenInteractorKosmos.kt @@ -25,7 +25,6 @@ import com.android.systemui.util.mockito.mock val Kosmos.shadeLockscreenInteractor by Kosmos.Fixture { ShadeLockscreenInteractorImpl( - applicationScope = applicationCoroutineScope, backgroundScope = applicationCoroutineScope, shadeInteractor = shadeInteractorImpl, sceneInteractor = sceneInteractor, diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/NotificationsShadeSceneViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/NotificationsShadeSceneViewModelKosmos.kt index 872eba06961e..1ca3509cbd79 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/NotificationsShadeSceneViewModelKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/NotificationsShadeSceneViewModelKosmos.kt @@ -17,13 +17,7 @@ package com.android.systemui.shade.ui.viewmodel import com.android.systemui.kosmos.Kosmos -import com.android.systemui.kosmos.applicationCoroutineScope import com.android.systemui.notifications.ui.viewmodel.NotificationsShadeSceneViewModel val Kosmos.notificationsShadeSceneViewModel: NotificationsShadeSceneViewModel by - Kosmos.Fixture { - NotificationsShadeSceneViewModel( - applicationScope = applicationCoroutineScope, - overlayShadeViewModel = overlayShadeViewModel, - ) - } + Kosmos.Fixture { NotificationsShadeSceneViewModel() } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/OverlayShadeViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/OverlayShadeViewModelKosmos.kt index 45ec03241495..fec102857619 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/OverlayShadeViewModelKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/OverlayShadeViewModelKosmos.kt @@ -14,21 +14,16 @@ * limitations under the License. */ -@file:OptIn(ExperimentalCoroutinesApi::class) - package com.android.systemui.shade.ui.viewmodel -import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.applicationCoroutineScope import com.android.systemui.scene.domain.interactor.sceneInteractor -import kotlinx.coroutines.ExperimentalCoroutinesApi val Kosmos.overlayShadeViewModel: OverlayShadeViewModel by Kosmos.Fixture { OverlayShadeViewModel( applicationScope = applicationCoroutineScope, sceneInteractor = sceneInteractor, - deviceEntryInteractor = deviceEntryInteractor, ) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/QuickSettingsShadeSceneViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/QuickSettingsShadeSceneViewModelKosmos.kt index c5625e47040a..4d81ea16220c 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/QuickSettingsShadeSceneViewModelKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/QuickSettingsShadeSceneViewModelKosmos.kt @@ -18,7 +18,6 @@ package com.android.systemui.shade.ui.viewmodel import com.android.systemui.brightness.ui.viewmodel.brightnessSliderViewModel import com.android.systemui.kosmos.Kosmos -import com.android.systemui.kosmos.applicationCoroutineScope import com.android.systemui.qs.panels.ui.viewmodel.editModeViewModel import com.android.systemui.qs.panels.ui.viewmodel.tileGridViewModel import com.android.systemui.qs.ui.adapter.qsSceneAdapter @@ -27,7 +26,6 @@ import com.android.systemui.qs.ui.viewmodel.QuickSettingsShadeSceneViewModel val Kosmos.quickSettingsShadeSceneViewModel: QuickSettingsShadeSceneViewModel by Kosmos.Fixture { QuickSettingsShadeSceneViewModel( - applicationScope = applicationCoroutineScope, overlayShadeViewModel = overlayShadeViewModel, brightnessSliderViewModel = brightnessSliderViewModel, tileGridViewModel = tileGridViewModel, diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModelKosmos.kt index 8d653f7212aa..0e21698ef271 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModelKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModelKosmos.kt @@ -21,6 +21,7 @@ import com.android.systemui.broadcast.broadcastDispatcher import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.applicationCoroutineScope import com.android.systemui.plugins.activityStarter +import com.android.systemui.scene.domain.interactor.sceneInteractor import com.android.systemui.shade.domain.interactor.privacyChipInteractor import com.android.systemui.shade.domain.interactor.shadeHeaderClockInteractor import com.android.systemui.shade.domain.interactor.shadeInteractor @@ -33,6 +34,7 @@ val Kosmos.shadeHeaderViewModel: ShadeHeaderViewModel by applicationScope = applicationCoroutineScope, context = applicationContext, activityStarter = activityStarter, + sceneInteractor = sceneInteractor, shadeInteractor = shadeInteractor, mobileIconsInteractor = mobileIconsInteractor, mobileIconsViewModel = mobileIconsViewModel, diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/mediaprojection/domain/interactor/MediaProjectionChipInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/mediaprojection/domain/interactor/MediaProjectionChipInteractorKosmos.kt new file mode 100644 index 000000000000..062b4484044c --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/mediaprojection/domain/interactor/MediaProjectionChipInteractorKosmos.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.systemui.statusbar.chips.mediaprojection.domain.interactor + +import android.content.packageManager +import com.android.systemui.animation.mockDialogTransitionAnimator +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.kosmos.applicationCoroutineScope +import com.android.systemui.mediaprojection.data.repository.fakeMediaProjectionRepository +import com.android.systemui.statusbar.phone.mockSystemUIDialogFactory +import com.android.systemui.util.time.fakeSystemClock + +val Kosmos.mediaProjectionChipInteractor: MediaProjectionChipInteractor by + Kosmos.Fixture { + MediaProjectionChipInteractor( + scope = applicationCoroutineScope, + mediaProjectionRepository = fakeMediaProjectionRepository, + packageManager = packageManager, + systemClock = fakeSystemClock, + dialogFactory = mockSystemUIDialogFactory, + dialogTransitionAnimator = mockDialogTransitionAnimator, + ) + } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelKosmos.kt index 88bde2ed5d8f..51ec54000261 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelKosmos.kt @@ -16,13 +16,14 @@ package com.android.systemui.statusbar.chips.ui.viewmodel +import com.android.systemui.animation.mockDialogTransitionAnimator import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.applicationCoroutineScope import com.android.systemui.kosmos.testScope -import com.android.systemui.mediaprojection.data.repository.fakeMediaProjectionRepository import com.android.systemui.screenrecord.data.repository.screenRecordRepository -import com.android.systemui.statusbar.chips.mediaprojection.domain.interactor.MediaProjectionChipInteractor +import com.android.systemui.statusbar.chips.mediaprojection.domain.interactor.mediaProjectionChipInteractor import com.android.systemui.statusbar.chips.screenrecord.domain.interactor.ScreenRecordChipInteractor +import com.android.systemui.statusbar.phone.mockSystemUIDialogFactory import com.android.systemui.util.time.fakeSystemClock val Kosmos.screenRecordChipInteractor: ScreenRecordChipInteractor by @@ -30,15 +31,8 @@ val Kosmos.screenRecordChipInteractor: ScreenRecordChipInteractor by ScreenRecordChipInteractor( scope = applicationCoroutineScope, screenRecordRepository = screenRecordRepository, - systemClock = fakeSystemClock, - ) - } - -val Kosmos.mediaProjectionChipInteractor: MediaProjectionChipInteractor by - Kosmos.Fixture { - MediaProjectionChipInteractor( - scope = applicationCoroutineScope, - mediaProjectionRepository = fakeMediaProjectionRepository, + dialogFactory = mockSystemUIDialogFactory, + dialogTransitionAnimator = mockDialogTransitionAnimator, systemClock = fakeSystemClock, ) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelKosmos.kt index d00eedf22efc..299486fc8ef4 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelKosmos.kt @@ -31,6 +31,7 @@ import com.android.systemui.keyguard.ui.viewmodel.glanceableHubToLockscreenTrans import com.android.systemui.keyguard.ui.viewmodel.goneToAodTransitionViewModel import com.android.systemui.keyguard.ui.viewmodel.goneToDozingTransitionViewModel import com.android.systemui.keyguard.ui.viewmodel.goneToDreamingTransitionViewModel +import com.android.systemui.keyguard.ui.viewmodel.goneToLockscreenTransitionViewModel import com.android.systemui.keyguard.ui.viewmodel.lockscreenToDreamingTransitionViewModel import com.android.systemui.keyguard.ui.viewmodel.lockscreenToGlanceableHubTransitionViewModel import com.android.systemui.keyguard.ui.viewmodel.lockscreenToGoneTransitionViewModel @@ -70,6 +71,7 @@ val Kosmos.sharedNotificationContainerViewModel by Fixture { goneToAodTransitionViewModel = goneToAodTransitionViewModel, goneToDozingTransitionViewModel = goneToDozingTransitionViewModel, goneToDreamingTransitionViewModel = goneToDreamingTransitionViewModel, + goneToLockscreenTransitionViewModel = goneToLockscreenTransitionViewModel, glanceableHubToLockscreenTransitionViewModel = glanceableHubToLockscreenTransitionViewModel, lockscreenToDreamingTransitionViewModel = lockscreenToDreamingTransitionViewModel, lockscreenToGlanceableHubTransitionViewModel = lockscreenToGlanceableHubTransitionViewModel, diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/SystemUIDialogFactoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/SystemUIDialogFactoryKosmos.kt index 3bb9580e69fa..1851c89ecd94 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/SystemUIDialogFactoryKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/SystemUIDialogFactoryKosmos.kt @@ -21,8 +21,9 @@ import com.android.systemui.animation.dialogTransitionAnimator import com.android.systemui.broadcast.broadcastDispatcher import com.android.systemui.kosmos.Kosmos import com.android.systemui.model.sysUiState +import com.android.systemui.util.mockito.mock -val Kosmos.systemUIDialogFactory by +val Kosmos.systemUIDialogFactory: SystemUIDialogFactory by Kosmos.Fixture { SystemUIDialogFactory( applicationContext, @@ -32,3 +33,6 @@ val Kosmos.systemUIDialogFactory by dialogTransitionAnimator, ) } + +val Kosmos.mockSystemUIDialogFactory: SystemUIDialog.Factory by + Kosmos.Fixture { mock<SystemUIDialog.Factory>() } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/shared/data/repository/ConnectivityRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/shared/data/repository/ConnectivityRepositoryKosmos.kt new file mode 100644 index 000000000000..8e656cf002ce --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/shared/data/repository/ConnectivityRepositoryKosmos.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.pipeline.shared.data.repository + +import com.android.systemui.kosmos.Kosmos + +val Kosmos.fakeConnectivityRepository: FakeConnectivityRepository by + Kosmos.Fixture { FakeConnectivityRepository() } +val Kosmos.connectivityRepository: ConnectivityRepository by + Kosmos.Fixture { fakeConnectivityRepository } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/FakeWifiRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/FakeWifiRepository.kt index 97c8d5fd9e4a..709be5edf4c0 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/FakeWifiRepository.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/FakeWifiRepository.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2022 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. diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositoryKosmos.kt new file mode 100644 index 000000000000..e44061a718d5 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositoryKosmos.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.statusbar.pipeline.wifi.data.repository + +import com.android.systemui.kosmos.Kosmos + +val Kosmos.fakeWifiRepository: FakeWifiRepository by Kosmos.Fixture { FakeWifiRepository() } +val Kosmos.wifiRepository: WifiRepository by Kosmos.Fixture { fakeWifiRepository } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractorKosmos.kt new file mode 100644 index 000000000000..703619958a26 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractorKosmos.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.pipeline.wifi.domain.interactor + +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.kosmos.applicationCoroutineScope +import com.android.systemui.statusbar.pipeline.shared.data.repository.connectivityRepository +import com.android.systemui.statusbar.pipeline.wifi.data.repository.wifiRepository + +val Kosmos.wifiInteractor: WifiInteractor by + Kosmos.Fixture { + WifiInteractorImpl( + connectivityRepository, + wifiRepository, + applicationCoroutineScope, + ) + } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/popup/ui/composable/VolumePanelPopupKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/popup/ui/composable/VolumePanelPopupKosmos.kt new file mode 100644 index 000000000000..49170d8b0a62 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/popup/ui/composable/VolumePanelPopupKosmos.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.panel.component.popup.ui.composable + +import com.android.systemui.animation.dialogTransitionAnimator +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.statusbar.phone.systemUIDialogFactory + +val Kosmos.volumePanelPopup: VolumePanelPopup by + Kosmos.Fixture { VolumePanelPopup(systemUIDialogFactory, dialogTransitionAnimator) } diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/dagger/NaturalRotation.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/dagger/NaturalRotation.kt new file mode 100644 index 000000000000..be02487c75e6 --- /dev/null +++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/dagger/NaturalRotation.kt @@ -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 com.android.systemui.unfold.dagger + +import javax.inject.Qualifier + +/** Qualifier annotation for a progress provider that emits animation events only when + * in natural rotation */ +@Qualifier @Retention(AnnotationRetention.RUNTIME) annotation class NaturalRotation diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/RotationChangeProvider.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/RotationChangeProvider.kt index 4f3aee99c206..fec6ff17a608 100644 --- a/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/RotationChangeProvider.kt +++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/RotationChangeProvider.kt @@ -27,6 +27,7 @@ import com.android.systemui.unfold.util.CallbackController import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject +import java.util.concurrent.CopyOnWriteArrayList /** * Allows to subscribe to rotation changes. Updates are provided for the display associated to @@ -41,7 +42,7 @@ constructor( @Assisted private val callbackHandler: Handler, ) : CallbackController<RotationChangeProvider.RotationListener> { - private val listeners = mutableListOf<RotationListener>() + private val listeners = CopyOnWriteArrayList<RotationListener>() private val displayListener = RotationDisplayListener() private var lastRotation: Int? = null diff --git a/packages/WAPPushManager/src/com/android/smspush/WapPushManager.java b/packages/WAPPushManager/src/com/android/smspush/WapPushManager.java index 951e64fab015..951e64fab015 100755..100644 --- a/packages/WAPPushManager/src/com/android/smspush/WapPushManager.java +++ b/packages/WAPPushManager/src/com/android/smspush/WapPushManager.java diff --git a/packages/WallpaperCropper/res/drawable-hdpi/ic_actionbar_accept.png b/packages/WallpaperCropper/res/drawable-hdpi/ic_actionbar_accept.png Binary files differindex 53cf6877eb72..53cf6877eb72 100755..100644 --- a/packages/WallpaperCropper/res/drawable-hdpi/ic_actionbar_accept.png +++ b/packages/WallpaperCropper/res/drawable-hdpi/ic_actionbar_accept.png diff --git a/packages/WallpaperCropper/res/drawable-mdpi/ic_actionbar_accept.png b/packages/WallpaperCropper/res/drawable-mdpi/ic_actionbar_accept.png Binary files differindex 35cda8e11213..35cda8e11213 100755..100644 --- a/packages/WallpaperCropper/res/drawable-mdpi/ic_actionbar_accept.png +++ b/packages/WallpaperCropper/res/drawable-mdpi/ic_actionbar_accept.png diff --git a/packages/WallpaperCropper/res/drawable-xhdpi/ic_actionbar_accept.png b/packages/WallpaperCropper/res/drawable-xhdpi/ic_actionbar_accept.png Binary files differindex b52dc37012a3..b52dc37012a3 100755..100644 --- a/packages/WallpaperCropper/res/drawable-xhdpi/ic_actionbar_accept.png +++ b/packages/WallpaperCropper/res/drawable-xhdpi/ic_actionbar_accept.png diff --git a/proto/src/ondeviceintelligence/OWNERS b/proto/src/ondeviceintelligence/OWNERS new file mode 100644 index 000000000000..09774f78d712 --- /dev/null +++ b/proto/src/ondeviceintelligence/OWNERS @@ -0,0 +1 @@ +file:/core/java/android/app/ondeviceintelligence/OWNERS diff --git a/ravenwood/OWNERS b/ravenwood/OWNERS index a90328c2e8c6..badfca0a040e 100644 --- a/ravenwood/OWNERS +++ b/ravenwood/OWNERS @@ -2,6 +2,7 @@ set noparent jsharkey@google.com omakoto@google.com +dplotnikov@google.com per-file ravenwood-annotation-allowed-classes.txt = dplotnikov@google.com per-file texts/ravenwood-annotation-allowed-classes.txt = dplotnikov@google.com diff --git a/ravenwood/runtime-helper-src/framework/com/android/platform/test/ravenwood/nativesubstitution/Parcel_host.java b/ravenwood/runtime-helper-src/framework/com/android/platform/test/ravenwood/nativesubstitution/Parcel_host.java index 61ec7b4bbc72..22e11e18ae31 100644 --- a/ravenwood/runtime-helper-src/framework/com/android/platform/test/ravenwood/nativesubstitution/Parcel_host.java +++ b/ravenwood/runtime-helper-src/framework/com/android/platform/test/ravenwood/nativesubstitution/Parcel_host.java @@ -164,6 +164,9 @@ public class Parcel_host { p.mPos = pos; } public static void nativeSetDataCapacity(long nativePtr, int size) { + if (size < 0) { + throw new IllegalArgumentException("size < 0: size=" + size); + } var p = getInstance(nativePtr); if (p.getCapacity() < size) { p.forceSetCapacity(size); diff --git a/ravenwood/test-authors.md b/ravenwood/test-authors.md index 2ab43bbeaad0..0a0b200adabc 100644 --- a/ravenwood/test-authors.md +++ b/ravenwood/test-authors.md @@ -6,6 +6,20 @@ Ravenwood explicitly does not support “large” integration tests that expect When writing tests under Ravenwood, all Android API symbols associated with your declared `sdk_version` are available to link against using, but unsupported APIs will throw an exception. This design choice enables mocking of unsupported APIs, and supports sharing of test code to build “bivalent” test suites that run against either Ravenwood or a traditional device. +## Manually running tests + +To run all Ravenwood tests, use: + +``` +./frameworks/base/ravenwood/scripts/run-ravenwood-tests.sh +``` + +To run a specific test, use "atest" as normal, selecting the test from a Ravenwood suite such as: + +``` +atest CtsOsTestCasesRavenwood:ParcelTest\#testSetDataCapacityNegative +``` + ## Typical test structure Below are the typical steps needed to add a straightforward “small” unit test: diff --git a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java index 7342b0003ed5..edb6390ea874 100644 --- a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java +++ b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java @@ -1379,30 +1379,6 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ } } - @RequiresNoPermission - @Override - public boolean isMagnificationSystemUIConnected() { - if (svcConnTracingEnabled()) { - logTraceSvcConn("isMagnificationSystemUIConnected", ""); - } - synchronized (mLock) { - if (!hasRightsToCurrentUserLocked()) { - return false; - } - if (!mSecurityPolicy.canControlMagnification(this)) { - return false; - } - final long identity = Binder.clearCallingIdentity(); - try { - MagnificationProcessor magnificationProcessor = - mSystemSupport.getMagnificationProcessor(); - return magnificationProcessor.isMagnificationSystemUIConnected(); - } finally { - Binder.restoreCallingIdentity(identity); - } - } - } - public boolean isMagnificationCallbackEnabled(int displayId) { return mInvocationHandler.isMagnificationCallbackEnabled(displayId); } @@ -1947,11 +1923,6 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ InvocationHandler.MSG_CLEAR_ACCESSIBILITY_CACHE); } - public void notifyMagnificationSystemUIConnectionChangedLocked(boolean connected) { - mInvocationHandler - .notifyMagnificationSystemUIConnectionChangedLocked(connected); - } - public void notifyMagnificationChangedLocked(int displayId, @NonNull Region region, @NonNull MagnificationConfig config) { mInvocationHandler @@ -2003,21 +1974,6 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ return (mGenericMotionEventSources & eventSourceWithoutClass) != 0; } - /** - * Called by the invocation handler to notify the service that the - * magnification systemui connection has changed. - */ - private void notifyMagnificationSystemUIConnectionChangedInternal(boolean connected) { - final IAccessibilityServiceClient listener = getServiceInterfaceSafely(); - if (listener != null) { - try { - listener.onMagnificationSystemUIConnectionChanged(connected); - } catch (RemoteException re) { - Slog.e(LOG_TAG, - "Error sending magnification sysui connection changes to " + mService, re); - } - } - } /** * Called by the invocation handler to notify the service that the @@ -2414,7 +2370,6 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ private static final int MSG_BIND_INPUT = 12; private static final int MSG_UNBIND_INPUT = 13; private static final int MSG_START_INPUT = 14; - private static final int MSG_ON_MAGNIFICATION_SYSTEM_UI_CONNECTION_CHANGED = 15; /** List of magnification callback states, mapping from displayId -> Boolean */ @GuardedBy("mlock") @@ -2441,13 +2396,6 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ notifyClearAccessibilityCacheInternal(); } break; - case MSG_ON_MAGNIFICATION_SYSTEM_UI_CONNECTION_CHANGED: { - final SomeArgs args = (SomeArgs) message.obj; - final boolean connected = args.argi1 == 1; - notifyMagnificationSystemUIConnectionChangedInternal(connected); - args.recycle(); - } break; - case MSG_ON_MAGNIFICATION_CHANGED: { final SomeArgs args = (SomeArgs) message.obj; final Region region = (Region) args.arg1; @@ -2505,15 +2453,6 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ } } - public void notifyMagnificationSystemUIConnectionChangedLocked(boolean connected) { - final SomeArgs args = SomeArgs.obtain(); - args.argi1 = connected ? 1 : 0; - - final Message msg = - obtainMessage(MSG_ON_MAGNIFICATION_SYSTEM_UI_CONNECTION_CHANGED, args); - msg.sendToTarget(); - } - public void notifyMagnificationChangedLocked(int displayId, @NonNull Region region, @NonNull MagnificationConfig config) { synchronized (mLock) { diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java index d09cb3ee103a..4f9db8bece63 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java @@ -1812,17 +1812,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub } /** - * Called by the MagnificationController when the magnification systemui connection changes. - * - * @param connected Whether the connection is ready. - */ - public void notifyMagnificationSystemUIConnectionChanged(boolean connected) { - synchronized (mLock) { - notifyMagnificationSystemUIConnectionChangedLocked(connected); - } - } - - /** * Called by the MagnificationController when the state of display * magnification changes. * @@ -2254,14 +2243,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub mProxyManager.clearCacheLocked(); } - private void notifyMagnificationSystemUIConnectionChangedLocked(boolean connected) { - final AccessibilityUserState state = getCurrentUserStateLocked(); - for (int i = state.mBoundServices.size() - 1; i >= 0; i--) { - final AccessibilityServiceConnection service = state.mBoundServices.get(i); - service.notifyMagnificationSystemUIConnectionChangedLocked(connected); - } - } - private void notifyMagnificationChangedLocked(int displayId, @NonNull Region region, @NonNull MagnificationConfig config) { final AccessibilityUserState state = getCurrentUserStateLocked(); diff --git a/services/accessibility/java/com/android/server/accessibility/ProxyAccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/ProxyAccessibilityServiceConnection.java index 420bac759ea6..4cb3d247edb0 100644 --- a/services/accessibility/java/com/android/server/accessibility/ProxyAccessibilityServiceConnection.java +++ b/services/accessibility/java/com/android/server/accessibility/ProxyAccessibilityServiceConnection.java @@ -489,14 +489,6 @@ public class ProxyAccessibilityServiceConnection extends AccessibilityServiceCon /** @throws UnsupportedOperationException since a proxy does not need magnification */ @RequiresNoPermission @Override - public boolean isMagnificationSystemUIConnected() throws UnsupportedOperationException { - throw new UnsupportedOperationException("isMagnificationSystemUIConnected is not" - + " supported"); - } - - /** @throws UnsupportedOperationException since a proxy does not need magnification */ - @RequiresNoPermission - @Override public boolean isMagnificationCallbackEnabled(int displayId) { throw new UnsupportedOperationException("isMagnificationCallbackEnabled is not supported"); } diff --git a/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java b/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java index 63a183d506f8..f85d786f89c5 100644 --- a/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java +++ b/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java @@ -101,8 +101,10 @@ class UiAutomationManager { SystemActionPerformer systemActionPerformer, AccessibilityWindowManager awm, int flags) { accessibilityServiceInfo.setComponentName(COMPONENT_NAME); - Slogf.i(LOG_TAG, "Registering UiTestAutomationService (id=%s) when called by user %d", - accessibilityServiceInfo.getId(), Binder.getCallingUserHandle().getIdentifier()); + Slogf.i(LOG_TAG, "Registering UiTestAutomationService (id=%s, flags=0x%x) when" + + " called by user %d", + accessibilityServiceInfo.getId(), flags, + Binder.getCallingUserHandle().getIdentifier()); if (mUiAutomationService != null) { throw new IllegalStateException( "UiAutomationService " + mUiAutomationService.mServiceInterface @@ -272,8 +274,10 @@ class UiAutomationManager { mMainHandler.post(() -> { try { final IAccessibilityServiceClient serviceInterface; + final UiAutomationService uiAutomationService; synchronized (mLock) { serviceInterface = mServiceInterface; + uiAutomationService = mUiAutomationService; if (serviceInterface == null) { mService = null; } else { @@ -283,8 +287,8 @@ class UiAutomationManager { } // If the serviceInterface is null, the UiAutomation has been shut down on // another thread. - if (serviceInterface != null) { - mUiAutomationService.addWindowTokensForAllDisplays(); + if (serviceInterface != null && uiAutomationService != null) { + uiAutomationService.addWindowTokensForAllDisplays(); if (mTrace.isA11yTracingEnabledForTypes( AccessibilityTrace.FLAGS_ACCESSIBILITY_SERVICE_CLIENT)) { mTrace.logTrace("UiAutomationService.connectServiceUnknownThread", diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationConnectionManager.java b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationConnectionManager.java index 7f4c808b2251..19e3e690924e 100644 --- a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationConnectionManager.java +++ b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationConnectionManager.java @@ -18,6 +18,7 @@ package com.android.server.accessibility.magnification; import static android.accessibilityservice.AccessibilityTrace.FLAGS_MAGNIFICATION_CONNECTION; import static android.accessibilityservice.AccessibilityTrace.FLAGS_MAGNIFICATION_CONNECTION_CALLBACK; +import static android.os.Build.HW_TIMEOUT_MULTIPLIER; import static android.view.accessibility.MagnificationAnimationCallback.STUB_ANIMATION_CALLBACK; import static com.android.server.accessibility.AccessibilityManagerService.INVALID_SERVICE_ID; @@ -126,9 +127,8 @@ public class MagnificationConnectionManager implements @ConnectionState private int mConnectionState = DISCONNECTED; - ConnectionStateChangedCallback mConnectionStateChangedCallback = null; - private static final int WAIT_CONNECTION_TIMEOUT_MILLIS = 100; + private static final int WAIT_CONNECTION_TIMEOUT_MILLIS = 200 * HW_TIMEOUT_MULTIPLIER; private final Object mLock; private final Context mContext; @@ -265,9 +265,6 @@ public class MagnificationConnectionManager implements } } } - if (mConnectionStateChangedCallback != null) { - mConnectionStateChangedCallback.onConnectionStateChanged(connection != null); - } } /** @@ -275,7 +272,7 @@ public class MagnificationConnectionManager implements */ public boolean isConnected() { synchronized (mLock) { - return mConnectionWrapper != null && mConnectionState == CONNECTED; + return mConnectionWrapper != null; } } @@ -683,8 +680,7 @@ public class MagnificationConnectionManager implements */ public boolean onFullscreenMagnificationActivationChanged(int displayId, boolean activated) { synchronized (mLock) { - waitForConnectionIfNeeded(); - if (mConnectionWrapper == null) { + if (!waitConnectionWithTimeoutIfNeeded()) { Slog.w(TAG, "onFullscreenMagnificationActivationChanged mConnectionWrapper is null. " + "mConnectionState=" + connectionStateToString(mConnectionState)); @@ -1294,8 +1290,7 @@ public class MagnificationConnectionManager implements float centerY, float magnificationFrameOffsetRatioX, float magnificationFrameOffsetRatioY, MagnificationAnimationCallback animationCallback) { - waitForConnectionIfNeeded(); - if (mConnectionWrapper == null) { + if (!waitConnectionWithTimeoutIfNeeded()) { Slog.w(TAG, "enableWindowMagnificationInternal mConnectionWrapper is null. " + "mConnectionState=" + connectionStateToString(mConnectionState)); @@ -1337,7 +1332,7 @@ public class MagnificationConnectionManager implements displayId, positionX, positionY, animationCallback); } - private void waitForConnectionIfNeeded() { + boolean waitConnectionWithTimeoutIfNeeded() { // Wait for the connection with a timeout. final long endMillis = SystemClock.uptimeMillis() + WAIT_CONNECTION_TIMEOUT_MILLIS; while (mConnectionState == CONNECTING && (SystemClock.uptimeMillis() < endMillis)) { @@ -1347,9 +1342,6 @@ public class MagnificationConnectionManager implements /* ignore */ } } - } - - interface ConnectionStateChangedCallback { - void onConnectionStateChanged(boolean connected); + return isConnected(); } } 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 9b7884711a6d..1489d16c3764 100644 --- a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java +++ b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java @@ -799,14 +799,18 @@ public class MagnificationController implements MagnificationConnectionManager.C this, mScaleProvider, mBackgroundExecutor, - () -> (isMagnificationConnectionManagerInitialized() - && getMagnificationConnectionManager().isConnected()) + () -> isMagnificationSystemUIConnectionReady() ); } } return mFullScreenMagnificationController; } + private boolean isMagnificationSystemUIConnectionReady() { + return isMagnificationConnectionManagerInitialized() + && getMagnificationConnectionManager().waitConnectionWithTimeoutIfNeeded(); + } + /** * Is {@link #mFullScreenMagnificationController} is initialized. * @return {code true} if {@link #mFullScreenMagnificationController} is initialized. @@ -828,8 +832,6 @@ public class MagnificationController implements MagnificationConnectionManager.C mMagnificationConnectionManager = new MagnificationConnectionManager(mContext, mLock, this, mAms.getTraceManager(), mScaleProvider); - mMagnificationConnectionManager.mConnectionStateChangedCallback = - mAms::notifyMagnificationSystemUIConnectionChanged; } return mMagnificationConnectionManager; } diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationProcessor.java b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationProcessor.java index 603683906d06..ed8f1ab3a1b2 100644 --- a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationProcessor.java +++ b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationProcessor.java @@ -147,10 +147,6 @@ public class MagnificationProcessor { return false; } - public boolean isMagnificationSystemUIConnected() { - return mController.getMagnificationConnectionManager().isConnected(); - } - private boolean setScaleAndCenterForFullScreenMagnification(int displayId, float scale, float centerX, float centerY, boolean animate, int id) { diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java index 6fc05b72da9b..eae516e10d6e 100644 --- a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java +++ b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java @@ -2003,7 +2003,7 @@ public final class AutofillManagerService final AutofillManagerServiceImpl service = peekServiceForUserWithLocalBinderIdentityLocked(userId); if (service != null) { - service.setViewAutofilled(sessionId, getCallingUid(), id); + service.setViewAutofilledLocked(sessionId, getCallingUid(), id); } else if (sVerbose) { Slog.v(TAG, "setAutofillFailure(): no service for " + userId); } diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java index 588266fba47a..9ad15b2d431f 100644 --- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java +++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java @@ -478,7 +478,7 @@ final class AutofillManagerServiceImpl } @GuardedBy("mLock") - void setViewAutofilled(int sessionId, int uid, @NonNull AutofillId id) { + void setViewAutofilledLocked(int sessionId, int uid, @NonNull AutofillId id) { if (!isEnabledLocked()) { Slog.wtf(TAG, "Service not enabled"); return; @@ -488,7 +488,7 @@ final class AutofillManagerServiceImpl Slog.v(TAG, "setViewAutofilled(): no session for " + sessionId + "(" + uid + ")"); return; } - session.setViewAutofilled(id); + session.setViewAutofilledLocked(id); } @GuardedBy("mLock") @@ -792,10 +792,9 @@ final class AutofillManagerServiceImpl * Initializes the last fill selection after an autofill service returned a new * {@link FillResponse}. */ - void setLastResponse(int sessionId, @NonNull FillResponse response) { - synchronized (mLock) { + @GuardedBy("mLock") + void setLastResponseLocked(int sessionId, @NonNull FillResponse response) { mEventHistory = new FillEventHistory(sessionId, response.getClientState()); - } } void setLastAugmentedAutofillResponse(int sessionId) { diff --git a/services/autofill/java/com/android/server/autofill/PresentationStatsEventLogger.java b/services/autofill/java/com/android/server/autofill/PresentationStatsEventLogger.java index f289115159b8..676abd1491d7 100644 --- a/services/autofill/java/com/android/server/autofill/PresentationStatsEventLogger.java +++ b/services/autofill/java/com/android/server/autofill/PresentationStatsEventLogger.java @@ -62,6 +62,7 @@ import android.annotation.Nullable; import android.content.ComponentName; import android.content.Context; import android.content.pm.PackageManager; +import android.os.SystemClock; import android.provider.Settings; import android.service.autofill.Dataset; import android.text.TextUtils; @@ -211,17 +212,21 @@ public final class PresentationStatsEventLogger { AUTOFILL_FILL_RESPONSE_REPORTED__DETECTION_PREFERENCE__DETECTION_PREFER_AUTOFILL_PROVIDER; public static final int DETECTION_PREFER_PCC = AUTOFILL_FILL_RESPONSE_REPORTED__DETECTION_PREFERENCE__DETECTION_PREFER_PCC; - private final int mSessionId; + private static final int DEFAULT_VALUE_INT = -1; + + private final int mSessionId; /** * For app_package_uid. */ private final int mCallingAppUid; private Optional<PresentationStatsEventInternal> mEventInternal; + private final long mSessionStartTimestamp; - private PresentationStatsEventLogger(int sessionId, int callingAppUid) { + private PresentationStatsEventLogger(int sessionId, int callingAppUid, long timestamp) { mSessionId = sessionId; mCallingAppUid = callingAppUid; + mSessionStartTimestamp = timestamp; mEventInternal = Optional.empty(); } @@ -229,8 +234,8 @@ public final class PresentationStatsEventLogger { * Create PresentationStatsEventLogger, populated with sessionId and the callingAppUid */ public static PresentationStatsEventLogger createPresentationLog( - int sessionId, int callingAppUid) { - return new PresentationStatsEventLogger(sessionId, callingAppUid); + int sessionId, int callingAppUid, long timestamp) { + return new PresentationStatsEventLogger(sessionId, callingAppUid, timestamp); } public void startNewEvent() { @@ -370,28 +375,50 @@ public final class PresentationStatsEventLogger { }); } + public void maybeSetFillRequestSentTimestampMs() { + maybeSetFillRequestSentTimestampMs(getElapsedTime()); + } + public void maybeSetFillResponseReceivedTimestampMs(int timestamp) { mEventInternal.ifPresent(event -> { event.mFillResponseReceivedTimestampMs = timestamp; }); } + public void maybeSetFillResponseReceivedTimestampMs() { + maybeSetFillResponseReceivedTimestampMs(getElapsedTime()); + } + public void maybeSetSuggestionSentTimestampMs(int timestamp) { mEventInternal.ifPresent(event -> { event.mSuggestionSentTimestampMs = timestamp; }); } + public void maybeSetSuggestionSentTimestampMs() { + maybeSetSuggestionSentTimestampMs(getElapsedTime()); + } + public void maybeSetSuggestionPresentedTimestampMs(int timestamp) { mEventInternal.ifPresent(event -> { - event.mSuggestionPresentedTimestampMs = timestamp; + // mSuggestionPresentedTimestampMs only tracks the first suggested timestamp. + if (event.mSuggestionPresentedTimestampMs == DEFAULT_VALUE_INT) { + event.mSuggestionPresentedTimestampMs = timestamp; + } + + event.mSuggestionPresentedLastTimestampMs = timestamp; }); } + public void maybeSetSuggestionPresentedTimestampMs() { + maybeSetSuggestionPresentedTimestampMs(getElapsedTime()); + } + public void maybeSetSelectedDatasetId(int selectedDatasetId) { mEventInternal.ifPresent(event -> { event.mSelectedDatasetId = selectedDatasetId; }); + setPresentationSelectedTimestamp(); } public void maybeSetDialogDismissed(boolean dialogDismissed) { @@ -479,6 +506,11 @@ public final class PresentationStatsEventLogger { }); } + /** Set latency_authentication_ui_display_millis as long as mEventInternal presents. */ + public void maybeSetLatencyAuthenticationUiDisplayMillis() { + maybeSetLatencyAuthenticationUiDisplayMillis(getElapsedTime()); + } + /** * Set latency_dataset_display_millis as long as mEventInternal presents. */ @@ -488,6 +520,11 @@ public final class PresentationStatsEventLogger { }); } + /** Set latency_dataset_display_millis as long as mEventInternal presents. */ + public void maybeSetLatencyDatasetDisplayMillis() { + maybeSetLatencyDatasetDisplayMillis(getElapsedTime()); + } + /** * Set available_pcc_count. */ @@ -524,6 +561,53 @@ public final class PresentationStatsEventLogger { }); } + /** + * Set various timestamps whenever the ViewState is modified + * + * <p>If the ViewState contains ViewState.STATE_AUTOFILLED, sets field_autofilled_timestamp_ms + * else, set field_first_modified_timestamp_ms (if unset) and field_last_modified_timestamp_ms + */ + public void onFieldTextUpdated(ViewState state) { + mEventInternal.ifPresent( + event -> { + int timestamp = getElapsedTime(); + // Focused id should be set before this is called + if (state.id != null && state.id.getViewId() != event.mFocusedId) { + // if these don't match, the currently field different than before + Slog.w( + TAG, + "current id: " + + state.id.getViewId() + + " is different than focused id: " + + event.mFocusedId); + return; + } + + if ((state.getState() & ViewState.STATE_AUTOFILLED) != 0) { + event.mAutofilledTimestampMs = timestamp; + } else { + if (event.mFieldModifiedFirstTimestampMs == DEFAULT_VALUE_INT) { + event.mFieldModifiedFirstTimestampMs = timestamp; + } + event.mFieldModifiedLastTimestampMs = timestamp; + } + }); + } + + public void setPresentationSelectedTimestamp() { + mEventInternal.ifPresent(event -> { + event.mSelectionTimestamp = getElapsedTime(); + }); + } + + /** + * Returns timestamp (relative to mSessionStartTimestamp) + */ + private int getElapsedTime() { + return (int)(SystemClock.elapsedRealtime() - mSessionStartTimestamp); + } + + private int convertDatasetPickReason(@Dataset.DatasetEligibleReason int val) { switch (val) { case 0: @@ -557,6 +641,15 @@ public final class PresentationStatsEventLogger { } /** + * Set how many views are filtered from fill because they are not in current session + */ + public void maybeSetFilteredFillableViewsCount(int filteredViewsCount) { + mEventInternal.ifPresent(event -> { + event.mFilteredFillabaleViewCount = filteredViewsCount; + }); + } + + /** * Set views_filled_failure_count using failure count as long as mEventInternal * presents. */ @@ -591,7 +684,14 @@ public final class PresentationStatsEventLogger { } else if (autofillIds.contains(autofillId)) { if (sVerbose) { Slog.v(TAG, "Logging autofill for id:" + autofillId); - event.mViewFillSuccessCount++; + } + event.mViewFillSuccessCount++; + autofillIds.remove(autofillId); + event.mAlreadyFilledAutofillIds.add(autofillId); + } else if (event.mAlreadyFilledAutofillIds.contains(autofillId)) { + if (sVerbose) { + Slog.v(TAG, "Successfully filled autofillId:" + autofillId + + " already processed "); } } else { Slog.w(TAG, "Successfully filled autofillId:" + autofillId @@ -644,11 +744,22 @@ public final class PresentationStatsEventLogger { + " mAppPackageUid=" + mCallingAppUid + " mIsCredentialRequest=" + event.mIsCredentialRequest + " mWebviewRequestedCredential=" + event.mWebviewRequestedCredential + + " mFilteredFillabaleViewCount=" + event.mFilteredFillabaleViewCount + " mViewFillableTotalCount=" + event.mViewFillableTotalCount + " mViewFillFailureCount=" + event.mViewFillFailureCount + " mFocusedId=" + event.mFocusedId + " mViewFillSuccessCount=" + event.mViewFillSuccessCount - + " mViewFilledButUnexpectedCount=" + event.mViewFilledButUnexpectedCount); + + " mViewFilledButUnexpectedCount=" + event.mViewFilledButUnexpectedCount + + " event.mSelectionTimestamp=" + event.mSelectionTimestamp + + " event.mAutofilledTimestampMs=" + event.mAutofilledTimestampMs + + " event.mFieldModifiedFirstTimestampMs=" + + event.mFieldModifiedFirstTimestampMs + + " event.mFieldModifiedLastTimestampMs=" + event.mFieldModifiedLastTimestampMs + + " event.mSuggestionPresentedLastTimestampMs=" + + event.mSuggestionPresentedLastTimestampMs + + " event.mFocusedVirtualAutofillId=" + event.mFocusedVirtualAutofillId + + " event.mFieldFirstLength=" + event.mFieldFirstLength + + " event.mFieldLastLength=" + event.mFieldLastLength); } // TODO(b/234185326): Distinguish empty responses from other no presentation reasons. @@ -690,11 +801,20 @@ public final class PresentationStatsEventLogger { mCallingAppUid, event.mIsCredentialRequest, event.mWebviewRequestedCredential, + event.mFilteredFillabaleViewCount, event.mViewFillableTotalCount, event.mViewFillFailureCount, event.mFocusedId, event.mViewFillSuccessCount, - event.mViewFilledButUnexpectedCount); + event.mViewFilledButUnexpectedCount, + event.mSelectionTimestamp, + event.mAutofilledTimestampMs, + event.mFieldModifiedFirstTimestampMs, + event.mFieldModifiedLastTimestampMs, + event.mSuggestionPresentedLastTimestampMs, + event.mFocusedVirtualAutofillId, + event.mFieldFirstLength, + event.mFieldLastLength); mEventInternal = Optional.empty(); } @@ -708,31 +828,40 @@ public final class PresentationStatsEventLogger { int mCountNotShownImePresentationNotDrawn; int mCountNotShownImeUserNotSeen; int mDisplayPresentationType = AUTOFILL_PRESENTATION_EVENT_REPORTED__DISPLAY_PRESENTATION_TYPE__UNKNOWN_AUTOFILL_DISPLAY_PRESENTATION_TYPE; - int mAutofillServiceUid = -1; - int mInlineSuggestionHostUid = -1; + int mAutofillServiceUid = DEFAULT_VALUE_INT; + int mInlineSuggestionHostUid = DEFAULT_VALUE_INT; boolean mIsRequestTriggered; - int mFillRequestSentTimestampMs; - int mFillResponseReceivedTimestampMs; - int mSuggestionSentTimestampMs; - int mSuggestionPresentedTimestampMs; - int mSelectedDatasetId = -1; + int mFillRequestSentTimestampMs = DEFAULT_VALUE_INT; + int mFillResponseReceivedTimestampMs = DEFAULT_VALUE_INT; + int mSuggestionSentTimestampMs = DEFAULT_VALUE_INT; + int mSuggestionPresentedTimestampMs = DEFAULT_VALUE_INT; + int mSelectedDatasetId = DEFAULT_VALUE_INT; boolean mDialogDismissed = false; boolean mNegativeCtaButtonClicked = false; boolean mPositiveCtaButtonClicked = false; int mAuthenticationType = AUTHENTICATION_TYPE_UNKNOWN; int mAuthenticationResult = AUTHENTICATION_RESULT_UNKNOWN; - int mLatencyAuthenticationUiDisplayMillis = -1; - int mLatencyDatasetDisplayMillis = -1; - int mAvailablePccCount = -1; - int mAvailablePccOnlyCount = -1; + int mLatencyAuthenticationUiDisplayMillis = DEFAULT_VALUE_INT; + int mLatencyDatasetDisplayMillis = DEFAULT_VALUE_INT; + int mAvailablePccCount = DEFAULT_VALUE_INT; + int mAvailablePccOnlyCount = DEFAULT_VALUE_INT; @DatasetPickedReason int mSelectedDatasetPickedReason = PICK_REASON_UNKNOWN; @DetectionPreference int mDetectionPreference = DETECTION_PREFER_UNKNOWN; - int mFieldClassificationRequestId = -1; + int mFieldClassificationRequestId = DEFAULT_VALUE_INT; boolean mIsCredentialRequest = false; boolean mWebviewRequestedCredential = false; - int mViewFillableTotalCount = -1; - int mViewFillFailureCount = -1; - int mFocusedId = -1; + int mFilteredFillabaleViewCount = DEFAULT_VALUE_INT; + int mViewFillableTotalCount = DEFAULT_VALUE_INT; + int mViewFillFailureCount = DEFAULT_VALUE_INT; + int mFocusedId = DEFAULT_VALUE_INT; + int mSelectionTimestamp = DEFAULT_VALUE_INT; + int mAutofilledTimestampMs = DEFAULT_VALUE_INT; + int mFieldModifiedFirstTimestampMs = DEFAULT_VALUE_INT; + int mFieldModifiedLastTimestampMs = DEFAULT_VALUE_INT; + int mSuggestionPresentedLastTimestampMs = DEFAULT_VALUE_INT; + int mFocusedVirtualAutofillId = DEFAULT_VALUE_INT; + int mFieldFirstLength = DEFAULT_VALUE_INT; + int mFieldLastLength = DEFAULT_VALUE_INT; // Default value for success count is set to 0 explicitly. Setting it to -1 for // uninitialized doesn't help much, as this would be non-zero only if callback is received. @@ -740,6 +869,7 @@ public final class PresentationStatsEventLogger { int mViewFilledButUnexpectedCount = 0; ArraySet<AutofillId> mAutofillIdsAttemptedAutofill; + ArraySet<AutofillId> mAlreadyFilledAutofillIds = new ArraySet<>(); PresentationStatsEventInternal() {} } diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java index c46464b7cac8..5b4faa24649c 100644 --- a/services/autofill/java/com/android/server/autofill/Session.java +++ b/services/autofill/java/com/android/server/autofill/Session.java @@ -1553,7 +1553,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState mLatencyBaseTime = mStartTime; mRequestCount = 0; mPresentationStatsEventLogger = PresentationStatsEventLogger.createPresentationLog( - sessionId, uid); + sessionId, uid, mLatencyBaseTime); mFillRequestEventLogger = FillRequestEventLogger.forSessionId(sessionId); mFillResponseEventLogger = FillResponseEventLogger.forSessionId(sessionId); mSessionCommittedEventLogger = SessionCommittedEventLogger.forSessionId(sessionId); @@ -1575,14 +1575,6 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState @Override public void notifyInlineUiShown(AutofillId autofillId) { notifyFillUiShown(autofillId); - - synchronized (mLock) { - // TODO(b/262448552): Log when chip inflates instead of here - final long inlineUiShownRelativeTimestamp = - SystemClock.elapsedRealtime() - mLatencyBaseTime; - mPresentationStatsEventLogger.maybeSetSuggestionPresentedTimestampMs( - (int) (inlineUiShownRelativeTimestamp)); - } } @Override @@ -1685,22 +1677,23 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState final LogMaker requestLog; - // Start a new FillResponse logger for the success case. - mFillResponseEventLogger.startLogForNewResponse(); - mFillResponseEventLogger.maybeSetRequestId(requestId); - mFillResponseEventLogger.maybeSetAppPackageUid(uid); - mFillResponseEventLogger.maybeSetResponseStatus(RESPONSE_STATUS_SUCCESS); - mFillResponseEventLogger.startResponseProcessingTime(); - // Time passed since session was created - final long fillRequestReceivedRelativeTimestamp = - SystemClock.elapsedRealtime() - mLatencyBaseTime; - mPresentationStatsEventLogger.maybeSetFillResponseReceivedTimestampMs( - (int) (fillRequestReceivedRelativeTimestamp)); - mFillResponseEventLogger.maybeSetLatencyFillResponseReceivedMillis( - (int) (fillRequestReceivedRelativeTimestamp)); - mFillResponseEventLogger.maybeSetDetectionPreference(getDetectionPreferenceForLogging()); - synchronized (mLock) { + // Start a new FillResponse logger for the success case. + mFillResponseEventLogger.startLogForNewResponse(); + mFillResponseEventLogger.maybeSetRequestId(requestId); + mFillResponseEventLogger.maybeSetAppPackageUid(uid); + mFillResponseEventLogger.maybeSetResponseStatus(RESPONSE_STATUS_SUCCESS); + mFillResponseEventLogger.startResponseProcessingTime(); + // Time passed since session was created + final long fillRequestReceivedRelativeTimestamp = + SystemClock.elapsedRealtime() - mLatencyBaseTime; + mPresentationStatsEventLogger.maybeSetFillResponseReceivedTimestampMs( + (int) (fillRequestReceivedRelativeTimestamp)); + mFillResponseEventLogger.maybeSetLatencyFillResponseReceivedMillis( + (int) (fillRequestReceivedRelativeTimestamp)); + mFillResponseEventLogger.maybeSetDetectionPreference( + getDetectionPreferenceForLogging()); + if (mDestroyed) { Slog.w(TAG, "Call to Session#onFillRequestSuccess() rejected - session: " + id + " destroyed"); @@ -1752,11 +1745,9 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState Slog.v(TAG, "Service requested to wait for delayed fill response."); registerDelayedFillBroadcastLocked(); } - } - mService.setLastResponse(id, response); + mService.setLastResponseLocked(id, response); - synchronized (mLock) { if (mLogViewEntered) { mLogViewEntered = false; mService.logViewEntered(id, null); @@ -1829,16 +1820,18 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } int datasetCount = (datasetList == null) ? 0 : datasetList.size(); - mFillResponseEventLogger.maybeSetTotalDatasetsProvided(datasetCount); - // It's possible that this maybe overwritten later on after PCC filtering. - mFillResponseEventLogger.maybeSetAvailableCount(datasetCount); + synchronized (mLock) { + mFillResponseEventLogger.maybeSetTotalDatasetsProvided(datasetCount); + // It's possible that this maybe overwritten later on after PCC filtering. + mFillResponseEventLogger.maybeSetAvailableCount(datasetCount); - // TODO(b/266379948): Ideally wait for PCC request to finish for a while more - // (say 100ms) before proceeding further on. + // TODO(b/266379948): Ideally wait for PCC request to finish for a while more + // (say 100ms) before proceeding further on. - processResponseLockedForPcc(response, response.getClientState(), requestFlags); - mFillResponseEventLogger.maybeSetLatencyResponseProcessingMillis(); - mFillResponseEventLogger.logAndEndEvent(); + processResponseLockedForPcc(response, response.getClientState(), requestFlags); + mFillResponseEventLogger.maybeSetLatencyResponseProcessingMillis(); + mFillResponseEventLogger.logAndEndEvent(); + } } @@ -2389,21 +2382,22 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState @Nullable CharSequence message) { boolean showMessage = !TextUtils.isEmpty(message); - // Start a new FillResponse logger for the failure or timeout case. - mFillResponseEventLogger.startLogForNewResponse(); - mFillResponseEventLogger.maybeSetRequestId(requestId); - mFillResponseEventLogger.maybeSetAppPackageUid(uid); - mFillResponseEventLogger.maybeSetAvailableCount( - AVAILABLE_COUNT_WHEN_FILL_REQUEST_FAILED_OR_TIMEOUT); - mFillResponseEventLogger.maybeSetTotalDatasetsProvided( - AVAILABLE_COUNT_WHEN_FILL_REQUEST_FAILED_OR_TIMEOUT); - mFillResponseEventLogger.maybeSetDetectionPreference(getDetectionPreferenceForLogging()); - final long fillRequestReceivedRelativeTimestamp = - SystemClock.elapsedRealtime() - mLatencyBaseTime; - mFillResponseEventLogger.maybeSetLatencyFillResponseReceivedMillis( - (int)(fillRequestReceivedRelativeTimestamp)); - synchronized (mLock) { + // Start a new FillResponse logger for the failure or timeout case. + mFillResponseEventLogger.startLogForNewResponse(); + mFillResponseEventLogger.maybeSetRequestId(requestId); + mFillResponseEventLogger.maybeSetAppPackageUid(uid); + mFillResponseEventLogger.maybeSetAvailableCount( + AVAILABLE_COUNT_WHEN_FILL_REQUEST_FAILED_OR_TIMEOUT); + mFillResponseEventLogger.maybeSetTotalDatasetsProvided( + AVAILABLE_COUNT_WHEN_FILL_REQUEST_FAILED_OR_TIMEOUT); + mFillResponseEventLogger.maybeSetDetectionPreference( + getDetectionPreferenceForLogging()); + final long fillRequestReceivedRelativeTimestamp = + SystemClock.elapsedRealtime() - mLatencyBaseTime; + mFillResponseEventLogger.maybeSetLatencyFillResponseReceivedMillis( + (int) (fillRequestReceivedRelativeTimestamp)); + unregisterDelayedFillBroadcastLocked(); if (mDestroyed) { Slog.w(TAG, "Call to Session#onFillRequestFailureOrTimeout(req=" + requestId @@ -2643,8 +2637,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState + id + " destroyed"); return; } + mSaveEventLogger.maybeSetLatencySaveRequestMillis(); } - mSaveEventLogger.maybeSetLatencySaveRequestMillis(); mHandler.sendMessage(obtainMessage( AutofillManagerServiceImpl::handleSessionSave, mService, this)); @@ -2678,6 +2672,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState mLoggedInlineDatasetShown = true; } mService.logDatasetShown(this.id, mClientState, uiType); + mPresentationStatsEventLogger.maybeSetSuggestionPresentedTimestampMs(); Slog.d(TAG, "onShown(): " + uiType); } } @@ -3222,6 +3217,58 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState return saveInfo == null ? 0 : saveInfo.getFlags(); } + static class SaveInfoStats { + public int saveInfoCount; + public int saveDataTypeCount; + } + + /** + * Get statistic information of save info in current session. Specifically + * 1. how many save info the current session has. + * 2. How many distinct save data types current session has. + * + * @return SaveInfoStats returns the above two number in a SaveInfoStats object + */ + @GuardedBy("mLock") + private SaveInfoStats getSaveInfoStatsLocked() { + SaveInfoStats retSaveInfoStats = new SaveInfoStats(); + retSaveInfoStats.saveInfoCount = -1; + retSaveInfoStats.saveDataTypeCount = -1; + + if (mContexts == null) { + if (sVerbose) { + Slog.v(TAG, "getSaveInfoStatsLocked(): mContexts is null"); + } + } else if (mResponses == null) { + // Happens when the activity / session was finished before the service replied, or + // when the service cannot autofill it (and returned a null response). + if (sVerbose) { + Slog.v(TAG, "getSaveInfoStatsLocked(): mResponses is null"); + } + return retSaveInfoStats; + } else { + int numSaveInfos = 0; + int numSaveDataTypes = 0; + ArraySet<Integer> saveDataTypeSeen = new ArraySet<>(); + final int numResponses = mResponses.size(); + for (int responseNum = 0; responseNum < numResponses; responseNum++) { + final FillResponse response = mResponses.valueAt(responseNum); + if (response != null && response.getSaveInfo() != null) { + numSaveInfos += 1; + int saveDataType = response.getSaveInfo().getType(); + if (!saveDataTypeSeen.contains(saveDataType)) { + saveDataTypeSeen.add(saveDataType); + numSaveDataTypes += 1; + } + } + } + retSaveInfoStats.saveInfoCount = numSaveInfos; + retSaveInfoStats.saveDataTypeCount = numSaveDataTypes; + } + + return retSaveInfoStats; + } + /** * Generates a {@link android.service.autofill.FillEventHistory.Event#TYPE_CONTEXT_COMMITTED} * when necessary. @@ -3234,7 +3281,9 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState mHandler.sendMessage(obtainMessage(Session::handleLogContextCommitted, this, Event.NO_SAVE_UI_REASON_NONE, COMMIT_REASON_UNKNOWN)); - logAllEvents(COMMIT_REASON_UNKNOWN); + synchronized (mLock) { + logAllEventsLocked(COMMIT_REASON_UNKNOWN); + } } /** @@ -3258,6 +3307,11 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState mSessionCommittedEventLogger.maybeSetCommitReason(commitReason); mSessionCommittedEventLogger.maybeSetRequestCount(mRequestCount); + SaveInfoStats saveInfoStats = getSaveInfoStatsLocked(); + mSessionCommittedEventLogger.maybeSetSaveInfoCount(saveInfoStats.saveInfoCount); + mSessionCommittedEventLogger.maybeSetSaveDataTypeCount(saveInfoStats.saveDataTypeCount); + mSessionCommittedEventLogger.maybeSetLastFillResponseHasSaveInfo( + getSaveInfoLocked() != null); mSaveEventLogger.maybeSetSaveUiNotShownReason(NO_SAVE_REASON_NONE); } @@ -4578,7 +4632,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState FillResponse response = viewState.getSecondaryResponse(); if (response != null) { - logPresentationStatsOnViewEntered(response, isCredmanRequested); + logPresentationStatsOnViewEnteredLocked(response, isCredmanRequested); } // If the ViewState is ready to be displayed, onReady() will be called. @@ -4669,7 +4723,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState FillResponse response = viewState.getResponse(); if (response != null) { - logPresentationStatsOnViewEntered(response, isCredmanRequested); + logPresentationStatsOnViewEnteredLocked(response, isCredmanRequested); } if (isSameViewEntered) { @@ -4710,7 +4764,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } @GuardedBy("mLock") - private void logPresentationStatsOnViewEntered(FillResponse response, + private void logPresentationStatsOnViewEnteredLocked(FillResponse response, boolean isCredmanRequested) { mPresentationStatsEventLogger.maybeSetRequestId(response.getRequestId()); mPresentationStatsEventLogger.maybeSetIsCredentialRequest(isCredmanRequested); @@ -4788,7 +4842,6 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState updateFilteringStateOnValueChangedLocked(textValue, viewState); viewState.setCurrentValue(value); - final String filterText = textValue; final AutofillValue filledValue = viewState.getAutofilledValue(); @@ -4815,6 +4868,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState currentView.maybeCallOnFillReady(flags); } } + mPresentationStatsEventLogger.onFieldTextUpdated(viewState); if (viewState.id.equals(this.mCurrentViewId) && (viewState.getState() & ViewState.STATE_INLINE_SHOWN) != 0) { @@ -4902,10 +4956,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState synchronized (mLock) { // Time passed since Session was created - final long suggestionSentRelativeTimestamp = - SystemClock.elapsedRealtime() - mLatencyBaseTime; - mPresentationStatsEventLogger.maybeSetSuggestionSentTimestampMs( - (int) (suggestionSentRelativeTimestamp)); + mPresentationStatsEventLogger.maybeSetSuggestionSentTimestampMs(); } final AutofillId[] ids = response.getFillDialogTriggerIds(); @@ -4922,13 +4973,6 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState // Note: Cannot disable before requestShowFillDialog() because the method // need to check whether fill dialog enabled. setFillDialogDisabled(); - synchronized (mLock) { - // Logs when fill dialog ui is shown; time since Session was created - final long fillDialogUiShownRelativeTimestamp = - SystemClock.elapsedRealtime() - mLatencyBaseTime; - mPresentationStatsEventLogger.maybeSetSuggestionPresentedTimestampMs( - (int) (fillDialogUiShownRelativeTimestamp)); - } return; } else { setFillDialogDisabled(); @@ -4970,10 +5014,6 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState // Log first time UI is shown. mUiShownTime = SystemClock.elapsedRealtime(); final long duration = mUiShownTime - mStartTime; - // This logs when dropdown ui was shown. Timestamp is relative to - // when the session was created - mPresentationStatsEventLogger.maybeSetSuggestionPresentedTimestampMs( - (int) (mUiShownTime - mLatencyBaseTime)); if (sDebug) { final StringBuilder msg = new StringBuilder("1st UI for ") @@ -5082,7 +5122,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState getUiForShowing().showFillDialog(filledId, response, filterText, mService.getServicePackageName(), mComponentName, serviceIcon, this, - id, mCompatMode, mPresentationStatsEventLogger); + id, mCompatMode, mPresentationStatsEventLogger, mLock); return true; } @@ -5441,7 +5481,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState * Sets the state of views that failed to autofill. */ @GuardedBy("mLock") - void setViewAutofilled(@NonNull AutofillId id) { + void setViewAutofilledLocked(@NonNull AutofillId id) { if (sVerbose) { Slog.v(TAG, "View autofilled: " + id); } @@ -6624,6 +6664,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState boolean waitingDatasetAuth = false; boolean hideHighlight = (entryCount == 1 && dataset.getFieldIds().get(0).equals(mCurrentViewId)); + // Count how many views are filtered because they are not in current session + int numOfViewsFiltered = 0; for (int i = 0; i < entryCount; i++) { if (dataset.getFieldValues().get(i) == null) { continue; @@ -6636,6 +6678,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState Slog.v(TAG, "Skipping filling view: " + viewId + " as it isn't part of the current session: " + id); } + numOfViewsFiltered += 1; continue; } ids.add(viewId); @@ -6649,6 +6692,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState viewState.resetState(ViewState.STATE_WAITING_DATASET_AUTH); } } + mPresentationStatsEventLogger.maybeSetFilteredFillableViewsCount( + numOfViewsFiltered); if (!ids.isEmpty()) { if (waitingDatasetAuth) { mUi.hideFillUi(this); @@ -6684,7 +6729,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } @GuardedBy("mLock") - private void logAllEvents(@AutofillCommitReason int val) { + private void logAllEventsLocked(@AutofillCommitReason int val) { if (sVerbose) { Slog.v(TAG, "logAllEvents(" + id + "): commitReason: " + val); } @@ -6716,7 +6761,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState if (sVerbose) { Slog.v(TAG, "destroyLocked for session: " + id); } - logAllEvents(COMMIT_REASON_SESSION_DESTROYED); + logAllEventsLocked(COMMIT_REASON_SESSION_DESTROYED); if (mDestroyed) { return null; diff --git a/services/autofill/java/com/android/server/autofill/SessionCommittedEventLogger.java b/services/autofill/java/com/android/server/autofill/SessionCommittedEventLogger.java index 1be8548c788f..8f3c8803154d 100644 --- a/services/autofill/java/com/android/server/autofill/SessionCommittedEventLogger.java +++ b/services/autofill/java/com/android/server/autofill/SessionCommittedEventLogger.java @@ -94,6 +94,33 @@ public final class SessionCommittedEventLogger { } /** + * Set how many save infos there are in current session as long as mEventInternal presents. + */ + public void maybeSetSaveInfoCount(int saveInfoCount) { + mEventInternal.ifPresent(event -> { + event.mSaveInfoCount = saveInfoCount; + }); + } + + /** + * Set how many save data types there are in current session as long as mEventInternal presents. + */ + public void maybeSetSaveDataTypeCount(int saveDataTypeCount) { + mEventInternal.ifPresent(event -> { + event.mSaveDataTypeCount = saveDataTypeCount; + }); + } + + /** + * Set whether last fill response in session has save info as long as mEventInternal presents. + */ + public void maybeSetLastFillResponseHasSaveInfo(boolean lastFillResponseHasSaveInfo) { + mEventInternal.ifPresent(event -> { + event.mLastFillResponseHasSaveInfo = lastFillResponseHasSaveInfo; + }); + } + + /** * Log an AUTOFILL_SESSION_COMMITTED event. */ public void logAndEndEvent() { @@ -109,7 +136,10 @@ public final class SessionCommittedEventLogger { + " mRequestCount=" + event.mRequestCount + " mCommitReason=" + event.mCommitReason + " mSessionDurationMillis=" + event.mSessionDurationMillis - + " mServiceUid=" + event.mServiceUid); + + " mServiceUid=" + event.mServiceUid + + " mSaveInfoCount=" + event.mSaveInfoCount + + " mSaveDataTypeCount=" + event.mSaveDataTypeCount + + " mLastFillResponseHasSaveInfo=" + event.mLastFillResponseHasSaveInfo); } FrameworkStatsLog.write( AUTOFILL_SESSION_COMMITTED, @@ -118,7 +148,10 @@ public final class SessionCommittedEventLogger { event.mRequestCount, event.mCommitReason, event.mSessionDurationMillis, - event.mServiceUid); + event.mServiceUid, + event.mSaveInfoCount, + event.mSaveDataTypeCount, + event.mLastFillResponseHasSaveInfo); mEventInternal = Optional.empty(); } @@ -127,6 +160,9 @@ public final class SessionCommittedEventLogger { int mRequestCount = 0; int mCommitReason = COMMIT_REASON_UNKNOWN; long mSessionDurationMillis = 0; + int mSaveInfoCount = -1; + int mSaveDataTypeCount = -1; + boolean mLastFillResponseHasSaveInfo = false; int mServiceUid = -1; SessionCommittedEventInternal() { diff --git a/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java b/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java index 3b9c54f79e61..8cc666b538ec 100644 --- a/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java +++ b/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java @@ -425,7 +425,8 @@ public final class AutoFillUI { @Nullable String filterText, @Nullable String servicePackageName, @NonNull ComponentName componentName, @Nullable Drawable serviceIcon, @NonNull AutoFillUiCallback callback, int sessionId, boolean compatMode, - @Nullable PresentationStatsEventLogger mPresentationStatsEventLogger) { + @Nullable PresentationStatsEventLogger presentationStatsEventLogger, + @NonNull Object sessionLock) { if (sVerbose) { Slog.v(TAG, "showFillDialog for " + componentName.toShortString() + ": " + response); @@ -467,9 +468,11 @@ public final class AutoFillUI { @Override public void onDatasetPicked(Dataset dataset) { log(MetricsEvent.TYPE_ACTION); - if (mPresentationStatsEventLogger != null) { - mPresentationStatsEventLogger.maybeSetPositiveCtaButtonClicked( - true); + synchronized (sessionLock) { + if (presentationStatsEventLogger != null) { + presentationStatsEventLogger.maybeSetPositiveCtaButtonClicked( + true); + } } hideFillDialogUiThread(callback); if (mCallback != null) { @@ -482,8 +485,10 @@ public final class AutoFillUI { @Override public void onDismissed() { log(MetricsEvent.TYPE_DISMISS); - if (mPresentationStatsEventLogger != null) { - mPresentationStatsEventLogger.maybeSetDialogDismissed(true); + synchronized (sessionLock) { + if (presentationStatsEventLogger != null) { + presentationStatsEventLogger.maybeSetDialogDismissed(true); + } } hideFillDialogUiThread(callback); callback.requestShowSoftInput(focusedId); @@ -493,9 +498,11 @@ public final class AutoFillUI { @Override public void onCanceled() { log(MetricsEvent.TYPE_CLOSE); - if (mPresentationStatsEventLogger != null) { - mPresentationStatsEventLogger.maybeSetNegativeCtaButtonClicked( - true); + synchronized (sessionLock) { + if (presentationStatsEventLogger != null) { + presentationStatsEventLogger.maybeSetNegativeCtaButtonClicked( + true); + } } hideFillDialogUiThread(callback); callback.requestShowSoftInput(focusedId); diff --git a/services/autofill/java/com/android/server/autofill/ui/FillUi.java b/services/autofill/java/com/android/server/autofill/ui/FillUi.java index 78edb8ead7a0..1831ecdaf77d 100644 --- a/services/autofill/java/com/android/server/autofill/ui/FillUi.java +++ b/services/autofill/java/com/android/server/autofill/ui/FillUi.java @@ -689,12 +689,20 @@ final class FillUi { Slog.v(TAG, "AutofillWindowPresenter.show(): fit=" + fitsSystemWindows + ", params=" + paramsToString(p)); } - UiThread.getHandler().post(() -> mWindow.show(p)); + UiThread.getHandler().post(() -> { + if (mWindow != null) { + mWindow.show(p); + } + }); } @Override public void hide(Rect transitionEpicenter) { - UiThread.getHandler().post(mWindow::hide); + UiThread.getHandler().post(() -> { + if (mWindow != null) { + mWindow.hide(); + } + }); } } diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java index 0d0c21dcf49c..0ab6bbc3e0d3 100644 --- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java +++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java @@ -18,6 +18,7 @@ package com.android.server.companion; import static android.Manifest.permission.ASSOCIATE_COMPANION_DEVICES; +import static android.Manifest.permission.BLUETOOTH_CONNECT; import static android.Manifest.permission.DELIVER_COMPANION_MESSAGES; import static android.Manifest.permission.MANAGE_COMPANION_DEVICES; import static android.Manifest.permission.REQUEST_COMPANION_SELF_MANAGED; @@ -52,6 +53,9 @@ import android.app.AppOpsManager; import android.app.NotificationManager; import android.app.PendingIntent; import android.app.ecm.EnhancedConfirmationManager; +import android.bluetooth.BluetoothAdapter; +import android.bluetooth.BluetoothDevice; +import android.bluetooth.BluetoothManager; import android.companion.AssociationInfo; import android.companion.AssociationRequest; import android.companion.IAssociationRequestCallback; @@ -528,7 +532,8 @@ public class CompanionDeviceManagerService extends SystemService { String packageName, int userId) { startObservingDevicePresence_enforcePermission(); - mDevicePresenceProcessor.startObservingDevicePresence(request, packageName, userId); + mDevicePresenceProcessor.startObservingDevicePresence( + request, packageName, userId, /* enforcePermissions */ true); } @Override @@ -537,7 +542,33 @@ public class CompanionDeviceManagerService extends SystemService { String packageName, int userId) { stopObservingDevicePresence_enforcePermission(); - mDevicePresenceProcessor.stopObservingDevicePresence(request, packageName, userId); + mDevicePresenceProcessor.stopObservingDevicePresence( + request, packageName, userId, /* enforcePermissions */ true); + } + + @Override + @EnforcePermission(BLUETOOTH_CONNECT) + public boolean removeBond(int associationId, String packageName, int userId) { + removeBond_enforcePermission(); + + Slog.i(TAG, "removeBond() " + + "associationId=" + associationId + ", " + + "package=u" + userId + "/" + packageName); + enforceCallerCanManageAssociationsForPackage(getContext(), userId, packageName, + "remove bonds"); + + AssociationInfo association = mAssociationStore + .getAssociationWithCallerChecks(associationId); + MacAddress address = association.getDeviceMacAddress(); + if (address == null) { + throw new IllegalArgumentException( + "Association id=[" + associationId + "] doesn't have a device address."); + } + + BluetoothAdapter btAdapter = getContext().getSystemService(BluetoothManager.class) + .getAdapter(); + BluetoothDevice btDevice = btAdapter.getRemoteDevice(address.toString().toUpperCase()); + return btDevice.removeBond(); } @Override diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java b/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java index c892b84ab4d5..3d53deb8d2bb 100644 --- a/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java +++ b/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java @@ -21,6 +21,7 @@ import static android.companion.CompanionDeviceManager.MESSAGE_REQUEST_CONTEXT_S import android.companion.AssociationInfo; import android.companion.ContextSyncMessage; import android.companion.Flags; +import android.companion.ObservingDevicePresenceRequest; import android.companion.Telecom; import android.companion.datatransfer.PermissionSyncRequest; import android.net.MacAddress; @@ -193,6 +194,43 @@ class CompanionDeviceShellCommand extends ShellCommand { break; } + case "start-observing-device-presence-uuid": { + if (Flags.devicePresence()) { + int userId = getNextIntArgRequired(); + String packageName = getNextArgRequired(); + String uuid = getNextArgRequired(); + if ("null".equals(uuid)) { + out.println("UUID can not be null."); + break; + } + ParcelUuid parcelUuid = ParcelUuid.fromString(uuid); + ObservingDevicePresenceRequest request = new ObservingDevicePresenceRequest + .Builder().setUuid(parcelUuid).build(); + mDevicePresenceProcessor.startObservingDevicePresence( + request, packageName, userId, /* enforcePermissions */ false); + + } + break; + } + + case "stop-observing-device-presence-uuid": { + if (Flags.devicePresence()) { + int userId = getNextIntArgRequired(); + String packageName = getNextArgRequired(); + String uuid = getNextArgRequired(); + if ("null".equals(uuid)) { + out.println("UUID can not be null."); + break; + } + ParcelUuid parcelUuid = ParcelUuid.fromString(uuid); + ObservingDevicePresenceRequest request = new ObservingDevicePresenceRequest + .Builder().setUuid(parcelUuid).build(); + mDevicePresenceProcessor.stopObservingDevicePresence( + request, packageName, userId, /* enforcePermissions */ false); + } + break; + } + case "get-backup-payload": { final int userId = getNextIntArgRequired(); byte[] payload = mBackupRestoreProcessor.getBackupPayload(userId); @@ -515,6 +553,14 @@ class CompanionDeviceShellCommand extends ShellCommand { pw.println(" callback after simulate-device-event-device-locked"); pw.println(" command has been called."); pw.println(" USE FOR DEBUGGING AND/OR TESTING PURPOSES ONLY."); + + pw.println(" start-observing-device-presence-uuid USER_ID PACKAGE_NAME UUID"); + pw.println(" Start observing device presence base on the UUID."); + pw.println(" USE FOR DEBUGGING AND/OR TESTING PURPOSES ONLY."); + + pw.println(" stop-observing-device-presence-uuid USER_ID PACKAGE_NAME UUID"); + pw.println(" Stop observing device presence base on the UUID."); + pw.println(" USE FOR DEBUGGING AND/OR TESTING PURPOSES ONLY."); } pw.println(" remove-inactive-associations"); 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 af49df69a979..a374d279af0b 100644 --- a/services/companion/java/com/android/server/companion/devicepresence/DevicePresenceProcessor.java +++ b/services/companion/java/com/android/server/companion/devicepresence/DevicePresenceProcessor.java @@ -181,14 +181,16 @@ public class DevicePresenceProcessor implements AssociationStore.OnChangeListene * Process device presence start request. */ public void startObservingDevicePresence(ObservingDevicePresenceRequest request, - String packageName, int userId) { + String packageName, int userId, boolean enforcePermissions) { Slog.i(TAG, "Start observing request=[" + request + "] for userId=[" + userId + "], package=[" + packageName + "]..."); final ParcelUuid requestUuid = request.getUuid(); if (requestUuid != null) { - enforceCallerCanObserveDevicePresenceByUuid(mContext); + if (enforcePermissions) { + enforceCallerCanObserveDevicePresenceByUuid(mContext, packageName, userId); + } // If it's already being observed, then no-op. if (mObservableUuidStore.isUuidBeingObserved(requestUuid, userId, packageName)) { @@ -236,7 +238,7 @@ public class DevicePresenceProcessor implements AssociationStore.OnChangeListene * Process device presence stop request. */ public void stopObservingDevicePresence(ObservingDevicePresenceRequest request, - String packageName, int userId) { + String packageName, int userId, boolean enforcePermissions) { Slog.i(TAG, "Stop observing request=[" + request + "] for userId=[" + userId + "], package=[" + packageName + "]..."); @@ -244,7 +246,9 @@ public class DevicePresenceProcessor implements AssociationStore.OnChangeListene final ParcelUuid requestUuid = request.getUuid(); if (requestUuid != null) { - enforceCallerCanObserveDevicePresenceByUuid(mContext); + if (enforcePermissions) { + enforceCallerCanObserveDevicePresenceByUuid(mContext, packageName, userId); + } if (!mObservableUuidStore.isUuidBeingObserved(requestUuid, userId, packageName)) { Slog.i(TAG, "UUID=[" + requestUuid + "], package=[" + packageName + "], userId=[" @@ -283,7 +287,7 @@ public class DevicePresenceProcessor implements AssociationStore.OnChangeListene * For legacy device presence below Android V. * * @deprecated Use {@link #startObservingDevicePresence(ObservingDevicePresenceRequest, String, - * int)} + * int, boolean)} */ @Deprecated public void startObservingDevicePresence(int userId, String packageName, String deviceAddress) @@ -306,14 +310,14 @@ public class DevicePresenceProcessor implements AssociationStore.OnChangeListene startObservingDevicePresence( new ObservingDevicePresenceRequest.Builder().setAssociationId(association.getId()) - .build(), packageName, userId); + .build(), packageName, userId, /* enforcePermissions */ true); } /** * For legacy device presence below Android V. * * @deprecated Use {@link #stopObservingDevicePresence(ObservingDevicePresenceRequest, String, - * int)} + * int, boolean)} */ @Deprecated public void stopObservingDevicePresence(int userId, String packageName, String deviceAddress) @@ -336,7 +340,7 @@ public class DevicePresenceProcessor implements AssociationStore.OnChangeListene stopObservingDevicePresence( new ObservingDevicePresenceRequest.Builder().setAssociationId(association.getId()) - .build(), packageName, userId); + .build(), packageName, userId, /* enforcePermissions */ true); } /** diff --git a/services/companion/java/com/android/server/companion/utils/PermissionsUtils.java b/services/companion/java/com/android/server/companion/utils/PermissionsUtils.java index 796d2851760f..c927cd04cf64 100644 --- a/services/companion/java/com/android/server/companion/utils/PermissionsUtils.java +++ b/services/companion/java/com/android/server/companion/utils/PermissionsUtils.java @@ -35,6 +35,8 @@ import static android.os.Binder.getCallingUid; import static android.os.Process.SYSTEM_UID; import static android.os.UserHandle.getCallingUserId; +import static com.android.server.companion.utils.RolesUtils.isRoleHolder; + import static java.util.Collections.unmodifiableMap; import android.Manifest; @@ -44,6 +46,7 @@ import android.annotation.UserIdInt; import android.companion.AssociationRequest; import android.companion.CompanionDeviceManager; import android.content.Context; +import android.os.Binder; import android.os.RemoteException; import android.os.ServiceManager; import android.util.ArrayMap; @@ -203,11 +206,9 @@ public final class PermissionsUtils { /** * Require the caller to hold necessary permission to observe device presence by UUID. */ - public static void enforceCallerCanObserveDevicePresenceByUuid(@NonNull Context context) { - if (context.checkCallingPermission(REQUEST_OBSERVE_DEVICE_UUID_PRESENCE) - != PERMISSION_GRANTED - || context.checkCallingPermission(BLUETOOTH_SCAN) != PERMISSION_GRANTED - || context.checkCallingPermission(BLUETOOTH_CONNECT) != PERMISSION_GRANTED) { + public static void enforceCallerCanObserveDevicePresenceByUuid(@NonNull Context context, + String packageName, int userId) { + if (!hasRequirePermissions(context, packageName, userId)) { throw new SecurityException("Caller (uid=" + getCallingUid() + ") does not have " + "permissions to request observing device presence base on the UUID"); } @@ -234,6 +235,17 @@ public final class PermissionsUtils { return sAppOpsService; } + private static boolean hasRequirePermissions( + @NonNull Context context, String packageName, int userId) { + return context.checkCallingPermission( + REQUEST_OBSERVE_DEVICE_UUID_PRESENCE) == PERMISSION_GRANTED + && context.checkCallingPermission(BLUETOOTH_SCAN) == PERMISSION_GRANTED + && context.checkCallingPermission(BLUETOOTH_CONNECT) == PERMISSION_GRANTED + && Boolean.TRUE.equals(Binder.withCleanCallingIdentity( + () -> isRoleHolder(context, userId, packageName, + DEVICE_PROFILE_AUTOMOTIVE_PROJECTION))); + } + // DO NOT USE DIRECTLY! Access via getAppOpsService(). private static IAppOpsService sAppOpsService = null; 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 4a9900763a94..6704049e3612 100644 --- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java +++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java @@ -1055,7 +1055,7 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub @Override // Binder call @EnforcePermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) - public int getVirtualCameraId(@NonNull VirtualCameraConfig cameraConfig) + public String getVirtualCameraId(@NonNull VirtualCameraConfig cameraConfig) throws RemoteException { super.getVirtualCameraId_enforcePermission(); Objects.requireNonNull(cameraConfig); diff --git a/services/companion/java/com/android/server/companion/virtual/camera/VirtualCameraController.java b/services/companion/java/com/android/server/companion/virtual/camera/VirtualCameraController.java index 743086ee1c16..62efafbe12c6 100644 --- a/services/companion/java/com/android/server/companion/virtual/camera/VirtualCameraController.java +++ b/services/companion/java/com/android/server/companion/virtual/camera/VirtualCameraController.java @@ -135,7 +135,7 @@ public final class VirtualCameraController implements IBinder.DeathRecipient { } /** Return the id of the virtual camera with the given config. */ - public int getCameraId(@NonNull VirtualCameraConfig cameraConfig) { + public String getCameraId(@NonNull VirtualCameraConfig cameraConfig) { connectVirtualCameraServiceIfNeeded(); try { diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java index 2607ed3193eb..89c888c6dfc8 100644 --- a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java +++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java @@ -18,6 +18,7 @@ package com.android.server.contentcapture; import static android.Manifest.permission.MANAGE_CONTENT_CAPTURE; import static android.content.Context.CONTENT_CAPTURE_MANAGER_SERVICE; +import static android.service.contentcapture.ContentCaptureService.ASSIST_CONTENT_ACTIVITY_START_KEY; import static android.service.contentcapture.ContentCaptureService.setClientState; import static android.view.contentcapture.ContentCaptureHelper.toList; import static android.view.contentcapture.ContentCaptureManager.DEVICE_CONFIG_PROPERTY_CONTENT_PROTECTION_ALLOWLIST_DELAY_MS; @@ -28,6 +29,7 @@ import static android.view.contentcapture.ContentCaptureManager.DEVICE_CONFIG_PR import static android.view.contentcapture.ContentCaptureManager.DEVICE_CONFIG_PROPERTY_CONTENT_PROTECTION_OPTIONAL_GROUPS_THRESHOLD; import static android.view.contentcapture.ContentCaptureManager.DEVICE_CONFIG_PROPERTY_CONTENT_PROTECTION_REQUIRED_GROUPS_CONFIG; import static android.view.contentcapture.ContentCaptureManager.DEVICE_CONFIG_PROPERTY_ENABLE_CONTENT_PROTECTION_RECEIVER; +import static android.view.contentcapture.ContentCaptureManager.DEVICE_CONFIG_ENABLE_ACTIVITY_START_ASSIST_CONTENT; import static android.view.contentcapture.ContentCaptureManager.RESULT_CODE_FALSE; import static android.view.contentcapture.ContentCaptureManager.RESULT_CODE_OK; import static android.view.contentcapture.ContentCaptureManager.RESULT_CODE_SECURITY_EXCEPTION; @@ -44,6 +46,7 @@ import static com.android.internal.util.FrameworkStatsLog.CONTENT_CAPTURE_SERVIC import static com.android.internal.util.FrameworkStatsLog.CONTENT_CAPTURE_SERVICE_EVENTS__EVENT__DATA_SHARE_WRITE_FINISHED; import static com.android.internal.util.FrameworkStatsLog.CONTENT_CAPTURE_SERVICE_EVENTS__EVENT__REJECT_DATA_SHARE_REQUEST; import static com.android.internal.util.SyncResultReceiver.bundleFor; +import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_CONTENT; import android.annotation.NonNull; import android.annotation.Nullable; @@ -52,10 +55,12 @@ import android.app.ActivityManagerInternal; import android.app.ActivityThread; import android.app.admin.DevicePolicyCache; import android.app.assist.ActivityId; +import android.app.assist.AssistContent; import android.content.ComponentName; import android.content.ContentCaptureOptions; import android.content.ContentResolver; import android.content.Context; +import android.content.Intent; import android.content.pm.ActivityPresentationInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; @@ -184,6 +189,9 @@ public class ContentCaptureManagerService extends @Nullable private boolean mDisabledByDeviceConfig; + @GuardedBy("mLock") + private boolean activityStartAssistDataEnabled; + // Device-config settings that are cached and passed back to apps @GuardedBy("mLock") int mDevCfgLoggingLevel; @@ -451,6 +459,8 @@ public class ContentCaptureManagerService extends case DEVICE_CONFIG_PROPERTY_CONTENT_PROTECTION_AUTO_DISCONNECT_TIMEOUT: setFineTuneParamsFromDeviceConfig(); return; + case DEVICE_CONFIG_ENABLE_ACTIVITY_START_ASSIST_CONTENT: + setActivityStartAssistDataEnabled(); default: Slog.i(TAG, "Ignoring change on " + key); } @@ -639,6 +649,15 @@ public class ContentCaptureManagerService extends final String enabled = DeviceConfig.getProperty(DeviceConfig.NAMESPACE_CONTENT_CAPTURE, ContentCaptureManager.DEVICE_CONFIG_PROPERTY_SERVICE_EXPLICITLY_ENABLED); setDisabledByDeviceConfig(enabled); + setActivityStartAssistDataEnabled(); + } + + private void setActivityStartAssistDataEnabled() { + synchronized (mLock) { + this.activityStartAssistDataEnabled = DeviceConfig.getBoolean( + DeviceConfig.NAMESPACE_CONTENT_CAPTURE, + DEVICE_CONFIG_ENABLE_ACTIVITY_START_ASSIST_CONTENT, false); + } } private void setDisabledByDeviceConfig(@Nullable String explicitlyEnabled) { @@ -908,6 +927,9 @@ public class ContentCaptureManagerService extends pw.print(prefix2); pw.print("contentProtectionAutoDisconnectTimeoutMs: "); pw.println(mDevCfgContentProtectionAutoDisconnectTimeoutMs); + pw.print(prefix2); + pw.print("activityStartAssistDataEnabled: "); + pw.println(activityStartAssistDataEnabled); pw.print(prefix); pw.println("Global Options:"); mGlobalContentCaptureOptions.dump(prefix2, pw); @@ -1327,6 +1349,33 @@ public class ContentCaptureManagerService extends } @Override + @SuppressWarnings("GuardedBy") + public boolean sendActivityStartAssistData(@UserIdInt int userId, + @NonNull IBinder activityToken, + @NonNull Intent intentData) { + synchronized (mLock) { + if (!activityStartAssistDataEnabled) { + return false; + } + Intent intent = new Intent(intentData); + intent.setFlags(intent.getFlags() & ~(Intent.FLAG_GRANT_WRITE_URI_PERMISSION + | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION)); + Bundle assistContentExtras = new Bundle(); + assistContentExtras.putBoolean(ASSIST_CONTENT_ACTIVITY_START_KEY, true); + AssistContent assistContent = new AssistContent(assistContentExtras); + assistContent.setDefaultIntent(intent); + + final Bundle activityAssistData = new Bundle(); + activityAssistData.putParcelable(ASSIST_KEY_CONTENT, assistContent); + final ContentCapturePerUserService service = peekServiceForUserLocked(userId); + if (service != null) { + return service.sendActivityAssistDataLocked(activityToken, activityAssistData); + } + } + return false; + } + + @Override public boolean sendActivityAssistData(@UserIdInt int userId, @NonNull IBinder activityToken, @NonNull Bundle data) { synchronized (mLock) { diff --git a/services/contextualsearch/java/com/android/server/contextualsearch/ContextualSearchManagerService.java b/services/contextualsearch/java/com/android/server/contextualsearch/ContextualSearchManagerService.java index f5db6e962d01..38bbfc4d9108 100644 --- a/services/contextualsearch/java/com/android/server/contextualsearch/ContextualSearchManagerService.java +++ b/services/contextualsearch/java/com/android/server/contextualsearch/ContextualSearchManagerService.java @@ -20,6 +20,7 @@ import static android.Manifest.permission.ACCESS_CONTEXTUAL_SEARCH; import static android.app.AppOpsManager.OP_ASSIST_SCREENSHOT; import static android.app.AppOpsManager.OP_ASSIST_STRUCTURE; import static android.content.Context.CONTEXTUAL_SEARCH_SERVICE; +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; @@ -46,6 +47,7 @@ import android.app.contextualsearch.ContextualSearchManager; import android.app.contextualsearch.ContextualSearchState; import android.app.contextualsearch.IContextualSearchCallback; import android.app.contextualsearch.IContextualSearchManager; +import android.app.contextualsearch.flags.Flags; import android.content.ComponentName; import android.content.Context; import android.content.Intent; @@ -90,6 +92,8 @@ public class ContextualSearchManagerService extends SystemService { private static final String TAG = ContextualSearchManagerService.class.getSimpleName(); private static final int MSG_RESET_TEMPORARY_PACKAGE = 0; private static final int MAX_TEMP_PACKAGE_DURATION_MS = 1_000 * 60 * 2; // 2 minutes + private static final int MSG_INVALIDATE_TOKEN = 1; + private static final int MAX_TOKEN_VALID_DURATION_MS = 1_000 * 60 * 10; // 10 minutes private final Context mContext; private final ActivityTaskManagerInternal mAtmInternal; @@ -144,6 +148,8 @@ public class ContextualSearchManagerService extends SystemService { private Handler mTemporaryHandler; @GuardedBy("this") private String mTemporaryPackage = null; + @GuardedBy("this") + private long mTokenValidDurationMs = MAX_TOKEN_VALID_DURATION_MS; @GuardedBy("mLock") private IContextualSearchCallback mStateCallback; @@ -211,6 +217,29 @@ public class ContextualSearchManagerService extends SystemService { } } + void resetTokenValidDurationMs() { + setTokenValidDurationMs(MAX_TOKEN_VALID_DURATION_MS); + } + + void setTokenValidDurationMs(int durationMs) { + synchronized (this) { + enforceOverridingPermission("setTokenValidDurationMs"); + if (durationMs > MAX_TOKEN_VALID_DURATION_MS) { + throw new IllegalArgumentException( + "Token max duration is " + MAX_TOKEN_VALID_DURATION_MS + " (called with " + + durationMs + ")"); + } + mTokenValidDurationMs = durationMs; + if (DEBUG_USER) Log.d(TAG, "mTokenValidDurationMs set to " + durationMs); + } + } + + private long getTokenValidDurationMs() { + synchronized (this) { + return mTokenValidDurationMs; + } + } + private Intent getResolvedLaunchIntent() { synchronized (this) { // If mTemporaryPackage is not null, use it to get the ContextualSearch intent. @@ -245,7 +274,7 @@ public class ContextualSearchManagerService extends SystemService { if (DEBUG_USER) Log.d(TAG, "Launch component: " + launchIntent.getComponent()); launchIntent.addFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_NO_ANIMATION - | FLAG_ACTIVITY_NO_USER_ACTION); + | FLAG_ACTIVITY_NO_USER_ACTION | FLAG_ACTIVITY_CLEAR_TASK); launchIntent.putExtra( ContextualSearchManager.EXTRA_INVOCATION_TIME_MS, SystemClock.uptimeMillis()); @@ -355,15 +384,51 @@ public class ContextualSearchManagerService extends SystemService { } private class ContextualSearchManagerStub extends IContextualSearchManager.Stub { + @GuardedBy("this") + private Handler mTokenHandler; private @Nullable CallbackToken mToken; + private void invalidateToken() { + synchronized (this) { + if (mTokenHandler != null) { + mTokenHandler.removeMessages(MSG_INVALIDATE_TOKEN); + mTokenHandler = null; + } + if (DEBUG_USER) Log.d(TAG, "mToken invalidated."); + mToken = null; + } + } + + private void issueToken() { + synchronized (this) { + mToken = new CallbackToken(); + if (mTokenHandler == null) { + mTokenHandler = new Handler(Looper.getMainLooper(), null, true) { + @Override + public void handleMessage(Message msg) { + if (msg.what == MSG_INVALIDATE_TOKEN) { + invalidateToken(); + } else { + Slog.wtf(TAG, "invalid token handler msg: " + msg); + } + } + }; + } else { + mTokenHandler.removeMessages(MSG_INVALIDATE_TOKEN); + } + mTokenHandler.sendEmptyMessageDelayed( + MSG_INVALIDATE_TOKEN, getTokenValidDurationMs()); + } + } + @Override public void startContextualSearch(int entrypoint) { synchronized (this) { if (DEBUG_USER) Log.d(TAG, "startContextualSearch"); enforcePermission("startContextualSearch"); mAssistDataRequester.cancel(); - mToken = new CallbackToken(); + // Creates a new CallbackToken at mToken and an expiration handler. + issueToken(); // We get the launch intent with the system server's identity because the system // server has READ_FRAME_BUFFER permission to get the screenshot and because only // the system server can invoke non-exported activities. @@ -396,7 +461,18 @@ public class ContextualSearchManagerService extends SystemService { } return; } - mToken = null; + invalidateToken(); + if (Flags.enableTokenRefresh()) { + issueToken(); + Bundle bundle = new Bundle(); + bundle.putParcelable(ContextualSearchManager.EXTRA_TOKEN, mToken); + try { + callback.onResult( + new ContextualSearchState(null, null, bundle)); + } catch (RemoteException e) { + Log.e(TAG, "Error invoking ContextualSearchCallback", e); + } + } synchronized (mLock) { mStateCallback = callback; } diff --git a/services/contextualsearch/java/com/android/server/contextualsearch/ContextualSearchManagerShellCommand.java b/services/contextualsearch/java/com/android/server/contextualsearch/ContextualSearchManagerShellCommand.java index 5777e1d154de..66a4e7b65aba 100644 --- a/services/contextualsearch/java/com/android/server/contextualsearch/ContextualSearchManagerShellCommand.java +++ b/services/contextualsearch/java/com/android/server/contextualsearch/ContextualSearchManagerShellCommand.java @@ -52,6 +52,19 @@ public class ContextualSearchManagerShellCommand extends ShellCommand { + packageName + " for " + duration + "ms"); break; } + case "token-duration": { + String durationStr = getNextArg(); + if (durationStr == null) { + mService.resetTokenValidDurationMs(); + pw.println("ContextualSearchManagerService token duration reset."); + return 0; + } + final int durationMs = Integer.parseInt(durationStr); + mService.setTokenValidDurationMs(durationMs); + pw.println("ContextualSearchManagerService temporarily set token duration" + + " to " + durationMs + "ms"); + break; + } } } break; @@ -72,6 +85,9 @@ public class ContextualSearchManagerShellCommand extends ShellCommand { pw.println(" Temporarily (for DURATION ms) changes the Contextual Search " + "implementation."); pw.println(" To reset, call without any arguments."); + pw.println(" set token-duration [DURATION]"); + pw.println(" Changes the Contextual Search token duration to DURATION ms."); + pw.println(" To reset, call without any arguments."); pw.println(""); } } diff --git a/services/core/Android.bp b/services/core/Android.bp index f1339e91d3d4..53730e317ac6 100644 --- a/services/core/Android.bp +++ b/services/core/Android.bp @@ -93,38 +93,6 @@ genrule { } genrule { - name: "checked-protolog.json", - srcs: [ - ":generate-protolog.json", - ":services.core.protolog.json", - ], - cmd: "cp $(location :generate-protolog.json) $(out) && " + - "{ ! (diff $(out) $(location :services.core.protolog.json) | grep -q '^<') || " + - "{ echo -e '\\n\\n################################################################\\n#\\n" + - "# ERROR: ProtoLog viewer config is stale. To update it, run:\\n#\\n" + - "# cp $${ANDROID_BUILD_TOP}/$(location :generate-protolog.json) " + - "$${ANDROID_BUILD_TOP}/$(location :services.core.protolog.json)\\n#\\n" + - "################################################################\\n\\n' >&2 && false; } }", - out: ["services.core.protolog.json"], -} - -genrule { - name: "checked-core.protolog.pb", - srcs: [ - ":gen-core.protolog.pb", - ":file-core.protolog.pb", - ], - cmd: "cp $(location :gen-core.protolog.pb) $(out) && " + - "{ ! (diff $(out) $(location :file-core.protolog.pb) | grep -q '^<') || " + - "{ echo -e '\\n\\n################################################################\\n#\\n" + - "# ERROR: ProtoLog viewer config is stale. To update it, run:\\n#\\n" + - "# cp $${ANDROID_BUILD_TOP}/$(location :gen-core.protolog.pb) " + - "$${ANDROID_BUILD_TOP}/$(location :file-core.protolog.pb)\\n#\\n" + - "################################################################\\n\\n' >&2 && false; } }", - out: ["core.protolog.pb"], -} - -genrule { name: "statslog-art-java-gen", tools: ["stats-log-api-gen"], cmd: "$(location stats-log-api-gen) --java $(out) --module art" + @@ -259,6 +227,7 @@ java_library_static { "connectivity_flags_lib", "dreams_flags_lib", "aconfig_new_storage_flags_lib", + "powerstats_flags_lib", ], javac_shard_size: 50, javacflags: [ @@ -303,7 +272,7 @@ prebuilt_etc { genrule { name: "services.core.json.gz", - srcs: [":checked-protolog.json"], + srcs: [":generate-protolog.json"], out: ["services.core.protolog.json.gz"], cmd: "gzip -c < $(in) > $(out)", } @@ -315,5 +284,5 @@ prebuilt_etc { prebuilt_etc { name: "core.protolog.pb", - src: ":checked-core.protolog.pb", + src: ":gen-core.protolog.pb", } diff --git a/services/core/java/android/content/pm/PackageManagerInternal.java b/services/core/java/android/content/pm/PackageManagerInternal.java index e64a87f3966b..43774bbc51ca 100644 --- a/services/core/java/android/content/pm/PackageManagerInternal.java +++ b/services/core/java/android/content/pm/PackageManagerInternal.java @@ -368,17 +368,17 @@ public abstract class PackageManagerInternal { Intent intent, @Nullable String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags, int filterCallingUid, int userId); - /** * Retrieve all receivers that can handle a broadcast of the given intent. + * * @param filterCallingUid The results will be filtered in the context of this UID instead * of the calling UID. - * @param forSend true if the invocation is intended for sending broadcasts. The value - * of this parameter affects how packages are filtered. + * @param forSend true if the invocation is intended for sending broadcasts. The value + * of this parameter affects how packages are filtered. */ - public abstract List<ResolveInfo> queryIntentReceivers(Intent intent, - String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags, - int filterCallingUid, int userId, boolean forSend); + public abstract List<ResolveInfo> queryIntentReceivers( + Intent intent, String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags, + int filterCallingUid, int callingPid, int userId, boolean forSend); /** * Retrieve all services that can be performed for the given intent. @@ -611,17 +611,9 @@ public abstract class PackageManagerInternal { @NonNull Set<String> outInvalidPackageNames); /** - * Resolves an activity intent, allowing instant apps to be resolved. - */ - public abstract ResolveInfo resolveIntent(Intent intent, String resolvedType, - @PackageManager.ResolveInfoFlagsBits long flags, - @PrivateResolveFlags long privateResolveFlags, int userId, boolean resolveForStart, - int filterCallingUid); - - /** * Resolves an exported activity intent, allowing instant apps to be resolved. */ - public abstract ResolveInfo resolveIntentExported(Intent intent, String resolvedType, + public abstract ResolveInfo resolveIntent(Intent intent, String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags, @PrivateResolveFlags long privateResolveFlags, int userId, boolean resolveForStart, int filterCallingUid, int callingPid); @@ -632,6 +624,15 @@ public abstract class PackageManagerInternal { public abstract ResolveInfo resolveService(Intent intent, String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags, int userId, int callingUid); + + /** + * Resolves a service intent for start. + */ + public abstract ResolveInfo resolveService( + Intent intent, String resolvedType, + @PackageManager.ResolveInfoFlagsBits long flags, int userId, + int callingUid, int callingPid); + /** * Resolves a content provider intent. */ diff --git a/services/core/java/com/android/server/OWNERS b/services/core/java/com/android/server/OWNERS index 2545620a2630..c4d38e46da3c 100644 --- a/services/core/java/com/android/server/OWNERS +++ b/services/core/java/com/android/server/OWNERS @@ -1,5 +1,5 @@ # BootReceiver / Watchdog -per-file BootReceiver.java,Watchdog.java = gaillard@google.com +per-file BootReceiver.java,Watchdog.java = benmiles@google.com # Connectivity / Networking per-file ConnectivityService.java,ConnectivityServiceInitializer.java,NetworkManagementService.java,NsdService.java,VpnManagerService.java = file:/services/core/java/com/android/server/net/OWNERS diff --git a/services/core/java/com/android/server/PackageWatchdog.java b/services/core/java/com/android/server/PackageWatchdog.java index a61925732256..eb037095c6c9 100644 --- a/services/core/java/com/android/server/PackageWatchdog.java +++ b/services/core/java/com/android/server/PackageWatchdog.java @@ -138,6 +138,12 @@ public class PackageWatchdog { static final long DEFAULT_BOOT_LOOP_TRIGGER_WINDOW_MS = TimeUnit.MINUTES.toMillis(10); + // Time needed to apply mitigation + private static final String MITIGATION_WINDOW_MS = + "persist.device_config.configuration.mitigation_window_ms"; + @VisibleForTesting + static final long DEFAULT_MITIGATION_WINDOW_MS = TimeUnit.SECONDS.toMillis(5); + // Threshold level at which or above user might experience significant disruption. private static final String MAJOR_USER_IMPACT_LEVEL_THRESHOLD = "persist.device_config.configuration.major_user_impact_level_threshold"; @@ -210,6 +216,9 @@ public class PackageWatchdog { @GuardedBy("mLock") private boolean mSyncRequired = false; + @GuardedBy("mLock") + private long mLastMitigation = -1000000; + @FunctionalInterface @VisibleForTesting interface SystemClock { @@ -400,6 +409,16 @@ public class PackageWatchdog { Slog.w(TAG, "Could not resolve a list of failing packages"); return; } + synchronized (mLock) { + final long now = mSystemClock.uptimeMillis(); + if (Flags.recoverabilityDetection()) { + if (now >= mLastMitigation + && (now - mLastMitigation) < getMitigationWindowMs()) { + Slog.i(TAG, "Skipping onPackageFailure mitigation"); + return; + } + } + } mLongTaskHandler.post(() -> { synchronized (mLock) { if (mAllObservers.isEmpty()) { @@ -500,10 +519,17 @@ public class PackageWatchdog { int currentObserverImpact, int mitigationCount) { if (currentObserverImpact < getUserImpactLevelLimit()) { + synchronized (mLock) { + mLastMitigation = mSystemClock.uptimeMillis(); + } currentObserverToNotify.execute(versionedPackage, failureReason, mitigationCount); } } + private long getMitigationWindowMs() { + return SystemProperties.getLong(MITIGATION_WINDOW_MS, DEFAULT_MITIGATION_WINDOW_MS); + } + /** * Called when the system server boots. If the system server is detected to be in a boot loop, @@ -637,6 +663,7 @@ public class PackageWatchdog { PackageHealthObserverImpact.USER_IMPACT_LEVEL_10, PackageHealthObserverImpact.USER_IMPACT_LEVEL_20, PackageHealthObserverImpact.USER_IMPACT_LEVEL_30, + PackageHealthObserverImpact.USER_IMPACT_LEVEL_40, PackageHealthObserverImpact.USER_IMPACT_LEVEL_50, PackageHealthObserverImpact.USER_IMPACT_LEVEL_70, PackageHealthObserverImpact.USER_IMPACT_LEVEL_71, @@ -652,6 +679,7 @@ public class PackageWatchdog { /* Actions having medium user impact, user of a device will likely notice. */ int USER_IMPACT_LEVEL_20 = 20; int USER_IMPACT_LEVEL_30 = 30; + int USER_IMPACT_LEVEL_40 = 40; int USER_IMPACT_LEVEL_50 = 50; int USER_IMPACT_LEVEL_70 = 70; /* Action has high user impact, a last resort, user of a device will be very frustrated. */ diff --git a/services/core/java/com/android/server/RescueParty.java b/services/core/java/com/android/server/RescueParty.java index 37c2d263d14f..189b2495c1b1 100644 --- a/services/core/java/com/android/server/RescueParty.java +++ b/services/core/java/com/android/server/RescueParty.java @@ -673,7 +673,7 @@ public class RescueParty { case RESCUE_LEVEL_SCOPED_DEVICE_CONFIG_RESET: return PackageHealthObserverImpact.USER_IMPACT_LEVEL_10; case RESCUE_LEVEL_ALL_DEVICE_CONFIG_RESET: - return PackageHealthObserverImpact.USER_IMPACT_LEVEL_20; + return PackageHealthObserverImpact.USER_IMPACT_LEVEL_40; case RESCUE_LEVEL_WARM_REBOOT: return PackageHealthObserverImpact.USER_IMPACT_LEVEL_50; case RESCUE_LEVEL_RESET_SETTINGS_UNTRUSTED_DEFAULTS: diff --git a/services/core/java/com/android/server/SystemConfig.java b/services/core/java/com/android/server/SystemConfig.java index 8c1bb3b0cb71..44aea15b2bde 100644 --- a/services/core/java/com/android/server/SystemConfig.java +++ b/services/core/java/com/android/server/SystemConfig.java @@ -352,7 +352,7 @@ public class SystemConfig { @NonNull private final Set<String> mInitialNonStoppedSystemPackages = new ArraySet<>(); // Which packages (key) are allowed to join particular SharedUid (value). - @NonNull private final Map<String, String> mPackageToSharedUidAllowList = new ArrayMap<>(); + @NonNull private final ArrayMap<String, String> mPackageToSharedUidAllowList = new ArrayMap<>(); // A map of preloaded package names and the path to its app metadata file path. private final ArrayMap<String, String> mAppMetadataFilePaths = new ArrayMap<>(); @@ -574,7 +574,7 @@ public class SystemConfig { } @NonNull - public Map<String, String> getPackageToSharedUidAllowList() { + public ArrayMap<String, String> getPackageToSharedUidAllowList() { return mPackageToSharedUidAllowList; } @@ -720,6 +720,9 @@ public class SystemConfig { } // Read configuration of features, libs and priv-app permissions from apex module. int apexPermissionFlag = ALLOW_LIBS | ALLOW_FEATURES | ALLOW_PRIVAPP_PERMISSIONS; + if (android.permission.flags.Flags.apexSignaturePermissionAllowlistEnabled()) { + apexPermissionFlag |= ALLOW_SIGNATURE_PERMISSIONS; + } // TODO: Use a solid way to filter apex module folders? for (File f: FileUtils.listFilesOrEmpty(Environment.getApexDirectory())) { if (f.isFile() || f.getPath().contains("@")) { @@ -1322,6 +1325,8 @@ public class SystemConfig { Environment.getProductDirectory().toPath() + "/"); boolean systemExt = permFile.toPath().startsWith( Environment.getSystemExtDirectory().toPath() + "/"); + boolean apex = permFile.toPath().startsWith( + Environment.getApexDirectory().toPath() + "/"); if (vendor) { readSignatureAppPermissions(parser, mPermissionAllowlist.getVendorSignatureAppAllowlist()); @@ -1331,6 +1336,9 @@ public class SystemConfig { } else if (systemExt) { readSignatureAppPermissions(parser, mPermissionAllowlist.getSystemExtSignatureAppAllowlist()); + } else if (apex) { + readSignatureAppPermissions(parser, + mPermissionAllowlist.getApexSignatureAppAllowlist()); } else { readSignatureAppPermissions(parser, mPermissionAllowlist.getSignatureAppAllowlist()); diff --git a/services/core/java/com/android/server/UiModeManagerService.java b/services/core/java/com/android/server/UiModeManagerService.java index f1d358486333..1c13ad5b3ceb 100644 --- a/services/core/java/com/android/server/UiModeManagerService.java +++ b/services/core/java/com/android/server/UiModeManagerService.java @@ -32,6 +32,7 @@ import static android.app.UiModeManager.PROJECTION_TYPE_AUTOMOTIVE; import static android.app.UiModeManager.PROJECTION_TYPE_NONE; import static android.os.UserHandle.USER_SYSTEM; import static android.os.UserHandle.getCallingUserId; +import static android.os.UserManager.isVisibleBackgroundUsersEnabled; import static android.provider.Settings.Secure.CONTRAST_LEVEL; import static android.util.TimeUtils.isTimeBetween; @@ -99,6 +100,7 @@ import com.android.internal.app.DisableCarModeActivity; import com.android.internal.messages.nano.SystemMessageProto.SystemMessage; import com.android.internal.notification.SystemNotificationChannels; import com.android.internal.util.DumpUtils; +import com.android.server.pm.UserManagerService; import com.android.server.twilight.TwilightListener; import com.android.server.twilight.TwilightManager; import com.android.server.twilight.TwilightState; @@ -848,6 +850,8 @@ final class UiModeManagerService extends SystemService { } final int user = UserHandle.getCallingUserId(); + enforceValidCallingUser(user); + final long ident = Binder.clearCallingIdentity(); try { synchronized (mLock) { @@ -910,6 +914,8 @@ final class UiModeManagerService extends SystemService { @AttentionModeThemeOverlayType int attentionModeThemeOverlayType) { setAttentionModeThemeOverlay_enforcePermission(); + enforceValidCallingUser(UserHandle.getCallingUserId()); + synchronized (mLock) { if (mAttentionModeThemeOverlay != attentionModeThemeOverlayType) { mAttentionModeThemeOverlay = attentionModeThemeOverlayType; @@ -999,6 +1005,8 @@ final class UiModeManagerService extends SystemService { return false; } final int user = Binder.getCallingUserHandle().getIdentifier(); + enforceValidCallingUser(user); + if (user != mCurrentUser && getContext().checkCallingOrSelfPermission( android.Manifest.permission.INTERACT_ACROSS_USERS) != PackageManager.PERMISSION_GRANTED) { @@ -1056,6 +1064,8 @@ final class UiModeManagerService extends SystemService { return; } final int user = UserHandle.getCallingUserId(); + enforceValidCallingUser(user); + final long ident = Binder.clearCallingIdentity(); try { LocalTime newTime = LocalTime.ofNanoOfDay(time * 1000); @@ -1084,6 +1094,8 @@ final class UiModeManagerService extends SystemService { return; } final int user = UserHandle.getCallingUserId(); + enforceValidCallingUser(user); + final long ident = Binder.clearCallingIdentity(); try { LocalTime newTime = LocalTime.ofNanoOfDay(time * 1000); @@ -1104,6 +1116,8 @@ final class UiModeManagerService extends SystemService { assertLegit(callingPackage); assertSingleProjectionType(projectionType); enforceProjectionTypePermissions(projectionType); + enforceValidCallingUser(getCallingUserId()); + synchronized (mLock) { if (mProjectionHolders == null) { mProjectionHolders = new SparseArray<>(1); @@ -1148,6 +1162,8 @@ final class UiModeManagerService extends SystemService { assertLegit(callingPackage); assertSingleProjectionType(projectionType); enforceProjectionTypePermissions(projectionType); + enforceValidCallingUser(getCallingUserId()); + return releaseProjectionUnchecked(projectionType, callingPackage); } @@ -1187,6 +1203,9 @@ final class UiModeManagerService extends SystemService { if (projectionType == PROJECTION_TYPE_NONE) { return; } + + enforceValidCallingUser(getCallingUserId()); + synchronized (mLock) { if (mProjectionListeners == null) { mProjectionListeners = new SparseArray<>(1); @@ -1234,6 +1253,32 @@ final class UiModeManagerService extends SystemService { } }; + // This method validates whether calling user is valid in visible background users + // feature. Valid user is the current user or the system or in the same profile group as + // the current user. + private void enforceValidCallingUser(int userId) { + if (!isVisibleBackgroundUsersEnabled()) { + return; + } + if (LOG) { + Slog.d(TAG, "enforceValidCallingUser: userId=" + userId + + " isSystemUser=" + (userId == USER_SYSTEM) + " current user=" + mCurrentUser + + " callingPid=" + Binder.getCallingPid() + + " callingUid=" + mInjector.getCallingUid()); + } + long ident = Binder.clearCallingIdentity(); + try { + if (userId != USER_SYSTEM && userId != mCurrentUser + && !UserManagerService.getInstance().isSameProfileGroup(userId, mCurrentUser)) { + throw new SecurityException( + "Calling user is not valid for level-1 compatibility in MUMD. " + + "callingUserId=" + userId + " currentUserId=" + mCurrentUser); + } + } finally { + Binder.restoreCallingIdentity(ident); + } + } + private void enforceProjectionTypePermissions(@UiModeManager.ProjectionType int p) { if ((p & PROJECTION_TYPE_AUTOMOTIVE) != 0) { getContext().enforceCallingPermission( diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java index 51da7f175bb9..e424ffa4afb1 100644 --- a/services/core/java/com/android/server/am/ActiveServices.java +++ b/services/core/java/com/android/server/am/ActiveServices.java @@ -830,7 +830,8 @@ public final class ActiveServices { for (int i = 0; i < smap.mServicesByInstanceName.size(); i++) { final ServiceRecord sr = smap.mServicesByInstanceName.valueAt(i); if (sr.appInfo.packageName.equals(pkg) && sr.isForeground) { - if (Objects.equals(sr.foregroundNoti.getChannelId(), channelId)) { + if (sr.foregroundNoti != null + && Objects.equals(sr.foregroundNoti.getChannelId(), channelId)) { if (DEBUG_FOREGROUND_SERVICE) { Slog.d(TAG_SERVICE, "Channel u" + userId + "/pkg=" + pkg + "/channelId=" + channelId @@ -2452,11 +2453,9 @@ public final class ActiveServices { + " foreground service type " + ServiceInfo.foregroundServiceTypeToLabel( foregroundServiceType); - // Only throw an exception if the new ANR behavior - // ("do nothing") is not gated or the new crashing logic gate + // Only throw an exception if the new crashing logic gate // is enabled; otherwise, reset the limit temporarily. - if (!android.app.Flags.gateFgsTimeoutAnrBehavior() - || android.app.Flags.enableFgsTimeoutCrashBehavior()) { + if (android.app.Flags.enableFgsTimeoutCrashBehavior()) { throw new ForegroundServiceStartNotAllowedException( exceptionMsg); } else { @@ -3893,9 +3892,10 @@ public final class ActiveServices { return; } - final long lastTopTime = sr.app.mState.getLastTopTime(); - final long constantTimeLimit = getTimeLimitForFgsType(fgsType); + final boolean currentlyTop = sr.app.mState.getCurProcState() <= PROCESS_STATE_TOP; final long nowUptime = SystemClock.uptimeMillis(); + final long lastTopTime = currentlyTop ? nowUptime : sr.app.mState.getLastTopTime(); + final long constantTimeLimit = getTimeLimitForFgsType(fgsType); if (lastTopTime != Long.MIN_VALUE && constantTimeLimit > (nowUptime - lastTopTime)) { // Discard any other messages for this service mAm.mHandler.removeMessages(ActivityManagerService.SERVICE_FGS_TIMEOUT_MSG, sr); @@ -3956,21 +3956,12 @@ public final class ActiveServices { // stop the service, decrement the number of parallel running services here. fgsTypeInfo.decNumParallelServices(); } - } - - final String reason = "A foreground service of type " - + ServiceInfo.foregroundServiceTypeToLabel(fgsType) - + " did not stop within its timeout: " + sr.getComponentName(); - if (android.app.Flags.gateFgsTimeoutAnrBehavior() - || !android.app.Flags.enableFgsTimeoutCrashBehavior()) { - // Log a WTF instead of crashing the app while the new behavior is gated. - Slog.wtf(TAG, reason); - return; - } - if (android.app.Flags.enableFgsTimeoutCrashBehavior()) { - // Crash the app - synchronized (mAm) { + final String reason = "A foreground service of type " + + ServiceInfo.foregroundServiceTypeToLabel(fgsType) + + " did not stop within its timeout: " + sr.getComponentName(); + if (android.app.Flags.enableFgsTimeoutCrashBehavior()) { + // Crash the app Slog.e(TAG_SERVICE, "FGS Crashed: " + sr); traceInstant("FGS Crash: ", sr); if (sr.app != null) { @@ -3980,6 +3971,9 @@ public final class ActiveServices { ForegroundServiceDidNotStopInTimeException .createExtrasForService(sr.getComponentName())); } + } else { + // Log a WTF instead of crashing the app while the new behavior is gated. + Slog.wtf(TAG, reason); } } } @@ -4870,7 +4864,7 @@ public final class ActiveServices { } // TODO: come back and remove this assumption to triage all services ResolveInfo rInfo = mAm.getPackageManagerInternal().resolveService(service, - resolvedType, flags, userId, callingUid); + resolvedType, flags, userId, callingUid, callingPid); ServiceInfo sInfo = rInfo != null ? rInfo.serviceInfo : null; if (sInfo == null) { Slog.w(TAG_SERVICE, "Unable to start service " + service + " U=" + userId + @@ -4990,7 +4984,7 @@ public final class ActiveServices { try { ResolveInfo rInfoForUserId0 = mAm.getPackageManagerInternal().resolveService(service, - resolvedType, flags, userId, callingUid); + resolvedType, flags, userId, callingUid, callingPid); if (rInfoForUserId0 == null) { Slog.w(TAG_SERVICE, "Unable to resolve service " + service + " U=" + userId @@ -6297,7 +6291,7 @@ public final class ActiveServices { final ComponentName clientSideComponentName = cr.aliasComponent != null ? cr.aliasComponent : r.name; try { - cr.conn.connected(r.name, null, true); + cr.conn.connected(clientSideComponentName, null, true); } catch (Exception e) { Slog.w(TAG, "Failure disconnecting service " + r.shortInstanceName + " to connection " + c.get(i).conn.asBinder() diff --git a/services/core/java/com/android/server/am/ActivityManagerConstants.java b/services/core/java/com/android/server/am/ActivityManagerConstants.java index 26aa0535d43e..f5a297bfd4f5 100644 --- a/services/core/java/com/android/server/am/ActivityManagerConstants.java +++ b/services/core/java/com/android/server/am/ActivityManagerConstants.java @@ -169,6 +169,17 @@ final class ActivityManagerConstants extends ContentObserver { */ static final String KEY_ENABLE_NEW_OOMADJ = "enable_new_oom_adj"; + /** + * Whether or not to enable the batching of OOM adjuster calls to LMKD + */ + static final String KEY_ENABLE_BATCHING_OOM_ADJ = "enable_batching_oom_adj"; + + /** + * How long to wait before scheduling another follow-up oomAdjuster update for time based state. + */ + static final String KEY_FOLLOW_UP_OOMADJ_UPDATE_WAIT_DURATION = + "follow_up_oomadj_update_wait_duration"; + private static final int DEFAULT_MAX_CACHED_PROCESSES = 1024; private static final boolean DEFAULT_PRIORITIZE_ALARM_BROADCASTS = true; private static final long DEFAULT_FGSERVICE_MIN_SHOWN_TIME = 2*1000; @@ -231,7 +242,7 @@ final class ActivityManagerConstants extends ContentObserver { static final long DEFAULT_BACKGROUND_SETTLE_TIME = 60 * 1000; static final long DEFAULT_KILL_BG_RESTRICTED_CACHED_IDLE_SETTLE_TIME_MS = 60 * 1000; - static final boolean DEFAULT_KILL_BG_RESTRICTED_CACHED_IDLE = true; + static final boolean DEFAULT_KILL_BG_RESTRICTED_CACHED_IDLE = false; static final int DEFAULT_MAX_SERVICE_CONNECTIONS_PER_PROCESS = 3000; @@ -244,6 +255,16 @@ final class ActivityManagerConstants extends ContentObserver { private static final boolean DEFAULT_ENABLE_NEW_OOM_ADJ = Flags.oomadjusterCorrectnessRewrite(); /** + * The default value to {@link #KEY_ENABLE_BATCHING_OOM_ADJ}. + */ + private static final boolean DEFAULT_ENABLE_BATCHING_OOM_ADJ = Flags.batchingOomAdj(); + + /** + * The default value to {@link #KEY_FOLLOW_UP_OOMADJ_UPDATE_WAIT_DURATION}. + */ + private static final long DEFAULT_FOLLOW_UP_OOMADJ_UPDATE_WAIT_DURATION = 1000L; + + /** * Same as {@link TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_NOT_ALLOWED} */ private static final int @@ -1136,6 +1157,13 @@ final class ActivityManagerConstants extends ContentObserver { /** @see #KEY_ENABLE_NEW_OOMADJ */ public boolean ENABLE_NEW_OOMADJ = DEFAULT_ENABLE_NEW_OOM_ADJ; + /** @see #KEY_ENABLE_BATCHING_OOM_ADJ */ + public boolean ENABLE_BATCHING_OOM_ADJ = DEFAULT_ENABLE_BATCHING_OOM_ADJ; + + /** @see #KEY_FOLLOW_UP_OOMADJ_UPDATE_WAIT_DURATION */ + public long FOLLOW_UP_OOMADJ_UPDATE_WAIT_DURATION = + DEFAULT_FOLLOW_UP_OOMADJ_UPDATE_WAIT_DURATION; + /** * Indicates whether PSS profiling in AppProfiler is disabled or not. */ @@ -1346,6 +1374,9 @@ final class ActivityManagerConstants extends ContentObserver { case KEY_PROC_STATE_DEBUG_UIDS: updateProcStateDebugUids(); break; + case KEY_FOLLOW_UP_OOMADJ_UPDATE_WAIT_DURATION: + updateFollowUpOomAdjUpdateWaitDuration(); + break; default: updateFGSPermissionEnforcementFlagsIfNecessary(name); break; @@ -1479,6 +1510,8 @@ final class ActivityManagerConstants extends ContentObserver { private void loadNativeBootDeviceConfigConstants() { ENABLE_NEW_OOMADJ = getDeviceConfigBoolean(KEY_ENABLE_NEW_OOMADJ, DEFAULT_ENABLE_NEW_OOM_ADJ); + ENABLE_BATCHING_OOM_ADJ = getDeviceConfigBoolean(KEY_ENABLE_BATCHING_OOM_ADJ, + DEFAULT_ENABLE_BATCHING_OOM_ADJ); } public void setOverrideMaxCachedProcesses(int value) { @@ -2231,6 +2264,13 @@ final class ActivityManagerConstants extends ContentObserver { DEFAULT_ENABLE_NEW_OOM_ADJ); } + private void updateFollowUpOomAdjUpdateWaitDuration() { + FOLLOW_UP_OOMADJ_UPDATE_WAIT_DURATION = DeviceConfig.getLong( + DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, + KEY_FOLLOW_UP_OOMADJ_UPDATE_WAIT_DURATION, + DEFAULT_FOLLOW_UP_OOMADJ_UPDATE_WAIT_DURATION); + } + private void updateFGSPermissionEnforcementFlagsIfNecessary(@NonNull String name) { ForegroundServiceTypePolicy.getDefaultPolicy() .updatePermissionEnforcementFlagIfNecessary(name); @@ -2248,6 +2288,13 @@ final class ActivityManagerConstants extends ContentObserver { mDefaultPssToRssThresholdModifier); } + private void updateEnableBatchingOomAdj() { + ENABLE_BATCHING_OOM_ADJ = DeviceConfig.getBoolean( + DeviceConfig.NAMESPACE_ACTIVITY_MANAGER_NATIVE_BOOT, + KEY_ENABLE_BATCHING_OOM_ADJ, + DEFAULT_ENABLE_BATCHING_OOM_ADJ); + } + boolean shouldDebugUidForProcState(int uid) { SparseBooleanArray ar = mProcStateDebugUids; final var size = ar.size(); @@ -2476,6 +2523,9 @@ final class ActivityManagerConstants extends ContentObserver { pw.print(" "); pw.print(KEY_MAX_PREVIOUS_TIME); pw.print("="); pw.println(MAX_PREVIOUS_TIME); + pw.print(" "); pw.print(KEY_ENABLE_BATCHING_OOM_ADJ); + pw.print("="); pw.println(ENABLE_BATCHING_OOM_ADJ); + pw.println(); if (mOverrideMaxCachedProcesses >= 0) { pw.print(" mOverrideMaxCachedProcesses="); pw.println(mOverrideMaxCachedProcesses); @@ -2489,6 +2539,9 @@ final class ActivityManagerConstants extends ContentObserver { pw.print(" ENABLE_WAIT_FOR_FINISH_ATTACH_APPLICATION="); pw.println(mEnableWaitForFinishAttachApplication); + pw.print(" "); pw.print(KEY_FOLLOW_UP_OOMADJ_UPDATE_WAIT_DURATION); + pw.print("="); pw.println(FOLLOW_UP_OOMADJ_UPDATE_WAIT_DURATION); + synchronized (mProcStateDebugUids) { pw.print(" "); pw.print(KEY_PROC_STATE_DEBUG_UIDS); pw.print("="); pw.println(mProcStateDebugUids); diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index f7278e9f986d..d750065ee96f 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -137,7 +137,6 @@ import static android.util.FeatureFlagUtils.SETTINGS_ENABLE_MONITOR_PHANTOM_PROC import static android.view.Display.INVALID_DISPLAY; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_CONFIGURATION; -import static com.android.internal.util.FrameworkStatsLog.UNSAFE_INTENT_EVENT_REPORTED__EVENT_TYPE__INTERNAL_NON_EXPORTED_COMPONENT_MATCH; import static com.android.internal.util.FrameworkStatsLog.UNSAFE_INTENT_EVENT_REPORTED__EVENT_TYPE__NEW_MUTABLE_IMPLICIT_PENDING_INTENT_RETRIEVED; import static com.android.sdksandbox.flags.Flags.sdkSandboxInstrumentationInfo; import static com.android.server.am.ActiveServices.FGS_SAW_RESTRICTIONS; @@ -268,9 +267,7 @@ import android.app.usage.UsageStatsManagerInternal; import android.appwidget.AppWidgetManager; import android.appwidget.AppWidgetManagerInternal; import android.compat.annotation.ChangeId; -import android.compat.annotation.EnabledAfter; import android.compat.annotation.EnabledSince; -import android.compat.annotation.Overridable; import android.content.AttributionSource; import android.content.AutofillOptions; import android.content.BroadcastReceiver; @@ -467,7 +464,7 @@ import com.android.server.net.NetworkManagementInternal; import com.android.server.os.NativeTombstoneManager; import com.android.server.pm.Computer; import com.android.server.pm.Installer; -import com.android.server.pm.PackageManagerServiceUtils; +import com.android.server.pm.SaferIntentUtils; import com.android.server.pm.UserManagerInternal; import com.android.server.pm.permission.PermissionManagerServiceInternal; import com.android.server.pm.pkg.AndroidPackage; @@ -666,18 +663,6 @@ public class ActivityManagerService extends IActivityManager.Stub private static final long DYNAMIC_RECEIVER_EXPLICIT_EXPORT_REQUIRED = 161145287L; /** - * Apps targeting Android U and above will need to export components in order to invoke them - * through implicit intents. - * - * If a component is not exported and invoked, it will be removed from the list of receivers. - * This applies specifically to activities and broadcasts. - */ - @ChangeId - @Overridable - @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.TIRAMISU) - public static final long IMPLICIT_INTENTS_ONLY_MATCH_EXPORTED_COMPONENTS = 229362273; - - /** * The maximum number of bytes that {@link #setProcessStateSummary} accepts. * * @see {@link android.app.ActivityManager#setProcessStateSummary(byte[])} @@ -1678,6 +1663,7 @@ public class ActivityManagerService extends IActivityManager.Stub static final int BIND_APPLICATION_TIMEOUT_HARD_MSG = 83; static final int SERVICE_FGS_TIMEOUT_MSG = 84; static final int SERVICE_FGS_CRASH_TIMEOUT_MSG = 85; + static final int FOLLOW_UP_OOMADJUSTER_UPDATE_MSG = 86; static final int FIRST_BROADCAST_QUEUE_MSG = 200; @@ -2051,6 +2037,9 @@ public class ActivityManagerService extends IActivityManager.Stub case SERVICE_FGS_CRASH_TIMEOUT_MSG: { mServices.onFgsCrashTimeout((ServiceRecord) msg.obj); } break; + case FOLLOW_UP_OOMADJUSTER_UPDATE_MSG: { + handleFollowUpOomAdjusterUpdate(); + } break; } } } @@ -3948,11 +3937,28 @@ public class ActivityManagerService extends IActivityManager.Stub + packageName + ": " + e); } if (mUserController.isUserRunning(user, userRunningFlags)) { + + String description; + if (reason == null) { + description = "from pid " + callingPid; + + // Add the name of the process if it's available + final ProcessRecord callerApp; + synchronized (mPidsSelfLocked) { + callerApp = mPidsSelfLocked.get(callingPid); + } + if (callerApp != null) { + description += " (" + callerApp.processName + ")"; + } + } else { + description = reason; + } + forceStopPackageLocked(packageName, UserHandle.getAppId(pkgUid), false /* callerWillRestart */, false /* purgeCache */, true /* doIt */, false /* evenPersistent */, - false /* uninstalling */, true /* packageStateStopped */, user, - reason == null ? ("from pid " + callingPid) : reason); + false /* uninstalling */, true /* packageStateStopped */, + user, description); finishForceStopPackageLocked(packageName, pkgUid); } } @@ -5101,6 +5107,15 @@ public class ActivityManagerService extends IActivityManager.Stub mAnrHelper.appNotResponding(app, TimeoutRecord.forAppStart(anrMessage)); } + private void handleFollowUpOomAdjusterUpdate() { + // Remove any existing duplicate messages on the handler here while no lock is being held. + // If another follow up update is needed, it will be scheduled by OomAdjuster. + mHandler.removeMessages(FOLLOW_UP_OOMADJUSTER_UPDATE_MSG); + synchronized (this) { + mOomAdjuster.updateOomAdjFollowUpTargetsLocked(); + } + } + /** * @return The last part of the string of an intent's action. */ @@ -5551,9 +5566,10 @@ public class ActivityManagerService extends IActivityManager.Stub packageName, UserHandle.of(userId)); String resolvedType = resolvedTypes == null || i >= resolvedTypes.length ? null : resolvedTypes[i]; - ActivityManagerUtils.logUnsafeIntentEvent( + SaferIntentUtils.reportUnsafeIntentEvent( UNSAFE_INTENT_EVENT_REPORTED__EVENT_TYPE__NEW_MUTABLE_IMPLICIT_PENDING_INTENT_RETRIEVED, - owningUid, intent, resolvedType, isChangeEnabled); + owningUid, Process.INVALID_PID, + intent, resolvedType, isChangeEnabled); if (isChangeEnabled) { String msg = packageName + ": Targeting U+ (version " + Build.VERSION_CODES.UPSIDE_DOWN_CAKE + " and above) disallows" @@ -5813,7 +5829,7 @@ public class ActivityManagerService extends IActivityManager.Stub intent, matchFlags, uid, userId)); case ActivityManager.INTENT_SENDER_BROADCAST: return new ParceledListSlice<>(mPackageManagerInt.queryIntentReceivers( - intent, resolvedType, matchFlags, uid, userId, false)); + intent, resolvedType, matchFlags, uid, Process.INVALID_PID, userId, false)); default: // ActivityManager.INTENT_SENDER_ACTIVITY_RESULT throw new IllegalStateException("Unsupported intent sender type: " + res.key.type); } @@ -9521,14 +9537,13 @@ public class ActivityManagerService extends IActivityManager.Stub * @param callback The binder used to communicate the violations. */ @Override - public void registerStrictModeCallback(IBinder callback) { + public synchronized void registerStrictModeCallback(IBinder callback) { int callingPid = Binder.getCallingPid(); mStrictModeCallbacks.put(callingPid, IUnsafeIntentStrictModeCallback.Stub.asInterface(callback)); try { - callback.linkToDeath(new DeathRecipient() { - @Override - public void binderDied() { + callback.linkToDeath(() -> { + synchronized (ActivityManagerService.this) { mStrictModeCallbacks.remove(callingPid); } }, 0); @@ -10222,6 +10237,19 @@ public class ActivityManagerService extends IActivityManager.Stub addStartInfoTimestampInternal(key, timestampNs, userId, callingUid); } + @Override + public void reportStartInfoViewTimestamps(long renderThreadDrawStartTimeNs, + long framePresentedTimeNs) { + int callingUid = Binder.getCallingUid(); + int userId = UserHandle.getUserId(callingUid); + addStartInfoTimestampInternal( + ApplicationStartInfo.START_TIMESTAMP_INITIAL_RENDERTHREAD_FRAME, + renderThreadDrawStartTimeNs, userId, callingUid); + addStartInfoTimestampInternal( + ApplicationStartInfo.START_TIMESTAMP_SURFACEFLINGER_COMPOSITION_COMPLETE, + framePresentedTimeNs, userId, callingUid); + } + private void addStartInfoTimestampInternal(int key, long timestampNs, int userId, int uid) { mProcessList.getAppStartInfoTracker().addTimestampToStart( Settings.getPackageNameForUid(mContext, uid), @@ -10439,11 +10467,6 @@ public class ActivityManagerService extends IActivityManager.Stub public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err, String[] args, ShellCallback callback, ResultReceiver resultReceiver) { - final int callingUid = Binder.getCallingUid(); - if (callingUid != ROOT_UID && callingUid != Process.SHELL_UID) { - resultReceiver.send(-1, null); - throw new SecurityException("Shell commands are only callable by root or shell"); - } (new ActivityManagerShellCommand(this, false)).exec( this, in, out, err, args, callback, resultReceiver); } @@ -13731,64 +13754,6 @@ public class ActivityManagerService extends IActivityManager.Stub } /** - * Filters out non-exported components in a given list of broadcast filters - * @param intent the original intent - * @param callingUid the calling UID - * @param query the list of broadcast filters - * @param platformCompat the instance of platform compat - */ - private void filterNonExportedComponents(Intent intent, int callingUid, int callingPid, - List query, PlatformCompat platformCompat, String callerPackage, String resolvedType) { - if (query == null - || intent.getPackage() != null - || intent.getComponent() != null - || ActivityManager.canAccessUnexportedComponents(callingUid)) { - return; - } - IUnsafeIntentStrictModeCallback callback = mStrictModeCallbacks.get(callingPid); - for (int i = query.size() - 1; i >= 0; i--) { - String componentInfo; - ResolveInfo resolveInfo; - BroadcastFilter broadcastFilter; - if (query.get(i) instanceof ResolveInfo) { - resolveInfo = (ResolveInfo) query.get(i); - if (resolveInfo.getComponentInfo().exported) { - continue; - } - componentInfo = resolveInfo.getComponentInfo() - .getComponentName().flattenToShortString(); - } else if (query.get(i) instanceof BroadcastFilter) { - broadcastFilter = (BroadcastFilter) query.get(i); - if (broadcastFilter.exported) { - continue; - } - componentInfo = broadcastFilter.packageName; - } else { - continue; - } - if (callback != null) { - mHandler.post(() -> { - try { - callback.onImplicitIntentMatchedInternalComponent(intent.cloneFilter()); - } catch (RemoteException e) { - mStrictModeCallbacks.remove(callingPid); - } - }); - } - boolean hasToBeExportedToMatch = platformCompat.isChangeEnabledByUid( - ActivityManagerService.IMPLICIT_INTENTS_ONLY_MATCH_EXPORTED_COMPONENTS, - callingUid); - ActivityManagerUtils.logUnsafeIntentEvent( - UNSAFE_INTENT_EVENT_REPORTED__EVENT_TYPE__INTERNAL_NON_EXPORTED_COMPONENT_MATCH, - callingUid, intent, resolvedType, hasToBeExportedToMatch); - if (!hasToBeExportedToMatch) { - return; - } - query.remove(i); - } - } - - /** * Main code for cleaning up a process when it has gone away. This is * called both as a result of the process dying, or directly when stopping * a process when running in single process mode. @@ -15101,8 +15066,9 @@ public class ActivityManagerService extends IActivityManager.Stub mProcessList.sendPackageBroadcastLocked(cmd, packages, userId); } - private List<ResolveInfo> collectReceiverComponents(Intent intent, String resolvedType, - int callingUid, int[] users, int[] broadcastAllowList) { + private List<ResolveInfo> collectReceiverComponents( + Intent intent, String resolvedType, int callingUid, int callingPid, + int[] users, int[] broadcastAllowList) { // TODO: come back and remove this assumption to triage all broadcasts long pmFlags = STOCK_PM_FLAGS | MATCH_DEBUG_TRIAGED_MISSING; @@ -15117,7 +15083,7 @@ public class ActivityManagerService extends IActivityManager.Stub continue; } List<ResolveInfo> newReceivers = mPackageManagerInt.queryIntentReceivers( - intent, resolvedType, pmFlags, callingUid, user, true /* forSend */); + intent, resolvedType, pmFlags, callingUid, callingPid, user, /* forSend */true); if (user != UserHandle.USER_SYSTEM && newReceivers != null) { // If this is not the system user, we need to check for // any receivers that should be filtered out. @@ -15135,7 +15101,7 @@ public class ActivityManagerService extends IActivityManager.Stub final ResolveInfo ri = newReceivers.get(i); final Resolution<ResolveInfo> resolution = mComponentAliasResolver.resolveReceiver(intent, ri, resolvedType, - pmFlags, user, callingUid, true /* forSend */); + pmFlags, user, callingUid, callingPid); if (resolution == null) { // It was an alias, but the target was not found. newReceivers.remove(i); @@ -15338,15 +15304,50 @@ public class ActivityManagerService extends IActivityManager.Stub BackgroundStartPrivileges backgroundStartPrivileges, @Nullable int[] broadcastAllowList, @Nullable BiFunction<Integer, Bundle, Bundle> filterExtrasForReceiver) { - final int cookie = BroadcastQueue.traceBegin("broadcastIntentLockedTraced"); - final int res = broadcastIntentLockedTraced(callerApp, callerPackage, callerFeatureId, - intent, resolvedType, resultToApp, resultTo, resultCode, resultData, resultExtras, - requiredPermissions, excludedPermissions, excludedPackages, appOp, - BroadcastOptions.fromBundleNullable(bOptions), ordered, sticky, - callingPid, callingUid, realCallingUid, realCallingPid, userId, - backgroundStartPrivileges, broadcastAllowList, filterExtrasForReceiver); - BroadcastQueue.traceEnd(cookie); - return res; + final int cookie = traceBroadcastIntentBegin(intent, resultTo, ordered, sticky, + callingUid, realCallingUid, userId); + try { + final int res = broadcastIntentLockedTraced(callerApp, callerPackage, callerFeatureId, + intent, resolvedType, resultToApp, resultTo, resultCode, resultData, + resultExtras, requiredPermissions, excludedPermissions, excludedPackages, + appOp, BroadcastOptions.fromBundleNullable(bOptions), ordered, sticky, + callingPid, callingUid, realCallingUid, realCallingPid, userId, + backgroundStartPrivileges, broadcastAllowList, filterExtrasForReceiver); + return res; + } finally { + traceBroadcastIntentEnd(cookie); + } + } + + private static int traceBroadcastIntentBegin(Intent intent, IIntentReceiver resultTo, + boolean ordered, boolean sticky, int callingUid, int realCallingUid, int userId) { + if (!Flags.traceReceiverRegistration()) { + return BroadcastQueue.traceBegin("broadcastIntentLockedTraced"); + } + if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) { + final StringBuilder sb = new StringBuilder("broadcastIntent: "); + sb.append(callingUid); sb.append('/'); + final String action = intent.getAction(); + sb.append(action == null ? null : action); sb.append('/'); + sb.append("0x"); sb.append(Integer.toHexString(intent.getFlags())); sb.append('/'); + sb.append(ordered ? "O" : "_"); + sb.append(sticky ? "S" : "_"); + sb.append(resultTo != null ? "C" : "_"); + sb.append('/'); + sb.append('u'); sb.append(userId); + if (callingUid != realCallingUid) { + sb.append('/'); + sb.append("sender="); sb.append(realCallingUid); + } + return BroadcastQueue.traceBegin(sb.toString()); + } + return 0; + } + + private static void traceBroadcastIntentEnd(int cookie) { + if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) { + BroadcastQueue.traceEnd(cookie); + } } @GuardedBy("this") @@ -15977,6 +15978,10 @@ public class ActivityManagerService extends IActivityManager.Stub users = new int[] {userId}; } + var args = new SaferIntentUtils.IntentArgs(intent, resolvedType, + true /* isReceiver */, true /* resolveForStart */, callingUid, callingPid); + args.platformCompat = mPlatformCompat; + // Figure out who all will receive this broadcast. final int cookie = BroadcastQueue.traceBegin("queryReceivers"); List receivers = null; @@ -15984,7 +15989,7 @@ public class ActivityManagerService extends IActivityManager.Stub // Need to resolve the intent to interested receivers... if ((intent.getFlags() & Intent.FLAG_RECEIVER_REGISTERED_ONLY) == 0) { receivers = collectReceiverComponents( - intent, resolvedType, callingUid, users, broadcastAllowList); + intent, resolvedType, callingUid, callingPid, users, broadcastAllowList); } if (intent.getComponent() == null) { final PackageDataSnapshot snapshot = getPackageManagerInternal().snapshot(); @@ -16009,9 +16014,7 @@ public class ActivityManagerService extends IActivityManager.Stub resolvedType, false /*defaultOnly*/, userId); } if (registeredReceivers != null) { - PackageManagerServiceUtils.applyNullActionBlocking( - mPlatformCompat, snapshot, registeredReceivers, - true, intent, callingUid); + SaferIntentUtils.blockNullAction(args, registeredReceivers); } } BroadcastQueue.traceEnd(cookie); @@ -16033,8 +16036,6 @@ public class ActivityManagerService extends IActivityManager.Stub } } - filterNonExportedComponents(intent, callingUid, callingPid, registeredReceivers, - mPlatformCompat, callerPackage, resolvedType); int NR = registeredReceivers != null ? registeredReceivers.size() : 0; // Merge into one list. @@ -16117,8 +16118,7 @@ public class ActivityManagerService extends IActivityManager.Stub if ((receivers != null && receivers.size() > 0) || resultTo != null) { BroadcastQueue queue = mBroadcastQueue; - filterNonExportedComponents(intent, callingUid, callingPid, receivers, - mPlatformCompat, callerPackage, resolvedType); + SaferIntentUtils.filterNonExportedComponents(args, receivers); BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp, callerPackage, callerFeatureId, callingPid, callingUid, callerInstantApp, resolvedType, requiredPermissions, excludedPermissions, excludedPackages, appOp, brOptions, @@ -19926,13 +19926,23 @@ public class ActivityManagerService extends IActivityManager.Stub } @Override - public IUnsafeIntentStrictModeCallback getRegisteredStrictModeCallback(int callingPid) { - return mStrictModeCallbacks.get(callingPid); - } - - @Override - public void unregisterStrictModeCallback(int callingPid) { - mStrictModeCallbacks.remove(callingPid); + public void triggerUnsafeIntentStrictMode(int callingPid, int type, Intent intent) { + final IUnsafeIntentStrictModeCallback callback; + final Intent i = intent.cloneFilter(); + synchronized (ActivityManagerService.this) { + callback = mStrictModeCallbacks.get(callingPid); + } + if (callback != null) { + BackgroundThread.getExecutor().execute(() -> { + try { + callback.onUnsafeIntent(type, i); + } catch (RemoteException e) { + synchronized (ActivityManagerService.this) { + mStrictModeCallbacks.remove(callingPid); + } + } + }); + } } @Override diff --git a/services/core/java/com/android/server/am/ActivityManagerUtils.java b/services/core/java/com/android/server/am/ActivityManagerUtils.java index 78a2ecb8fba1..3e43a82b4abf 100644 --- a/services/core/java/com/android/server/am/ActivityManagerUtils.java +++ b/services/core/java/com/android/server/am/ActivityManagerUtils.java @@ -17,13 +17,11 @@ package com.android.server.am; import android.app.ActivityThread; import android.content.ContentResolver; -import android.content.Intent; import android.provider.Settings; import android.util.ArrayMap; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.util.FrameworkStatsLog; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; @@ -127,25 +125,4 @@ public class ActivityManagerUtils { return (((double) hash) / Integer.MAX_VALUE) <= rate; } - - /** - * Helper method to log an unsafe intent event. - */ - public static void logUnsafeIntentEvent(int event, int callingUid, - Intent intent, String resolvedType, boolean blocked) { - String[] categories = intent.getCategories() == null ? new String[0] - : intent.getCategories().toArray(String[]::new); - String component = intent.getComponent() == null ? null - : intent.getComponent().flattenToString(); - FrameworkStatsLog.write(FrameworkStatsLog.UNSAFE_INTENT_EVENT_REPORTED, - event, - callingUid, - component, - intent.getPackage(), - intent.getAction(), - categories, - resolvedType, - intent.getScheme(), - blocked); - } } diff --git a/services/core/java/com/android/server/am/AppStartInfoTracker.java b/services/core/java/com/android/server/am/AppStartInfoTracker.java index a8227fa8e38b..4a7ad31cba3e 100644 --- a/services/core/java/com/android/server/am/AppStartInfoTracker.java +++ b/services/core/java/com/android/server/am/AppStartInfoTracker.java @@ -89,6 +89,14 @@ public final class AppStartInfoTracker { @VisibleForTesting static final int APP_START_INFO_HISTORY_LIST_SIZE = 16; + /** + * The max number of records that can be present in {@link mInProgressRecords}. + * + * The magic number of 5 records is expected to be enough because this covers in progress + * activity starts only, of which more than a 1-2 at a time is very uncommon/unlikely. + */ + @VisibleForTesting static final int MAX_IN_PROGRESS_RECORDS = 5; + private static final int APP_START_INFO_MONITORING_MODE_LIST_SIZE = 100; @VisibleForTesting static final String APP_START_STORE_DIR = "procstartstore"; @@ -147,7 +155,6 @@ public final class AppStartInfoTracker { /** The path to the historical proc start info file, persisted in the storage. */ @VisibleForTesting File mProcStartInfoFile; - /** * Temporary list of records that have not been completed. * @@ -155,7 +162,12 @@ public final class AppStartInfoTracker { */ @GuardedBy("mLock") @VisibleForTesting - final ArrayMap<Long, ApplicationStartInfo> mInProgRecords = new ArrayMap<>(); + final ArrayMap<Long, ApplicationStartInfo> mInProgressRecords = new ArrayMap<>(); + + /** Temporary list of keys present in {@link mInProgressRecords} for sorting. */ + @GuardedBy("mLock") + @VisibleForTesting + final ArrayList<Integer> mTemporaryInProgressIndexes = new ArrayList<>(); AppStartInfoTracker() { mCallbacks = new SparseArray<>(); @@ -193,6 +205,60 @@ public final class AppStartInfoTracker { }); } + /** + * Trim in progress records structure to acceptable size. To be called after each time a new + * record is added. + * + * This is necessary both for robustness, as well as because the call to + * {@link onReportFullyDrawn} which triggers the removal in the success case is not guaranteed. + * + * <p class="note"> Note: this is the expected path for removal of in progress records for + * successful activity triggered starts that don't report fully drawn. It is *not* only an edge + * case.</p> + */ + @GuardedBy("mLock") + private void maybeTrimInProgressRecordsLocked() { + if (mInProgressRecords.size() <= MAX_IN_PROGRESS_RECORDS) { + // Size is acceptable, do nothing. + return; + } + + // Make sure the temporary list is empty. + mTemporaryInProgressIndexes.clear(); + + // Populate the list with indexes for size of {@link mInProgressRecords}. + for (int i = 0; i < mInProgressRecords.size(); i++) { + mTemporaryInProgressIndexes.add(i, i); + } + + // Sort the index collection by value of the corresponding key in {@link mInProgressRecords} + // from smallest to largest. + Collections.sort(mTemporaryInProgressIndexes, (a, b) -> Long.compare( + mInProgressRecords.keyAt(a), mInProgressRecords.keyAt(b))); + + if (mTemporaryInProgressIndexes.size() == MAX_IN_PROGRESS_RECORDS + 1) { + // Only removing a single record so don't bother sorting again as we don't have to worry + // about indexes changing. + mInProgressRecords.removeAt(mTemporaryInProgressIndexes.get(0)); + } else { + // Removing more than 1 record, remove the records we want to keep from the list and + // then sort again so we can remove in reverse order of indexes. + mTemporaryInProgressIndexes.subList( + mTemporaryInProgressIndexes.size() - MAX_IN_PROGRESS_RECORDS, + mTemporaryInProgressIndexes.size()).clear(); + Collections.sort(mTemporaryInProgressIndexes); + + // Remove all remaining record indexes in reverse order to avoid changing the already + // calculated indexes. + for (int i = mTemporaryInProgressIndexes.size() - 1; i >= 0; i--) { + mInProgressRecords.removeAt(mTemporaryInProgressIndexes.get(i)); + } + } + + // Clear the temorary list. + mTemporaryInProgressIndexes.clear(); + } + void onIntentStarted(@NonNull Intent intent, long timestampNanos) { synchronized (mLock) { if (!mEnabled) { @@ -211,7 +277,8 @@ public final class AppStartInfoTracker { } else { start.setReason(ApplicationStartInfo.START_REASON_START_ACTIVITY); } - mInProgRecords.put(timestampNanos, start); + mInProgressRecords.put(timestampNanos, start); + maybeTrimInProgressRecordsLocked(); } } @@ -220,17 +287,17 @@ public final class AppStartInfoTracker { if (!mEnabled) { return; } - int index = mInProgRecords.indexOfKey(id); + int index = mInProgressRecords.indexOfKey(id); if (index < 0) { return; } - ApplicationStartInfo info = mInProgRecords.valueAt(index); + ApplicationStartInfo info = mInProgressRecords.valueAt(index); if (info == null) { - mInProgRecords.removeAt(index); + mInProgressRecords.removeAt(index); return; } info.setStartupState(ApplicationStartInfo.STARTUP_STATE_ERROR); - mInProgRecords.removeAt(index); + mInProgressRecords.removeAt(index); } } @@ -239,13 +306,13 @@ public final class AppStartInfoTracker { if (!mEnabled) { return; } - int index = mInProgRecords.indexOfKey(id); + int index = mInProgressRecords.indexOfKey(id); if (index < 0) { return; } - ApplicationStartInfo info = mInProgRecords.valueAt(index); + ApplicationStartInfo info = mInProgressRecords.valueAt(index); if (info == null || app == null) { - mInProgRecords.removeAt(index); + mInProgressRecords.removeAt(index); return; } info.setStartType((int) temperature); @@ -254,9 +321,9 @@ public final class AppStartInfoTracker { if (newInfo == null) { // newInfo can be null if records are added before load from storage is // complete. In this case the newly added record will be lost. - mInProgRecords.removeAt(index); + mInProgressRecords.removeAt(index); } else { - mInProgRecords.setValueAt(index, newInfo); + mInProgressRecords.setValueAt(index, newInfo); } } } @@ -266,17 +333,17 @@ public final class AppStartInfoTracker { if (!mEnabled) { return; } - int index = mInProgRecords.indexOfKey(id); + int index = mInProgressRecords.indexOfKey(id); if (index < 0) { return; } - ApplicationStartInfo info = mInProgRecords.valueAt(index); + ApplicationStartInfo info = mInProgressRecords.valueAt(index); if (info == null) { - mInProgRecords.removeAt(index); + mInProgressRecords.removeAt(index); return; } info.setStartupState(ApplicationStartInfo.STARTUP_STATE_ERROR); - mInProgRecords.removeAt(index); + mInProgressRecords.removeAt(index); } } @@ -286,13 +353,13 @@ public final class AppStartInfoTracker { if (!mEnabled) { return; } - int index = mInProgRecords.indexOfKey(id); + int index = mInProgressRecords.indexOfKey(id); if (index < 0) { return; } - ApplicationStartInfo info = mInProgRecords.valueAt(index); + ApplicationStartInfo info = mInProgressRecords.valueAt(index); if (info == null) { - mInProgRecords.removeAt(index); + mInProgressRecords.removeAt(index); return; } info.setLaunchMode(launchMode); @@ -308,18 +375,18 @@ public final class AppStartInfoTracker { if (!mEnabled) { return; } - int index = mInProgRecords.indexOfKey(id); + int index = mInProgressRecords.indexOfKey(id); if (index < 0) { return; } - ApplicationStartInfo info = mInProgRecords.valueAt(index); + ApplicationStartInfo info = mInProgressRecords.valueAt(index); if (info == null) { - mInProgRecords.removeAt(index); + mInProgressRecords.removeAt(index); return; } info.addStartupTimestamp(ApplicationStartInfo.START_TIMESTAMP_FULLY_DRAWN, timestampNanos); - mInProgRecords.removeAt(index); + mInProgressRecords.removeAt(index); } } @@ -964,7 +1031,7 @@ public final class AppStartInfoTracker { mProcStartInfoFile.delete(); } mData.getMap().clear(); - mInProgRecords.clear(); + mInProgressRecords.clear(); } } @@ -1128,21 +1195,8 @@ public final class AppStartInfoTracker { // Records are sorted newest to oldest, grab record at index 0. ApplicationStartInfo startInfo = mInfos.get(0); - int startupState = startInfo.getStartupState(); - // If startup state is error then don't accept any further timestamps. - if (startupState == ApplicationStartInfo.STARTUP_STATE_ERROR) { - if (DEBUG) Slog.d(TAG, "Startup state is error, not accepting new timestamps."); - return; - } - - // If startup state is first frame drawn then only accept fully drawn timestamp. - if (startupState == ApplicationStartInfo.STARTUP_STATE_FIRST_FRAME_DRAWN - && key != ApplicationStartInfo.START_TIMESTAMP_FULLY_DRAWN) { - if (DEBUG) { - Slog.d(TAG, "Startup state is first frame drawn and timestamp is not fully " - + "drawn, not accepting new timestamps."); - } + if (!isAddTimestampAllowed(startInfo, key, timestampNs)) { return; } @@ -1155,6 +1209,55 @@ public final class AppStartInfoTracker { } } + private boolean isAddTimestampAllowed(ApplicationStartInfo startInfo, int key, + long timestampNs) { + int startupState = startInfo.getStartupState(); + + // If startup state is error then don't accept any further timestamps. + if (startupState == ApplicationStartInfo.STARTUP_STATE_ERROR) { + if (DEBUG) Slog.d(TAG, "Startup state is error, not accepting new timestamps."); + return false; + } + + Map<Integer, Long> timestamps = startInfo.getStartupTimestamps(); + + if (startupState == ApplicationStartInfo.STARTUP_STATE_FIRST_FRAME_DRAWN) { + switch (key) { + case ApplicationStartInfo.START_TIMESTAMP_FULLY_DRAWN: + // Allowed, continue to confirm it's not already added. + break; + case ApplicationStartInfo.START_TIMESTAMP_INITIAL_RENDERTHREAD_FRAME: + Long firstFrameTimeNs = timestamps + .get(ApplicationStartInfo.START_TIMESTAMP_FIRST_FRAME); + if (firstFrameTimeNs == null) { + // This should never happen. State can't be first frame drawn if first + // frame timestamp was not provided. + return false; + } + + if (timestampNs > firstFrameTimeNs) { + // Initial renderthread frame has to occur before first frame. + return false; + } + + // Allowed, continue to confirm it's not already added. + break; + case ApplicationStartInfo.START_TIMESTAMP_SURFACEFLINGER_COMPOSITION_COMPLETE: + // Allowed, continue to confirm it's not already added. + break; + default: + return false; + } + } + + if (timestamps.get(key) != null) { + // Timestamp should not occur more than once for a given start. + return false; + } + + return true; + } + @GuardedBy("mLock") void dumpLocked(PrintWriter pw, String prefix, SimpleDateFormat sdf) { if (mMonitoringModeEnabled) { diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java index 7c0325e7376a..1ac37ad99ada 100644 --- a/services/core/java/com/android/server/am/BatteryStatsService.java +++ b/services/core/java/com/android/server/am/BatteryStatsService.java @@ -60,6 +60,7 @@ import android.os.BatteryUsageStats; import android.os.BatteryUsageStatsQuery; import android.os.Binder; import android.os.BluetoothBatteryStats; +import android.os.Bundle; import android.os.Handler; import android.os.HandlerThread; import android.os.IBinder; @@ -71,6 +72,7 @@ import android.os.PowerManagerInternal; import android.os.PowerSaveState; import android.os.Process; import android.os.RemoteException; +import android.os.ResultReceiver; import android.os.ServiceManager; import android.os.SystemClock; import android.os.Trace; @@ -120,12 +122,16 @@ import com.android.server.net.BaseNetworkObserver; import com.android.server.pm.UserManagerInternal; import com.android.server.power.optimization.Flags; import com.android.server.power.stats.AggregatedPowerStatsConfig; +import com.android.server.power.stats.AudioPowerStatsProcessor; import com.android.server.power.stats.BatteryExternalStatsWorker; import com.android.server.power.stats.BatteryStatsDumpHelperImpl; import com.android.server.power.stats.BatteryStatsImpl; import com.android.server.power.stats.BatteryUsageStatsProvider; import com.android.server.power.stats.BluetoothPowerStatsProcessor; +import com.android.server.power.stats.CameraPowerStatsProcessor; import com.android.server.power.stats.CpuPowerStatsProcessor; +import com.android.server.power.stats.FlashlightPowerStatsProcessor; +import com.android.server.power.stats.GnssPowerStatsProcessor; import com.android.server.power.stats.MobileRadioPowerStatsProcessor; import com.android.server.power.stats.PhoneCallPowerStatsProcessor; import com.android.server.power.stats.PowerStatsAggregator; @@ -134,6 +140,7 @@ import com.android.server.power.stats.PowerStatsScheduler; import com.android.server.power.stats.PowerStatsStore; import com.android.server.power.stats.PowerStatsUidResolver; import com.android.server.power.stats.SystemServerCpuThreadReader.SystemServiceCpuThreadTimes; +import com.android.server.power.stats.VideoPowerStatsProcessor; import com.android.server.power.stats.WifiPowerStatsProcessor; import com.android.server.power.stats.wakeups.CpuWakeupStats; @@ -192,7 +199,7 @@ public final class BatteryStatsService extends IBatteryStats.Stub private final BatteryUsageStatsProvider mBatteryUsageStatsProvider; private final AtomicFile mConfigFile; private final BatteryStats.BatteryStatsDumpHelper mDumpHelper; - private final PowerStatsUidResolver mPowerStatsUidResolver; + private final PowerStatsUidResolver mPowerStatsUidResolver = new PowerStatsUidResolver(); private final AggregatedPowerStatsConfig mAggregatedPowerStatsConfig; private volatile boolean mMonitorEnabled = true; @@ -420,7 +427,6 @@ public final class BatteryStatsService extends IBatteryStats.Stub setPowerStatsThrottlePeriods(batteryStatsConfigBuilder, context.getResources().getString( com.android.internal.R.string.config_powerStatsThrottlePeriods)); mBatteryStatsConfig = batteryStatsConfigBuilder.build(); - mPowerStatsUidResolver = new PowerStatsUidResolver(); mStats = new BatteryStatsImpl(mBatteryStatsConfig, Clock.SYSTEM_CLOCK, mMonotonicClock, systemDir, mHandler, this, this, mUserManagerUserInfoProvider, mPowerProfile, mCpuScalingPolicies, mPowerStatsUidResolver); @@ -514,6 +520,60 @@ public final class BatteryStatsService extends IBatteryStats.Stub AggregatedPowerStatsConfig.STATE_PROCESS_STATE) .setProcessor( new BluetoothPowerStatsProcessor(mPowerProfile)); + + config.trackPowerComponent(BatteryConsumer.POWER_COMPONENT_AUDIO) + .trackDeviceStates( + AggregatedPowerStatsConfig.STATE_POWER, + AggregatedPowerStatsConfig.STATE_SCREEN) + .trackUidStates( + AggregatedPowerStatsConfig.STATE_POWER, + AggregatedPowerStatsConfig.STATE_SCREEN, + AggregatedPowerStatsConfig.STATE_PROCESS_STATE) + .setProcessor( + new AudioPowerStatsProcessor(mPowerProfile, mPowerStatsUidResolver)); + + config.trackPowerComponent(BatteryConsumer.POWER_COMPONENT_VIDEO) + .trackDeviceStates( + AggregatedPowerStatsConfig.STATE_POWER, + AggregatedPowerStatsConfig.STATE_SCREEN) + .trackUidStates( + AggregatedPowerStatsConfig.STATE_POWER, + AggregatedPowerStatsConfig.STATE_SCREEN, + AggregatedPowerStatsConfig.STATE_PROCESS_STATE) + .setProcessor(new VideoPowerStatsProcessor(mPowerProfile, mPowerStatsUidResolver)); + + config.trackPowerComponent(BatteryConsumer.POWER_COMPONENT_FLASHLIGHT) + .trackDeviceStates( + AggregatedPowerStatsConfig.STATE_POWER, + AggregatedPowerStatsConfig.STATE_SCREEN) + .trackUidStates( + AggregatedPowerStatsConfig.STATE_POWER, + AggregatedPowerStatsConfig.STATE_SCREEN, + AggregatedPowerStatsConfig.STATE_PROCESS_STATE) + .setProcessor( + new FlashlightPowerStatsProcessor(mPowerProfile, mPowerStatsUidResolver)); + + config.trackPowerComponent(BatteryConsumer.POWER_COMPONENT_CAMERA) + .trackDeviceStates( + AggregatedPowerStatsConfig.STATE_POWER, + AggregatedPowerStatsConfig.STATE_SCREEN) + .trackUidStates( + AggregatedPowerStatsConfig.STATE_POWER, + AggregatedPowerStatsConfig.STATE_SCREEN, + AggregatedPowerStatsConfig.STATE_PROCESS_STATE) + .setProcessor( + new CameraPowerStatsProcessor(mPowerProfile, mPowerStatsUidResolver)); + + config.trackPowerComponent(BatteryConsumer.POWER_COMPONENT_GNSS) + .trackDeviceStates( + AggregatedPowerStatsConfig.STATE_POWER, + AggregatedPowerStatsConfig.STATE_SCREEN) + .trackUidStates( + AggregatedPowerStatsConfig.STATE_POWER, + AggregatedPowerStatsConfig.STATE_SCREEN, + AggregatedPowerStatsConfig.STATE_PROCESS_STATE) + .setProcessor( + new GnssPowerStatsProcessor(mPowerProfile, mPowerStatsUidResolver)); return config; } @@ -581,6 +641,30 @@ public final class BatteryStatsService extends IBatteryStats.Stub BatteryConsumer.POWER_COMPONENT_BLUETOOTH, Flags.streamlinedConnectivityBatteryStats()); + mStats.setPowerStatsCollectorEnabled(BatteryConsumer.POWER_COMPONENT_AUDIO, + Flags.streamlinedMiscBatteryStats()); + mBatteryUsageStatsProvider.setPowerStatsExporterEnabled( + BatteryConsumer.POWER_COMPONENT_AUDIO, + Flags.streamlinedMiscBatteryStats()); + + mStats.setPowerStatsCollectorEnabled(BatteryConsumer.POWER_COMPONENT_VIDEO, + Flags.streamlinedMiscBatteryStats()); + mBatteryUsageStatsProvider.setPowerStatsExporterEnabled( + BatteryConsumer.POWER_COMPONENT_VIDEO, + Flags.streamlinedMiscBatteryStats()); + + mStats.setPowerStatsCollectorEnabled(BatteryConsumer.POWER_COMPONENT_FLASHLIGHT, + Flags.streamlinedMiscBatteryStats()); + mBatteryUsageStatsProvider.setPowerStatsExporterEnabled( + BatteryConsumer.POWER_COMPONENT_FLASHLIGHT, + Flags.streamlinedMiscBatteryStats()); + + mStats.setPowerStatsCollectorEnabled(BatteryConsumer.POWER_COMPONENT_CAMERA, + Flags.streamlinedMiscBatteryStats()); + mBatteryUsageStatsProvider.setPowerStatsExporterEnabled( + BatteryConsumer.POWER_COMPONENT_CAMERA, + Flags.streamlinedMiscBatteryStats()); + mWorker.systemServicesReady(); mStats.systemServicesReady(mContext); mCpuWakeupStats.systemServicesReady(); @@ -3337,6 +3421,59 @@ public final class BatteryStatsService extends IBatteryStats.Stub } } + /** + * Gets a snapshot of the system health for a number of uids. + */ + @Override + public void takeUidSnapshotsAsync(int[] requestUids, ResultReceiver resultReceiver) { + if (!onlyCaller(requestUids)) { + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.BATTERY_STATS, null); + } + + Future future; + if (shouldCollectExternalStats()) { + future = mWorker.scheduleSync("get-health-stats-for-uids", + BatteryExternalStatsWorker.UPDATE_ALL); + } else { + future = null; + } + + mHandler.post(() -> { + if (future != null) { + try { + // Worker uses a separate thread pool, so waiting here won't cause a deadlock + future.get(); + } catch (InterruptedException | ExecutionException e) { + Slog.e(TAG, "Sync failed", e); + } + } + + final long ident = Binder.clearCallingIdentity(); + int i = -1; + try { + final int count = requestUids.length; + final HealthStatsParceler[] results = new HealthStatsParceler[count]; + synchronized (mStats) { + for (i = 0; i < count; i++) { + results[i] = getHealthStatsForUidLocked(requestUids[i]); + } + } + Bundle resultData = new Bundle(1); + resultData.putParcelableArray(IBatteryStats.KEY_UID_SNAPSHOTS, results); + resultReceiver.send(0, resultData); + } catch (Exception ex) { + if (DBG) { + Slog.d(TAG, "Crashed while returning results for takeUidSnapshots(" + + Arrays.toString(requestUids) + ") i=" + i, ex); + } + throw ex; + } finally { + Binder.restoreCallingIdentity(ident); + } + }); + } + private boolean shouldCollectExternalStats() { return (SystemClock.elapsedRealtime() - mWorker.getLastCollectionTimeStamp()) > mStats.getExternalStatsCollectionRateLimitMs(); diff --git a/services/core/java/com/android/server/am/BroadcastFilter.java b/services/core/java/com/android/server/am/BroadcastFilter.java index 749427730ca0..adb2392a8484 100644 --- a/services/core/java/com/android/server/am/BroadcastFilter.java +++ b/services/core/java/com/android/server/am/BroadcastFilter.java @@ -26,7 +26,7 @@ import dalvik.annotation.optimization.NeverCompile; import java.io.PrintWriter; -final class BroadcastFilter extends IntentFilter { +public final class BroadcastFilter extends IntentFilter { // Back-pointer to the list this filter is in. final ReceiverList receiverList; final String packageName; @@ -37,7 +37,7 @@ final class BroadcastFilter extends IntentFilter { final int owningUserId; final boolean instantApp; final boolean visibleToInstantApp; - final boolean exported; + public final boolean exported; BroadcastFilter(IntentFilter _filter, ReceiverList _receiverList, String _packageName, String _featureId, String _receiverId, String _requiredPermission, diff --git a/services/core/java/com/android/server/am/ComponentAliasResolver.java b/services/core/java/com/android/server/am/ComponentAliasResolver.java index 3fa6102b64e0..5d84fd925356 100644 --- a/services/core/java/com/android/server/am/ComponentAliasResolver.java +++ b/services/core/java/com/android/server/am/ComponentAliasResolver.java @@ -455,9 +455,9 @@ public class ComponentAliasResolver { } @Nullable - public Resolution<ResolveInfo> resolveReceiver(@NonNull Intent intent, - @NonNull ResolveInfo receiver, @Nullable String resolvedType, - long packageFlags, int userId, int callingUid, boolean forSend) { + public Resolution<ResolveInfo> resolveReceiver( + @NonNull Intent intent, @NonNull ResolveInfo receiver, @Nullable String resolvedType, + long packageFlags, int userId, int callingUid, int callingPid) { // Resolve this alias. final Resolution<ComponentName> resolution = resolveComponentAlias(() -> receiver.activityInfo.getComponentName()); @@ -481,7 +481,7 @@ public class ComponentAliasResolver { i.setComponent(resolution.getTarget()); List<ResolveInfo> resolved = pmi.queryIntentReceivers( - i, resolvedType, packageFlags, callingUid, userId, forSend); + i, resolvedType, packageFlags, callingUid, callingPid, userId, /*forSend*/ true); if (resolved == null || resolved.size() == 0) { // Target component not found. Slog.w(TAG, "Alias target " + target.flattenToShortString() + " not found"); diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java index ab34dd4477fd..3b73e0690a83 100644 --- a/services/core/java/com/android/server/am/OomAdjuster.java +++ b/services/core/java/com/android/server/am/OomAdjuster.java @@ -49,6 +49,7 @@ import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_BIND_SERVICE; import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_COMPONENT_DISABLED; import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_EXECUTING_SERVICE; import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_FINISH_RECEIVER; +import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_FOLLOW_UP; import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_GET_PROVIDER; import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_NONE; import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_PROCESS_BEGIN; @@ -68,9 +69,7 @@ import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_UNBIND_SERVICE; import static android.content.Context.BIND_TREAT_LIKE_VISIBLE_FOREGROUND_SERVICE; import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_CAMERA; import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_LOCATION; -import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK; import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_MICROPHONE; -import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_PHONE_CALL; import static android.media.audio.Flags.roForegroundAudioControl; import static android.os.Process.THREAD_GROUP_BACKGROUND; import static android.os.Process.THREAD_GROUP_DEFAULT; @@ -91,6 +90,7 @@ import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PSS; import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_UID_OBSERVERS; import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_USAGE_STATS; import static com.android.server.am.ActivityManagerService.DISPATCH_OOM_ADJ_OBSERVER_MSG; +import static com.android.server.am.ActivityManagerService.FOLLOW_UP_OOMADJUSTER_UPDATE_MSG; import static com.android.server.am.ActivityManagerService.IDLE_UIDS_MSG; import static com.android.server.am.ActivityManagerService.TAG_BACKUP; import static com.android.server.am.ActivityManagerService.TAG_LRU; @@ -226,6 +226,8 @@ public class OomAdjuster { return AppProtoEnums.OOM_ADJ_REASON_RESTRICTION_CHANGE; case OOM_ADJ_REASON_COMPONENT_DISABLED: return AppProtoEnums.OOM_ADJ_REASON_COMPONENT_DISABLED; + case OOM_ADJ_REASON_FOLLOW_UP: + return AppProtoEnums.OOM_ADJ_REASON_FOLLOW_UP; default: return AppProtoEnums.OOM_ADJ_REASON_UNKNOWN_TO_PROTO; } @@ -280,6 +282,8 @@ public class OomAdjuster { return OOM_ADJ_REASON_METHOD + "_restrictionChange"; case OOM_ADJ_REASON_COMPONENT_DISABLED: return OOM_ADJ_REASON_METHOD + "_componentDisabled"; + case OOM_ADJ_REASON_FOLLOW_UP: + return OOM_ADJ_REASON_METHOD + "_followUp"; default: return "_unknown"; } @@ -370,6 +374,7 @@ public class OomAdjuster { protected final int[] mTmpSchedGroup = new int[1]; final ActivityManagerService mService; + final Injector mInjector; final ProcessList mProcessList; final ActivityManagerGlobalLock mProcLock; @@ -384,6 +389,13 @@ public class OomAdjuster { protected final ArraySet<ProcessRecord> mProcessesInCycle = new ArraySet<>(); /** + * List of processes that we want to batch for LMKD to adjust their respective + * OOM scores. + */ + @GuardedBy("mService") + protected final ArrayList<ProcessRecord> mProcsToOomAdj = new ArrayList<ProcessRecord>(); + + /** * Flag to mark if there is an ongoing oomAdjUpdate: potentially the oomAdjUpdate * could be called recursively because of the indirect calls during the update; * however the oomAdjUpdate itself doesn't support recursion - in this case we'd @@ -413,12 +425,33 @@ public class OomAdjuster { @GuardedBy("mService") protected int mProcessStateCurTop = PROCESS_STATE_TOP; - /** Overrideable by a test */ + @GuardedBy("mService") + private final ArraySet<ProcessRecord> mFollowUpUpdateSet = new ArraySet<>(); + + private static final long NO_FOLLOW_UP_TIME = Long.MAX_VALUE; + @GuardedBy("mService") + private long mNextFollowUpUpdateUptimeMs = NO_FOLLOW_UP_TIME; + @VisibleForTesting - protected boolean isChangeEnabled(@CachedCompatChangeId int cachedCompatChangeId, + public static class Injector { + boolean isChangeEnabled(@CachedCompatChangeId int cachedCompatChangeId, + ApplicationInfo app, boolean defaultValue) { + return PlatformCompatCache.getInstance() + .isChangeEnabled(cachedCompatChangeId, app, defaultValue); + } + + long getUptimeMillis() { + return SystemClock.uptimeMillis(); + } + + long getElapsedRealtimeMillis() { + return SystemClock.elapsedRealtime(); + } + } + + boolean isChangeEnabled(@CachedCompatChangeId int cachedCompatChangeId, ApplicationInfo app, boolean defaultValue) { - return PlatformCompatCache.getInstance() - .isChangeEnabled(cachedCompatChangeId, app, defaultValue); + return mInjector.isChangeEnabled(cachedCompatChangeId, app, defaultValue); } OomAdjuster(ActivityManagerService service, ProcessList processList, ActiveUids activeUids) { @@ -436,7 +469,18 @@ public class OomAdjuster { OomAdjuster(ActivityManagerService service, ProcessList processList, ActiveUids activeUids, ServiceThread adjusterThread) { + this(service, processList, activeUids, adjusterThread, new Injector()); + } + + OomAdjuster(ActivityManagerService service, ProcessList processList, ActiveUids activeUids, + Injector injector) { + this(service, processList, activeUids, createAdjusterThread(), injector); + } + + OomAdjuster(ActivityManagerService service, ProcessList processList, ActiveUids activeUids, + ServiceThread adjusterThread, Injector injector) { mService = service; + mInjector = injector; mProcessList = processList; mProcLock = service.mProcLock; mActiveUids = activeUids; @@ -624,8 +668,8 @@ public class OomAdjuster { // processes, its adj could be still unknown as of now, assign one. processes.add(app); assignCachedAdjIfNecessary(processes); - applyOomAdjLSP(app, false, SystemClock.uptimeMillis(), - SystemClock.elapsedRealtime(), oomAdjReason); + applyOomAdjLSP(app, false, mInjector.getUptimeMillis(), + mInjector.getElapsedRealtimeMillis(), oomAdjReason); } mTmpProcessList.clear(); mService.clearPendingTopAppLocked(); @@ -842,6 +886,40 @@ public class OomAdjuster { } @GuardedBy("mService") + void updateOomAdjFollowUpTargetsLocked() { + final long now = mInjector.getUptimeMillis(); + long nextFollowUpUptimeMs = Long.MAX_VALUE; + mNextFollowUpUpdateUptimeMs = NO_FOLLOW_UP_TIME; + for (int i = mFollowUpUpdateSet.size() - 1; i >= 0; i--) { + final ProcessRecord proc = mFollowUpUpdateSet.valueAtUnchecked(i); + final long followUpUptimeMs = proc.mState.getFollowupUpdateUptimeMs(); + + if (proc.isKilled()) { + // Process is dead, just remove from follow up set. + mFollowUpUpdateSet.removeAt(i); + } else if (followUpUptimeMs <= now) { + // Add processes that need a follow up update. + mPendingProcessSet.add(proc); + proc.mState.setFollowupUpdateUptimeMs(NO_FOLLOW_UP_TIME); + mFollowUpUpdateSet.removeAt(i); + } else if (followUpUptimeMs < nextFollowUpUptimeMs) { + // Figure out when to schedule the next follow up update. + nextFollowUpUptimeMs = followUpUptimeMs; + } else if (followUpUptimeMs == NO_FOLLOW_UP_TIME) { + // The follow up is no longer needed for this process. + mFollowUpUpdateSet.removeAt(i); + } + } + + if (nextFollowUpUptimeMs != Long.MAX_VALUE) { + // There is still at least one process that needs a follow up. + scheduleFollowUpOomAdjusterUpdateLocked(nextFollowUpUptimeMs, now); + } + + updateOomAdjPendingTargetsLocked(OOM_ADJ_REASON_FOLLOW_UP); + } + + @GuardedBy("mService") protected void performUpdateOomAdjPendingTargetsLocked(@OomAdjReason int oomAdjReason) { final ProcessRecord topApp = mService.getTopApp(); @@ -892,8 +970,8 @@ public class OomAdjuster { if (startProfiling) { Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, oomAdjReasonToString(oomAdjReason)); } - final long now = SystemClock.uptimeMillis(); - final long nowElapsed = SystemClock.elapsedRealtime(); + final long now = mInjector.getUptimeMillis(); + final long nowElapsed = mInjector.getElapsedRealtimeMillis(); final long oldTime = now - mConstants.mMaxEmptyTimeMillis; final int numProc = activeProcesses.size(); @@ -1019,7 +1097,7 @@ public class OomAdjuster { updateUidsLSP(activeUids, nowElapsed); synchronized (mService.mProcessStats.mLock) { - final long nowUptime = SystemClock.uptimeMillis(); + final long nowUptime = mInjector.getUptimeMillis(); if (mService.mProcessStats.shouldWriteNowLocked(nowUptime)) { mService.mHandler.post(new ActivityManagerService.ProcStatsRunnable(mService, mService.mProcessStats)); @@ -1030,7 +1108,7 @@ public class OomAdjuster { } if (DEBUG_OOM_ADJ) { - final long duration = SystemClock.uptimeMillis() - now; + final long duration = mInjector.getUptimeMillis() - now; if (false) { Slog.d(TAG_OOM_ADJ, "Did OOM ADJ in " + duration + "ms", new RuntimeException("here").fillInStackTrace()); @@ -1044,7 +1122,7 @@ public class OomAdjuster { protected void assignCachedAdjIfNecessary(ArrayList<ProcessRecord> lruList) { final int numLru = lruList.size(); if (mConstants.USE_TIERED_CACHED_ADJ) { - final long now = SystemClock.uptimeMillis(); + final long now = mInjector.getUptimeMillis(); for (int i = numLru - 1; i >= 0; i--) { ProcessRecord app = lruList.get(i); final ProcessStateRecord state = app.mState; @@ -1246,7 +1324,7 @@ public class OomAdjuster { if (!app.isKilledByAm() && app.getThread() != null) { // We don't need to apply the update for the process which didn't get computed if (state.getCompletedAdjSeq() == mAdjSeq) { - applyOomAdjLSP(app, doingAll, now, nowElapsed, oomAdjReason); + applyOomAdjLSP(app, doingAll, now, nowElapsed, oomAdjReason, true); } if (app.isPendingFinishAttach()) { @@ -1348,6 +1426,11 @@ public class OomAdjuster { } } + if (!mProcsToOomAdj.isEmpty()) { + ProcessList.batchSetOomAdj(mProcsToOomAdj); + mProcsToOomAdj.clear(); + } + if (proactiveKillsEnabled // Proactive kills enabled? && doKillExcessiveProcesses // Should kill excessive processes? && freeSwapPercent < lowSwapThresholdPercent // Swap below threshold? @@ -1722,6 +1805,10 @@ public class OomAdjuster { int prevProcState = getInitialProcState(app); int prevCapability = getInitialCapability(app); + // Remove any follow up update this process might have. It will be rescheduled if still + // needed. + app.mState.setFollowupUpdateUptimeMs(NO_FOLLOW_UP_TIME); + if (app.getThread() == null) { state.setAdjSeq(mAdjSeq); state.setCurrentSchedulingGroup(SCHED_GROUP_BACKGROUND); @@ -1989,6 +2076,8 @@ public class OomAdjuster { if (DEBUG_OOM_ADJ_REASON || logUid == appUid) { reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise to recent fg: " + app); } + maybeSetProcessFollowUpUpdateLocked(app, + state.getLastTopTime() + mConstants.TOP_TO_FGS_GRACE_DURATION, now); } // If the app was recently in the foreground and has expedited jobs running, @@ -2009,6 +2098,9 @@ public class OomAdjuster { if (DEBUG_OOM_ADJ_REASON || logUid == appUid) { reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise to recent fg for EJ: " + app); } + maybeSetProcessFollowUpUpdateLocked(app, + state.getLastTopTime() + mConstants.TOP_TO_ALMOST_PERCEPTIBLE_GRACE_DURATION, + now); } if (adj > PERCEPTIBLE_APP_ADJ @@ -2074,7 +2166,7 @@ public class OomAdjuster { // app to be demoted to cached. if (procState >= PROCESS_STATE_LAST_ACTIVITY && state.getSetProcState() == PROCESS_STATE_LAST_ACTIVITY - && (state.getLastStateTime() + mConstants.MAX_PREVIOUS_TIME) < now) { + && (state.getLastStateTime() + mConstants.MAX_PREVIOUS_TIME) <= now) { procState = PROCESS_STATE_LAST_ACTIVITY; schedGroup = SCHED_GROUP_BACKGROUND; state.setAdjType("previous-expired"); @@ -2098,6 +2190,14 @@ public class OomAdjuster { reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise procstate to prev: " + app); } } + final long lastStateTime; + if (state.getSetProcState() == PROCESS_STATE_LAST_ACTIVITY) { + lastStateTime = state.getLastStateTime(); + } else { + lastStateTime = now; + } + maybeSetProcessFollowUpUpdateLocked(app, + lastStateTime + mConstants.MAX_PREVIOUS_TIME, now); } } @@ -2186,6 +2286,8 @@ public class OomAdjuster { reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise adj to started service: " + app); } + maybeSetProcessFollowUpUpdateLocked(app, + s.lastActivity + mConstants.MAX_SERVICE_INACTIVITY, now); } } // If we have let the service slide into the background @@ -2205,14 +2307,8 @@ public class OomAdjuster { != 0 ? PROCESS_CAPABILITY_FOREGROUND_LOCATION : 0; if (roForegroundAudioControl()) { // flag check - // TODO revisit restriction of FOREGROUND_AUDIO_CONTROL when it can be - // limited to specific FGS types - //final int fgsAudioType = FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK - // | FOREGROUND_SERVICE_TYPE_CAMERA - // | FOREGROUND_SERVICE_TYPE_MICROPHONE - // | FOREGROUND_SERVICE_TYPE_PHONE_CALL; - //capabilityFromFGS |= (psr.getForegroundServiceTypes() & fgsAudioType) != 0 - // ? PROCESS_CAPABILITY_FOREGROUND_AUDIO_CONTROL : 0; + // TODO(b/335373208) - revisit restriction of FOREGROUND_AUDIO_CONTROL + // when it can be limited to specific FGS types capabilityFromFGS |= PROCESS_CAPABILITY_FOREGROUND_AUDIO_CONTROL; } @@ -2340,6 +2436,8 @@ public class OomAdjuster { reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise adj to recent provider: " + app); } + maybeSetProcessFollowUpUpdateLocked(app, + ppr.getLastProviderTime() + mConstants.CONTENT_PROVIDER_RETAIN_TIME, now); } if (procState > PROCESS_STATE_LAST_ACTIVITY) { procState = PROCESS_STATE_LAST_ACTIVITY; @@ -2348,6 +2446,8 @@ public class OomAdjuster { reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise procstate to recent provider: " + app); } + maybeSetProcessFollowUpUpdateLocked(app, + ppr.getLastProviderTime() + mConstants.CONTENT_PROVIDER_RETAIN_TIME, now); } } @@ -3246,10 +3346,16 @@ public class OomAdjuster { mCachedAppOptimizer.onWakefulnessChanged(wakefulness); } + @GuardedBy({"mService", "mProcLock"}) + protected boolean applyOomAdjLSP(ProcessRecord app, boolean doingAll, long now, + long nowElapsed, @OomAdjReason int oomAdjReason) { + return applyOomAdjLSP(app, doingAll, now, nowElapsed, oomAdjReason, false); + } + /** Applies the computed oomadj, procstate and sched group values and freezes them in set* */ @GuardedBy({"mService", "mProcLock"}) protected boolean applyOomAdjLSP(ProcessRecord app, boolean doingAll, long now, - long nowElapsed, @OomAdjReason int oomAdjReson) { + long nowElapsed, @OomAdjReason int oomAdjReson, boolean isBatchingOomAdj) { boolean success = true; final ProcessStateRecord state = app.mState; final UidRecord uidRec = app.getUidRecord(); @@ -3266,7 +3372,12 @@ public class OomAdjuster { final int oldOomAdj = state.getSetAdj(); if (state.getCurAdj() != state.getSetAdj()) { - ProcessList.setOomAdj(app.getPid(), app.uid, state.getCurAdj()); + if (isBatchingOomAdj && mConstants.ENABLE_BATCHING_OOM_ADJ) { + mProcsToOomAdj.add(app); + } else { + ProcessList.setOomAdj(app.getPid(), app.uid, state.getCurAdj()); + } + if (DEBUG_SWITCH || DEBUG_OOM_ADJ || mService.mCurOomAdjUid == app.info.uid) { String msg = "Set " + app.getPid() + " " + app.processName + " adj " + state.getCurAdj() + ": " + state.getAdjType(); @@ -3649,7 +3760,7 @@ public class OomAdjuster { if (N <= 0) { return; } - final long nowElapsed = SystemClock.elapsedRealtime(); + final long nowElapsed = mInjector.getElapsedRealtimeMillis(); final long maxBgTime = nowElapsed - mConstants.BACKGROUND_SETTLE_TIME; long nextTime = 0; if (mService.mLocalPowerManager != null) { @@ -3884,7 +3995,7 @@ public class OomAdjuster { } // Take a dry run of the computeServiceHostOomAdjLSP, this would't be expensive // since it's only evaluating one service connection. - return computeServiceHostOomAdjLSP(cr, app, client, SystemClock.uptimeMillis(), + return computeServiceHostOomAdjLSP(cr, app, client, mInjector.getUptimeMillis(), mService.getTopApp(), false, false, false, OOM_ADJ_REASON_NONE, CACHED_APP_MIN_ADJ, false, true /* dryRun */); } @@ -3919,7 +4030,7 @@ public class OomAdjuster { // The provider host process has better states than the client, so no change. return false; } - return computeProviderHostOomAdjLSP(null, app, client, SystemClock.uptimeMillis(), + return computeProviderHostOomAdjLSP(null, app, client, mInjector.getUptimeMillis(), mService.getTopApp(), false, false, false, OOM_ADJ_REASON_NONE, CACHED_APP_MIN_ADJ, false, true /* dryRun */); } @@ -3947,4 +4058,43 @@ public class OomAdjuster { } return false; } + + @GuardedBy("mService") + private void maybeSetProcessFollowUpUpdateLocked(ProcessRecord proc, + long updateUptimeMs, long now) { + if (!Flags.followUpOomadjUpdates()) { + return; + } + if (updateUptimeMs <= now) { + // Time sensitive period has already passed. No need to schedule a follow up. + return; + } + + mFollowUpUpdateSet.add(proc); + proc.mState.setFollowupUpdateUptimeMs(updateUptimeMs); + + scheduleFollowUpOomAdjusterUpdateLocked(updateUptimeMs, now); + } + + + @GuardedBy("mService") + private void scheduleFollowUpOomAdjusterUpdateLocked(long updateUptimeMs, + long now) { + if (updateUptimeMs + mConstants.FOLLOW_UP_OOMADJ_UPDATE_WAIT_DURATION + >= mNextFollowUpUpdateUptimeMs) { + // Update time is too close or later than the next follow up update. + return; + } + if (updateUptimeMs < now + mConstants.FOLLOW_UP_OOMADJ_UPDATE_WAIT_DURATION) { + // Use a minimum delay for the follow up to possibly batch multiple process + // evaluations and avoid rapid updates. + updateUptimeMs = now + mConstants.FOLLOW_UP_OOMADJ_UPDATE_WAIT_DURATION; + } + + // Schedulate a follow up update. Don't bother deleting existing handler messages, they + // will be cleared during the message while no locks are being held. + mNextFollowUpUpdateUptimeMs = updateUptimeMs; + mService.mHandler.sendEmptyMessageAtTime(FOLLOW_UP_OOMADJUSTER_UPDATE_MSG, + mNextFollowUpUpdateUptimeMs); + } } diff --git a/services/core/java/com/android/server/am/OomAdjusterModernImpl.java b/services/core/java/com/android/server/am/OomAdjusterModernImpl.java index a67af89ad39d..21842db590b0 100644 --- a/services/core/java/com/android/server/am/OomAdjusterModernImpl.java +++ b/services/core/java/com/android/server/am/OomAdjusterModernImpl.java @@ -68,7 +68,6 @@ import android.app.ActivityManager; import android.app.ActivityManagerInternal.OomAdjReason; import android.content.pm.ServiceInfo; import android.os.IBinder; -import android.os.SystemClock; import android.os.Trace; import android.util.ArrayMap; import android.util.ArraySet; @@ -764,6 +763,11 @@ public class OomAdjusterModernImpl extends OomAdjuster { super(service, processList, activeUids, adjusterThread); } + OomAdjusterModernImpl(ActivityManagerService service, ProcessList processList, + ActiveUids activeUids, Injector injector) { + super(service, processList, activeUids, injector); + } + private final ProcessRecordNodes mProcessRecordProcStateNodes = new ProcessRecordNodes( ProcessRecordNode.NODE_TYPE_PROC_STATE, PROC_STATE_SLOTS.length); private final ProcessRecordNodes mProcessRecordAdjNodes = new ProcessRecordNodes( @@ -924,8 +928,8 @@ public class OomAdjusterModernImpl extends OomAdjuster { @GuardedBy({"mService", "mProcLock"}) private void fullUpdateLSP(@OomAdjReason int oomAdjReason) { final ProcessRecord topApp = mService.getTopApp(); - final long now = SystemClock.uptimeMillis(); - final long nowElapsed = SystemClock.elapsedRealtime(); + final long now = mInjector.getUptimeMillis(); + final long nowElapsed = mInjector.getElapsedRealtimeMillis(); final long oldTime = now - mConstants.mMaxEmptyTimeMillis; mAdjSeq++; @@ -1015,8 +1019,8 @@ public class OomAdjusterModernImpl extends OomAdjuster { @GuardedBy({"mService", "mProcLock"}) private void partialUpdateLSP(@OomAdjReason int oomAdjReason, ArraySet<ProcessRecord> targets) { final ProcessRecord topApp = mService.getTopApp(); - final long now = SystemClock.uptimeMillis(); - final long nowElapsed = SystemClock.elapsedRealtime(); + final long now = mInjector.getUptimeMillis(); + final long nowElapsed = mInjector.getElapsedRealtimeMillis(); final long oldTime = now - mConstants.mMaxEmptyTimeMillis; ActiveUids activeUids = mTmpUidRecords; diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java index 219de709fb70..c0947247de4b 100644 --- a/services/core/java/com/android/server/am/ProcessList.java +++ b/services/core/java/com/android/server/am/ProcessList.java @@ -354,6 +354,7 @@ public final class ProcessList { // LMK_KILL_OCCURRED // LMK_START_MONITORING // LMK_BOOT_COMPLETED + // LMK_PROCS_PRIO static final byte LMK_TARGET = 0; static final byte LMK_PROCPRIO = 1; static final byte LMK_PROCREMOVE = 2; @@ -365,6 +366,7 @@ public final class ProcessList { static final byte LMK_KILL_OCCURRED = 8; // Msg to subscribed clients on kill occurred event static final byte LMK_START_MONITORING = 9; // Start monitoring if delayed earlier static final byte LMK_BOOT_COMPLETED = 10; + static final byte LMK_PROCS_PRIO = 11; // Batch option for LMK_PROCPRIO // Low Memory Killer Daemon command codes. // These must be kept in sync with async_event_type definitions in lmkd.h @@ -1561,6 +1563,50 @@ public final class ProcessList { } } + + // The max size for PROCS_PRIO cmd in LMKD + private static final int MAX_PROCS_PRIO_PACKET_SIZE = 3; + + // (4 bytes per field * 4 fields * 3 processes per batch) + 4 bytes for the LMKD cmd + private static final int MAX_OOM_ADJ_BATCH_LENGTH = ((4 * 4) * MAX_PROCS_PRIO_PACKET_SIZE) + 4; + + /** + * Set the out-of-memory badness adjustment for a list of processes. + * + * @param apps App list to adjust their respective oom score. + * + * {@hide} + */ + public static void batchSetOomAdj(ArrayList<ProcessRecord> apps) { + final int totalApps = apps.size(); + if (totalApps == 0) { + return; + } + + ByteBuffer buf = ByteBuffer.allocate(MAX_OOM_ADJ_BATCH_LENGTH); + int total_procs_in_buf = 0; + buf.putInt(LMK_PROCS_PRIO); + for (int i = 0; i < totalApps; i++) { + final int pid = apps.get(i).getPid(); + final int amt = apps.get(i).mState.getCurAdj(); + final int uid = apps.get(i).uid; + if (pid <= 0 || amt == UNKNOWN_ADJ) continue; + if (total_procs_in_buf >= MAX_PROCS_PRIO_PACKET_SIZE) { + writeLmkd(buf, null); + buf.clear(); + total_procs_in_buf = 0; + buf.allocate(MAX_OOM_ADJ_BATCH_LENGTH); + buf.putInt(LMK_PROCS_PRIO); + } + buf.putInt(pid); + buf.putInt(uid); + buf.putInt(amt); + buf.putInt(0); // Default proc type to PROC_TYPE_APP + total_procs_in_buf++; + } + writeLmkd(buf, null); + } + /* * {@hide} */ diff --git a/services/core/java/com/android/server/am/ProcessStateRecord.java b/services/core/java/com/android/server/am/ProcessStateRecord.java index 8de748e37b5e..7c64298a6053 100644 --- a/services/core/java/com/android/server/am/ProcessStateRecord.java +++ b/services/core/java/com/android/server/am/ProcessStateRecord.java @@ -449,6 +449,9 @@ final class ProcessStateRecord { @GuardedBy("mService") private boolean mScheduleLikeTopApp = false; + @GuardedBy("mService") + private long mFollowupUpdateUptimeMs = Long.MAX_VALUE; + ProcessStateRecord(ProcessRecord app) { mApp = app; mService = app.mService; @@ -1164,6 +1167,16 @@ final class ProcessStateRecord { mScheduleLikeTopApp = scheduleLikeTopApp; } + @GuardedBy("mService") + long getFollowupUpdateUptimeMs() { + return mFollowupUpdateUptimeMs; + } + + @GuardedBy("mService") + void setFollowupUpdateUptimeMs(long updateUptimeMs) { + mFollowupUpdateUptimeMs = updateUptimeMs; + } + @GuardedBy(anyOf = {"mService", "mProcLock"}) public String makeAdjReason() { if (mAdjSource != null || mAdjTarget != null) { diff --git a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java index 032093b91746..cbea7aa9a92c 100644 --- a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java +++ b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java @@ -141,6 +141,8 @@ public class SettingsToPropertiesMapper { "app_widgets", "arc_next", "art_mainline", + "art_performance", + "attack_tools", "avic", "biometrics", "biometrics_framework", @@ -171,6 +173,7 @@ public class SettingsToPropertiesMapper { "haptics", "hardware_backed_security_mainline", "input", + "llvm_and_toolchains", "lse_desktop_experience", "machine_learning", "mainline_modularization", diff --git a/services/core/java/com/android/server/am/flags.aconfig b/services/core/java/com/android/server/am/flags.aconfig index afde4f71a95f..bb52857a99c4 100644 --- a/services/core/java/com/android/server/am/flags.aconfig +++ b/services/core/java/com/android/server/am/flags.aconfig @@ -134,3 +134,21 @@ flag { purpose: PURPOSE_BUGFIX } } + +flag { + name: "batching_oom_adj" + namespace: "backstage_power" + description: "Batch OOM adjustment calls to LMKD" + bug: "244232958" + is_fixed_read_only: true +} + +flag { + name: "follow_up_oomadj_updates" + namespace: "backstage_power" + description: "Schedule follow up OomAdjuster updates for time sensitive states." + bug: "333450932" + metadata { + purpose: PURPOSE_BUGFIX + } +} diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java index 039e7f424552..bce183050a74 100644 --- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java +++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java @@ -275,7 +275,7 @@ public class AudioDeviceBroker { * Handle BluetoothHeadset intents where the action is one of * {@link BluetoothHeadset#ACTION_ACTIVE_DEVICE_CHANGED} or * {@link BluetoothHeadset#ACTION_AUDIO_STATE_CHANGED}. - * @param intent + * @param intent the Intent received from BT service */ private void onReceiveBtEvent(@NonNull Intent intent) { mBtHelper.onReceiveBtEvent(intent); @@ -296,7 +296,7 @@ public class AudioDeviceBroker { /** * Turns speakerphone on/off - * @param on + * @param on true to enable speakerphone * @param eventSource for logging purposes */ /*package*/ void setSpeakerphoneOn( @@ -310,21 +310,21 @@ public class AudioDeviceBroker { on, BtHelper.SCO_MODE_UNDEFINED, eventSource, isPrivileged)); } + private static final long SET_COMMUNICATION_DEVICE_TIMEOUT_MS = 3000; + + /** synchronization for setCommunicationDevice() and getCommunicationDevice */ + private final Object mCommunicationDeviceLock = new Object(); + @GuardedBy("mCommunicationDeviceLock") + private int mCommunicationDeviceUpdateCount = 0; + /** * Select device for use for communication use cases. * @param cb Client binder for death detection * @param uid Client uid * @param device Device selected or null to unselect. * @param eventSource for logging purposes + * @return false if there is no device and no communication client */ - - private static final long SET_COMMUNICATION_DEVICE_TIMEOUT_MS = 3000; - - /** synchronization for setCommunicationDevice() and getCommunicationDevice */ - private Object mCommunicationDeviceLock = new Object(); - @GuardedBy("mCommunicationDeviceLock") - private int mCommunicationDeviceUpdateCount = 0; - /*package*/ boolean setCommunicationDevice(IBinder cb, int uid, AudioDeviceInfo device, boolean isPrivileged, String eventSource) { @@ -355,7 +355,6 @@ public class AudioDeviceBroker { * Sets or resets the communication device for matching client. If no client matches and the * request is to reset for a given device (deviceInfo.mOn == false), the method is a noop. * @param deviceInfo information on the device and requester {@link #CommunicationDeviceInfo} - * @return true if the communication device is set or reset */ @GuardedBy("mDeviceStateLock") /*package*/ void onSetCommunicationDeviceForClient(CommunicationDeviceInfo deviceInfo) { @@ -742,15 +741,6 @@ public class AudioDeviceBroker { } /** - * Helper method on top of isDeviceRequestedForCommunication() indicating if - * speakerphone ON is currently requested or not. - * @return true if speakerphone ON requested, false otherwise. - */ - private boolean isSpeakerphoneRequested() { - return isDeviceRequestedForCommunication(AudioDeviceInfo.TYPE_BUILTIN_SPEAKER); - } - - /** * Indicates if preferred route selection for communication is speakerphone. * @return true if speakerphone is active, false otherwise. */ @@ -929,6 +919,12 @@ public class AudioDeviceBroker { } @Override + public int hashCode() { + // only hashing on the fields used in equals() + return Objects.hash(mProfile, mDevice); + } + + @Override public String toString() { return "BtDeviceInfo: device=" + mDevice.toString() + " state=" + mState @@ -990,7 +986,7 @@ public class AudioDeviceBroker { /** * will block on mDeviceStateLock, which is held during an A2DP (dis) connection * not just a simple message post - * @param info struct with the (dis)connection information + * @param data struct with the (dis)connection information */ /*package*/ void queueOnBluetoothActiveDeviceChanged(@NonNull BtDeviceChangedData data) { if (data.mPreviousDevice != null @@ -1379,6 +1375,7 @@ public class AudioDeviceBroker { mCommDevDispatchers.getBroadcastItem(i) .dispatchCommunicationDeviceChanged(portId); } catch (RemoteException e) { + Log.e(TAG, "dispatchCommunicationDevice error", e); } } mCommDevDispatchers.finishBroadcast(); @@ -1581,6 +1578,12 @@ public class AudioDeviceBroker { } @Override + public int hashCode() { + // only hashing on the fields used in equals() + return Objects.hash(mCb.hashCode(), mUid); + } + + @Override public String toString() { return "CommunicationDeviceInfo mCb=" + mCb.toString() + " mUid=" + mUid @@ -1600,9 +1603,9 @@ public class AudioDeviceBroker { //@GuardedBy("mConnectedDevices") /*package*/ void setBluetoothA2dpOnInt(boolean on, boolean fromA2dp, String source) { // for logging only - final String eventSource = new StringBuilder("setBluetoothA2dpOn(").append(on) - .append(") from u/pid:").append(Binder.getCallingUid()).append("/") - .append(Binder.getCallingPid()).append(" src:").append(source).toString(); + final String eventSource = "setBluetoothA2dpOn(" + on + + ") from u/pid:" + Binder.getCallingUid() + "/" + + Binder.getCallingPid() + " src:" + source; mBluetoothA2dpEnabled.set(on); onSetForceUse( @@ -2203,10 +2206,6 @@ public class AudioDeviceBroker { sendIILMsg(msg, existingMsgPolicy, 0, 0, obj, delay); } - private void sendIMsg(int msg, int existingMsgPolicy, int arg, int delay) { - sendIILMsg(msg, existingMsgPolicy, arg, 0, null, delay); - } - private void sendMsgNoDelay(int msg, int existingMsgPolicy) { sendIILMsg(msg, existingMsgPolicy, 0, 0, null, 0); } diff --git a/services/core/java/com/android/server/audio/AudioDeviceInventory.java b/services/core/java/com/android/server/audio/AudioDeviceInventory.java index 287c92f86f0f..ba7aee05159a 100644 --- a/services/core/java/com/android/server/audio/AudioDeviceInventory.java +++ b/services/core/java/com/android/server/audio/AudioDeviceInventory.java @@ -449,7 +449,7 @@ public class AudioDeviceInventory { @Override public DeviceInfo put(String key, DeviceInfo value) { final DeviceInfo result = super.put(key, value); - record("put", true /* connected */, key, value); + record("put", true /* connected */, value); return result; } @@ -457,7 +457,7 @@ public class AudioDeviceInventory { public DeviceInfo putIfAbsent(String key, DeviceInfo value) { final DeviceInfo result = super.putIfAbsent(key, value); if (result == null) { - record("putIfAbsent", true /* connected */, key, value); + record("putIfAbsent", true /* connected */, value); } return result; } @@ -466,7 +466,7 @@ public class AudioDeviceInventory { public DeviceInfo remove(Object key) { final DeviceInfo result = super.remove(key); if (result != null) { - record("remove", false /* connected */, (String) key, result); + record("remove", false /* connected */, result); } return result; } @@ -475,7 +475,7 @@ public class AudioDeviceInventory { public boolean remove(Object key, Object value) { final boolean result = super.remove(key, value); if (result) { - record("remove", false /* connected */, (String) key, (DeviceInfo) value); + record("remove", false /* connected */, (DeviceInfo) value); } return result; } @@ -489,7 +489,7 @@ public class AudioDeviceInventory { // putAll // replace // replaceAll - private void record(String event, boolean connected, String key, DeviceInfo value) { + private void record(String event, boolean connected, DeviceInfo value) { // DeviceInfo - int mDeviceType; // DeviceInfo - int mDeviceCodecFormat; new MediaMetrics.Item(MediaMetrics.Name.AUDIO_DEVICE @@ -662,7 +662,7 @@ public class AudioDeviceInventory { /** * A class just for packaging up a set of connection parameters. */ - /*package*/ class WiredDeviceConnectionState { + /*package*/ static class WiredDeviceConnectionState { public final AudioDeviceAttributes mAttributes; public final @AudioService.ConnectionState int mState; public final String mCaller; @@ -1054,7 +1054,9 @@ public class AudioDeviceInventory { IAudioRoutesObserver obs = mRoutesObservers.getBroadcastItem(n); try { obs.dispatchAudioRoutesChanged(routes); - } catch (RemoteException e) { } + } catch (RemoteException e) { + Log.e(TAG, "onReportNewRoutes", e); + } } } mRoutesObservers.finishBroadcast(); @@ -1835,7 +1837,8 @@ public class AudioDeviceInventory { .set(MediaMetrics.Property.EVENT, "disconnectHearingAid") .record(); if (toRemove.size() > 0) { - final int delay = checkSendBecomingNoisyIntentInt(DEVICE_OUT_HEARING_AID, + /*final int delay = */ + checkSendBecomingNoisyIntentInt(DEVICE_OUT_HEARING_AID, AudioService.CONNECTION_STATE_DISCONNECTED, AudioSystem.DEVICE_NONE); toRemove.stream().forEach(deviceAddress -> // TODO delay not used? @@ -2697,10 +2700,6 @@ public class AudioDeviceInventory { private static final String CONNECT_INTENT_KEY_PORT_NAME = "portName"; private static final String CONNECT_INTENT_KEY_STATE = "state"; private static final String CONNECT_INTENT_KEY_ADDRESS = "address"; - private static final String CONNECT_INTENT_KEY_HAS_PLAYBACK = "hasPlayback"; - private static final String CONNECT_INTENT_KEY_HAS_CAPTURE = "hasCapture"; - private static final String CONNECT_INTENT_KEY_HAS_MIDI = "hasMIDI"; - private static final String CONNECT_INTENT_KEY_DEVICE_CLASS = "class"; private void sendDeviceConnectionIntent(int device, int state, String address, String deviceName) { @@ -2863,6 +2862,7 @@ public class AudioDeviceInventory { mPrefDevDispatchers.getBroadcastItem(i).dispatchPrefDevicesChanged( strategy, devices); } catch (RemoteException e) { + Log.e(TAG, "dispatchPreferredDevice ", e); } } mPrefDevDispatchers.finishBroadcast(); @@ -2879,6 +2879,7 @@ public class AudioDeviceInventory { mNonDefDevDispatchers.getBroadcastItem(i).dispatchNonDefDevicesChanged( strategy, devices); } catch (RemoteException e) { + Log.e(TAG, "dispatchNonDefaultDevice ", e); } } mNonDefDevDispatchers.finishBroadcast(); @@ -2895,6 +2896,7 @@ public class AudioDeviceInventory { mDevRoleCapturePresetDispatchers.getBroadcastItem(i).dispatchDevicesRoleChanged( capturePreset, role, devices); } catch (RemoteException e) { + Log.e(TAG, "dispatchDevicesRoleForCapturePreset ", e); } } mDevRoleCapturePresetDispatchers.finishBroadcast(); @@ -2960,7 +2962,7 @@ public class AudioDeviceInventory { /** * Check if device is in the list of connected devices - * @param device + * @param device the device to query * @return true if connected */ @VisibleForTesting diff --git a/services/core/java/com/android/server/audio/AudioServerPermissionProvider.java b/services/core/java/com/android/server/audio/AudioServerPermissionProvider.java index 5ea3c4bf538d..76191bbe1a78 100644 --- a/services/core/java/com/android/server/audio/AudioServerPermissionProvider.java +++ b/services/core/java/com/android/server/audio/AudioServerPermissionProvider.java @@ -16,40 +16,76 @@ package com.android.server.audio; +import static android.Manifest.permission.CALL_AUDIO_INTERCEPTION; +import static android.Manifest.permission.MODIFY_AUDIO_ROUTING; +import static android.Manifest.permission.MODIFY_PHONE_STATE; +import static android.Manifest.permission.RECORD_AUDIO; + import android.annotation.Nullable; import android.os.RemoteException; import android.os.UserHandle; import android.util.ArraySet; +import android.util.IntArray; import com.android.internal.annotations.GuardedBy; import com.android.media.permission.INativePermissionController; +import com.android.media.permission.PermissionEnum; import com.android.media.permission.UidPackageState; import com.android.server.pm.pkg.PackageState; +import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Set; +import java.util.function.BiPredicate; +import java.util.function.Supplier; import java.util.stream.Collector; import java.util.stream.Collectors; /** Responsible for synchronizing system server permission state to the native audioserver. */ public class AudioServerPermissionProvider { + static final String[] MONITORED_PERMS = new String[PermissionEnum.ENUM_SIZE]; + + static { + MONITORED_PERMS[PermissionEnum.MODIFY_AUDIO_ROUTING] = MODIFY_AUDIO_ROUTING; + MONITORED_PERMS[PermissionEnum.MODIFY_PHONE_STATE] = MODIFY_PHONE_STATE; + MONITORED_PERMS[PermissionEnum.RECORD_AUDIO] = RECORD_AUDIO; + MONITORED_PERMS[PermissionEnum.CALL_AUDIO_INTERCEPTION] = CALL_AUDIO_INTERCEPTION; + } + private final Object mLock = new Object(); + private final Supplier<int[]> mUserIdSupplier; + private final BiPredicate<Integer, String> mPermissionPredicate; @GuardedBy("mLock") private INativePermissionController mDest; @GuardedBy("mLock") private final Map<Integer, Set<String>> mPackageMap; + // Values are sorted + @GuardedBy("mLock") + private final int[][] mPermMap = new int[PermissionEnum.ENUM_SIZE][]; + + @GuardedBy("mLock") + private boolean mIsUpdateDeferred = true; /** * @param appInfos - PackageState for all apps on the device, used to populate init state */ - public AudioServerPermissionProvider(Collection<PackageState> appInfos) { + public AudioServerPermissionProvider( + Collection<PackageState> appInfos, + BiPredicate<Integer, String> permissionPredicate, + Supplier<int[]> userIdSupplier) { + for (int i = 0; i < PermissionEnum.ENUM_SIZE; i++) { + Objects.requireNonNull(MONITORED_PERMS[i]); + } + mUserIdSupplier = userIdSupplier; + mPermissionPredicate = permissionPredicate; // Initialize the package state mPackageMap = generatePackageMappings(appInfos); } @@ -64,6 +100,18 @@ public class AudioServerPermissionProvider { synchronized (mLock) { mDest = pc; resetNativePackageState(); + try { + for (byte i = 0; i < PermissionEnum.ENUM_SIZE; i++) { + if (mIsUpdateDeferred) { + mPermMap[i] = getUidsHoldingPerm(MONITORED_PERMS[i]); + } + mDest.populatePermissionState(i, mPermMap[i]); + } + mIsUpdateDeferred = false; + } catch (RemoteException e) { + // We will re-init the state when the service comes back up + mDest = null; + } } } @@ -106,6 +154,30 @@ public class AudioServerPermissionProvider { } } + /** Called whenever any package/permission changes occur which invalidate uids holding perms */ + public void onPermissionStateChanged() { + synchronized (mLock) { + if (mDest == null) { + mIsUpdateDeferred = true; + return; + } + try { + for (byte i = 0; i < PermissionEnum.ENUM_SIZE; i++) { + var newPerms = getUidsHoldingPerm(MONITORED_PERMS[i]); + if (!Arrays.equals(newPerms, mPermMap[i])) { + mPermMap[i] = newPerms; + mDest.populatePermissionState(i, newPerms); + } + } + } catch (RemoteException e) { + // We will re-init the state when the service comes back up + mDest = null; + // We didn't necessarily finish + mIsUpdateDeferred = true; + } + } + } + /** Called when full syncing package state to audioserver. */ @GuardedBy("mLock") private void resetNativePackageState() { @@ -128,6 +200,23 @@ public class AudioServerPermissionProvider { } } + @GuardedBy("mLock") + /** Return all uids (not app-ids) which currently hold a given permission. Not app-op aware */ + private int[] getUidsHoldingPerm(String perm) { + IntArray acc = new IntArray(); + for (int userId : mUserIdSupplier.get()) { + for (int appId : mPackageMap.keySet()) { + int uid = UserHandle.getUid(userId, appId); + if (mPermissionPredicate.test(uid, perm)) { + acc.add(uid); + } + } + } + var unwrapped = acc.toArray(); + Arrays.sort(unwrapped); + return unwrapped; + } + /** * Aggregation operation on all package states list: groups by states by app-id and merges the * packageName for each state into an ArraySet. diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index ef65b2523024..5dd1480c2052 100644 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -219,6 +219,7 @@ import android.os.VibrationAttributes; import android.os.VibrationEffect; import android.os.Vibrator; import android.os.VibratorManager; +import android.permission.PermissionManager; import android.provider.Settings; import android.provider.Settings.System; import android.service.notification.ZenModeConfig; @@ -256,6 +257,7 @@ import com.android.server.pm.PackageManagerLocal; import com.android.server.pm.UserManagerInternal; import com.android.server.pm.UserManagerInternal.UserRestrictionsListener; import com.android.server.pm.UserManagerService; +import com.android.server.pm.permission.PermissionManagerServiceInternal; import com.android.server.pm.pkg.PackageState; import com.android.server.utils.EventLogger; import com.android.server.wm.ActivityTaskManagerInternal; @@ -284,6 +286,8 @@ import java.util.concurrent.Executor; import java.util.concurrent.Executors; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicLong; +import java.util.concurrent.atomic.AtomicReference; import java.util.function.BooleanSupplier; import java.util.stream.Collectors; @@ -10327,7 +10331,7 @@ public class AudioService extends IAudioService.Stub try { if (!permissionOverridesCheck && mHardeningEnforcer.blockFocusMethod(uid, HardeningEnforcer.METHOD_AUDIO_MANAGER_REQUEST_AUDIO_FOCUS, - clientId, durationHint, callingPackageName)) { + clientId, durationHint, callingPackageName, attributionTag, sdk)) { final String reason = "Audio focus request blocked by hardening"; Log.w(TAG, reason); mmi.set(MediaMetrics.Property.EARLY_RETURN, reason).record(); @@ -10339,7 +10343,7 @@ public class AudioService extends IAudioService.Stub mmi.record(); return mMediaFocusControl.requestAudioFocus(aa, durationHint, cb, fd, - clientId, callingPackageName, attributionTag, flags, sdk, + clientId, callingPackageName, flags, sdk, forceFocusDuckingForAccessibility(aa, durationHint, uid), -1 /*testUid, ignored*/, permissionOverridesCheck); } @@ -10357,7 +10361,7 @@ public class AudioService extends IAudioService.Stub return AudioManager.AUDIOFOCUS_REQUEST_FAILED; } return mMediaFocusControl.requestAudioFocus(aa, durationHint, cb, fd, - clientId, callingPackageName, null, flags, + clientId, callingPackageName, flags, sdk, false /*forceDuck*/, fakeUid, true /*permissionOverridesCheck*/); } @@ -11950,12 +11954,40 @@ public class AudioService extends IAudioService.Stub .withUnfilteredSnapshot()) { packageStates = snapshot.getPackageStates().values(); } - var provider = new AudioServerPermissionProvider(packageStates); + var umi = LocalServices.getService(UserManagerInternal.class); + var pmsi = LocalServices.getService(PermissionManagerServiceInternal.class); + var provider = new AudioServerPermissionProvider(packageStates, + (Integer uid, String perm) -> (pmsi.checkUidPermission(uid, perm, + Context.DEVICE_ID_DEFAULT) == PackageManager.PERMISSION_GRANTED), + () -> umi.getUserIds() + ); audioPolicy.registerOnStartTask(() -> { provider.onServiceStart(audioPolicy.getPermissionController()); }); // Set up event listeners + // Must be kept in sync with PermissionManager + Runnable cacheSysPropHandler = new Runnable() { + private AtomicReference<SystemProperties.Handle> mHandle = new AtomicReference(); + private AtomicLong mNonce = new AtomicLong(); + @Override + public void run() { + if (mHandle.get() == null) { + // Cache the handle + mHandle.compareAndSet(null, SystemProperties.find( + PermissionManager.CACHE_KEY_PACKAGE_INFO)); + } + long nonce; + SystemProperties.Handle ref; + if ((ref = mHandle.get()) != null && (nonce = ref.getLong(0)) != 0 && + mNonce.getAndSet(nonce) != nonce) { + audioserverExecutor.execute(() -> provider.onPermissionStateChanged()); + } + } + }; + + SystemProperties.addChangeCallback(cacheSysPropHandler); + IntentFilter packageUpdateFilter = new IntentFilter(); packageUpdateFilter.addAction(ACTION_PACKAGE_ADDED); packageUpdateFilter.addAction(ACTION_PACKAGE_REMOVED); diff --git a/services/core/java/com/android/server/audio/HardeningEnforcer.java b/services/core/java/com/android/server/audio/HardeningEnforcer.java index 409ed17001b7..8ae04accb62f 100644 --- a/services/core/java/com/android/server/audio/HardeningEnforcer.java +++ b/services/core/java/com/android/server/audio/HardeningEnforcer.java @@ -19,6 +19,7 @@ import static android.media.audio.Flags.autoPublicVolumeApiHardening; import android.Manifest; import android.annotation.NonNull; +import android.annotation.Nullable; import android.app.ActivityManager; import android.app.AppOpsManager; import android.content.Context; @@ -26,6 +27,7 @@ import android.content.pm.PackageManager; import android.media.AudioFocusRequest; import android.media.AudioManager; import android.os.Binder; +import android.os.Build; import android.os.UserHandle; import android.text.TextUtils; import android.util.Slog; @@ -128,19 +130,28 @@ public class HardeningEnforcer { * @param focusMethod name of the method to check, for logging purposes * @param clientId id of the requester * @param durationHint focus type being requested + * @param attributionTag attribution of the caller + * @param targetSdk target SDK of the caller * @return false if the method call is allowed, true if it should be a no-op */ + @SuppressWarnings("AndroidFrameworkCompatChange") protected boolean blockFocusMethod(int callingUid, int focusMethod, @NonNull String clientId, - int durationHint, @NonNull String packageName) { + int durationHint, @NonNull String packageName, String attributionTag, int targetSdk) { if (packageName.isEmpty()) { packageName = getPackNameForUid(callingUid); } - if (checkAppOp(AppOpsManager.OP_TAKE_AUDIO_FOCUS, callingUid, packageName)) { + if (noteOp(AppOpsManager.OP_TAKE_AUDIO_FOCUS, callingUid, packageName, attributionTag)) { if (DEBUG) { Slog.i(TAG, "blockFocusMethod pack:" + packageName + " NOT blocking"); } return false; + } else if (targetSdk < Build.VERSION_CODES.VANILLA_ICE_CREAM) { + if (DEBUG) { + Slog.i(TAG, "blockFocusMethod pack:" + packageName + " NOT blocking due to sdk=" + + targetSdk); + } + return false; } String errorMssg = "Focus request DENIED for uid:" + callingUid @@ -169,14 +180,17 @@ public class HardeningEnforcer { } /** - * Checks the given op without throwing + * Notes the given op without throwing * @param op the appOp code * @param uid the calling uid * @param packageName the package name of the caller + * @param attributionTag attribution of the caller * @return return false if the operation is not allowed */ - private boolean checkAppOp(int op, int uid, @NonNull String packageName) { - if (mAppOps.checkOpNoThrow(op, uid, packageName) != AppOpsManager.MODE_ALLOWED) { + private boolean noteOp(int op, int uid, @NonNull String packageName, + @Nullable String attributionTag) { + if (mAppOps.noteOpNoThrow(op, uid, packageName, attributionTag, null) + != AppOpsManager.MODE_ALLOWED) { return false; } return true; diff --git a/services/core/java/com/android/server/audio/MediaFocusControl.java b/services/core/java/com/android/server/audio/MediaFocusControl.java index 35d38e2373f5..70f319321d30 100644 --- a/services/core/java/com/android/server/audio/MediaFocusControl.java +++ b/services/core/java/com/android/server/audio/MediaFocusControl.java @@ -1082,7 +1082,6 @@ public class MediaFocusControl implements PlayerFocusEnforcer { * @param fd * @param clientId * @param callingPackageName - * @param attributionTag * @param flags * @param sdk * @param forceDuck only true if @@ -1096,7 +1095,7 @@ public class MediaFocusControl implements PlayerFocusEnforcer { */ protected int requestAudioFocus(@NonNull AudioAttributes aa, int focusChangeHint, IBinder cb, IAudioFocusDispatcher fd, @NonNull String clientId, @NonNull String callingPackageName, - String attributionTag, int flags, int sdk, boolean forceDuck, int testUid, + int flags, int sdk, boolean forceDuck, int testUid, boolean permissionOverridesCheck) { new MediaMetrics.Item(mMetricsId) .setUid(Binder.getCallingUid()) @@ -1129,12 +1128,6 @@ public class MediaFocusControl implements PlayerFocusEnforcer { return AudioManager.AUDIOFOCUS_REQUEST_FAILED; } - final int res = mAppOps.noteOp(AppOpsManager.OP_TAKE_AUDIO_FOCUS, Binder.getCallingUid(), - callingPackageName, attributionTag, null); - if (!permissionOverridesCheck && res != AppOpsManager.MODE_ALLOWED) { - return AudioManager.AUDIOFOCUS_REQUEST_FAILED; - } - synchronized(mAudioFocusLock) { // check whether a focus freeze is in place and filter if (isFocusFrozenForTest()) { diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java index f9f56ee4d134..266093229186 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java @@ -316,6 +316,8 @@ public class FingerprintAuthenticationClient if (getBiometricContext().isAwake()) { mALSProbeCallback.getProbe().enable(); + } else { + mALSProbeCallback.getProbe().disable(); } } catch (RemoteException e) { Slog.e(TAG, "Remote exception", e); diff --git a/services/core/java/com/android/server/broadcastradio/IRadioServiceAidlImpl.java b/services/core/java/com/android/server/broadcastradio/IRadioServiceAidlImpl.java index e6de14bcf9aa..16514fa813dc 100644 --- a/services/core/java/com/android/server/broadcastradio/IRadioServiceAidlImpl.java +++ b/services/core/java/com/android/server/broadcastradio/IRadioServiceAidlImpl.java @@ -29,6 +29,7 @@ import android.os.Binder; import android.os.IBinder; import android.os.RemoteException; import android.os.ServiceManager; +import android.util.IndentingPrintWriter; import android.util.Log; import com.android.internal.annotations.VisibleForTesting; @@ -121,8 +122,7 @@ final class IRadioServiceAidlImpl extends IRadioService.Stub { + " without permission " + Manifest.permission.DUMP); return; } - android.util.IndentingPrintWriter radioPrintWriter = - new android.util.IndentingPrintWriter(printWriter); + IndentingPrintWriter radioPrintWriter = new IndentingPrintWriter(printWriter); radioPrintWriter.printf("BroadcastRadioService\n"); radioPrintWriter.increaseIndent(); diff --git a/services/core/java/com/android/server/broadcastradio/IRadioServiceHidlImpl.java b/services/core/java/com/android/server/broadcastradio/IRadioServiceHidlImpl.java index 93fb7b2525fb..ab083429a200 100644 --- a/services/core/java/com/android/server/broadcastradio/IRadioServiceHidlImpl.java +++ b/services/core/java/com/android/server/broadcastradio/IRadioServiceHidlImpl.java @@ -26,6 +26,7 @@ import android.hardware.radio.ITunerCallback; import android.hardware.radio.RadioManager; import android.os.Binder; import android.os.RemoteException; +import android.util.IndentingPrintWriter; import android.util.Log; import android.util.Slog; @@ -138,7 +139,7 @@ final class IRadioServiceHidlImpl extends IRadioService.Stub { + " without permission " + Manifest.permission.DUMP); return; } - android.util.IndentingPrintWriter radioPw = new android.util.IndentingPrintWriter(pw); + IndentingPrintWriter radioPw = new IndentingPrintWriter(pw); radioPw.printf("BroadcastRadioService\n"); radioPw.increaseIndent(); diff --git a/services/core/java/com/android/server/broadcastradio/RadioEventLogger.java b/services/core/java/com/android/server/broadcastradio/RadioEventLogger.java index 2c8f499c619b..b71589c2196c 100644 --- a/services/core/java/com/android/server/broadcastradio/RadioEventLogger.java +++ b/services/core/java/com/android/server/broadcastradio/RadioEventLogger.java @@ -17,6 +17,7 @@ package com.android.server.broadcastradio; import android.text.TextUtils; +import android.util.IndentingPrintWriter; import android.util.LocalLog; import android.util.Log; @@ -54,7 +55,7 @@ public final class RadioEventLogger { * Dump broadcast radio service event * @param pw Indenting print writer for dump */ - public void dump(android.util.IndentingPrintWriter pw) { + public void dump(IndentingPrintWriter pw) { mEventLogger.dump(pw); } } diff --git a/services/core/java/com/android/server/broadcastradio/aidl/AnnouncementAggregator.java b/services/core/java/com/android/server/broadcastradio/aidl/AnnouncementAggregator.java index 9654a93d2036..b618aa3d65dc 100644 --- a/services/core/java/com/android/server/broadcastradio/aidl/AnnouncementAggregator.java +++ b/services/core/java/com/android/server/broadcastradio/aidl/AnnouncementAggregator.java @@ -22,6 +22,7 @@ import android.hardware.radio.IAnnouncementListener; import android.hardware.radio.ICloseHandle; import android.os.IBinder; import android.os.RemoteException; +import android.util.IndentingPrintWriter; import android.util.Log; import com.android.internal.annotations.GuardedBy; @@ -93,7 +94,7 @@ public final class AnnouncementAggregator extends ICloseHandle.Stub { if (mCloseHandle != null) mCloseHandle.close(); } - public void dumpInfo(android.util.IndentingPrintWriter pw) { + public void dumpInfo(IndentingPrintWriter pw) { pw.printf("ModuleWatcher:\n"); pw.increaseIndent(); @@ -191,8 +192,7 @@ public final class AnnouncementAggregator extends ICloseHandle.Stub { @Override protected void dump(FileDescriptor fd, PrintWriter printWriter, String[] args) { - android.util.IndentingPrintWriter announcementPrintWriter = - new android.util.IndentingPrintWriter(printWriter); + IndentingPrintWriter announcementPrintWriter = new IndentingPrintWriter(printWriter); announcementPrintWriter.printf("AnnouncementAggregator\n"); announcementPrintWriter.increaseIndent(); diff --git a/services/core/java/com/android/server/broadcastradio/aidl/BroadcastRadioServiceImpl.java b/services/core/java/com/android/server/broadcastradio/aidl/BroadcastRadioServiceImpl.java index 1c421614599d..d9f8588039da 100644 --- a/services/core/java/com/android/server/broadcastradio/aidl/BroadcastRadioServiceImpl.java +++ b/services/core/java/com/android/server/broadcastradio/aidl/BroadcastRadioServiceImpl.java @@ -29,6 +29,7 @@ import android.os.IServiceCallback; import android.os.RemoteException; import android.os.ServiceManager; import android.util.ArrayMap; +import android.util.IndentingPrintWriter; import android.util.Log; import android.util.SparseArray; @@ -128,7 +129,7 @@ public final class BroadcastRadioServiceImpl { if (entry.getValue() == mModuleId) { Slogf.w(TAG, "Service %s died, removed RadioModule with ID %d", entry.getKey(), mModuleId); - return; + break; } } } @@ -260,7 +261,7 @@ public final class BroadcastRadioServiceImpl { * * @param pw The file to which {@link BroadcastRadioServiceImpl} state is dumped. */ - public void dumpInfo(android.util.IndentingPrintWriter pw) { + public void dumpInfo(IndentingPrintWriter pw) { synchronized (mLock) { pw.printf("Next module id available: %d\n", mNextModuleId); pw.printf("ServiceName to module id map:\n"); diff --git a/services/core/java/com/android/server/broadcastradio/aidl/ConversionUtils.java b/services/core/java/com/android/server/broadcastradio/aidl/ConversionUtils.java index 5b77c5214dd9..077e8eea485b 100644 --- a/services/core/java/com/android/server/broadcastradio/aidl/ConversionUtils.java +++ b/services/core/java/com/android/server/broadcastradio/aidl/ConversionUtils.java @@ -446,6 +446,7 @@ final class ConversionUtils { sel.secondaryIds[i]); if (id == null) { Slogf.e(TAG, "invalid secondary id: %s", sel.secondaryIds[i]); + continue; } secondaryIdList.add(id); } diff --git a/services/core/java/com/android/server/broadcastradio/aidl/RadioModule.java b/services/core/java/com/android/server/broadcastradio/aidl/RadioModule.java index 0cac35641ed0..03e347a903b5 100644 --- a/services/core/java/com/android/server/broadcastradio/aidl/RadioModule.java +++ b/services/core/java/com/android/server/broadcastradio/aidl/RadioModule.java @@ -38,6 +38,7 @@ import android.os.Looper; import android.os.RemoteException; import android.os.UserHandle; import android.util.ArraySet; +import android.util.IndentingPrintWriter; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; @@ -524,7 +525,7 @@ final class RadioModule { return BitmapFactory.decodeByteArray(rawImage, 0, rawImage.length); } - void dumpInfo(android.util.IndentingPrintWriter pw) { + void dumpInfo(IndentingPrintWriter pw) { pw.printf("RadioModule\n"); pw.increaseIndent(); diff --git a/services/core/java/com/android/server/broadcastradio/aidl/TunerSession.java b/services/core/java/com/android/server/broadcastradio/aidl/TunerSession.java index 925f149b12cf..e90a1dda6cf5 100644 --- a/services/core/java/com/android/server/broadcastradio/aidl/TunerSession.java +++ b/services/core/java/com/android/server/broadcastradio/aidl/TunerSession.java @@ -29,6 +29,7 @@ import android.os.Binder; import android.os.RemoteException; import android.util.ArrayMap; import android.util.ArraySet; +import android.util.IndentingPrintWriter; import com.android.internal.annotations.GuardedBy; import com.android.server.broadcastradio.RadioEventLogger; @@ -434,7 +435,7 @@ final class TunerSession extends ITuner.Stub { } } - void dumpInfo(android.util.IndentingPrintWriter pw) { + void dumpInfo(IndentingPrintWriter pw) { pw.printf("TunerSession\n"); pw.increaseIndent(); diff --git a/services/core/java/com/android/server/broadcastradio/hal2/BroadcastRadioService.java b/services/core/java/com/android/server/broadcastradio/hal2/BroadcastRadioService.java index e1650c227266..a4efa2e330f8 100644 --- a/services/core/java/com/android/server/broadcastradio/hal2/BroadcastRadioService.java +++ b/services/core/java/com/android/server/broadcastradio/hal2/BroadcastRadioService.java @@ -30,6 +30,7 @@ import android.hidl.manager.V1_0.IServiceNotification; import android.os.IHwBinder.DeathRecipient; import android.os.RemoteException; import android.util.ArrayMap; +import android.util.IndentingPrintWriter; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; @@ -115,7 +116,7 @@ public final class BroadcastRadioService { if (entry.getValue() == moduleId) { Slogf.i(TAG, "service " + entry.getKey() + " died; removed RadioModule with ID " + moduleId); - return; + break; } } } @@ -221,7 +222,7 @@ public final class BroadcastRadioService { * * @param pw The file to which BroadcastRadioService state is dumped. */ - public void dumpInfo(android.util.IndentingPrintWriter pw) { + public void dumpInfo(IndentingPrintWriter pw) { synchronized (mLock) { pw.printf("Next module id available: %d\n", mNextModuleId); pw.printf("ServiceName to module id map:\n"); diff --git a/services/core/java/com/android/server/broadcastradio/hal2/Convert.java b/services/core/java/com/android/server/broadcastradio/hal2/Convert.java index 34bfa6cb2d46..02a9f0963e5f 100644 --- a/services/core/java/com/android/server/broadcastradio/hal2/Convert.java +++ b/services/core/java/com/android/server/broadcastradio/hal2/Convert.java @@ -304,10 +304,7 @@ final class Convert { private static boolean isEmpty( @NonNull android.hardware.broadcastradio.V2_0.ProgramSelector sel) { - if (sel.primaryId.type != 0) return false; - if (sel.primaryId.value != 0) return false; - if (!sel.secondaryIds.isEmpty()) return false; - return true; + return sel.primaryId.type == 0 && sel.primaryId.value == 0 && sel.secondaryIds.isEmpty(); } static @Nullable ProgramSelector programSelectorFromHal( diff --git a/services/core/java/com/android/server/broadcastradio/hal2/RadioModule.java b/services/core/java/com/android/server/broadcastradio/hal2/RadioModule.java index 7269f24fc4b1..d3b244886a64 100644 --- a/services/core/java/com/android/server/broadcastradio/hal2/RadioModule.java +++ b/services/core/java/com/android/server/broadcastradio/hal2/RadioModule.java @@ -40,6 +40,7 @@ import android.os.Looper; import android.os.RemoteException; import android.os.UserHandle; import android.util.ArraySet; +import android.util.IndentingPrintWriter; import android.util.MutableInt; import com.android.internal.annotations.GuardedBy; @@ -453,7 +454,7 @@ final class RadioModule { return BitmapFactory.decodeByteArray(rawImage, 0, rawImage.length); } - void dumpInfo(android.util.IndentingPrintWriter pw) { + void dumpInfo(IndentingPrintWriter pw) { pw.printf("RadioModule\n"); pw.increaseIndent(); pw.printf("BroadcastRadioService: %s\n", mService); diff --git a/services/core/java/com/android/server/broadcastradio/hal2/TunerSession.java b/services/core/java/com/android/server/broadcastradio/hal2/TunerSession.java index b1b5d3488a5b..80efacdb12ee 100644 --- a/services/core/java/com/android/server/broadcastradio/hal2/TunerSession.java +++ b/services/core/java/com/android/server/broadcastradio/hal2/TunerSession.java @@ -31,6 +31,7 @@ import android.os.Binder; import android.os.RemoteException; import android.util.ArrayMap; import android.util.ArraySet; +import android.util.IndentingPrintWriter; import android.util.MutableBoolean; import android.util.MutableInt; @@ -324,9 +325,7 @@ final class TunerSession extends ITuner.Stub { try { isConfigFlagSet(flag); return true; - } catch (IllegalStateException ex) { - return true; - } catch (UnsupportedOperationException ex) { + } catch (IllegalStateException | UnsupportedOperationException ex) { return false; } } @@ -389,7 +388,7 @@ final class TunerSession extends ITuner.Stub { } } - void dumpInfo(android.util.IndentingPrintWriter pw) { + void dumpInfo(IndentingPrintWriter pw) { pw.printf("TunerSession\n"); pw.increaseIndent(); pw.printf("HIDL HAL Session: %s\n", mHwSession); diff --git a/services/core/java/com/android/server/camera/CameraServiceProxy.java b/services/core/java/com/android/server/camera/CameraServiceProxy.java index 390ee96a3417..17835b2d085b 100644 --- a/services/core/java/com/android/server/camera/CameraServiceProxy.java +++ b/services/core/java/com/android/server/camera/CameraServiceProxy.java @@ -18,6 +18,10 @@ package com.android.server.camera; import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE; import static android.os.Build.VERSION_CODES.M; +import static com.android.internal.util.FrameworkStatsLog.CAMERA_FEATURE_COMBINATION_QUERY_EVENT__STATUS_CODE__OK; +import static com.android.internal.util.FrameworkStatsLog.CAMERA_FEATURE_COMBINATION_QUERY_EVENT__STATUS_CODE__ERROR_ILLEGAL_ARGUMENT; +import static com.android.internal.util.FrameworkStatsLog.CAMERA_FEATURE_COMBINATION_QUERY_EVENT__STATUS_CODE__ERROR_INVALID_OPERATION; + import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; @@ -40,6 +44,7 @@ import android.content.res.Configuration; import android.graphics.ImageFormat; import android.graphics.Rect; import android.hardware.CameraExtensionSessionStats; +import android.hardware.CameraFeatureCombinationStats; import android.hardware.CameraSessionStats; import android.hardware.CameraStreamStats; import android.hardware.ICameraService; @@ -217,7 +222,7 @@ public class CameraServiceProxy extends SystemService // Map of currently active camera IDs private final ArrayMap<String, CameraUsageEvent> mActiveCameraUsage = new ArrayMap<>(); - private final List<CameraUsageEvent> mCameraUsageHistory = new ArrayList<>(); + private final List<CameraEvent> mCameraEventHistory = new ArrayList<CameraEvent>(); private static final String NFC_NOTIFICATION_PROP = "ro.camera.notify_nfc"; private static final IBinder nfcInterfaceToken = new Binder(); @@ -228,9 +233,16 @@ public class CameraServiceProxy extends SystemService /*corePoolSize*/ 1); /** + * Interface to track camera analytics + */ + private interface CameraEvent { + void logSelf(); + } + + /** * Structure to track camera usage */ - private static class CameraUsageEvent { + private static class CameraUsageEvent implements CameraEvent { public final String mCameraId; public final int mCameraFacing; public final String mClientName; @@ -311,6 +323,214 @@ public class CameraServiceProxy extends SystemService public long getDuration() { return mCompleted ? mDurationOrStartTimeMs : 0; } + + public void logSelf() { + int facing = FrameworkStatsLog.CAMERA_ACTION_EVENT__FACING__UNKNOWN; + switch(mCameraFacing) { + case CameraSessionStats.CAMERA_FACING_BACK: + facing = FrameworkStatsLog.CAMERA_ACTION_EVENT__FACING__BACK; + break; + case CameraSessionStats.CAMERA_FACING_FRONT: + facing = FrameworkStatsLog.CAMERA_ACTION_EVENT__FACING__FRONT; + break; + case CameraSessionStats.CAMERA_FACING_EXTERNAL: + facing = FrameworkStatsLog.CAMERA_ACTION_EVENT__FACING__EXTERNAL; + break; + default: + Slog.w(TAG, "Unknown camera facing: " + mCameraFacing); + } + + int extensionType = FrameworkStatsLog.CAMERA_ACTION_EVENT__EXT_TYPE__EXTENSION_NONE; + boolean extensionIsAdvanced = false; + int extensionCaptureFormat = ImageFormat.UNKNOWN; + if (mExtSessionStats != null) { + switch (mExtSessionStats.type) { + case CameraExtensionSessionStats.Type.EXTENSION_AUTOMATIC: + extensionType = FrameworkStatsLog + .CAMERA_ACTION_EVENT__EXT_TYPE__EXTENSION_AUTOMATIC; + break; + case CameraExtensionSessionStats.Type.EXTENSION_FACE_RETOUCH: + extensionType = FrameworkStatsLog + .CAMERA_ACTION_EVENT__EXT_TYPE__EXTENSION_FACE_RETOUCH; + break; + case CameraExtensionSessionStats.Type.EXTENSION_BOKEH: + extensionType = + FrameworkStatsLog.CAMERA_ACTION_EVENT__EXT_TYPE__EXTENSION_BOKEH; + break; + case CameraExtensionSessionStats.Type.EXTENSION_HDR: + extensionType = + FrameworkStatsLog.CAMERA_ACTION_EVENT__EXT_TYPE__EXTENSION_HDR; + break; + case CameraExtensionSessionStats.Type.EXTENSION_NIGHT: + extensionType = + FrameworkStatsLog.CAMERA_ACTION_EVENT__EXT_TYPE__EXTENSION_NIGHT; + break; + default: + Slog.w(TAG, "Unknown extension type: " + mExtSessionStats.type); + } + extensionIsAdvanced = mExtSessionStats.isAdvanced; + if (Flags.analytics24q3()) { + extensionCaptureFormat = mExtSessionStats.captureFormat; + } + } + + int streamCount = 0; + if (mStreamStats != null) { + streamCount = mStreamStats.size(); + } + if (CameraServiceProxy.DEBUG) { + String ultrawideDebug = Flags.logUltrawideUsage() + ? ", wideAngleUsage " + mUsedUltraWide + : ""; + String zoomOverrideDebug = Flags.logZoomOverrideUsage() + ? ", zoomOverrideUsage " + mUsedZoomOverride + : ""; + String mostRequestedFpsRangeDebug = Flags.analytics24q3() + ? ", mostRequestedFpsRange " + mMostRequestedFpsRange + : ""; + String extensionCaptureFormatDebug = Flags.analytics24q3() + ? " extensionCaptureFormat " + mExtSessionStats.captureFormat + : ""; + + Slog.v(TAG, "CAMERA_ACTION_EVENT: action " + mAction + + " clientName " + mClientName + + ", duration " + getDuration() + + ", APILevel " + mAPILevel + + ", cameraId " + mCameraId + + ", facing " + facing + + ", isNdk " + mIsNdk + + ", latencyMs " + mLatencyMs + + ", operatingMode " + mOperatingMode + + ", internalReconfigure " + mInternalReconfigure + + ", requestCount " + mRequestCount + + ", resultErrorCount " + mResultErrorCount + + ", deviceError " + mDeviceError + + ", streamCount is " + streamCount + + ", userTag is " + mUserTag + + ", videoStabilizationMode " + mVideoStabilizationMode + + ultrawideDebug + + zoomOverrideDebug + + mostRequestedFpsRangeDebug + + ", logId " + mLogId + + ", sessionIndex " + mSessionIndex + + ", mExtSessionStats {type " + extensionType + + " isAdvanced " + extensionIsAdvanced + + extensionCaptureFormatDebug + "}"); + } + + // Convert from CameraStreamStats to CameraStreamProto + CameraStreamProto[] streamProtos = new CameraStreamProto[MAX_STREAM_STATISTICS]; + for (int i = 0; i < MAX_STREAM_STATISTICS; i++) { + streamProtos[i] = new CameraStreamProto(); + if (i < streamCount) { + CameraStreamStats streamStats = mStreamStats.get(i); + streamProtos[i].width = streamStats.getWidth(); + streamProtos[i].height = streamStats.getHeight(); + streamProtos[i].format = streamStats.getFormat(); + streamProtos[i].dataSpace = streamStats.getDataSpace(); + streamProtos[i].usage = streamStats.getUsage(); + streamProtos[i].requestCount = streamStats.getRequestCount(); + streamProtos[i].errorCount = streamStats.getErrorCount(); + streamProtos[i].firstCaptureLatencyMillis = streamStats.getStartLatencyMs(); + streamProtos[i].maxHalBuffers = streamStats.getMaxHalBuffers(); + streamProtos[i].maxAppBuffers = streamStats.getMaxAppBuffers(); + streamProtos[i].histogramType = streamStats.getHistogramType(); + streamProtos[i].histogramBins = streamStats.getHistogramBins(); + streamProtos[i].histogramCounts = streamStats.getHistogramCounts(); + streamProtos[i].dynamicRangeProfile = streamStats.getDynamicRangeProfile(); + streamProtos[i].streamUseCase = streamStats.getStreamUseCase(); + streamProtos[i].colorSpace = streamStats.getColorSpace(); + + if (CameraServiceProxy.DEBUG) { + String histogramTypeName = + cameraHistogramTypeToString(streamProtos[i].histogramType); + Slog.v(TAG, "Stream " + i + ": width " + streamProtos[i].width + + ", height " + streamProtos[i].height + + ", format " + streamProtos[i].format + + ", maxPreviewFps " + streamStats.getMaxPreviewFps() + + ", dataSpace " + streamProtos[i].dataSpace + + ", usage " + streamProtos[i].usage + + ", requestCount " + streamProtos[i].requestCount + + ", errorCount " + streamProtos[i].errorCount + + ", firstCaptureLatencyMillis " + + streamProtos[i].firstCaptureLatencyMillis + + ", maxHalBuffers " + streamProtos[i].maxHalBuffers + + ", maxAppBuffers " + streamProtos[i].maxAppBuffers + + ", histogramType " + histogramTypeName + + ", histogramBins " + + Arrays.toString(streamProtos[i].histogramBins) + + ", histogramCounts " + + Arrays.toString(streamProtos[i].histogramCounts) + + ", dynamicRangeProfile " + streamProtos[i].dynamicRangeProfile + + ", streamUseCase " + streamProtos[i].streamUseCase + + ", colorSpace " + streamProtos[i].colorSpace); + } + } + } + FrameworkStatsLog.write(FrameworkStatsLog.CAMERA_ACTION_EVENT, getDuration(), + mAPILevel, mClientName, facing, mCameraId, mAction, mIsNdk, + mLatencyMs, mOperatingMode, mInternalReconfigure, + mRequestCount, mResultErrorCount, mDeviceError, + streamCount, MessageNano.toByteArray(streamProtos[0]), + MessageNano.toByteArray(streamProtos[1]), + MessageNano.toByteArray(streamProtos[2]), + MessageNano.toByteArray(streamProtos[3]), + MessageNano.toByteArray(streamProtos[4]), + mUserTag, mVideoStabilizationMode, + mLogId, mSessionIndex, + extensionType, extensionIsAdvanced, mUsedUltraWide, + mUsedZoomOverride, + mMostRequestedFpsRange.getLower(), mMostRequestedFpsRange.getUpper(), + extensionCaptureFormat); + + } + } + /** + * Structure to track feature combination query + */ + private static class CameraFeatureCombinationQueryEvent implements CameraEvent { + private CameraFeatureCombinationStats mFeatureCombinationStats; + + CameraFeatureCombinationQueryEvent(CameraFeatureCombinationStats featureCombinationStats) { + mFeatureCombinationStats = featureCombinationStats; + } + + public void logSelf() { + int statusCode = -1; + switch (mFeatureCombinationStats.mStatus) { + case 0: + statusCode = CAMERA_FEATURE_COMBINATION_QUERY_EVENT__STATUS_CODE__OK; + break; + case ICameraService.ERROR_ILLEGAL_ARGUMENT: + statusCode = CAMERA_FEATURE_COMBINATION_QUERY_EVENT__STATUS_CODE__ERROR_ILLEGAL_ARGUMENT; + break; + case ICameraService.ERROR_INVALID_OPERATION: + statusCode = CAMERA_FEATURE_COMBINATION_QUERY_EVENT__STATUS_CODE__ERROR_INVALID_OPERATION; + break; + default: + break; + } + if (statusCode == -1) { + Slog.w(TAG, "Unknown feature combination query status code: " + + mFeatureCombinationStats.mStatus); + return; + } + + if (CameraServiceProxy.DEBUG) { + Slog.v(TAG, "CAMERA_FEATURE_COMBINATION_QUERY_EVENT: uid " + + mFeatureCombinationStats.mUid + + ", cameraId " + mFeatureCombinationStats.mCameraId + + ", queryType " + mFeatureCombinationStats.mQueryType + + ", featureCombination " + mFeatureCombinationStats.mFeatureCombination + + ", status " + statusCode); + } + FrameworkStatsLog.write(FrameworkStatsLog.CAMERA_FEATURE_COMBINATION_QUERY_EVENT, + mFeatureCombinationStats.mUid, + mFeatureCombinationStats.mCameraId, + mFeatureCombinationStats.mQueryType, + mFeatureCombinationStats.mFeatureCombination, + statusCode); + } } private final class DisplayWindowListener extends IDisplayWindowListener.Stub { @@ -625,6 +845,32 @@ public class CameraServiceProxy extends SystemService } @Override + public void notifyFeatureCombinationStats(CameraFeatureCombinationStats featureCombStats) { + if (!Flags.analytics24q3()) { + return; + } + if (Binder.getCallingUid() != Process.CAMERASERVER_UID) { + Slog.e(TAG, "Calling UID: " + Binder.getCallingUid() + " doesn't match expected " + + " camera service UID!"); + return; + } + + if (DEBUG) { + String combinationTypeStr = cameraFeatureCombinationTypeToString( + featureCombStats.mQueryType); + String combinationStr = cameraFeatureCombinationToString( + featureCombStats.mFeatureCombination); + Slog.v(TAG, "Camera " + featureCombStats.mCameraId + + " query " + combinationTypeStr + + " combination " + combinationStr + + " for client UID " + featureCombStats.mUid + + " returns " + featureCombStats.mStatus); + } + + updateFeatureCombinationQuery(featureCombStats); + } + + @Override public boolean isCameraDisabled(int userId) { if (Binder.getCallingUid() != Process.CAMERASERVER_UID) { Slog.e(TAG, "Calling UID: " + Binder.getCallingUid() @@ -684,7 +930,7 @@ public class CameraServiceProxy extends SystemService switch (cmd.replace('-', '_')) { case "dump_events": int eventCount = mCameraServiceProxy.getUsageEventCount(); - mCameraServiceProxy.dumpUsageEvents(); + mCameraServiceProxy.dumpCameraEvents(); pw.println("Camera usage events dumped: " + eventCount); break; default: @@ -865,18 +1111,18 @@ public class CameraServiceProxy extends SystemService } private class EventWriterTask implements Runnable { - private ArrayList<CameraUsageEvent> mEventList; + private List<CameraEvent> mEventList; private static final long WRITER_SLEEP_MS = 100; - public EventWriterTask(ArrayList<CameraUsageEvent> eventList) { + EventWriterTask(List<CameraEvent> eventList) { mEventList = eventList; } @Override public void run() { if (mEventList != null) { - for (CameraUsageEvent event : mEventList) { - logCameraUsageEvent(event); + for (CameraEvent event : mEventList) { + event.logSelf(); try { Thread.sleep(WRITER_SLEEP_MS); } catch (InterruptedException e) {} @@ -884,170 +1130,6 @@ public class CameraServiceProxy extends SystemService mEventList.clear(); } } - - /** - * Write camera usage events to stats log. - * Package-private - */ - private void logCameraUsageEvent(CameraUsageEvent e) { - int facing = FrameworkStatsLog.CAMERA_ACTION_EVENT__FACING__UNKNOWN; - switch(e.mCameraFacing) { - case CameraSessionStats.CAMERA_FACING_BACK: - facing = FrameworkStatsLog.CAMERA_ACTION_EVENT__FACING__BACK; - break; - case CameraSessionStats.CAMERA_FACING_FRONT: - facing = FrameworkStatsLog.CAMERA_ACTION_EVENT__FACING__FRONT; - break; - case CameraSessionStats.CAMERA_FACING_EXTERNAL: - facing = FrameworkStatsLog.CAMERA_ACTION_EVENT__FACING__EXTERNAL; - break; - default: - Slog.w(TAG, "Unknown camera facing: " + e.mCameraFacing); - } - - int extensionType = FrameworkStatsLog.CAMERA_ACTION_EVENT__EXT_TYPE__EXTENSION_NONE; - boolean extensionIsAdvanced = false; - int extensionCaptureFormat = ImageFormat.UNKNOWN; - if (e.mExtSessionStats != null) { - switch (e.mExtSessionStats.type) { - case CameraExtensionSessionStats.Type.EXTENSION_AUTOMATIC: - extensionType = FrameworkStatsLog - .CAMERA_ACTION_EVENT__EXT_TYPE__EXTENSION_AUTOMATIC; - break; - case CameraExtensionSessionStats.Type.EXTENSION_FACE_RETOUCH: - extensionType = FrameworkStatsLog - .CAMERA_ACTION_EVENT__EXT_TYPE__EXTENSION_FACE_RETOUCH; - break; - case CameraExtensionSessionStats.Type.EXTENSION_BOKEH: - extensionType = - FrameworkStatsLog.CAMERA_ACTION_EVENT__EXT_TYPE__EXTENSION_BOKEH; - break; - case CameraExtensionSessionStats.Type.EXTENSION_HDR: - extensionType = - FrameworkStatsLog.CAMERA_ACTION_EVENT__EXT_TYPE__EXTENSION_HDR; - break; - case CameraExtensionSessionStats.Type.EXTENSION_NIGHT: - extensionType = - FrameworkStatsLog.CAMERA_ACTION_EVENT__EXT_TYPE__EXTENSION_NIGHT; - break; - default: - Slog.w(TAG, "Unknown extension type: " + e.mExtSessionStats.type); - } - extensionIsAdvanced = e.mExtSessionStats.isAdvanced; - if (Flags.analytics24q3()) { - extensionCaptureFormat = e.mExtSessionStats.captureFormat; - } - } - - int streamCount = 0; - if (e.mStreamStats != null) { - streamCount = e.mStreamStats.size(); - } - if (CameraServiceProxy.DEBUG) { - String ultrawideDebug = Flags.logUltrawideUsage() - ? ", wideAngleUsage " + e.mUsedUltraWide - : ""; - String zoomOverrideDebug = Flags.logZoomOverrideUsage() - ? ", zoomOverrideUsage " + e.mUsedZoomOverride - : ""; - String mostRequestedFpsRangeDebug = Flags.analytics24q3() - ? ", mostRequestedFpsRange " + e.mMostRequestedFpsRange - : ""; - String extensionCaptureFormatDebug = Flags.analytics24q3() - ? " extensionCaptureFormat " + e.mExtSessionStats.captureFormat - : ""; - - Slog.v(TAG, "CAMERA_ACTION_EVENT: action " + e.mAction - + " clientName " + e.mClientName - + ", duration " + e.getDuration() - + ", APILevel " + e.mAPILevel - + ", cameraId " + e.mCameraId - + ", facing " + facing - + ", isNdk " + e.mIsNdk - + ", latencyMs " + e.mLatencyMs - + ", operatingMode " + e.mOperatingMode - + ", internalReconfigure " + e.mInternalReconfigure - + ", requestCount " + e.mRequestCount - + ", resultErrorCount " + e.mResultErrorCount - + ", deviceError " + e.mDeviceError - + ", streamCount is " + streamCount - + ", userTag is " + e.mUserTag - + ", videoStabilizationMode " + e.mVideoStabilizationMode - + ultrawideDebug - + zoomOverrideDebug - + mostRequestedFpsRangeDebug - + ", logId " + e.mLogId - + ", sessionIndex " + e.mSessionIndex - + ", mExtSessionStats {type " + extensionType - + " isAdvanced " + extensionIsAdvanced - + extensionCaptureFormatDebug + "}"); - } - - // Convert from CameraStreamStats to CameraStreamProto - CameraStreamProto[] streamProtos = new CameraStreamProto[MAX_STREAM_STATISTICS]; - for (int i = 0; i < MAX_STREAM_STATISTICS; i++) { - streamProtos[i] = new CameraStreamProto(); - if (i < streamCount) { - CameraStreamStats streamStats = e.mStreamStats.get(i); - streamProtos[i].width = streamStats.getWidth(); - streamProtos[i].height = streamStats.getHeight(); - streamProtos[i].format = streamStats.getFormat(); - streamProtos[i].dataSpace = streamStats.getDataSpace(); - streamProtos[i].usage = streamStats.getUsage(); - streamProtos[i].requestCount = streamStats.getRequestCount(); - streamProtos[i].errorCount = streamStats.getErrorCount(); - streamProtos[i].firstCaptureLatencyMillis = streamStats.getStartLatencyMs(); - streamProtos[i].maxHalBuffers = streamStats.getMaxHalBuffers(); - streamProtos[i].maxAppBuffers = streamStats.getMaxAppBuffers(); - streamProtos[i].histogramType = streamStats.getHistogramType(); - streamProtos[i].histogramBins = streamStats.getHistogramBins(); - streamProtos[i].histogramCounts = streamStats.getHistogramCounts(); - streamProtos[i].dynamicRangeProfile = streamStats.getDynamicRangeProfile(); - streamProtos[i].streamUseCase = streamStats.getStreamUseCase(); - streamProtos[i].colorSpace = streamStats.getColorSpace(); - - if (CameraServiceProxy.DEBUG) { - String histogramTypeName = - cameraHistogramTypeToString(streamProtos[i].histogramType); - Slog.v(TAG, "Stream " + i + ": width " + streamProtos[i].width - + ", height " + streamProtos[i].height - + ", format " + streamProtos[i].format - + ", maxPreviewFps " + streamStats.getMaxPreviewFps() - + ", dataSpace " + streamProtos[i].dataSpace - + ", usage " + streamProtos[i].usage - + ", requestCount " + streamProtos[i].requestCount - + ", errorCount " + streamProtos[i].errorCount - + ", firstCaptureLatencyMillis " - + streamProtos[i].firstCaptureLatencyMillis - + ", maxHalBuffers " + streamProtos[i].maxHalBuffers - + ", maxAppBuffers " + streamProtos[i].maxAppBuffers - + ", histogramType " + histogramTypeName - + ", histogramBins " - + Arrays.toString(streamProtos[i].histogramBins) - + ", histogramCounts " - + Arrays.toString(streamProtos[i].histogramCounts) - + ", dynamicRangeProfile " + streamProtos[i].dynamicRangeProfile - + ", streamUseCase " + streamProtos[i].streamUseCase - + ", colorSpace " + streamProtos[i].colorSpace); - } - } - } - FrameworkStatsLog.write(FrameworkStatsLog.CAMERA_ACTION_EVENT, e.getDuration(), - e.mAPILevel, e.mClientName, facing, e.mCameraId, e.mAction, e.mIsNdk, - e.mLatencyMs, e.mOperatingMode, e.mInternalReconfigure, - e.mRequestCount, e.mResultErrorCount, e.mDeviceError, - streamCount, MessageNano.toByteArray(streamProtos[0]), - MessageNano.toByteArray(streamProtos[1]), - MessageNano.toByteArray(streamProtos[2]), - MessageNano.toByteArray(streamProtos[3]), - MessageNano.toByteArray(streamProtos[4]), - e.mUserTag, e.mVideoStabilizationMode, - e.mLogId, e.mSessionIndex, - extensionType, extensionIsAdvanced, e.mUsedUltraWide, - e.mUsedZoomOverride, - e.mMostRequestedFpsRange.getLower(), e.mMostRequestedFpsRange.getUpper(), - extensionCaptureFormat); - } } /** @@ -1055,22 +1137,22 @@ public class CameraServiceProxy extends SystemService */ int getUsageEventCount() { synchronized (mLock) { - return mCameraUsageHistory.size(); + return mCameraEventHistory.size(); } } /** - * Dump camera usage events to log. + * Dump camera events to log. * Package-private */ - void dumpUsageEvents() { + void dumpCameraEvents() { synchronized(mLock) { // Randomize order of events so that it's not meaningful - Collections.shuffle(mCameraUsageHistory); + Collections.shuffle(mCameraEventHistory); mLogWriterService.execute(new EventWriterTask( - new ArrayList<CameraUsageEvent>(mCameraUsageHistory))); + new ArrayList(mCameraEventHistory))); - mCameraUsageHistory.clear(); + mCameraEventHistory.clear(); } final long ident = Binder.clearCallingIdentity(); try { @@ -1288,7 +1370,7 @@ public class CameraServiceProxy extends SystemService cameraId, facing, clientName, apiLevel, isNdk, FrameworkStatsLog.CAMERA_ACTION_EVENT__ACTION__OPEN, latencyMs, sessionType, deviceError, logId, sessionIdx); - mCameraUsageHistory.add(openEvent); + mCameraEventHistory.add(openEvent); break; case CameraSessionStats.CAMERA_STATE_ACTIVE: // Check current active camera IDs to see if this package is already talking to @@ -1323,7 +1405,7 @@ public class CameraServiceProxy extends SystemService /*userTag*/"", /*videoStabilizationMode*/-1, /*usedUltraWide*/false, /*usedZoomOverride*/false, new Range<Integer>(0, 0), new CameraExtensionSessionStats()); - mCameraUsageHistory.add(oldEvent); + mCameraEventHistory.add(oldEvent); } break; case CameraSessionStats.CAMERA_STATE_IDLE: @@ -1335,7 +1417,7 @@ public class CameraServiceProxy extends SystemService resultErrorCount, deviceError, streamStats, userTag, videoStabilizationMode, usedUltraWide, usedZoomOverride, mostRequestedFpsRange, extSessionStats); - mCameraUsageHistory.add(doneEvent); + mCameraEventHistory.add(doneEvent); // Do not double count device error deviceError = false; @@ -1362,11 +1444,11 @@ public class CameraServiceProxy extends SystemService cameraId, facing, clientName, apiLevel, isNdk, FrameworkStatsLog.CAMERA_ACTION_EVENT__ACTION__CLOSE, latencyMs, sessionType, deviceError, logId, sessionIdx); - mCameraUsageHistory.add(closeEvent); + mCameraEventHistory.add(closeEvent); } - if (mCameraUsageHistory.size() > MAX_USAGE_HISTORY) { - dumpUsageEvents(); + if (mCameraEventHistory.size() > MAX_USAGE_HISTORY) { + dumpCameraEvents(); } break; @@ -1378,6 +1460,18 @@ public class CameraServiceProxy extends SystemService } } + private void updateFeatureCombinationQuery(CameraFeatureCombinationStats featureCombStats) { + synchronized (mLock) { + CameraFeatureCombinationQueryEvent e = + new CameraFeatureCombinationQueryEvent(featureCombStats); + mCameraEventHistory.add(e); + + if (mCameraEventHistory.size() > MAX_USAGE_HISTORY) { + dumpCameraEvents(); + } + } + } + private void notifyNfcService(boolean enablePolling) { NfcManager nfcManager = mContext.getSystemService(NfcManager.class); if (nfcManager == null) { @@ -1434,4 +1528,41 @@ public class CameraServiceProxy extends SystemService return "HISTOGRAM_TYPE_UNKNOWN"; } + private static String cameraFeatureCombinationTypeToString(int featureCombinationType) { + switch (featureCombinationType) { + case CameraFeatureCombinationStats.QueryType.QUERY_FEATURE_COMBINATION: + return "QUERY_FEATURE_COMBINATION"; + case CameraFeatureCombinationStats.QueryType.QUERY_SESSION_CHARACTERISTICS: + return "QUERY_SESSION_CHARACTERISTICS"; + default: + break; + } + return "FEATURE_COMBINATION_TYPE_UNKNOWN"; + } + + private static String cameraFeatureCombinationToString(long featureCombination) { + StringBuilder combinationStr = new StringBuilder("{"); + if ((featureCombination & CameraFeatureCombinationStats.CAMERA_FEATURE_60_FPS) != 0) { + combinationStr.append("60fps "); + } + if ((featureCombination & CameraFeatureCombinationStats.CAMERA_FEATURE_STABILIZATION) + != 0) { + combinationStr.append("stabilization "); + } + if ((featureCombination & CameraFeatureCombinationStats.CAMERA_FEATURE_HLG10) != 0) { + combinationStr.append("hlg10 "); + } + if ((featureCombination & CameraFeatureCombinationStats.CAMERA_FEATURE_JPEG) != 0) { + combinationStr.append("jpeg "); + } + if ((featureCombination & CameraFeatureCombinationStats.CAMERA_FEATURE_JPEG_R) != 0) { + combinationStr.append("jpeg_r "); + } + if ((featureCombination & CameraFeatureCombinationStats.CAMERA_FEATURE_4K) != 0) { + combinationStr.append("4k "); + } + combinationStr.append("}"); + + return combinationStr.toString(); + } } diff --git a/services/core/java/com/android/server/camera/CameraStatsJobService.java b/services/core/java/com/android/server/camera/CameraStatsJobService.java index b8a6846ced76..1227ca7ef057 100644 --- a/services/core/java/com/android/server/camera/CameraStatsJobService.java +++ b/services/core/java/com/android/server/camera/CameraStatsJobService.java @@ -21,14 +21,13 @@ import android.app.job.JobParameters; import android.app.job.JobScheduler; import android.app.job.JobService; import android.content.ComponentName; -import android.content.ContentResolver; import android.content.Context; import android.util.Slog; -import java.util.concurrent.TimeUnit; - import com.android.server.LocalServices; +import java.util.concurrent.TimeUnit; + /** * A JobService to periodically collect camera usage stats. */ @@ -50,7 +49,7 @@ public class CameraStatsJobService extends JobService { return false; } - serviceProxy.dumpUsageEvents(); + serviceProxy.dumpCameraEvents(); return false; } diff --git a/services/core/java/com/android/server/clipboard/ClipboardService.java b/services/core/java/com/android/server/clipboard/ClipboardService.java index 4c3020f58870..0afca92d78f7 100644 --- a/services/core/java/com/android/server/clipboard/ClipboardService.java +++ b/services/core/java/com/android/server/clipboard/ClipboardService.java @@ -30,7 +30,6 @@ import android.annotation.Nullable; import android.annotation.UserIdInt; import android.annotation.WorkerThread; import android.app.ActivityManagerInternal; -import android.app.AppGlobals; import android.app.AppOpsManager; import android.app.IUriGrantsManager; import android.app.KeyguardManager; @@ -48,9 +47,8 @@ import android.content.IClipboard; import android.content.IOnPrimaryClipChangedListener; import android.content.Intent; import android.content.IntentFilter; -import android.content.pm.IPackageManager; -import android.content.pm.PackageInfo; import android.content.pm.PackageManager; +import android.content.pm.PackageManagerInternal; import android.content.pm.UserInfo; import android.graphics.drawable.Drawable; import android.hardware.display.DisplayManager; @@ -1247,20 +1245,13 @@ public class ClipboardService extends SystemService { @GuardedBy("mLock") private void addActiveOwnerLocked(int uid, int deviceId, String pkg) { - final IPackageManager pm = AppGlobals.getPackageManager(); + final PackageManagerInternal pm = LocalServices.getService(PackageManagerInternal.class); final int targetUserHandle = UserHandle.getCallingUserId(); final long oldIdentity = Binder.clearCallingIdentity(); try { - PackageInfo pi = pm.getPackageInfo(pkg, 0, targetUserHandle); - if (pi == null) { - throw new IllegalArgumentException("Unknown package " + pkg); + if (!pm.isSameApp(pkg, 0, uid, targetUserHandle)) { + throw new SecurityException("Calling uid " + uid + " does not own package " + pkg); } - if (!UserHandle.isSameApp(pi.applicationInfo.uid, uid)) { - throw new SecurityException("Calling uid " + uid - + " does not own package " + pkg); - } - } catch (RemoteException e) { - // Can't happen; the package manager is in the same process } finally { Binder.restoreCallingIdentity(oldIdentity); } diff --git a/services/core/java/com/android/server/contentcapture/ContentCaptureManagerInternal.java b/services/core/java/com/android/server/contentcapture/ContentCaptureManagerInternal.java index 0812fd9fe36c..de7341d2acfb 100644 --- a/services/core/java/com/android/server/contentcapture/ContentCaptureManagerInternal.java +++ b/services/core/java/com/android/server/contentcapture/ContentCaptureManagerInternal.java @@ -21,6 +21,7 @@ import android.annotation.UserIdInt; import android.app.assist.ActivityId; import android.content.ComponentName; import android.content.ContentCaptureOptions; +import android.content.Intent; import android.os.Bundle; import android.os.IBinder; import android.service.contentcapture.ActivityEvent.ActivityEventType; @@ -40,6 +41,14 @@ public abstract class ContentCaptureManagerInternal { public abstract boolean isContentCaptureServiceForUser(int uid, @UserIdInt int userId); /** + * Notifies the intelligence service of new intent data associated with an activity start event. + * + * @return {@code false} if there was no service set for the given user + */ + public abstract boolean sendActivityStartAssistData(@UserIdInt int userId, + @NonNull IBinder activityToken, @NonNull Intent intentData); + + /** * Notifies the intelligence service of new assist data for the given activity. * * @return {@code false} if there was no service set for the given user diff --git a/services/core/java/com/android/server/criticalevents/OWNERS b/services/core/java/com/android/server/criticalevents/OWNERS index 9c3136c592f1..7935bedbbb7a 100644 --- a/services/core/java/com/android/server/criticalevents/OWNERS +++ b/services/core/java/com/android/server/criticalevents/OWNERS @@ -1,2 +1 @@ benmiles@google.com -gaillard@google.com diff --git a/services/core/java/com/android/server/display/AutomaticBrightnessController.java b/services/core/java/com/android/server/display/AutomaticBrightnessController.java index 1949e6f2981e..7b5cff739ba1 100644 --- a/services/core/java/com/android/server/display/AutomaticBrightnessController.java +++ b/services/core/java/com/android/server/display/AutomaticBrightnessController.java @@ -54,7 +54,6 @@ import com.android.internal.display.BrightnessSynchronizer; import com.android.internal.os.BackgroundThread; import com.android.server.EventLogTags; import com.android.server.display.brightness.BrightnessEvent; -import com.android.server.display.brightness.clamper.BrightnessClamperController; import com.android.server.display.config.HysteresisLevels; import com.android.server.display.feature.DisplayManagerFlags; @@ -256,7 +255,6 @@ public class AutomaticBrightnessController { // Controls Brightness range (including High Brightness Mode). private final BrightnessRangeController mBrightnessRangeController; - private final BrightnessClamperController mBrightnessClamperController; // Throttles (caps) maximum allowed brightness private final BrightnessThrottler mBrightnessThrottler; @@ -295,7 +293,6 @@ public class AutomaticBrightnessController { BrightnessRangeController brightnessModeController, BrightnessThrottler brightnessThrottler, int ambientLightHorizonShort, int ambientLightHorizonLong, float userLux, float userNits, - BrightnessClamperController brightnessClamperController, DisplayManagerFlags displayManagerFlags) { this(new Injector(), callbacks, looper, sensorManager, lightSensor, brightnessMappingStrategyMap, lightSensorWarmUpTime, brightnessMin, brightnessMax, @@ -306,7 +303,7 @@ public class AutomaticBrightnessController { screenBrightnessThresholds, ambientBrightnessThresholdsIdle, screenBrightnessThresholdsIdle, context, brightnessModeController, brightnessThrottler, ambientLightHorizonShort, ambientLightHorizonLong, userLux, - userNits, brightnessClamperController, displayManagerFlags + userNits, displayManagerFlags ); } @@ -325,7 +322,6 @@ public class AutomaticBrightnessController { BrightnessRangeController brightnessRangeController, BrightnessThrottler brightnessThrottler, int ambientLightHorizonShort, int ambientLightHorizonLong, float userLux, float userNits, - BrightnessClamperController brightnessClamperController, DisplayManagerFlags displayManagerFlags) { mInjector = injector; mClock = injector.createClock(displayManagerFlags.offloadControlsDozeAutoBrightness()); @@ -370,7 +366,6 @@ public class AutomaticBrightnessController { mForegroundAppCategory = ApplicationInfo.CATEGORY_UNDEFINED; mPendingForegroundAppCategory = ApplicationInfo.CATEGORY_UNDEFINED; mBrightnessRangeController = brightnessRangeController; - mBrightnessClamperController = brightnessClamperController; mBrightnessThrottler = brightnessThrottler; mBrightnessMappingStrategyMap = brightnessMappingStrategyMap; mDisplayManagerFlags = displayManagerFlags; @@ -771,7 +766,6 @@ public class AutomaticBrightnessController { mAmbientBrightnessThresholds.getDarkeningThreshold(lux); } mBrightnessRangeController.onAmbientLuxChange(mAmbientLux); - mBrightnessClamperController.onAmbientLuxChange(mAmbientLux); // If the short term model was invalidated and the change is drastic enough, reset it. mShortTermModel.maybeReset(mAmbientLux); diff --git a/services/core/java/com/android/server/display/DisplayDeviceConfig.java b/services/core/java/com/android/server/display/DisplayDeviceConfig.java index eeacc53f0fd4..e4db634c0e26 100644 --- a/services/core/java/com/android/server/display/DisplayDeviceConfig.java +++ b/services/core/java/com/android/server/display/DisplayDeviceConfig.java @@ -230,6 +230,16 @@ import javax.xml.datatype.DatatypeConfigurationException; * <nits>55.2</nits> * </displayBrightnessPoint> * </blockingZoneThreshold> + * <supportedModes> + * <point> + * <first>60</first> // refresh rate + * <second>60</second> // vsync + * </point> + * <point> + * <first>120</first> // refresh rate + * <second>120</second> // vsync + * </point> + * </supportedModes> * </lowerBlockingZoneConfigs> * <higherBlockingZoneConfigs> * <defaultRefreshRate>90</defaultRefreshRate> @@ -244,6 +254,16 @@ import javax.xml.datatype.DatatypeConfigurationException; * </displayBrightnessPoint> * </blockingZoneThreshold> * </higherBlockingZoneConfigs> + * <lowPowerSupportedModes> + * <point> + * <first>60</first> // refresh rate + * <second>60</second> // vsync + * </point> + * <point> + * <first>60</first> // refresh rate + * <second>240</second> // vsync + * </point> + * </lowPowerSupportedModes> * </refreshRate> * * <highBrightnessMode enabled="true"> diff --git a/services/core/java/com/android/server/display/DisplayDeviceInfo.java b/services/core/java/com/android/server/display/DisplayDeviceInfo.java index b1b1dbaf988c..93bd92614403 100644 --- a/services/core/java/com/android/server/display/DisplayDeviceInfo.java +++ b/services/core/java/com/android/server/display/DisplayDeviceInfo.java @@ -212,24 +212,46 @@ final class DisplayDeviceInfo { public static final int TOUCH_VIRTUAL = 3; /** + * Diff result: Other fields differ. + */ + public static final int DIFF_OTHER = 1 << 0; + + /** * Diff result: The {@link #state} or {@link #committedState} fields differ. */ - public static final int DIFF_STATE = 1 << 0; + public static final int DIFF_STATE = 1 << 1; /** - * Diff result: Other fields differ. + * Diff result: The committed state differs. Note this is slightly different from the state, + * which is what most of the device should care about. */ - public static final int DIFF_OTHER = 1 << 1; + public static final int DIFF_COMMITTED_STATE = 1 << 2; /** * Diff result: The color mode fields differ. */ - public static final int DIFF_COLOR_MODE = 1 << 2; + public static final int DIFF_COLOR_MODE = 1 << 3; /** * Diff result: The hdr/sdr ratio differs */ - public static final int DIFF_HDR_SDR_RATIO = 1 << 3; + public static final int DIFF_HDR_SDR_RATIO = 1 << 4; + + /** + * Diff result: The rotation differs + */ + public static final int DIFF_ROTATION = 1 << 5; + + /** + * Diff result: The render timings. Note this could be any of {@link #renderFrameRate}, + * {@link #presentationDeadlineNanos}, or {@link #appVsyncOffsetNanos}. + */ + public static final int DIFF_RENDER_TIMINGS = 1 << 6; + + /** + * Diff result: The mode ID differs. + */ + public static final int DIFF_MODE_ID = 1 << 7; /** * Diff result: Catch-all for "everything changed" @@ -462,21 +484,33 @@ final class DisplayDeviceInfo { */ public int diff(DisplayDeviceInfo other) { int diff = 0; - if (state != other.state || committedState != other.committedState) { + if (state != other.state) { diff |= DIFF_STATE; } + if (committedState != other.committedState) { + diff |= DIFF_COMMITTED_STATE; + } if (colorMode != other.colorMode) { diff |= DIFF_COLOR_MODE; } if (!BrightnessSynchronizer.floatEquals(hdrSdrRatio, other.hdrSdrRatio)) { diff |= DIFF_HDR_SDR_RATIO; } + if (rotation != other.rotation) { + diff |= DIFF_ROTATION; + } + if (renderFrameRate != other.renderFrameRate + || presentationDeadlineNanos != other.presentationDeadlineNanos + || appVsyncOffsetNanos != other.appVsyncOffsetNanos) { + diff |= DIFF_RENDER_TIMINGS; + } + if (modeId != other.modeId) { + diff |= DIFF_MODE_ID; + } if (!Objects.equals(name, other.name) || !Objects.equals(uniqueId, other.uniqueId) || width != other.width || height != other.height - || modeId != other.modeId - || renderFrameRate != other.renderFrameRate || defaultModeId != other.defaultModeId || userPreferredModeId != other.userPreferredModeId || !Arrays.equals(supportedModes, other.supportedModes) @@ -487,12 +521,9 @@ final class DisplayDeviceInfo { || densityDpi != other.densityDpi || xDpi != other.xDpi || yDpi != other.yDpi - || appVsyncOffsetNanos != other.appVsyncOffsetNanos - || presentationDeadlineNanos != other.presentationDeadlineNanos || flags != other.flags || !Objects.equals(displayCutout, other.displayCutout) || touch != other.touch - || rotation != other.rotation || type != other.type || !Objects.equals(address, other.address) || !Objects.equals(deviceProductInfo, other.deviceProductInfo) diff --git a/services/core/java/com/android/server/display/DisplayDeviceRepository.java b/services/core/java/com/android/server/display/DisplayDeviceRepository.java index 6164154b1e63..086f8a94d9b8 100644 --- a/services/core/java/com/android/server/display/DisplayDeviceRepository.java +++ b/services/core/java/com/android/server/display/DisplayDeviceRepository.java @@ -21,6 +21,7 @@ import android.os.Trace; import android.util.Slog; import android.view.Display; import android.view.DisplayAddress; +import android.view.Surface; import com.android.internal.annotations.GuardedBy; import com.android.server.display.DisplayManagerService.SyncRoot; @@ -179,6 +180,20 @@ class DisplayDeviceRepository implements DisplayAdapter.Listener { 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)) { + Slog.i(TAG, "Display device changed render timings: \"" + info.name + + "\", renderFrameRate=" + info.renderFrameRate + + ", presentationDeadlineNanos=" + info.presentationDeadlineNanos + + ", appVsyncOffsetNanos=" + info.appVsyncOffsetNanos); + } else if (diff == DisplayDeviceInfo.DIFF_COMMITTED_STATE) { + if (DEBUG) { + Slog.i(TAG, "Display device changed committed state: \"" + info.name + + "\", " + Display.stateToString(info.committedState)); + } } else if (diff != DisplayDeviceInfo.DIFF_HDR_SDR_RATIO) { Slog.i(TAG, "Display device changed: " + info); } diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java index 5fd025399b12..2d5f38e2dd8c 100644 --- a/services/core/java/com/android/server/display/DisplayManagerService.java +++ b/services/core/java/com/android/server/display/DisplayManagerService.java @@ -3406,6 +3406,31 @@ public final class DisplayManagerService extends SystemService { } } + boolean requestDisplayPower(int displayId, boolean on) { + synchronized (mSyncRoot) { + final var display = mLogicalDisplayMapper.getDisplayLocked(displayId); + if (display == null) { + Slog.w(TAG, "requestDisplayPower: Cannot find a display with displayId=" + + displayId); + return false; + } + final BrightnessPair brightnessPair = mDisplayBrightnesses.get(displayId); + var runnable = display.getPrimaryDisplayDeviceLocked().requestDisplayStateLocked( + on ? Display.STATE_ON : Display.STATE_OFF, + on ? brightnessPair.brightness : PowerManager.BRIGHTNESS_OFF_FLOAT, + brightnessPair.sdrBrightness, + display.getDisplayOffloadSessionLocked()); + if (runnable == null) { + Slog.w(TAG, "requestDisplayPower: Cannot update the power state to ON=" + on + + " for a display with displayId=" + displayId + ", runnable is null"); + return false; + } + runnable.run(); + Slog.i(TAG, "requestDisplayPower(displayId=" + displayId + ", on=" + on + ")"); + } + return true; + } + /** * This is the object that everything in the display manager locks on. * We make it an inner class within the {@link DisplayManagerService} to so that it is @@ -4629,6 +4654,12 @@ public final class DisplayManagerService extends SystemService { DisplayManagerService.this.enableConnectedDisplay(displayId, false); } + @EnforcePermission(MANAGE_DISPLAYS) + public boolean requestDisplayPower(int displayId, boolean on) { + requestDisplayPower_enforcePermission(); + return DisplayManagerService.this.requestDisplayPower(displayId, on); + } + @EnforcePermission(RESTRICT_DISPLAY_MODES) @Override // Binder call public void requestDisplayModes(IBinder token, int displayId, @Nullable int[] modeIds) { diff --git a/services/core/java/com/android/server/display/DisplayManagerShellCommand.java b/services/core/java/com/android/server/display/DisplayManagerShellCommand.java index 8c39d7de54f7..d973b71366b1 100644 --- a/services/core/java/com/android/server/display/DisplayManagerShellCommand.java +++ b/services/core/java/com/android/server/display/DisplayManagerShellCommand.java @@ -106,6 +106,10 @@ class DisplayManagerShellCommand extends ShellCommand { return setDisplayEnabled(true); case "disable-display": return setDisplayEnabled(false); + case "power-on": + return requestDisplayPower(true); + case "power-off": + return requestDisplayPower(false); default: return handleDefaultCommands(cmd); } @@ -592,4 +596,21 @@ class DisplayManagerShellCommand extends ShellCommand { mService.enableConnectedDisplay(displayId, enable); return 0; } + + private int requestDisplayPower(boolean enable) { + final String displayIdText = getNextArg(); + if (displayIdText == null) { + getErrPrintWriter().println("Error: no displayId specified"); + return 1; + } + final int displayId; + try { + displayId = Integer.parseInt(displayIdText); + } catch (NumberFormatException e) { + getErrPrintWriter().println("Error: invalid displayId: '" + displayIdText + "'"); + return 1; + } + mService.requestDisplayPower(displayId, enable); + return 0; + } } diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java index 8d71c701cf87..65a729a0293e 100644 --- a/services/core/java/com/android/server/display/DisplayPowerController.java +++ b/services/core/java/com/android/server/display/DisplayPowerController.java @@ -159,7 +159,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call private static final int MSG_STATSD_HBM_BRIGHTNESS = 11; private static final int MSG_SWITCH_USER = 12; private static final int MSG_BOOT_COMPLETED = 13; - private static final int MSG_SET_DWBC_STRONG_MODE = 14; + private static final int MSG_SWITCH_AUTOBRIGHTNESS_MODE = 14; private static final int MSG_SET_DWBC_COLOR_OVERRIDE = 15; private static final int MSG_SET_DWBC_LOGGING_ENABLED = 16; private static final int MSG_SET_BRIGHTNESS_FROM_OFFLOAD = 17; @@ -587,7 +587,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call mUniqueDisplayId, mThermalBrightnessThrottlingDataId, logicalDisplay.getPowerThrottlingDataIdLocked(), - mDisplayDeviceConfig), mContext, flags); + mDisplayDeviceConfig), mContext, flags, mSensorManager); // Seed the cached brightness saveBrightnessInfo(getScreenBrightnessSetting()); mAutomaticBrightnessStrategy = @@ -1184,15 +1184,9 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call @Override public void setAutomaticScreenBrightnessMode( @AutomaticBrightnessController.AutomaticBrightnessMode int mode) { - boolean isIdle = mode == AUTO_BRIGHTNESS_MODE_IDLE; - if (mAutomaticBrightnessController != null) { - // Set sendUpdate to true to make sure that updatePowerState() gets called - mAutomaticBrightnessController.switchMode(mode, /* sendUpdate= */ true); - setAnimatorRampSpeeds(isIdle); - } Message msg = mHandler.obtainMessage(); - msg.what = MSG_SET_DWBC_STRONG_MODE; - msg.arg1 = isIdle ? 1 : 0; + msg.what = MSG_SWITCH_AUTOBRIGHTNESS_MODE; + msg.arg1 = mode; mHandler.sendMessageAtTime(msg, mClock.uptimeMillis()); } @@ -1361,7 +1355,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call state = mPowerState.getScreenState(); DisplayBrightnessState displayBrightnessState = mDisplayBrightnessController - .updateBrightness(mPowerRequest, state); + .updateBrightness(mPowerRequest, state, mDisplayOffloadSession); float brightnessState = displayBrightnessState.getBrightness(); float rawBrightnessState = displayBrightnessState.getBrightness(); mBrightnessReasonTemp.set(displayBrightnessState.getBrightnessReason()); @@ -1374,6 +1368,10 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call if (displayBrightnessState.getBrightnessEvent() != null) { mTempBrightnessEvent.copyFrom(displayBrightnessState.getBrightnessEvent()); } + + boolean allowAutoBrightnessWhileDozing = + mDisplayBrightnessController.isAllowAutoBrightnessWhileDozing(); + if (!mFlags.isRefactorDisplayPowerControllerEnabled()) { // Set up the ScreenOff controller used when coming out of SCREEN_OFF and the ALS sensor // doesn't yet have a valid lux value to use with auto-brightness. @@ -1381,8 +1379,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call mScreenOffBrightnessSensorController .setLightSensorEnabled(displayBrightnessState.getShouldUseAutoBrightness() && mIsEnabled && (state == Display.STATE_OFF - || (state == Display.STATE_DOZE - && !mDisplayBrightnessController.isAllowAutoBrightnessWhileDozingConfig())) + || (state == Display.STATE_DOZE && !allowAutoBrightnessWhileDozing)) && mLeadDisplayId == Layout.NO_LEAD_DISPLAY); } } @@ -1392,12 +1389,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call final boolean wasShortTermModelActive = mAutomaticBrightnessStrategy.isShortTermModelActive(); boolean userInitiatedChange = displayBrightnessState.isUserInitiatedChange(); - boolean allowAutoBrightnessWhileDozing = - mDisplayBrightnessController.isAllowAutoBrightnessWhileDozingConfig(); - if (mFlags.offloadControlsDozeAutoBrightness() && mFlags.isDisplayOffloadEnabled() - && mDisplayOffloadSession != null) { - allowAutoBrightnessWhileDozing &= mDisplayOffloadSession.allowAutoBrightnessInDoze(); - } + if (!mFlags.isRefactorDisplayPowerControllerEnabled()) { // Switch to doze auto-brightness mode if needed if (mFlags.areAutoBrightnessModesEnabled() && mAutomaticBrightnessController != null @@ -1430,7 +1422,6 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call : AutomaticBrightnessController.AUTO_BRIGHTNESS_DISABLED; mBrightnessRangeController.setAutoBrightnessEnabled(autoBrightnessState); - mBrightnessClamperController.setAutoBrightnessState(autoBrightnessState); boolean updateScreenBrightnessSetting = displayBrightnessState.shouldUpdateScreenBrightnessSetting(); @@ -1557,7 +1548,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call // we broadcast this change through setting. final float unthrottledBrightnessState = brightnessState; DisplayBrightnessState clampedState = mBrightnessClamperController.clamp(mPowerRequest, - brightnessState, slowChange); + brightnessState, slowChange, /* displayState= */ state); brightnessState = clampedState.getBrightness(); slowChange = clampedState.isSlowChange(); @@ -1868,7 +1859,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call private void setDwbcStrongMode(int arg) { if (mDisplayWhiteBalanceController != null) { - final boolean isIdle = (arg == 1); + final boolean isIdle = (arg == AUTO_BRIGHTNESS_MODE_IDLE); mDisplayWhiteBalanceController.setStrongModeEnabled(isIdle); } } @@ -2486,7 +2477,6 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call public void setBrightnessToFollow(float leadDisplayBrightness, float nits, float ambientLux, boolean slowChange) { mBrightnessRangeController.onAmbientLuxChange(ambientLux); - mBrightnessClamperController.onAmbientLuxChange(ambientLux); if (nits == BrightnessMappingStrategy.INVALID_NITS) { mDisplayBrightnessController.setBrightnessToFollow(leadDisplayBrightness, slowChange); } else { @@ -3034,7 +3024,12 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call updatePowerState(); break; - case MSG_SET_DWBC_STRONG_MODE: + case MSG_SWITCH_AUTOBRIGHTNESS_MODE: + boolean isIdle = msg.arg1 == AUTO_BRIGHTNESS_MODE_IDLE; + if (mAutomaticBrightnessController != null) { + mAutomaticBrightnessController.switchMode(msg.arg1, /* sendUpdate= */ true); + setAnimatorRampSpeeds(isIdle); + } setDwbcStrongMode(msg.arg1); break; @@ -3197,7 +3192,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call screenBrightnessThresholds, ambientBrightnessThresholdsIdle, screenBrightnessThresholdsIdle, context, brightnessModeController, brightnessThrottler, ambientLightHorizonShort, ambientLightHorizonLong, userLux, - userNits, brightnessClamperController, displayManagerFlags); + userNits, displayManagerFlags); } BrightnessMappingStrategy getDefaultModeBrightnessMapper(Context context, @@ -3246,10 +3241,10 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call BrightnessClamperController getBrightnessClamperController(Handler handler, BrightnessClamperController.ClamperChangeListener clamperChangeListener, BrightnessClamperController.DisplayDeviceData data, Context context, - DisplayManagerFlags flags) { + DisplayManagerFlags flags, SensorManager sensorManager) { return new BrightnessClamperController(handler, clamperChangeListener, data, context, - flags); + flags, sensorManager); } DisplayWhiteBalanceController getDisplayWhiteBalanceController(Handler handler, diff --git a/services/core/java/com/android/server/display/brightness/DisplayBrightnessController.java b/services/core/java/com/android/server/display/brightness/DisplayBrightnessController.java index d567331a59ce..4982a0b0b836 100644 --- a/services/core/java/com/android/server/display/brightness/DisplayBrightnessController.java +++ b/services/core/java/com/android/server/display/brightness/DisplayBrightnessController.java @@ -146,11 +146,13 @@ public final class DisplayBrightnessController { */ public DisplayBrightnessState updateBrightness( DisplayManagerInternal.DisplayPowerRequest displayPowerRequest, - int targetDisplayState) { + int targetDisplayState, + DisplayManagerInternal.DisplayOffloadSession displayOffloadSession) { DisplayBrightnessState state; synchronized (mLock) { mDisplayBrightnessStrategy = mDisplayBrightnessStrategySelector.selectStrategy( - constructStrategySelectionRequest(displayPowerRequest, targetDisplayState)); + constructStrategySelectionRequest(displayPowerRequest, targetDisplayState, + displayOffloadSession)); state = mDisplayBrightnessStrategy .updateBrightness(constructStrategyExecutionRequest(displayPowerRequest)); } @@ -204,6 +206,16 @@ public final class DisplayBrightnessController { * Returns a boolean flag indicating if the light sensor is to be used to decide the screen * brightness when dozing */ + public boolean isAllowAutoBrightnessWhileDozing() { + synchronized (mLock) { + return mDisplayBrightnessStrategySelector.isAllowAutoBrightnessWhileDozing(); + } + } + + /** + * Returns the config value indicating the auto brightness while dozing is to be + * allowed ot not. Note that this is a config value, but the actual status can differ from this. + */ public boolean isAllowAutoBrightnessWhileDozingConfig() { synchronized (mLock) { return mDisplayBrightnessStrategySelector.isAllowAutoBrightnessWhileDozingConfig(); @@ -587,14 +599,15 @@ public final class DisplayBrightnessController { private StrategySelectionRequest constructStrategySelectionRequest( DisplayManagerInternal.DisplayPowerRequest displayPowerRequest, - int targetDisplayState) { + int targetDisplayState, + DisplayManagerInternal.DisplayOffloadSession displayOffloadSession) { boolean userSetBrightnessChanged = updateUserSetScreenBrightness(); float lastUserSetScreenBrightness; synchronized (mLock) { lastUserSetScreenBrightness = mLastUserSetScreenBrightness; } return new StrategySelectionRequest(displayPowerRequest, targetDisplayState, - lastUserSetScreenBrightness, userSetBrightnessChanged); + lastUserSetScreenBrightness, userSetBrightnessChanged, displayOffloadSession); } private StrategyExecutionRequest constructStrategyExecutionRequest( diff --git a/services/core/java/com/android/server/display/brightness/DisplayBrightnessStrategySelector.java b/services/core/java/com/android/server/display/brightness/DisplayBrightnessStrategySelector.java index feec4e6b2259..7835220d5c13 100644 --- a/services/core/java/com/android/server/display/brightness/DisplayBrightnessStrategySelector.java +++ b/services/core/java/com/android/server/display/brightness/DisplayBrightnessStrategySelector.java @@ -48,9 +48,14 @@ import java.io.PrintWriter; */ public class DisplayBrightnessStrategySelector { private static final String TAG = "DisplayBrightnessStrategySelector"; - // True if light sensor is to be used to automatically determine doze screen brightness. + // True if the config to use the light sensor to automatically determine doze screen brightness + // is enabled. Note that the actual value representing if the auto-brightness is to be kept + // enabled while dozing can differ, but is dependent on this private final boolean mAllowAutoBrightnessWhileDozingConfig; + // True if light sensor is to be used to automatically determine doze screen brightness. + private boolean mAllowAutoBrightnessWhileDozing; + // The brightness strategy used to manage the brightness state when the display is dozing. private final DozeBrightnessStrategy mDozeBrightnessStrategy; // The brightness strategy used to manage the brightness state when the display is in @@ -149,6 +154,7 @@ public class DisplayBrightnessStrategySelector { mAutoBrightnessFallbackStrategy, mFallbackBrightnessStrategy}; mAllowAutoBrightnessWhileDozingConfig = context.getResources().getBoolean( R.bool.config_allowAutoBrightnessWhileDozing); + mAllowAutoBrightnessWhileDozing = mAllowAutoBrightnessWhileDozingConfig; mOldBrightnessStrategyName = mInvalidBrightnessStrategy.getName(); } @@ -163,6 +169,7 @@ public class DisplayBrightnessStrategySelector { int targetDisplayState = strategySelectionRequest.getTargetDisplayState(); DisplayManagerInternal.DisplayPowerRequest displayPowerRequest = strategySelectionRequest .getDisplayPowerRequest(); + setAllowAutoBrightnessWhileDozing(strategySelectionRequest.getDisplayOffloadSession()); if (targetDisplayState == Display.STATE_OFF) { displayBrightnessStrategy = mScreenOffBrightnessStrategy; } else if (shouldUseDozeBrightnessStrategy(displayPowerRequest)) { @@ -231,6 +238,14 @@ public class DisplayBrightnessStrategySelector { * Returns a boolean flag indicating if the light sensor is to be used to decide the screen * brightness when dozing */ + public boolean isAllowAutoBrightnessWhileDozing() { + return mAllowAutoBrightnessWhileDozing; + } + + /** + * Returns the config value indicating whether auto brightness while dozing is to be + * allowed ot not + */ public boolean isAllowAutoBrightnessWhileDozingConfig() { return mAllowAutoBrightnessWhileDozingConfig; } @@ -251,6 +266,8 @@ public class DisplayBrightnessStrategySelector { writer.println( " mAllowAutoBrightnessWhileDozingConfig= " + mAllowAutoBrightnessWhileDozingConfig); + writer.println( + " mAllowAutoBrightnessWhileDozing= " + mAllowAutoBrightnessWhileDozing); IndentingPrintWriter ipw = new IndentingPrintWriter(writer, " "); for (DisplayBrightnessStrategy displayBrightnessStrategy : mDisplayBrightnessStrategies) { if (displayBrightnessStrategy != null) { @@ -259,6 +276,17 @@ public class DisplayBrightnessStrategySelector { } } + @VisibleForTesting + void setAllowAutoBrightnessWhileDozing( + DisplayManagerInternal.DisplayOffloadSession displayOffloadSession) { + mAllowAutoBrightnessWhileDozing = mAllowAutoBrightnessWhileDozingConfig; + if (mDisplayManagerFlags.offloadControlsDozeAutoBrightness() + && mDisplayManagerFlags.isDisplayOffloadEnabled() + && displayOffloadSession != null) { + mAllowAutoBrightnessWhileDozing &= displayOffloadSession.allowAutoBrightnessInDoze(); + } + } + private boolean isAutoBrightnessFallbackStrategyValid() { return mDisplayManagerFlags.isRefactorDisplayPowerControllerEnabled() && mAutoBrightnessFallbackStrategy != null @@ -270,7 +298,7 @@ public class DisplayBrightnessStrategySelector { StrategySelectionRequest strategySelectionRequest) { mAutomaticBrightnessStrategy1.setAutoBrightnessState( strategySelectionRequest.getTargetDisplayState(), - mAllowAutoBrightnessWhileDozingConfig, + mAllowAutoBrightnessWhileDozing, BrightnessReason.REASON_UNKNOWN, strategySelectionRequest.getDisplayPowerRequest().policy, strategySelectionRequest.getLastUserSetScreenBrightness(), @@ -287,7 +315,7 @@ public class DisplayBrightnessStrategySelector { selectedDisplayBrightnessStrategy, strategySelectionRequest.getLastUserSetScreenBrightness(), strategySelectionRequest.isUserSetBrightnessChanged(), - isAllowAutoBrightnessWhileDozingConfig(), + mAllowAutoBrightnessWhileDozing, getAutomaticBrightnessStrategy().shouldUseAutoBrightness()); } @@ -309,7 +337,7 @@ public class DisplayBrightnessStrategySelector { // a user can define a different display state(displayPowerRequest.dozeScreenState) too // in the request with the Doze policy return displayPowerRequest.policy == DisplayManagerInternal.DisplayPowerRequest.POLICY_DOZE - && !mAllowAutoBrightnessWhileDozingConfig + && !mAllowAutoBrightnessWhileDozing && BrightnessUtils.isValidBrightnessValue(displayPowerRequest.dozeScreenBrightness); } diff --git a/services/core/java/com/android/server/display/brightness/StrategySelectionRequest.java b/services/core/java/com/android/server/display/brightness/StrategySelectionRequest.java index ae745efc8683..aa2f23ef9ec1 100644 --- a/services/core/java/com/android/server/display/brightness/StrategySelectionRequest.java +++ b/services/core/java/com/android/server/display/brightness/StrategySelectionRequest.java @@ -38,13 +38,17 @@ public final class StrategySelectionRequest { // Represents if the user set screen brightness was changed or not. private boolean mUserSetBrightnessChanged; + private DisplayManagerInternal.DisplayOffloadSession mDisplayOffloadSession; + public StrategySelectionRequest(DisplayManagerInternal.DisplayPowerRequest displayPowerRequest, int targetDisplayState, float lastUserSetScreenBrightness, - boolean userSetBrightnessChanged) { + boolean userSetBrightnessChanged, + DisplayManagerInternal.DisplayOffloadSession displayOffloadSession) { mDisplayPowerRequest = displayPowerRequest; mTargetDisplayState = targetDisplayState; mLastUserSetScreenBrightness = lastUserSetScreenBrightness; mUserSetBrightnessChanged = userSetBrightnessChanged; + mDisplayOffloadSession = displayOffloadSession; } public DisplayManagerInternal.DisplayPowerRequest getDisplayPowerRequest() { @@ -64,6 +68,10 @@ public final class StrategySelectionRequest { return mUserSetBrightnessChanged; } + public DisplayManagerInternal.DisplayOffloadSession getDisplayOffloadSession() { + return mDisplayOffloadSession; + } + @Override public boolean equals(Object obj) { if (!(obj instanceof StrategySelectionRequest)) { @@ -73,12 +81,13 @@ public final class StrategySelectionRequest { return Objects.equals(mDisplayPowerRequest, other.getDisplayPowerRequest()) && mTargetDisplayState == other.getTargetDisplayState() && mLastUserSetScreenBrightness == other.getLastUserSetScreenBrightness() - && mUserSetBrightnessChanged == other.isUserSetBrightnessChanged(); + && mUserSetBrightnessChanged == other.isUserSetBrightnessChanged() + && mDisplayOffloadSession.equals(other.getDisplayOffloadSession()); } @Override public int hashCode() { return Objects.hash(mDisplayPowerRequest, mTargetDisplayState, - mLastUserSetScreenBrightness, mUserSetBrightnessChanged); + mLastUserSetScreenBrightness, mUserSetBrightnessChanged, mDisplayOffloadSession); } } 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 11ef5776a47a..101ad307f50a 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 @@ -16,21 +16,30 @@ package com.android.server.display.brightness.clamper; +import static android.view.Display.STATE_ON; + import static com.android.server.display.brightness.clamper.BrightnessClamper.Type; import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Context; +import android.content.res.Resources; +import android.hardware.Sensor; +import android.hardware.SensorEvent; +import android.hardware.SensorEventListener; +import android.hardware.SensorManager; import android.hardware.display.BrightnessInfo; import android.hardware.display.DisplayManagerInternal; import android.os.Handler; import android.os.HandlerExecutor; import android.os.PowerManager; +import android.os.SystemClock; import android.provider.DeviceConfig; import android.provider.DeviceConfigInterface; import android.util.IndentingPrintWriter; import android.util.Slog; +import com.android.internal.R; import com.android.internal.annotations.VisibleForTesting; import com.android.server.display.DisplayBrightnessState; import com.android.server.display.DisplayDeviceConfig; @@ -41,20 +50,30 @@ import com.android.server.display.brightness.BrightnessReason; import com.android.server.display.config.SensorData; import com.android.server.display.feature.DeviceConfigParameterProvider; import com.android.server.display.feature.DisplayManagerFlags; +import com.android.server.display.utils.AmbientFilter; +import com.android.server.display.utils.AmbientFilterFactory; +import com.android.server.display.utils.DebugUtils; +import com.android.server.display.utils.SensorUtils; import java.io.PrintWriter; import java.util.ArrayList; import java.util.List; import java.util.concurrent.Executor; +import java.util.concurrent.TimeUnit; /** * Clampers controller, all in DisplayControllerHandler */ public class BrightnessClamperController { private static final String TAG = "BrightnessClamperController"; + // To enable these logs, run: + // 'adb shell setprop persist.log.tag.BrightnessClamperController DEBUG && adb reboot' + private static final boolean DEBUG = DebugUtils.isDebuggable(TAG); + public static final float INVALID_LUX = -1f; private final DeviceConfigParameterProvider mDeviceConfigParameterProvider; private final Handler mHandler; + private final SensorManager mSensorManager; private final ClamperChangeListener mClamperChangeListenerExternal; private final Executor mExecutor; private final List<BrightnessClamper<? super DisplayDeviceData>> mClampers; @@ -66,24 +85,55 @@ public class BrightnessClamperController { private float mCustomAnimationRate = DisplayBrightnessState.CUSTOM_ANIMATION_RATE_NOT_SET; @Nullable private Type mClamperType = null; - private int mAutoBrightnessState = -1; - + private final SensorEventListener mLightSensorListener; + private Sensor mRegisteredLightSensor = null; + private Sensor mLightSensor; + private String mLightSensorType; + private String mLightSensorName; + private AmbientFilter mAmbientFilter; + private final DisplayDeviceConfig mDisplayDeviceConfig; + private final Resources mResources; + private final int mLightSensorRate; + + private final Injector mInjector; private boolean mClamperApplied = false; public BrightnessClamperController(Handler handler, ClamperChangeListener clamperChangeListener, DisplayDeviceData data, Context context, - DisplayManagerFlags flags) { - this(new Injector(), handler, clamperChangeListener, data, context, flags); + DisplayManagerFlags flags, SensorManager sensorManager) { + this(null, handler, clamperChangeListener, data, context, flags, sensorManager); } @VisibleForTesting BrightnessClamperController(Injector injector, Handler handler, ClamperChangeListener clamperChangeListener, DisplayDeviceData data, Context context, - DisplayManagerFlags flags) { - mDeviceConfigParameterProvider = injector.getDeviceConfigParameterProvider(); + DisplayManagerFlags flags, SensorManager sensorManager) { + mInjector = injector == null ? new Injector() : injector; + mDeviceConfigParameterProvider = mInjector.getDeviceConfigParameterProvider(); mHandler = handler; + mSensorManager = sensorManager; + mDisplayDeviceConfig = data.mDisplayDeviceConfig; + mLightSensorListener = new SensorEventListener() { + @Override + public void onSensorChanged(SensorEvent event) { + long now = SystemClock.elapsedRealtime(); + mAmbientFilter.addValue(TimeUnit.NANOSECONDS.toMillis(event.timestamp), + event.values[0]); + final float lux = mAmbientFilter.getEstimate(now); + mModifiers.forEach(mModifier -> mModifier.setAmbientLux(lux)); + } + + @Override + public void onAccuracyChanged(Sensor sensor, int accuracy) { + // unused + } + }; + mClamperChangeListenerExternal = clamperChangeListener; mExecutor = new HandlerExecutor(handler); + mResources = context.getResources(); + mLightSensorRate = context.getResources().getInteger( + R.integer.config_autoBrightnessLightSensorRate); Runnable clamperChangeRunnableInternal = this::recalculateBrightnessCap; @@ -93,10 +143,10 @@ public class BrightnessClamperController { } }; - mClampers = injector.getClampers(handler, clamperChangeListenerInternal, data, flags, + mClampers = mInjector.getClampers(handler, clamperChangeListenerInternal, data, flags, context); - mModifiers = injector.getModifiers(flags, context, handler, clamperChangeListener, - data.mDisplayDeviceConfig); + mModifiers = mInjector.getModifiers(flags, context, handler, clamperChangeListener, + data.mDisplayDeviceConfig, mSensorManager); mOnPropertiesChangedListener = properties -> mClampers.forEach(BrightnessClamper::onDeviceConfigChanged); start(); @@ -114,7 +164,7 @@ public class BrightnessClamperController { * Called in DisplayControllerHandler */ public DisplayBrightnessState clamp(DisplayManagerInternal.DisplayPowerRequest request, - float brightnessValue, boolean slowChange) { + float brightnessValue, boolean slowChange, int displayState) { float cappedBrightness = Math.min(brightnessValue, mBrightnessCap); DisplayBrightnessState.Builder builder = DisplayBrightnessState.builder(); @@ -133,6 +183,12 @@ public class BrightnessClamperController { mClamperApplied = false; } + if (displayState != STATE_ON) { + unregisterSensorListener(); + } else { + maybeRegisterLightSensor(); + } + for (int i = 0; i < mModifiers.size(); i++) { mModifiers.get(i).apply(request, builder); } @@ -175,6 +231,8 @@ public class BrightnessClamperController { writer.println(" mBrightnessCap: " + mBrightnessCap); writer.println(" mClamperType: " + mClamperType); writer.println(" mClamperApplied: " + mClamperApplied); + writer.println(" mLightSensor=" + mLightSensor); + writer.println(" mRegisteredLightSensor=" + mRegisteredLightSensor); IndentingPrintWriter ipw = new IndentingPrintWriter(writer, " "); mClampers.forEach(clamper -> clamper.dump(ipw)); mModifiers.forEach(modifier -> modifier.dump(ipw)); @@ -191,26 +249,6 @@ public class BrightnessClamperController { mModifiers.forEach(BrightnessStateModifier::stop); } - /** - * Notifies modifiers that ambient lux has changed. - * @param ambientLux current lux, debounced - */ - public void onAmbientLuxChange(float ambientLux) { - mModifiers.forEach(modifier -> modifier.onAmbientLuxChange(ambientLux)); - } - - /** - * Sets the autobrightness state for clampers that need to be aware of the state. - * @param state autobrightness state - */ - public void setAutoBrightnessState(int state) { - if (state == mAutoBrightnessState) { - return; - } - mModifiers.forEach(modifier -> modifier.setAutoBrightnessState(state)); - mAutoBrightnessState = state; - recalculateBrightnessCap(); - } // Called in DisplayControllerHandler private void recalculateBrightnessCap() { @@ -243,6 +281,10 @@ public class BrightnessClamperController { if (!mClampers.isEmpty()) { mDeviceConfigParameterProvider.addOnPropertiesChangedListener( mExecutor, mOnPropertiesChangedListener); + reloadLightSensorData(mDisplayDeviceConfig); + mLightSensor = mInjector.getLightSensor( + mSensorManager, mLightSensorType, mLightSensorName); + maybeRegisterLightSensor(); } } @@ -281,7 +323,7 @@ public class BrightnessClamperController { List<BrightnessStateModifier> getModifiers(DisplayManagerFlags flags, Context context, Handler handler, ClamperChangeListener listener, - DisplayDeviceConfig displayDeviceConfig) { + DisplayDeviceConfig displayDeviceConfig, SensorManager sensorManager) { List<BrightnessStateModifier> modifiers = new ArrayList<>(); modifiers.add(new DisplayDimModifier(context)); modifiers.add(new BrightnessLowPowerModeModifier()); @@ -292,13 +334,19 @@ public class BrightnessClamperController { } return modifiers; } + + Sensor getLightSensor(SensorManager sensorManager, String type, String name) { + return SensorUtils.findSensor(sensorManager, type, + name, Sensor.TYPE_LIGHT); + } + } /** * Config Data for clampers */ public static class DisplayDeviceData implements BrightnessThermalClamper.ThermalData, - BrightnessPowerClamper.PowerData, + BrightnessPowerClamper.PowerData, BrightnessWearBedtimeModeClamper.WearBedtimeModeData { @NonNull private final String mUniqueDisplayId; @@ -368,4 +416,51 @@ public class BrightnessClamperController { return mDisplayDeviceConfig.getTempSensor(); } } + + private void maybeRegisterLightSensor() { + if (mModifiers.stream().noneMatch(BrightnessStateModifier::shouldListenToLightSensor)) { + return; + } + + if (mRegisteredLightSensor == mLightSensor) { + return; + } + + if (mRegisteredLightSensor != null) { + unregisterSensorListener(); + } + + mAmbientFilter = AmbientFilterFactory.createBrightnessFilter(TAG, mResources); + mSensorManager.registerListener(mLightSensorListener, + mLightSensor, mLightSensorRate * 1000, mHandler); + mRegisteredLightSensor = mLightSensor; + + if (DEBUG) { + Slog.d(TAG, "maybeRegisterLightSensor"); + } + } + + private void unregisterSensorListener() { + mSensorManager.unregisterListener(mLightSensorListener); + mRegisteredLightSensor = null; + mModifiers.forEach(mModifier -> mModifier.setAmbientLux(INVALID_LUX)); // set lux to invalid + if (DEBUG) { + Slog.d(TAG, "unregisterSensorListener"); + } + } + + private void reloadLightSensorData(DisplayDeviceConfig displayDeviceConfig) { + // The displayDeviceConfig (ddc) contains display specific preferences. When loaded, + // it naturally falls back to the global config.xml. + if (displayDeviceConfig != null + && displayDeviceConfig.getAmbientLightSensor() != null) { + // This covers both the ddc and the config.xml fallback + mLightSensorType = displayDeviceConfig.getAmbientLightSensor().type; + mLightSensorName = displayDeviceConfig.getAmbientLightSensor().name; + } else if (mLightSensorName == null && mLightSensorType == null) { + mLightSensorType = mResources.getString( + com.android.internal.R.string.config_displayLightSensorType); + mLightSensorName = ""; + } + } } diff --git a/services/core/java/com/android/server/display/brightness/clamper/BrightnessLowLuxModifier.java b/services/core/java/com/android/server/display/brightness/clamper/BrightnessLowLuxModifier.java index 7ba4a4d9c4dd..951980adac8c 100644 --- a/services/core/java/com/android/server/display/brightness/clamper/BrightnessLowLuxModifier.java +++ b/services/core/java/com/android/server/display/brightness/clamper/BrightnessLowLuxModifier.java @@ -16,7 +16,6 @@ package com.android.server.display.brightness.clamper; -import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_ENABLED; import android.content.ContentResolver; import android.content.Context; @@ -30,6 +29,7 @@ import android.util.Slog; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.display.BrightnessSynchronizer; +import com.android.server.display.BrightnessMappingStrategy; import com.android.server.display.DisplayBrightnessState; import com.android.server.display.DisplayDeviceConfig; import com.android.server.display.brightness.BrightnessReason; @@ -56,7 +56,6 @@ public class BrightnessLowLuxModifier extends BrightnessModifier { private float mBrightnessLowerBound; private float mMinNitsAllowed; private boolean mIsActive; - private boolean mAutoBrightnessEnabled; private float mAmbientLux; private final DisplayDeviceConfig mDisplayDeviceConfig; @@ -87,15 +86,15 @@ public class BrightnessLowLuxModifier extends BrightnessModifier { mContentResolver, Settings.Secure.EVEN_DIMMER_MIN_NITS, /* def= */ MIN_NITS_DEFAULT, userId); - boolean isActive = isSettingEnabled() && mAutoBrightnessEnabled; - - float luxBasedNitsLowerBound = mDisplayDeviceConfig.getMinNitsFromLux(mAmbientLux); + boolean isActive = isSettingEnabled() + && mAmbientLux != BrightnessMappingStrategy.INVALID_LUX; final int reason; float minNitsAllowed = -1f; // undefined, if setting is off. final float minBrightnessAllowed; if (isActive) { + float luxBasedNitsLowerBound = mDisplayDeviceConfig.getMinNitsFromLux(mAmbientLux); minNitsAllowed = Math.max(settingNitsLowerBound, luxBasedNitsLowerBound); minBrightnessAllowed = getBrightnessFromNits(minNitsAllowed); @@ -127,6 +126,12 @@ public class BrightnessLowLuxModifier extends BrightnessModifier { } @VisibleForTesting + public void setAmbientLux(float lux) { + mAmbientLux = lux; + recalculateLowerBound(); + } + + @VisibleForTesting public boolean isActive() { return mIsActive; } @@ -164,10 +169,10 @@ public class BrightnessLowLuxModifier extends BrightnessModifier { @Override public void apply(DisplayManagerInternal.DisplayPowerRequest request, DisplayBrightnessState.Builder stateBuilder) { + stateBuilder.setMinBrightness(mBrightnessLowerBound); float boundedBrightness = Math.max(mBrightnessLowerBound, stateBuilder.getBrightness()); stateBuilder.setBrightness(boundedBrightness); - if (BrightnessSynchronizer.floatEquals(stateBuilder.getBrightness(), mBrightnessLowerBound)) { stateBuilder.getBrightnessReason().addModifier(mReason); @@ -180,14 +185,8 @@ public class BrightnessLowLuxModifier extends BrightnessModifier { } @Override - public void onAmbientLuxChange(float ambientLux) { - mAmbientLux = ambientLux; - recalculateLowerBound(); - } - - @Override - public void setAutoBrightnessState(int state) { - mAutoBrightnessEnabled = state == AUTO_BRIGHTNESS_ENABLED; + public boolean shouldListenToLightSensor() { + return isSettingEnabled(); } @Override @@ -217,6 +216,7 @@ public class BrightnessLowLuxModifier extends BrightnessModifier { } private final class SettingsObserver extends ContentObserver { + SettingsObserver(Handler handler) { super(handler); mContentResolver.registerContentObserver( diff --git a/services/core/java/com/android/server/display/brightness/clamper/BrightnessLowPowerModeModifier.java b/services/core/java/com/android/server/display/brightness/clamper/BrightnessLowPowerModeModifier.java index b478952ee5bd..5661ede4b2ef 100644 --- a/services/core/java/com/android/server/display/brightness/clamper/BrightnessLowPowerModeModifier.java +++ b/services/core/java/com/android/server/display/brightness/clamper/BrightnessLowPowerModeModifier.java @@ -51,4 +51,14 @@ class BrightnessLowPowerModeModifier extends BrightnessModifier { IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " "); super.dump(ipw); } + + @Override + public boolean shouldListenToLightSensor() { + return false; + } + + @Override + public void setAmbientLux(float lux) { + // unused + } } diff --git a/services/core/java/com/android/server/display/brightness/clamper/BrightnessModifier.java b/services/core/java/com/android/server/display/brightness/clamper/BrightnessModifier.java index db5a524da71d..be8fa5a0f0ce 100644 --- a/services/core/java/com/android/server/display/brightness/clamper/BrightnessModifier.java +++ b/services/core/java/com/android/server/display/brightness/clamper/BrightnessModifier.java @@ -68,14 +68,4 @@ abstract class BrightnessModifier implements BrightnessStateModifier { public void stop() { // do nothing } - - @Override - public void onAmbientLuxChange(float ambientLux) { - // do nothing - } - - @Override - public void setAutoBrightnessState(int state) { - // do nothing - } } diff --git a/services/core/java/com/android/server/display/brightness/clamper/BrightnessStateModifier.java b/services/core/java/com/android/server/display/brightness/clamper/BrightnessStateModifier.java index 1606159cb247..fd40ce363016 100644 --- a/services/core/java/com/android/server/display/brightness/clamper/BrightnessStateModifier.java +++ b/services/core/java/com/android/server/display/brightness/clamper/BrightnessStateModifier.java @@ -44,14 +44,15 @@ public interface BrightnessStateModifier { void stop(); /** - * Allows modifiers to react to ambient lux changes. - * @param ambientLux current debounced lux. + * + * @return whether the brightness state modifier needs to listen to the ambient lux in order to + * calculate its bounds. */ - void onAmbientLuxChange(float ambientLux); + boolean shouldListenToLightSensor(); /** - * Sets the autobrightness state for clampers that need to be aware of the state. - * @param state autobrightness state + * Current ambient lux + * @param lux - ambient lux */ - void setAutoBrightnessState(int state); + void setAmbientLux(float lux); } diff --git a/services/core/java/com/android/server/display/brightness/clamper/DisplayDimModifier.java b/services/core/java/com/android/server/display/brightness/clamper/DisplayDimModifier.java index 4ff7bdb93d22..ab880bf28743 100644 --- a/services/core/java/com/android/server/display/brightness/clamper/DisplayDimModifier.java +++ b/services/core/java/com/android/server/display/brightness/clamper/DisplayDimModifier.java @@ -76,4 +76,14 @@ class DisplayDimModifier extends BrightnessModifier { IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " "); super.dump(ipw); } + + @Override + public boolean shouldListenToLightSensor() { + return false; + } + + @Override + public void setAmbientLux(float lux) { + // unused + } } diff --git a/services/core/java/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategy.java b/services/core/java/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategy.java index 2b5241fd5baf..b43b35ba50bc 100644 --- a/services/core/java/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategy.java +++ b/services/core/java/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategy.java @@ -102,6 +102,9 @@ public class AutomaticBrightnessStrategy extends AutomaticBrightnessStrategy2 private DisplayManagerFlags mDisplayManagerFlags; + // Indicates if the current auto-brightness should be ramped up or down slowly. + private boolean mIsSlowChange; + @VisibleForTesting AutomaticBrightnessStrategy(Context context, int displayId, Injector injector, DisplayManagerFlags displayManagerFlags) { @@ -172,6 +175,11 @@ public class AutomaticBrightnessStrategy extends AutomaticBrightnessStrategy2 isValid = true; } } + + // A change is slow when the auto-brightness was already applied, and there are no new + // auto-brightness adjustments from an external client(e.g. Moving the slider). As such, + // it is important to record this value before applying the current auto-brightness. + mIsSlowChange = hasAppliedAutoBrightness() && !getAutoBrightnessAdjustmentChanged(); setAutoBrightnessApplied(isValid); return isValid; } @@ -284,8 +292,7 @@ public class AutomaticBrightnessStrategy extends AutomaticBrightnessStrategy2 .setSdrBrightness(brightness) .setBrightnessReason(brightnessReason) .setDisplayBrightnessStrategyName(getName()) - .setIsSlowChange(hasAppliedAutoBrightness() - && !getAutoBrightnessAdjustmentChanged()) + .setIsSlowChange(mIsSlowChange) .setBrightnessEvent(brightnessEvent) .setBrightnessAdjustmentFlag(mAutoBrightnessAdjustmentReasonsFlags) .setShouldUpdateScreenBrightnessSetting( diff --git a/services/core/java/com/android/server/display/config/RefreshRateData.java b/services/core/java/com/android/server/display/config/RefreshRateData.java index d7ed904e398d..f769a89551c7 100644 --- a/services/core/java/com/android/server/display/config/RefreshRateData.java +++ b/services/core/java/com/android/server/display/config/RefreshRateData.java @@ -64,18 +64,22 @@ public class RefreshRateData { public final List<SupportedModeData> lowPowerSupportedModes; + public final List<SupportedModeData> lowLightBlockingZoneSupportedModes; + @VisibleForTesting public RefreshRateData(int defaultRefreshRate, int defaultPeakRefreshRate, int defaultRefreshRateInHbmHdr, int defaultRefreshRateInHbmSunlight, - List<SupportedModeData> lowPowerSupportedModes) { + List<SupportedModeData> lowPowerSupportedModes, + List<SupportedModeData> lowLightBlockingZoneSupportedModes) { this.defaultRefreshRate = defaultRefreshRate; this.defaultPeakRefreshRate = defaultPeakRefreshRate; this.defaultRefreshRateInHbmHdr = defaultRefreshRateInHbmHdr; this.defaultRefreshRateInHbmSunlight = defaultRefreshRateInHbmSunlight; this.lowPowerSupportedModes = Collections.unmodifiableList(lowPowerSupportedModes); + this.lowLightBlockingZoneSupportedModes = + Collections.unmodifiableList(lowLightBlockingZoneSupportedModes); } - @Override public String toString() { return "RefreshRateData {" @@ -84,6 +88,7 @@ public class RefreshRateData { + ", defaultRefreshRateInHbmHdr: " + defaultRefreshRateInHbmHdr + ", defaultRefreshRateInHbmSunlight: " + defaultRefreshRateInHbmSunlight + ", lowPowerSupportedModes=" + lowPowerSupportedModes + + ", lowLightBlockingZoneSupportedModes=" + lowLightBlockingZoneSupportedModes + "} "; } @@ -100,13 +105,19 @@ public class RefreshRateData { int defaultRefreshRateInHbmSunlight = loadDefaultRefreshRateInHbmSunlight( refreshRateConfigs, resources); - NonNegativeFloatToFloatMap modes = + NonNegativeFloatToFloatMap lowPowerModes = refreshRateConfigs == null ? null : refreshRateConfigs.getLowPowerSupportedModes(); - List<SupportedModeData> lowPowerSupportedModes = SupportedModeData.load(modes); + List<SupportedModeData> lowPowerSupportedModes = SupportedModeData.load(lowPowerModes); + + BlockingZoneConfig lowerZoneConfig = refreshRateConfigs == null ? null + : refreshRateConfigs.getLowerBlockingZoneConfigs(); + NonNegativeFloatToFloatMap lowerZoneModes = + lowerZoneConfig == null ? null : lowerZoneConfig.getSupportedModes(); + List<SupportedModeData> lowLightSupportedModes = SupportedModeData.load(lowerZoneModes); return new RefreshRateData(defaultRefreshRate, defaultPeakRefreshRate, defaultRefreshRateInHbmHdr, defaultRefreshRateInHbmSunlight, - lowPowerSupportedModes); + lowPowerSupportedModes, lowLightSupportedModes); } private static int loadDefaultRefreshRate( 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 d519748929bf..d610f086b3b5 100644 --- a/services/core/java/com/android/server/display/mode/DisplayModeDirector.java +++ b/services/core/java/com/android/server/display/mode/DisplayModeDirector.java @@ -2157,8 +2157,19 @@ public class DisplayModeDirector { } } + private boolean hasLowLightVrrConfig() { + DisplayDeviceConfig config; + synchronized (mLock) { + config = mDefaultDisplayDeviceConfig; + } + return mVsyncLowLightBlockingVoteEnabled + && config != null + && config.isVrrSupportEnabled() + && !config.getRefreshRateData().lowLightBlockingZoneSupportedModes.isEmpty(); + } + private void restartObserver() { - if (mRefreshRateInLowZone > 0) { + if (mRefreshRateInLowZone > 0 || hasLowLightVrrConfig()) { mShouldObserveDisplayLowChange = hasValidThreshold( mLowDisplayBrightnessThresholds); mShouldObserveAmbientLowChange = hasValidThreshold( @@ -2300,6 +2311,7 @@ public class DisplayModeDirector { return false; } + @GuardedBy("mLock") private void onBrightnessChangedLocked() { if (!mRefreshRateChangeable || mLowPowerModeEnabled) { return; @@ -2315,8 +2327,14 @@ public class DisplayModeDirector { boolean insideLowZone = hasValidLowZone() && isInsideLowZone(mBrightness, mAmbientLux); if (insideLowZone) { - refreshRateVote = - Vote.forPhysicalRefreshRates(mRefreshRateInLowZone, mRefreshRateInLowZone); + if (hasLowLightVrrConfig()) { + refreshRateVote = Vote.forSupportedRefreshRates(mDefaultDisplayDeviceConfig + .getRefreshRateData().lowLightBlockingZoneSupportedModes); + } else { + refreshRateVote = Vote.forPhysicalRefreshRates( + mRefreshRateInLowZone, mRefreshRateInLowZone); + refreshRateSwitchingVote = Vote.forDisableRefreshRateSwitching(); + } if (mLowZoneRefreshRateForThermals != null) { RefreshRateRange range = SkinThermalStatusObserver .findBestMatchingRefreshRateRange(mThermalStatus, @@ -2326,18 +2344,6 @@ public class DisplayModeDirector { Vote.forPhysicalRefreshRates(range.min, range.max); } } - - if (mVsyncLowLightBlockingVoteEnabled - && isVrrSupportedLocked(Display.DEFAULT_DISPLAY)) { - refreshRateSwitchingVote = Vote.forSupportedRefreshRatesAndDisableSwitching( - List.of( - new SupportedRefreshRatesVote.RefreshRates( - /* peakRefreshRate= */ 60f, /* vsyncRate= */ 60f), - new SupportedRefreshRatesVote.RefreshRates( - /* peakRefreshRate= */120f, /* vsyncRate= */ 120f))); - } else { - refreshRateSwitchingVote = Vote.forDisableRefreshRateSwitching(); - } } boolean insideHighZone = hasValidHighZone() @@ -2368,7 +2374,7 @@ public class DisplayModeDirector { } private boolean hasValidLowZone() { - return mRefreshRateInLowZone > 0 + return (mRefreshRateInLowZone > 0 || hasLowLightVrrConfig()) && (mShouldObserveDisplayLowChange || mShouldObserveAmbientLowChange); } diff --git a/services/core/java/com/android/server/display/mode/Vote.java b/services/core/java/com/android/server/display/mode/Vote.java index 1ec469c21f7b..7cbdd13152b5 100644 --- a/services/core/java/com/android/server/display/mode/Vote.java +++ b/services/core/java/com/android/server/display/mode/Vote.java @@ -16,10 +16,13 @@ package com.android.server.display.mode; +import android.annotation.IntDef; import android.annotation.NonNull; import com.android.server.display.config.SupportedModeData; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.List; @@ -132,15 +135,40 @@ interface Vote { // to function, so this needs to be the highest priority of all votes. int PRIORITY_UDFPS = 20; + @IntDef(prefix = { "PRIORITY_" }, value = { + PRIORITY_DEFAULT_RENDER_FRAME_RATE, + PRIORITY_FLICKER_REFRESH_RATE, + PRIORITY_HIGH_BRIGHTNESS_MODE, + PRIORITY_USER_SETTING_MIN_RENDER_FRAME_RATE, + PRIORITY_USER_SETTING_DISPLAY_PREFERRED_SIZE, + PRIORITY_APP_REQUEST_RENDER_FRAME_RATE_RANGE, + PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE, + PRIORITY_APP_REQUEST_SIZE, + PRIORITY_USER_SETTING_PEAK_REFRESH_RATE, + PRIORITY_USER_SETTING_PEAK_RENDER_FRAME_RATE, + PRIORITY_SYNCHRONIZED_REFRESH_RATE, + PRIORITY_LIMIT_MODE, + PRIORITY_AUTH_OPTIMIZER_RENDER_FRAME_RATE, + PRIORITY_LAYOUT_LIMITED_FRAME_RATE, + PRIORITY_SYSTEM_REQUESTED_MODES, + PRIORITY_LOW_POWER_MODE_MODES, + PRIORITY_LOW_POWER_MODE_RENDER_RATE, + PRIORITY_FLICKER_REFRESH_RATE_SWITCH, + PRIORITY_SKIN_TEMPERATURE, + PRIORITY_PROXIMITY, + PRIORITY_UDFPS + }) + @Retention(RetentionPolicy.SOURCE) + @interface Priority {} + // Whenever a new priority is added, remember to update MIN_PRIORITY, MAX_PRIORITY, and // APP_REQUEST_REFRESH_RATE_RANGE_PRIORITY_CUTOFF, as well as priorityToString. - - int MIN_PRIORITY = PRIORITY_DEFAULT_RENDER_FRAME_RATE; - int MAX_PRIORITY = PRIORITY_UDFPS; + @Priority int MIN_PRIORITY = PRIORITY_DEFAULT_RENDER_FRAME_RATE; + @Priority int MAX_PRIORITY = PRIORITY_UDFPS; // The cutoff for the app request refresh rate range. Votes with priorities lower than this // value will not be considered when constructing the app request refresh rate range. - int APP_REQUEST_REFRESH_RATE_RANGE_PRIORITY_CUTOFF = + @Priority int APP_REQUEST_REFRESH_RATE_RANGE_PRIORITY_CUTOFF = PRIORITY_APP_REQUEST_RENDER_FRAME_RATE_RANGE; /** @@ -205,13 +233,6 @@ interface Vote { return new SupportedModesVote(modeIds); } - static Vote forSupportedRefreshRatesAndDisableSwitching( - List<SupportedRefreshRatesVote.RefreshRates> supportedRefreshRates) { - return new CombinedVote( - List.of(forDisableRefreshRateSwitching(), - new SupportedRefreshRatesVote(supportedRefreshRates))); - } - static String priorityToString(int priority) { switch (priority) { case PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE: diff --git a/services/core/java/com/android/server/display/mode/VotesStorage.java b/services/core/java/com/android/server/display/mode/VotesStorage.java index 6becf1c46d05..d41ef6546bc3 100644 --- a/services/core/java/com/android/server/display/mode/VotesStorage.java +++ b/services/core/java/com/android/server/display/mode/VotesStorage.java @@ -79,12 +79,12 @@ class VotesStorage { } /** updates vote storage for all displays */ - void updateGlobalVote(int priority, @Nullable Vote vote) { + void updateGlobalVote(@Vote.Priority int priority, @Nullable Vote vote) { updateVote(GLOBAL_ID, priority, vote); } /** updates vote storage */ - void updateVote(int displayId, int priority, @Nullable Vote vote) { + void updateVote(int displayId, @Vote.Priority int priority, @Nullable Vote vote) { if (mLoggingEnabled) { Slog.i(TAG, "updateVoteLocked(displayId=" + displayId + ", priority=" + Vote.priorityToString(priority) @@ -126,7 +126,7 @@ class VotesStorage { } /** removes all votes with certain priority from vote storage */ - void removeAllVotesForPriority(int priority) { + void removeAllVotesForPriority(@Vote.Priority int priority) { if (mLoggingEnabled) { Slog.i(TAG, "removeAllVotesForPriority(priority=" + Vote.priorityToString(priority) + ")"); diff --git a/services/core/java/com/android/server/dreams/OWNERS b/services/core/java/com/android/server/dreams/OWNERS index 7302f6e38af2..b9286f864031 100644 --- a/services/core/java/com/android/server/dreams/OWNERS +++ b/services/core/java/com/android/server/dreams/OWNERS @@ -1,4 +1,3 @@ -brycelee@google.com -dsandler@android.com -michaelwr@google.com -roosa@google.com +# Bug component: 66910 +include /core/java/android/service/dreams/OWNERS + diff --git a/services/core/java/com/android/server/hdmi/DeviceDiscoveryAction.java b/services/core/java/com/android/server/hdmi/DeviceDiscoveryAction.java index 10c01864457e..10c01864457e 100755..100644 --- a/services/core/java/com/android/server/hdmi/DeviceDiscoveryAction.java +++ b/services/core/java/com/android/server/hdmi/DeviceDiscoveryAction.java diff --git a/services/core/java/com/android/server/health/HealthServiceWrapperAidl.java b/services/core/java/com/android/server/health/HealthServiceWrapperAidl.java index 8a3a56cdc9ca..fd3a92e97c26 100644 --- a/services/core/java/com/android/server/health/HealthServiceWrapperAidl.java +++ b/services/core/java/com/android/server/health/HealthServiceWrapperAidl.java @@ -212,6 +212,16 @@ class HealthServiceWrapperAidl extends HealthServiceWrapper { } } + public void setChargingPolicy(int policy) throws RemoteException { + IHealth service = mLastService.get(); + if (service == null) return; + try { + service.setChargingPolicy(policy); + } catch (UnsupportedOperationException | ServiceSpecificException ex) { + return; + } + } + private static void traceBegin(String name) { Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, name); } diff --git a/services/core/java/com/android/server/health/OWNERS b/services/core/java/com/android/server/health/OWNERS index 81522fcaa09f..44ab7f75532c 100644 --- a/services/core/java/com/android/server/health/OWNERS +++ b/services/core/java/com/android/server/health/OWNERS @@ -1 +1 @@ -file:platform/hardware/interfaces:/health/aidl/OWNERS +file:platform/hardware/interfaces:/health/OWNERS diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java index 48cccd5b5b39..e5dbce9931a1 100644 --- a/services/core/java/com/android/server/input/InputManagerService.java +++ b/services/core/java/com/android/server/input/InputManagerService.java @@ -3348,6 +3348,14 @@ public class InputManagerService extends IInputManager.Stub mPointerIconCache.setUseLargePointerIcons(useLargeIcons); } + void setPointerFillStyle(@PointerIcon.PointerIconVectorStyleFill int fillStyle) { + mPointerIconCache.setPointerFillStyle(fillStyle); + } + + void setPointerScale(float scale) { + mPointerIconCache.setPointerScale(scale); + } + interface KeyboardBacklightControllerInterface { default void incrementKeyboardBacklight(int deviceId) {} default void decrementKeyboardBacklight(int deviceId) {} diff --git a/services/core/java/com/android/server/input/InputSettingsObserver.java b/services/core/java/com/android/server/input/InputSettingsObserver.java index a1341b771eb7..593b0917efc7 100644 --- a/services/core/java/com/android/server/input/InputSettingsObserver.java +++ b/services/core/java/com/android/server/input/InputSettingsObserver.java @@ -16,6 +16,10 @@ package com.android.server.input; +import static android.view.PointerIcon.DEFAULT_POINTER_SCALE; +import static android.view.PointerIcon.POINTER_ICON_VECTOR_STYLE_FILL_BLACK; +import static android.view.flags.Flags.enableVectorCursorA11ySettings; + import static com.android.input.flags.Flags.rateLimitUserActivityPokeInDispatcher; import android.content.BroadcastReceiver; @@ -96,7 +100,11 @@ class InputSettingsObserver extends ContentObserver { Map.entry(Settings.Secure.getUriFor(Settings.Secure.ACCESSIBILITY_STICKY_KEYS), (reason) -> updateAccessibilityStickyKeys()), Map.entry(Settings.Secure.getUriFor(Settings.Secure.STYLUS_POINTER_ICON_ENABLED), - (reason) -> updateStylusPointerIconEnabled())); + (reason) -> updateStylusPointerIconEnabled()), + Map.entry(Settings.System.getUriFor(Settings.System.POINTER_FILL_STYLE), + (reason) -> updatePointerFillStyleFromSettings()), + Map.entry(Settings.System.getUriFor(Settings.System.POINTER_SCALE), + (reason) -> updatePointerScaleFromSettings())); } /** @@ -261,4 +269,25 @@ class InputSettingsObserver extends ContentObserver { mNative.setStylusPointerIconEnabled( InputSettings.isStylusPointerIconEnabled(mContext, true /* forceReloadSetting */)); } + + private void updatePointerFillStyleFromSettings() { + if (!enableVectorCursorA11ySettings()) { + return; + } + final int pointerFillStyle = Settings.System.getIntForUser( + mContext.getContentResolver(), Settings.System.POINTER_FILL_STYLE, + POINTER_ICON_VECTOR_STYLE_FILL_BLACK, + UserHandle.USER_CURRENT); + mService.setPointerFillStyle(pointerFillStyle); + } + + private void updatePointerScaleFromSettings() { + if (!enableVectorCursorA11ySettings()) { + return; + } + final float pointerScale = Settings.System.getFloatForUser(mContext.getContentResolver(), + Settings.System.POINTER_SCALE, DEFAULT_POINTER_SCALE, + UserHandle.USER_CURRENT); + mService.setPointerScale(pointerScale); + } } diff --git a/services/core/java/com/android/server/input/PointerIconCache.java b/services/core/java/com/android/server/input/PointerIconCache.java index 233b865c69a6..44622d80da0e 100644 --- a/services/core/java/com/android/server/input/PointerIconCache.java +++ b/services/core/java/com/android/server/input/PointerIconCache.java @@ -16,13 +16,18 @@ package com.android.server.input; +import static android.view.PointerIcon.DEFAULT_POINTER_SCALE; +import static android.view.PointerIcon.POINTER_ICON_VECTOR_STYLE_FILL_BLACK; + import android.annotation.NonNull; import android.content.Context; +import android.content.res.Resources; import android.hardware.display.DisplayManager; import android.os.Handler; import android.util.Slog; import android.util.SparseArray; import android.util.SparseIntArray; +import android.view.ContextThemeWrapper; import android.view.Display; import android.view.DisplayInfo; import android.view.PointerIcon; @@ -56,6 +61,11 @@ final class PointerIconCache { private final SparseArray<Context> mDisplayContexts = new SparseArray<>(); @GuardedBy("mLoadedPointerIconsByDisplayAndType") private final SparseIntArray mDisplayDensities = new SparseIntArray(); + @GuardedBy("mLoadedPointerIconsByDisplayAndType") + private @PointerIcon.PointerIconVectorStyleFill int mPointerIconFillStyle = + POINTER_ICON_VECTOR_STYLE_FILL_BLACK; + @GuardedBy("mLoadedPointerIconsByDisplayAndType") + private float mPointerIconScale = DEFAULT_POINTER_SCALE; private final DisplayManager.DisplayListener mDisplayListener = new DisplayManager.DisplayListener() { @@ -105,6 +115,16 @@ final class PointerIconCache { mUiThreadHandler.post(() -> handleSetUseLargePointerIcons(useLargeIcons)); } + /** Set the fill style for vector pointer icons. */ + public void setPointerFillStyle(@PointerIcon.PointerIconVectorStyleFill int fillStyle) { + mUiThreadHandler.post(() -> handleSetPointerFillStyle(fillStyle)); + } + + /** Set the scale for vector pointer icons. */ + public void setPointerScale(float scale) { + mUiThreadHandler.post(() -> handleSetPointerScale(scale)); + } + /** * Get a loaded system pointer icon. This will fetch the icon from the cache, or load it if * it isn't already cached. @@ -119,8 +139,13 @@ final class PointerIconCache { } PointerIcon icon = iconsByType.get(type); if (icon == null) { - icon = PointerIcon.getLoadedSystemIcon(getContextForDisplayLocked(displayId), type, - mUseLargePointerIcons); + Context context = getContextForDisplayLocked(displayId); + Resources.Theme theme = context.getResources().newTheme(); + theme.setTo(context.getTheme()); + theme.applyStyle(PointerIcon.vectorFillStyleToResource(mPointerIconFillStyle), + /* force= */ true); + icon = PointerIcon.getLoadedSystemIcon(new ContextThemeWrapper(context, theme), + type, mUseLargePointerIcons, mPointerIconScale); iconsByType.put(type, icon); } return Objects.requireNonNull(icon); @@ -185,6 +210,32 @@ final class PointerIconCache { mNative.reloadPointerIcons(); } + @android.annotation.UiThread + private void handleSetPointerFillStyle(@PointerIcon.PointerIconVectorStyleFill int fillStyle) { + synchronized (mLoadedPointerIconsByDisplayAndType) { + if (mPointerIconFillStyle == fillStyle) { + return; + } + mPointerIconFillStyle = fillStyle; + // Clear all cached icons on all displays. + mLoadedPointerIconsByDisplayAndType.clear(); + } + mNative.reloadPointerIcons(); + } + + @android.annotation.UiThread + private void handleSetPointerScale(float scale) { + synchronized (mLoadedPointerIconsByDisplayAndType) { + if (mPointerIconScale == scale) { + return; + } + mPointerIconScale = scale; + // Clear all cached icons on all displays. + mLoadedPointerIconsByDisplayAndType.clear(); + } + mNative.reloadPointerIcons(); + } + // Updates the cached display density for the given displayId, and returns true if // the cached density changed. @GuardedBy("mLoadedPointerIconsByDisplayAndType") diff --git a/services/core/java/com/android/server/inputmethod/DefaultImeVisibilityApplier.java b/services/core/java/com/android/server/inputmethod/DefaultImeVisibilityApplier.java index 73f1aad31d72..7d485271e35c 100644 --- a/services/core/java/com/android/server/inputmethod/DefaultImeVisibilityApplier.java +++ b/services/core/java/com/android/server/inputmethod/DefaultImeVisibilityApplier.java @@ -32,11 +32,13 @@ import static com.android.server.inputmethod.ImeVisibilityStateComputer.STATE_SH import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.UserIdInt; import android.os.IBinder; import android.os.ResultReceiver; import android.util.EventLog; import android.util.Slog; import android.view.MotionEvent; +import android.view.inputmethod.Flags; import android.view.inputmethod.ImeTracker; import android.view.inputmethod.InputMethod; import android.view.inputmethod.InputMethodManager; @@ -136,56 +138,75 @@ final class DefaultImeVisibilityApplier implements ImeVisibilityApplier { @GuardedBy("ImfLock.class") @Override public void applyImeVisibility(IBinder windowToken, @NonNull ImeTracker.Token statsToken, - @ImeVisibilityStateComputer.VisibilityState int state) { + @ImeVisibilityStateComputer.VisibilityState int state, @UserIdInt int userId) { applyImeVisibility(windowToken, statsToken, state, - SoftInputShowHideReason.NOT_SET /* ignore reason */); + SoftInputShowHideReason.NOT_SET /* ignore reason */, userId); } @GuardedBy("ImfLock.class") void applyImeVisibility(IBinder windowToken, @Nullable ImeTracker.Token statsToken, @ImeVisibilityStateComputer.VisibilityState int state, - @SoftInputShowHideReason int reason) { + @SoftInputShowHideReason int reason, @UserIdInt int userId) { + final var bindingController = mService.getInputMethodBindingController(userId); + final int displayIdToShowIme = bindingController.getDisplayIdToShowIme(); switch (state) { case STATE_SHOW_IME: - ImeTracker.forLogging().onProgress(statsToken, - ImeTracker.PHASE_SERVER_APPLY_IME_VISIBILITY); - // Send to window manager to show IME after IME layout finishes. - mWindowManagerInternal.showImePostLayout(windowToken, statsToken); - break; - case STATE_HIDE_IME: - if (mService.hasAttachedClient()) { + if (!Flags.refactorInsetsController()) { ImeTracker.forLogging().onProgress(statsToken, ImeTracker.PHASE_SERVER_APPLY_IME_VISIBILITY); - // IMMS only knows of focused window, not the actual IME target. - // e.g. it isn't aware of any window that has both - // NOT_FOCUSABLE, ALT_FOCUSABLE_IM flags set and can the IME target. - // Send it to window manager to hide IME from the actual IME control target - // of the target display. - mWindowManagerInternal.hideIme(windowToken, - mService.getDisplayIdToShowImeLocked(), statsToken); - } else { - ImeTracker.forLogging().onFailed(statsToken, - ImeTracker.PHASE_SERVER_APPLY_IME_VISIBILITY); + // Send to window manager to show IME after IME layout finishes. + mWindowManagerInternal.showImePostLayout(windowToken, statsToken); + } + break; + case STATE_HIDE_IME: + if (!Flags.refactorInsetsController()) { + if (mService.hasAttachedClient()) { + ImeTracker.forLogging().onProgress(statsToken, + ImeTracker.PHASE_SERVER_APPLY_IME_VISIBILITY); + // IMMS only knows of focused window, not the actual IME target. + // e.g. it isn't aware of any window that has both + // NOT_FOCUSABLE, ALT_FOCUSABLE_IM flags set and can the IME target. + // Send it to window manager to hide IME from the actual IME control target + // of the target display. + mWindowManagerInternal.hideIme(windowToken, displayIdToShowIme, statsToken); + } else { + ImeTracker.forLogging().onFailed(statsToken, + ImeTracker.PHASE_SERVER_APPLY_IME_VISIBILITY); + } } break; case STATE_HIDE_IME_EXPLICIT: - mService.hideCurrentInputLocked(windowToken, statsToken, + if (Flags.refactorInsetsController()) { + setImeVisibilityOnFocusedWindowClient(false); + } else { + mService.hideCurrentInputLocked(windowToken, statsToken, 0 /* flags */, null /* resultReceiver */, reason); + } break; case STATE_HIDE_IME_NOT_ALWAYS: - mService.hideCurrentInputLocked(windowToken, statsToken, - InputMethodManager.HIDE_NOT_ALWAYS, null /* resultReceiver */, reason); + if (Flags.refactorInsetsController()) { + setImeVisibilityOnFocusedWindowClient(false); + } else { + mService.hideCurrentInputLocked(windowToken, statsToken, + InputMethodManager.HIDE_NOT_ALWAYS, null /* resultReceiver */, reason); + } break; case STATE_SHOW_IME_IMPLICIT: - mService.showCurrentInputLocked(windowToken, statsToken, - InputMethodManager.SHOW_IMPLICIT, MotionEvent.TOOL_TYPE_UNKNOWN, + if (Flags.refactorInsetsController()) { + // This can be triggered by IMMS#startInputOrWindowGainedFocus. We need to + // set the requestedVisibleTypes in InsetsController first, before applying it. + setImeVisibilityOnFocusedWindowClient(true); + } else { + mService.showCurrentInputLocked(windowToken, statsToken, + InputMethodManager.SHOW_IMPLICIT, MotionEvent.TOOL_TYPE_UNKNOWN, null /* resultReceiver */, reason); + } break; case STATE_SHOW_IME_SNAPSHOT: - showImeScreenshot(windowToken, mService.getDisplayIdToShowImeLocked()); + showImeScreenshot(windowToken, displayIdToShowIme); break; case STATE_REMOVE_IME_SNAPSHOT: - removeImeScreenshot(mService.getDisplayIdToShowImeLocked()); + removeImeScreenshot(displayIdToShowIme); break; default: throw new IllegalArgumentException("Invalid IME visibility state: " + state); @@ -214,4 +235,14 @@ final class DefaultImeVisibilityApplier implements ImeVisibilityApplier { } return false; } + + private void setImeVisibilityOnFocusedWindowClient(boolean visibility) { + if (mService.mImeBindingState != null + && mService.mImeBindingState.mFocusedWindowClient != null + && mService.mImeBindingState.mFocusedWindowClient.mClient != null) { + mService.mImeBindingState.mFocusedWindowClient.mClient.setImeVisibility(visibility); + } else { + // TODO(b/329229469): ImeTracker? + } + } } diff --git a/services/core/java/com/android/server/inputmethod/IInputMethodClientInvoker.java b/services/core/java/com/android/server/inputmethod/IInputMethodClientInvoker.java index 7251ac42c582..eada2882e410 100644 --- a/services/core/java/com/android/server/inputmethod/IInputMethodClientInvoker.java +++ b/services/core/java/com/android/server/inputmethod/IInputMethodClientInvoker.java @@ -250,6 +250,24 @@ final class IInputMethodClientInvoker { } @AnyThread + void setImeVisibility(boolean visible) { + if (mIsProxy) { + setImeVisibilityInternal(visible); + } else { + mHandler.post(() -> setImeVisibilityInternal(visible)); + } + } + + @AnyThread + private void setImeVisibilityInternal(boolean visible) { + try { + mTarget.setImeVisibility(visible); + } catch (RemoteException e) { + logRemoteException(e); + } + } + + @AnyThread void scheduleStartInputIfNecessary(boolean fullscreen) { if (mIsProxy) { scheduleStartInputIfNecessaryInternal(fullscreen); diff --git a/services/core/java/com/android/server/inputmethod/ImeVisibilityApplier.java b/services/core/java/com/android/server/inputmethod/ImeVisibilityApplier.java index 9f2b84d9bfa5..a5f9b7a986c9 100644 --- a/services/core/java/com/android/server/inputmethod/ImeVisibilityApplier.java +++ b/services/core/java/com/android/server/inputmethod/ImeVisibilityApplier.java @@ -17,6 +17,7 @@ package com.android.server.inputmethod; import android.annotation.NonNull; +import android.annotation.UserIdInt; import android.os.IBinder; import android.os.ResultReceiver; import android.view.inputmethod.ImeTracker; @@ -63,7 +64,7 @@ interface ImeVisibilityApplier { * @param state The new IME visibility state for the applier to handle */ default void applyImeVisibility(IBinder windowToken, @NonNull ImeTracker.Token statsToken, - @ImeVisibilityStateComputer.VisibilityState int state) {} + @ImeVisibilityStateComputer.VisibilityState int state, @UserIdInt int userId) {} /** * Updates the IME Z-ordering relative to the given window. diff --git a/services/core/java/com/android/server/inputmethod/ImeVisibilityStateComputer.java b/services/core/java/com/android/server/inputmethod/ImeVisibilityStateComputer.java index cdfde87f042f..9d80844ee9eb 100644 --- a/services/core/java/com/android/server/inputmethod/ImeVisibilityStateComputer.java +++ b/services/core/java/com/android/server/inputmethod/ImeVisibilityStateComputer.java @@ -47,6 +47,7 @@ import android.util.Slog; import android.util.proto.ProtoOutputStream; import android.view.MotionEvent; import android.view.WindowManager; +import android.view.inputmethod.Flags; import android.view.inputmethod.ImeTracker; import android.view.inputmethod.InputMethod; import android.view.inputmethod.InputMethodManager; @@ -424,7 +425,8 @@ public final class ImeVisibilityStateComputer { switch (softInputVisibility) { case WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED: - if (state.hasImeFocusChanged() && (!state.hasEditorFocused() || !doAutoShow)) { + if (state.hasImeFocusChanged() && (!state.hasEditorFocused() || (!doAutoShow + && !Flags.refactorInsetsController()))) { if (WindowManager.LayoutParams.mayUseInputMethod(state.getWindowFlags())) { // There is no focus view, and this window will // be behind any soft input window, so hide the @@ -455,14 +457,22 @@ public final class ImeVisibilityStateComputer { } break; case WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN: - if (isForwardNavigation) { + if (Flags.refactorInsetsController()) { + // In this case, we don't have to manipulate the requested visible types of + // the WindowState, as they're already in the correct state + break; + } else if (isForwardNavigation) { if (DEBUG) Slog.v(TAG, "Window asks to hide input going forward"); return new ImeVisibilityResult(STATE_HIDE_IME_EXPLICIT, SoftInputShowHideReason.HIDE_STATE_HIDDEN_FORWARD_NAV); } break; case WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN: - if (state.hasImeFocusChanged()) { + if (Flags.refactorInsetsController()) { + // In this case, we don't have to manipulate the requested visible types of + // the WindowState, as they're already in the correct state + break; + } else if (state.hasImeFocusChanged()) { if (DEBUG) Slog.v(TAG, "Window asks to hide input"); return new ImeVisibilityResult(STATE_HIDE_IME_EXPLICIT, SoftInputShowHideReason.HIDE_ALWAYS_HIDDEN_STATE); @@ -519,6 +529,9 @@ public final class ImeVisibilityStateComputer { // 2) SOFT_INPUT_STATE_VISIBLE state without an editor // 3) SOFT_INPUT_STATE_ALWAYS_VISIBLE state without an editor if (DEBUG) Slog.v(TAG, "Window without editor will hide input"); + if (Flags.refactorInsetsController()) { + state.setRequestedImeVisible(false); + } return new ImeVisibilityResult(STATE_HIDE_IME_EXPLICIT, SoftInputShowHideReason.HIDE_WINDOW_GAINED_FOCUS_WITHOUT_EDITOR); } diff --git a/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java b/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java index 8191ee14adff..9e8bf0e2a060 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java @@ -17,7 +17,9 @@ package com.android.server.inputmethod; import static android.app.ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_DENIED; +import static android.content.Context.DEVICE_ID_DEFAULT; import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER; +import static android.view.Display.INVALID_DISPLAY; import android.annotation.NonNull; import android.annotation.Nullable; @@ -82,12 +84,16 @@ final class InputMethodBindingController { @GuardedBy("ImfLock.class") @Nullable private IInputMethodInvoker mCurMethod; @GuardedBy("ImfLock.class") private int mCurMethodUid = Process.INVALID_UID; @GuardedBy("ImfLock.class") @Nullable private IBinder mCurToken; - @GuardedBy("ImfLock.class") private int mCurTokenDisplayId = Display.INVALID_DISPLAY; + @GuardedBy("ImfLock.class") private int mCurTokenDisplayId = INVALID_DISPLAY; @GuardedBy("ImfLock.class") private int mCurSeq; @GuardedBy("ImfLock.class") private boolean mVisibleBound; @GuardedBy("ImfLock.class") private boolean mSupportsStylusHw; @GuardedBy("ImfLock.class") private boolean mSupportsConnectionlessStylusHw; + /** The display id for which the latest startInput was called. */ + @GuardedBy("ImfLock.class") private int mDisplayIdToShowIme = INVALID_DISPLAY; + @GuardedBy("ImfLock.class") private int mDeviceIdToShowIme = DEVICE_ID_DEFAULT; + @Nullable private CountDownLatch mLatchForTesting; /** @@ -455,7 +461,7 @@ final class InputMethodBindingController { mWindowManagerInternal.removeWindowToken(mCurToken, true /* removeWindows */, false /* animateExit */, mCurTokenDisplayId); mCurToken = null; - mCurTokenDisplayId = Display.INVALID_DISPLAY; + mCurTokenDisplayId = INVALID_DISPLAY; } @GuardedBy("ImfLock.class") @@ -478,16 +484,15 @@ final class InputMethodBindingController { mCurId = info.getId(); mLastBindTime = SystemClock.uptimeMillis(); - final int displayIdToShowIme = mService.getDisplayIdToShowImeLocked(); mCurToken = new Binder(); - mCurTokenDisplayId = displayIdToShowIme; + mCurTokenDisplayId = mDisplayIdToShowIme; if (DEBUG) { Slog.v(TAG, "Adding window token: " + mCurToken + " for display: " - + displayIdToShowIme); + + mDisplayIdToShowIme); } mWindowManagerInternal.addWindowToken(mCurToken, WindowManager.LayoutParams.TYPE_INPUT_METHOD, - displayIdToShowIme, null /* options */); + mDisplayIdToShowIme, null /* options */); return new InputBindResult( InputBindResult.ResultCode.SUCCESS_WAITING_IME_BINDING, null, null, null, mCurId, mCurSeq, false); @@ -596,4 +601,24 @@ final class InputMethodBindingController { unbindVisibleConnection(); } } + + @GuardedBy("ImfLock.class") + void setDisplayIdToShowIme(int displayId) { + mDisplayIdToShowIme = displayId; + } + + @GuardedBy("ImfLock.class") + int getDisplayIdToShowIme() { + return mDisplayIdToShowIme; + } + + @GuardedBy("ImfLock.class") + void setDeviceIdToShowIme(int deviceId) { + mDeviceIdToShowIme = deviceId; + } + + @GuardedBy("ImfLock.class") + int getDeviceIdToShowIme() { + return mDeviceIdToShowIme; + } } diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java index 76f441cc1c9b..223d54842b16 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java @@ -227,6 +227,16 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. } /** + * Indicates that the annotated field is shared by all the users. + * + * <p>See b/305849394 for details.</p> + */ + @Retention(SOURCE) + @Target({ElementType.FIELD}) + private @interface SharedByAllUsersField { + } + + /** * Indicates that the annotated field is not yet ready for concurrent multi-user support. * * <p>See b/305849394 for details.</p> @@ -272,6 +282,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. * {@link LayoutParams#SOFT_INPUT_STATE_ALWAYS_VISIBLE SOFT_INPUT_STATE_ALWAYS_VISIBLE} * starting from {@link android.os.Build.VERSION_CODES#P}. */ + @SharedByAllUsersField private final boolean mPreventImeStartupUnlessTextEditor; /** @@ -279,6 +290,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. * from the IME startup avoidance behavior that is enabled by * {@link #mPreventImeStartupUnlessTextEditor}. */ + @SharedByAllUsersField @NonNull private final String[] mNonPreemptibleInputMethods; @@ -286,6 +298,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. * See {@link #shouldEnableExperimentalConcurrentMultiUserMode(Context)} about when set to be * {@code true}. */ + @SharedByAllUsersField private final boolean mExperimentalConcurrentMultiUserModeEnabled; /** @@ -327,6 +340,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. final PackageManagerInternal mPackageManagerInternal; final InputManagerInternal mInputManagerInternal; final ImePlatformCompatUtils mImePlatformCompatUtils; + @SharedByAllUsersField final InputMethodDeviceConfigs mInputMethodDeviceConfigs; private final UserManagerInternal mUserManagerInternal; @@ -339,6 +353,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. private final ImeVisibilityStateComputer mVisibilityStateComputer; @GuardedBy("ImfLock.class") + @SharedByAllUsersField @NonNull private final DefaultImeVisibilityApplier mVisibilityApplier; @@ -355,7 +370,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. // Mapping from deviceId to the device-specific imeId for that device. @GuardedBy("ImfLock.class") - @MultiUserUnawareField + @SharedByAllUsersField private final SparseArray<String> mVirtualDeviceMethodMap = new SparseArray<>(); // TODO: Instantiate mSwitchingController for each user. @@ -367,36 +382,15 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. @MultiUserUnawareField private HardwareKeyboardShortcutController mHardwareKeyboardShortcutController; - /** - * Tracks how many times {@link #mSettings} was updated. - */ - @GuardedBy("ImfLock.class") - private int mMethodMapUpdateCount = 0; - - /** - * The display id for which the latest startInput was called. - */ - @GuardedBy("ImfLock.class") - int getDisplayIdToShowImeLocked() { - return mDisplayIdToShowIme; - } - - @GuardedBy("ImfLock.class") - @MultiUserUnawareField - private int mDisplayIdToShowIme = INVALID_DISPLAY; - - @GuardedBy("ImfLock.class") - @MultiUserUnawareField - private int mDeviceIdToShowIme = DEVICE_ID_DEFAULT; - @Nullable private StatusBarManagerInternal mStatusBarManagerInternal; + @SharedByAllUsersField private boolean mShowOngoingImeSwitcherForPhones; @GuardedBy("ImfLock.class") @MultiUserUnawareField private final HandwritingModeController mHwController; @GuardedBy("ImfLock.class") - @MultiUserUnawareField + @SharedByAllUsersField private IntArray mStylusIds; @GuardedBy("ImfLock.class") @@ -475,6 +469,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. /** * Manages the IME clients. */ + @SharedByAllUsersField private final ClientController mClientController; /** @@ -486,14 +481,22 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. /** * Set once the system is ready to run third party code. */ + @SharedByAllUsersField boolean mSystemReady; @GuardedBy("ImfLock.class") @NonNull + UserDataRepository.UserData getUserData(@UserIdInt int userId) { + return mUserDataRepository.getOrCreate(userId); + } + + @GuardedBy("ImfLock.class") + @NonNull InputMethodBindingController getInputMethodBindingController(@UserIdInt int userId) { - return mUserDataRepository.getOrCreate(userId).mBindingController; + return getUserData(userId).mBindingController; } + /** * Id obtained with {@link InputMethodInfo#getId()} for the currently selected input method. * This is to be synchronized with the secure settings keyed with @@ -522,6 +525,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. /** * The client that is currently bound to an input method. */ + @MultiUserUnawareField @Nullable private ClientState mCurClient; @@ -573,6 +577,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. * {@link android.view.InsetsController} for the given window. */ @GuardedBy("ImfLock.class") + @SharedByAllUsersField private final WeakHashMap<IBinder, Boolean> mFocusedWindowPerceptible = new WeakHashMap<>(); /** @@ -677,28 +682,36 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. @MultiUserUnawareField int mImeWindowVis; + @SharedByAllUsersField private final MyPackageMonitor mMyPackageMonitor = new MyPackageMonitor(); + + @SharedByAllUsersField private final String mSlotIme; /** * Registered {@link InputMethodListListener}. * This variable can be accessed from both of MainThread and BinderThread. */ + @SharedByAllUsersField private final CopyOnWriteArrayList<InputMethodListListener> mInputMethodListListeners = new CopyOnWriteArrayList<>(); @GuardedBy("ImfLock.class") + @SharedByAllUsersField private final WeakHashMap<IBinder, IBinder> mImeTargetWindowMap = new WeakHashMap<>(); @GuardedBy("ImfLock.class") + @SharedByAllUsersField @NonNull private final StartInputHistory mStartInputHistory = new StartInputHistory(); @GuardedBy("ImfLock.class") + @SharedByAllUsersField @NonNull private final SoftInputShowHideHistory mSoftInputShowHideHistory = new SoftInputShowHideHistory(); + @SharedByAllUsersField @NonNull private final ImeTrackerService mImeTrackerService; @@ -1120,6 +1133,9 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. publishBinderService(Context.INPUT_METHOD_SERVICE, IInputMethodManagerImpl.create(service), false /*allowIsolated*/, DUMP_FLAG_PRIORITY_CRITICAL | DUMP_FLAG_PRIORITY_NORMAL | DUMP_FLAG_PROTO); + if (Flags.refactorInsetsController()) { + mService.registerImeRequestedChangedListener(); + } } @Override @@ -1159,7 +1175,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. final int userId = user.getUserIdentifier(); SecureSettingsWrapper.onUserStarting(userId); synchronized (ImfLock.class) { - mService.mUserDataRepository.getOrCreate(userId); + mService.getUserData(userId); if (mService.mExperimentalConcurrentMultiUserModeEnabled) { if (mService.mCurrentUserId != userId && mService.mSystemReady) { mService.experimentalInitializeVisibleBackgroundUserLocked(userId); @@ -1281,7 +1297,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. bindingControllerForTesting != null ? bindingControllerForTesting : bindingControllerFactory); for (int id : mUserManagerInternal.getUserIds()) { - mUserDataRepository.getOrCreate(id); + getUserData(id); } final InputMethodSettings settings = InputMethodSettingsRepository.get(mCurrentUserId); @@ -1559,6 +1575,19 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. } } + void registerImeRequestedChangedListener() { + mWindowManagerInternal.setOnImeRequestedChangedListener( + (windowToken, imeVisible) -> { + if (Flags.refactorInsetsController()) { + if (imeVisible) { + showSoftInputInternal(windowToken); + } else { + hideSoftInputInternal(windowToken); + } + } + }); + } + /** * Returns true iff the caller is identified to be the current input method with the token. * @@ -1915,6 +1944,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. } /** + * TODO(b/338404383) Remove * Called when {@link #resetCurrentMethodAndClientLocked(int)} invoked for clean-up states * before unbinding the current method. */ @@ -1934,7 +1964,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. final var statsToken = createStatsTokenForFocusedClient(false /* show */, SoftInputShowHideReason.UNBIND_CURRENT_METHOD); mVisibilityApplier.applyImeVisibility(mImeBindingState.mFocusedWindow, statsToken, - STATE_HIDE_IME); + STATE_HIDE_IME, mCurrentUserId); } } @@ -2010,16 +2040,23 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. setEnabledSessionLocked(session); session.mMethod.startInput(startInputToken, mCurInputConnection, mCurEditorInfo, restarting, navButtonFlags, mCurImeDispatcher); - if (isShowRequestedForCurrentWindow()) { - if (DEBUG) Slog.v(TAG, "Attach new input asks to show input"); - // Re-use current statsToken, if it exists. - final var statsToken = mCurStatsToken != null ? mCurStatsToken + if (Flags.refactorInsetsController()) { + if (isShowRequestedForCurrentWindow() && mImeBindingState != null + && mImeBindingState.mFocusedWindow != null) { + showSoftInputInternal(mImeBindingState.mFocusedWindow); + } + } else { + if (isShowRequestedForCurrentWindow()) { + if (DEBUG) Slog.v(TAG, "Attach new input asks to show input"); + // Re-use current statsToken, if it exists. + final var statsToken = mCurStatsToken != null ? mCurStatsToken : createStatsTokenForFocusedClient(true /* show */, SoftInputShowHideReason.ATTACH_NEW_INPUT); - mCurStatsToken = null; - showCurrentInputLocked(mImeBindingState.mFocusedWindow, statsToken, - mVisibilityStateComputer.getShowFlags(), MotionEvent.TOOL_TYPE_UNKNOWN, - null /* resultReceiver */, SoftInputShowHideReason.ATTACH_NEW_INPUT); + mCurStatsToken = null; + showCurrentInputLocked(mImeBindingState.mFocusedWindow, statsToken, + mVisibilityStateComputer.getShowFlags(), MotionEvent.TOOL_TYPE_UNKNOWN, + null /* resultReceiver */, SoftInputShowHideReason.ATTACH_NEW_INPUT); + } } final var curId = bindingController.getCurId(); @@ -2098,7 +2135,8 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. return InputBindResult.NOT_IME_TARGET_WINDOW; } final int csDisplayId = cs.mSelfReportedDisplayId; - mDisplayIdToShowIme = mVisibilityStateComputer.computeImeDisplayId(winState, csDisplayId); + bindingController.setDisplayIdToShowIme( + mVisibilityStateComputer.computeImeDisplayId(winState, csDisplayId)); // Potentially override the selected input method if the new display belongs to a virtual // device with a custom IME. @@ -2108,7 +2146,8 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. if (deviceMethodId == null) { mVisibilityStateComputer.getImePolicy().setImeHiddenByDisplayPolicy(true); } else if (!Objects.equals(deviceMethodId, selectedMethodId)) { - setInputMethodLocked(deviceMethodId, NOT_A_SUBTYPE_ID, mDeviceIdToShowIme); + setInputMethodLocked(deviceMethodId, NOT_A_SUBTYPE_ID, + bindingController.getDeviceIdToShowIme()); selectedMethodId = deviceMethodId; } @@ -2169,8 +2208,9 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. // We expect the caller has already verified that the client is allowed to access this // display ID. final String curId = bindingController.getCurId(); + final int displayIdToShowIme = bindingController.getDisplayIdToShowIme(); if (curId != null && curId.equals(bindingController.getSelectedMethodId()) - && mDisplayIdToShowIme == getCurTokenDisplayIdLocked()) { + && displayIdToShowIme == getCurTokenDisplayIdLocked()) { if (cs.mCurSession != null) { // Fast case: if we are already connected to the input method, // then just return it. @@ -2220,9 +2260,12 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. } final InputMethodSettings settings = InputMethodSettingsRepository.get(userId); - final int oldDeviceId = mDeviceIdToShowIme; - mDeviceIdToShowIme = mVdmInternal.getDeviceIdForDisplayId(mDisplayIdToShowIme); - if (mDeviceIdToShowIme == DEVICE_ID_DEFAULT) { + final var bindingController = getInputMethodBindingController(userId); + final int oldDeviceId = bindingController.getDeviceIdToShowIme(); + final int displayIdToShowIme = bindingController.getDisplayIdToShowIme(); + final int newDeviceId = mVdmInternal.getDeviceIdForDisplayId(displayIdToShowIme); + bindingController.setDeviceIdToShowIme(newDeviceId); + if (newDeviceId == DEVICE_ID_DEFAULT) { if (oldDeviceId == DEVICE_ID_DEFAULT) { return currentMethodId; } @@ -2234,13 +2277,12 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. return defaultDeviceMethodId; } - final String deviceMethodId = - mVirtualDeviceMethodMap.get(mDeviceIdToShowIme, currentMethodId); + final String deviceMethodId = mVirtualDeviceMethodMap.get(newDeviceId, currentMethodId); if (Objects.equals(deviceMethodId, currentMethodId)) { return currentMethodId; } else if (!settings.getMethodMap().containsKey(deviceMethodId)) { if (DEBUG) { - Slog.v(TAG, "Disabling IME on virtual device with id " + mDeviceIdToShowIme + Slog.v(TAG, "Disabling IME on virtual device with id " + newDeviceId + " because its custom input method is not available: " + deviceMethodId); } return null; @@ -2255,7 +2297,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. if (DEBUG) { Slog.v(TAG, "Switching current input method from " + currentMethodId + " to device-specific one " + deviceMethodId + " because the current display " - + mDisplayIdToShowIme + " belongs to device with id " + mDeviceIdToShowIme); + + displayIdToShowIme + " belongs to device with id " + newDeviceId); } return deviceMethodId; } @@ -2303,10 +2345,12 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. @NonNull InputMethodBindingController bindingController, @NonNull ClientState cs) { if (bindingController.hasMainConnection()) { if (getCurMethodLocked() != null) { - // Return to client, and we will get back with it when - // we have had a session made for it. - requestClientSessionLocked(cs); - requestClientSessionForAccessibilityLocked(cs); + if (!Flags.useZeroJankProxy()) { + // Return to client, and we will get back with it when + // we have had a session made for it. + requestClientSessionLocked(cs); + requestClientSessionForAccessibilityLocked(cs); + } return new InputBindResult( InputBindResult.ResultCode.SUCCESS_WAITING_IME_SESSION, null, null, null, @@ -2452,8 +2496,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. @GuardedBy("ImfLock.class") void resetCurrentMethodAndClientLocked(@UnbindReason int unbindClientReason) { - final var bindingController = - mUserDataRepository.getOrCreate(mCurrentUserId).mBindingController; + final var bindingController = getInputMethodBindingController(mCurrentUserId); bindingController.setSelectedMethodId(null); // Callback before clean-up binding states. @@ -2669,25 +2712,27 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. final boolean canImeDrawsImeNavBar = mImeDrawsImeNavBarRes != null && mImeDrawsImeNavBarRes.get() && hasNavigationBar; final boolean shouldShowImeSwitcherWhenImeIsShown = shouldShowImeSwitcherLocked( - InputMethodService.IME_ACTIVE | InputMethodService.IME_VISIBLE); + InputMethodService.IME_ACTIVE | InputMethodService.IME_VISIBLE, + mCurrentUserId); return (canImeDrawsImeNavBar ? InputMethodNavButtonFlags.IME_DRAWS_IME_NAV_BAR : 0) | (shouldShowImeSwitcherWhenImeIsShown ? InputMethodNavButtonFlags.SHOW_IME_SWITCHER_WHEN_IME_IS_SHOWN : 0); } @GuardedBy("ImfLock.class") - private boolean shouldShowImeSwitcherLocked(int visibility) { + private boolean shouldShowImeSwitcherLocked(int visibility, @UserIdInt int userId) { if (!mShowOngoingImeSwitcherForPhones) return false; // When the IME switcher dialog is shown, the IME switcher button should be hidden. + // TODO(b/305849394): Make mMenuController multi-user aware. if (mMenuController.getSwitchingDialogLocked() != null) return false; // When we are switching IMEs, the IME switcher button should be hidden. - final var bindingController = getInputMethodBindingController(mCurrentUserId); + final var bindingController = getInputMethodBindingController(userId); if (!Objects.equals(bindingController.getCurId(), bindingController.getSelectedMethodId())) { return false; } if (mWindowManagerInternal.isKeyguardShowingAndNotOccluded() - && mWindowManagerInternal.isKeyguardSecure(mCurrentUserId)) { + && mWindowManagerInternal.isKeyguardSecure(userId)) { return false; } if ((visibility & InputMethodService.IME_ACTIVE) == 0 @@ -2704,7 +2749,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. return false; } - final InputMethodSettings settings = InputMethodSettingsRepository.get(mCurrentUserId); + final InputMethodSettings settings = InputMethodSettingsRepository.get(userId); List<InputMethodInfo> imes = settings.getEnabledInputMethodListWithFilter( InputMethodInfo::shouldShowInInputMethodPicker); final int numImes = imes.size(); @@ -2819,14 +2864,22 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. // Caution! This method is called in this class. Handle multi-user carefully @GuardedBy("ImfLock.class") private void updateSystemUiLocked(int vis, int backDisposition) { - if (getCurTokenLocked() == null) { + updateSystemUiLocked(vis, backDisposition, mCurrentUserId); + } + + @GuardedBy("ImfLock.class") + private void updateSystemUiLocked(int vis, int backDisposition, @UserIdInt int userId) { + final var bindingController = getInputMethodBindingController(userId); + final var curToken = bindingController.getCurToken(); + if (curToken == null) { return; } + final int curTokenDisplayId = bindingController.getCurTokenDisplayId(); if (DEBUG) { Slog.d(TAG, "IME window vis: " + vis + " active: " + (vis & InputMethodService.IME_ACTIVE) + " inv: " + (vis & InputMethodService.IME_INVISIBLE) - + " displayId: " + getCurTokenDisplayIdLocked()); + + " displayId: " + curTokenDisplayId); } final IBinder focusedWindowToken = mImeBindingState != null ? mImeBindingState.mFocusedWindow : null; @@ -2845,17 +2898,18 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. } else { vis &= ~InputMethodService.IME_VISIBLE_IMPERCEPTIBLE; } - final var curId = getInputMethodBindingController(mCurrentUserId).getCurId(); + final var curId = bindingController.getCurId(); + // TODO(b/305849394): Make mMenuController multi-user aware. if (mMenuController.getSwitchingDialogLocked() != null - || !Objects.equals(curId, getSelectedMethodIdLocked())) { + || !Objects.equals(curId, bindingController.getSelectedMethodId())) { // When the IME switcher dialog is shown, or we are switching IMEs, // the back button should be in the default state (as if the IME is not shown). backDisposition = InputMethodService.BACK_DISPOSITION_ADJUST_NOTHING; } - final boolean needsToShowImeSwitcher = shouldShowImeSwitcherLocked(vis); + final boolean needsToShowImeSwitcher = shouldShowImeSwitcherLocked(vis, userId); if (mStatusBarManagerInternal != null) { - mStatusBarManagerInternal.setImeWindowStatus(getCurTokenDisplayIdLocked(), - getCurTokenLocked(), vis, backDisposition, needsToShowImeSwitcher); + mStatusBarManagerInternal.setImeWindowStatus(curTokenDisplayId, + curToken, vis, backDisposition, needsToShowImeSwitcher); } } finally { Binder.restoreCallingIdentity(ident); @@ -2883,7 +2937,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. * <li> * {@link PackageManager#COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED} is not updated. * </li> - * <li>{@link #mDeviceIdToShowIme} is ignored.</li> + * <li>{@link InputMethodBindingController#getDeviceIdToShowIme()} is ignored.</li> * <li>{@link #mSwitchingController} is ignored.</li> * <li>{@link #mHardwareKeyboardShortcutController} is ignored.</li> * <li>{@link #mPreventImeStartupUnlessTextEditor} is ignored.</li> @@ -2918,16 +2972,15 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. id = imi.getId(); settings.putSelectedInputMethod(id); } - final var bindingController = getInputMethodBindingController(userId); - bindingController.setSelectedMethodId(id); } @GuardedBy("ImfLock.class") void updateInputMethodsFromSettingsLocked(boolean enabledMayChange) { - final InputMethodSettings settings = InputMethodSettingsRepository.get(mCurrentUserId); + final int userId = mCurrentUserId; + final InputMethodSettings settings = InputMethodSettingsRepository.get(userId); if (enabledMayChange) { final PackageManager userAwarePackageManager = getPackageManagerForUser(mContext, - settings.getUserId()); + userId); List<InputMethodInfo> enabled = settings.getEnabledInputMethodList(); for (int i = 0; i < enabled.size(); i++) { @@ -2954,22 +3007,22 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. } } - if (mDeviceIdToShowIme == DEVICE_ID_DEFAULT) { + final var bindingController = getInputMethodBindingController(mCurrentUserId); + if (bindingController.getDeviceIdToShowIme() == DEVICE_ID_DEFAULT) { String ime = SecureSettingsWrapper.getString( - Settings.Secure.DEFAULT_INPUT_METHOD, null, settings.getUserId()); + Settings.Secure.DEFAULT_INPUT_METHOD, null, userId); String defaultDeviceIme = SecureSettingsWrapper.getString( - Settings.Secure.DEFAULT_DEVICE_INPUT_METHOD, null, settings.getUserId()); + Settings.Secure.DEFAULT_DEVICE_INPUT_METHOD, null, userId); if (defaultDeviceIme != null && !Objects.equals(ime, defaultDeviceIme)) { if (DEBUG) { Slog.v(TAG, "Current input method " + ime + " differs from the stored default" - + " device input method for user " + settings.getUserId() + + " device input method for user " + userId + " - restoring " + defaultDeviceIme); } SecureSettingsWrapper.putString( - Settings.Secure.DEFAULT_INPUT_METHOD, defaultDeviceIme, - settings.getUserId()); + Settings.Secure.DEFAULT_INPUT_METHOD, defaultDeviceIme, userId); SecureSettingsWrapper.putString( - Settings.Secure.DEFAULT_DEVICE_INPUT_METHOD, null, settings.getUserId()); + Settings.Secure.DEFAULT_DEVICE_INPUT_METHOD, null, userId); } } @@ -2995,18 +3048,18 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. } // TODO: Instantiate mSwitchingController for each user. - if (settings.getUserId() == mSwitchingController.getUserId()) { + if (userId == mSwitchingController.getUserId()) { mSwitchingController.resetCircularListLocked(settings.getMethodMap()); } else { mSwitchingController = InputMethodSubtypeSwitchingController.createInstanceLocked( - mContext, settings.getMethodMap(), settings.getUserId()); + mContext, settings.getMethodMap(), userId); } // TODO: Instantiate mHardwareKeyboardShortcutController for each user. - if (settings.getUserId() == mHardwareKeyboardShortcutController.getUserId()) { + if (userId == mHardwareKeyboardShortcutController.getUserId()) { mHardwareKeyboardShortcutController.reset(settings.getMethodMap()); } else { mHardwareKeyboardShortcutController = new HardwareKeyboardShortcutController( - settings.getMethodMap(), settings.getUserId()); + settings.getMethodMap(), userId); } sendOnNavButtonFlagsChangedLocked(); } @@ -3030,15 +3083,16 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. @GuardedBy("ImfLock.class") void setInputMethodLocked(String id, int subtypeId, int deviceId) { - final InputMethodSettings settings = InputMethodSettingsRepository.get(mCurrentUserId); + final int userId = mCurrentUserId; + final InputMethodSettings settings = InputMethodSettingsRepository.get(userId); InputMethodInfo info = settings.getMethodMap().get(id); if (info == null) { throw getExceptionForUnknownImeId(id); } + final var bindingController = getInputMethodBindingController(userId); // See if we need to notify a subtype change within the same IME. - if (id.equals(getSelectedMethodIdLocked())) { - final int userId = settings.getUserId(); + if (id.equals(bindingController.getSelectedMethodId())) { final int subtypeCount = info.getSubtypeCount(); if (subtypeCount <= 0) { notifyInputMethodSubtypeChangedLocked(userId, info, null); @@ -3074,7 +3128,8 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. } // Changing to a different IME. - if (mDeviceIdToShowIme != DEVICE_ID_DEFAULT && deviceId == DEVICE_ID_DEFAULT) { + if (bindingController.getDeviceIdToShowIme() != DEVICE_ID_DEFAULT + && deviceId == DEVICE_ID_DEFAULT) { // This change should only be applicable to the default device but the current input // method is a custom one specific to a virtual device. So only update the settings // entry used to restore the default device input method once we want to show the IME @@ -3094,7 +3149,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. // mCurMethodId should be updated after setSelectedInputMethodAndSubtypeLocked() // because mCurMethodId is stored as a history in // setSelectedInputMethodAndSubtypeLocked(). - getInputMethodBindingController(mCurrentUserId).setSelectedMethodId(id); + bindingController.setSelectedMethodId(id); if (mActivityManagerInternal.isSystemReady()) { Intent intent = new Intent(Intent.ACTION_INPUT_METHOD_CHANGED); @@ -3127,8 +3182,58 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. final long ident = Binder.clearCallingIdentity(); try { if (DEBUG) Slog.v(TAG, "Client requesting input be shown"); - return showCurrentInputLocked(windowToken, statsToken, flags, lastClickToolType, - resultReceiver, reason); + if (Flags.refactorInsetsController()) { + boolean wasVisible = isInputShownLocked(); + if (mImeBindingState != null && mImeBindingState.mFocusedWindowClient != null + && mImeBindingState.mFocusedWindowClient.mClient != null) { + mImeBindingState.mFocusedWindowClient.mClient.setImeVisibility(true); + if (resultReceiver != null) { + resultReceiver.send( + wasVisible ? InputMethodManager.RESULT_UNCHANGED_SHOWN + : InputMethodManager.RESULT_SHOWN, null); + } + return true; + } + return false; + } else { + return showCurrentInputLocked(windowToken, statsToken, flags, lastClickToolType, + resultReceiver, reason); + } + } finally { + Binder.restoreCallingIdentity(ident); + Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); + } + } + } + + boolean showSoftInputInternal(IBinder windowToken) { + Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMMS.showSoftInputInternal"); + ImeTracing.getInstance().triggerManagerServiceDump( + "InputMethodManagerService#showSoftInput", mDumper); + synchronized (ImfLock.class) { + final long ident = Binder.clearCallingIdentity(); + try { + if (DEBUG) Slog.v(TAG, "Client requesting input be shown"); + return showCurrentInputLocked(windowToken, null /* statsToken */, 0 /* flags */, + 0 /* lastClickTooType */, null /* resultReceiver */, + SoftInputShowHideReason.SHOW_SOFT_INPUT); + } finally { + Binder.restoreCallingIdentity(ident); + Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); + } + } + } + + boolean hideSoftInputInternal(IBinder windowToken) { + Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMMS.hideSoftInputInternal"); + ImeTracing.getInstance().triggerManagerServiceDump( + "InputMethodManagerService#hideSoftInput", mDumper); + synchronized (ImfLock.class) { + final long ident = Binder.clearCallingIdentity(); + try { + if (DEBUG) Slog.v(TAG, "Client requesting input be hidden"); + return hideCurrentInputLocked(windowToken, null /* statsToken */, 0 /* flags */, + null /* resultReceiver */, SoftInputShowHideReason.HIDE_SOFT_INPUT); } finally { Binder.restoreCallingIdentity(ident); Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); @@ -3494,8 +3599,23 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. try { Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMMS.hideSoftInput"); if (DEBUG) Slog.v(TAG, "Client requesting input be hidden"); - return InputMethodManagerService.this.hideCurrentInputLocked(windowToken, - statsToken, flags, resultReceiver, reason); + if (Flags.refactorInsetsController()) { + if (mImeBindingState != null && mImeBindingState.mFocusedWindowClient != null + && mImeBindingState.mFocusedWindowClient.mClient != null) { + boolean wasVisible = isInputShownLocked(); + // TODO add windowToken to interface + mImeBindingState.mFocusedWindowClient.mClient.setImeVisibility(false); + if (resultReceiver != null) { + resultReceiver.send(wasVisible ? InputMethodManager.RESULT_HIDDEN + : InputMethodManager.RESULT_UNCHANGED_HIDDEN, null); + } + return true; + } + return false; + } else { + return InputMethodManagerService.this.hideCurrentInputLocked(windowToken, + statsToken, flags, resultReceiver, reason); + } } finally { Binder.restoreCallingIdentity(ident); Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); @@ -4553,7 +4673,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. windowToken); mVisibilityApplier.applyImeVisibility(requestToken, statsToken, setVisible ? ImeVisibilityStateComputer.STATE_SHOW_IME - : ImeVisibilityStateComputer.STATE_HIDE_IME); + : ImeVisibilityStateComputer.STATE_HIDE_IME, mCurrentUserId); } } finally { Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); @@ -4642,8 +4762,18 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. ImeTracker.PHASE_SERVER_CURRENT_ACTIVE_IME); final long ident = Binder.clearCallingIdentity(); try { - hideCurrentInputLocked(mLastImeTargetWindow, statsToken, flags, - null /* resultReceiver */, reason); + if (Flags.refactorInsetsController()) { + mCurClient.mClient.setImeVisibility(false); + // TODO we will loose the flags here + if (mImeBindingState != null + && mImeBindingState.mFocusedWindowClient != null + && mImeBindingState.mFocusedWindowClient.mClient != null) { + mImeBindingState.mFocusedWindowClient.mClient.setImeVisibility(false); + } + } else { + hideCurrentInputLocked(mLastImeTargetWindow, statsToken, flags, + null /* resultReceiver */, reason); + } } finally { Binder.restoreCallingIdentity(ident); } @@ -4668,8 +4798,18 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. ImeTracker.PHASE_SERVER_CURRENT_ACTIVE_IME); final long ident = Binder.clearCallingIdentity(); try { - showCurrentInputLocked(mLastImeTargetWindow, statsToken, flags, - MotionEvent.TOOL_TYPE_UNKNOWN, null /* resultReceiver */, reason); + if (Flags.refactorInsetsController()) { + mCurClient.mClient.setImeVisibility(false); + // TODO we will loose the flags here + if (mImeBindingState != null + && mImeBindingState.mFocusedWindowClient != null + && mImeBindingState.mFocusedWindowClient.mClient != null) { + mImeBindingState.mFocusedWindowClient.mClient.setImeVisibility(true); + } + } else { + showCurrentInputLocked(mLastImeTargetWindow, statsToken, flags, + MotionEvent.TOOL_TYPE_UNKNOWN, null /* resultReceiver */, reason); + } } finally { Binder.restoreCallingIdentity(ident); } @@ -4800,8 +4940,17 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. case MSG_HIDE_ALL_INPUT_METHODS: synchronized (ImfLock.class) { - @SoftInputShowHideReason final int reason = (int) msg.obj; - hideCurrentInputLocked(mImeBindingState.mFocusedWindow, 0 /* flags */, reason); + if (Flags.refactorInsetsController()) { + if (mImeBindingState != null + && mImeBindingState.mFocusedWindowClient != null + && mImeBindingState.mFocusedWindowClient.mClient != null) { + mImeBindingState.mFocusedWindowClient.mClient.setImeVisibility(false); + } + } else { + @SoftInputShowHideReason final int reason = (int) msg.obj; + hideCurrentInputLocked(mImeBindingState.mFocusedWindow, 0 /* flags */, + reason); + } } return true; case MSG_REMOVE_IME_SURFACE: { @@ -5097,9 +5246,9 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. Slog.e(TAG, "buildInputMethodListLocked is not allowed until system is ready"); return; } - mMethodMapUpdateCount++; - final InputMethodSettings settings = InputMethodSettingsRepository.get(mCurrentUserId); + final int userId = mCurrentUserId; + final InputMethodSettings settings = InputMethodSettingsRepository.get(userId); boolean reenableMinimumNonAuxSystemImes = false; // TODO: The following code should find better place to live. @@ -5162,18 +5311,18 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. updateDefaultVoiceImeIfNeededLocked(); // TODO: Instantiate mSwitchingController for each user. - if (settings.getUserId() == mSwitchingController.getUserId()) { + if (userId == mSwitchingController.getUserId()) { mSwitchingController.resetCircularListLocked(settings.getMethodMap()); } else { mSwitchingController = InputMethodSubtypeSwitchingController.createInstanceLocked( mContext, settings.getMethodMap(), mCurrentUserId); } // TODO: Instantiate mHardwareKeyboardShortcutController for each user. - if (settings.getUserId() == mHardwareKeyboardShortcutController.getUserId()) { + if (userId == mHardwareKeyboardShortcutController.getUserId()) { mHardwareKeyboardShortcutController.reset(settings.getMethodMap()); } else { mHardwareKeyboardShortcutController = new HardwareKeyboardShortcutController( - settings.getMethodMap(), settings.getUserId()); + settings.getMethodMap(), userId); } sendOnNavButtonFlagsChangedLocked(); @@ -5181,7 +5330,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. // Notify InputMethodListListeners of the new installed InputMethods. final List<InputMethodInfo> inputMethodList = settings.getMethodList(); mHandler.obtainMessage(MSG_DISPATCH_ON_INPUT_METHOD_LIST_UPDATED, - settings.getUserId(), 0 /* unused */, inputMethodList).sendToTarget(); + userId, 0 /* unused */, inputMethodList).sendToTarget(); } @GuardedBy("ImfLock.class") @@ -5239,7 +5388,8 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. */ @GuardedBy("ImfLock.class") private boolean setInputMethodEnabledLocked(String id, boolean enabled) { - final InputMethodSettings settings = InputMethodSettingsRepository.get(mCurrentUserId); + final int userId = mCurrentUserId; + final InputMethodSettings settings = InputMethodSettingsRepository.get(userId); if (enabled) { final String enabledImeIdsStr = settings.getEnabledInputMethodsStr(); final String newEnabledImeIdsStr = InputMethodUtils.concatEnabledImeIds( @@ -5258,7 +5408,8 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. StringBuilder builder = new StringBuilder(); if (settings.buildAndPutEnabledInputMethodsStrRemovingId( builder, enabledInputMethodsList, id)) { - if (mDeviceIdToShowIme == DEVICE_ID_DEFAULT) { + final var bindingController = getInputMethodBindingController(userId); + if (bindingController.getDeviceIdToShowIme() == DEVICE_ID_DEFAULT) { // Disabled input method is currently selected, switch to another one. final String selId = settings.getSelectedInputMethod(); if (id.equals(selId) && !chooseNewDefaultIMELocked()) { @@ -5316,8 +5467,9 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. @GuardedBy("ImfLock.class") private void resetSelectedInputMethodAndSubtypeLocked(String newDefaultIme) { - mDeviceIdToShowIme = DEVICE_ID_DEFAULT; - mDisplayIdToShowIme = INVALID_DISPLAY; + final var bindingController = getInputMethodBindingController(mCurrentUserId); + bindingController.setDisplayIdToShowIme(INVALID_DISPLAY); + bindingController.setDeviceIdToShowIme(DEVICE_ID_DEFAULT); final InputMethodSettings settings = InputMethodSettingsRepository.get(mCurrentUserId); settings.putSelectedDefaultDeviceInputMethod(null); @@ -5374,44 +5526,18 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. */ @GuardedBy("ImfLock.class") InputMethodSubtype getCurrentInputMethodSubtypeLocked() { - String selectedMethodId = getSelectedMethodIdLocked(); + final int userId = mCurrentUserId; + final var selectedMethodId = getInputMethodBindingController(userId).getSelectedMethodId(); if (selectedMethodId == null) { return null; } - final InputMethodSettings settings = InputMethodSettingsRepository.get(mCurrentUserId); - final boolean subtypeIsSelected = settings.isSubtypeSelected(); + final InputMethodSettings settings = InputMethodSettingsRepository.get(userId); final InputMethodInfo imi = settings.getMethodMap().get(selectedMethodId); if (imi == null || imi.getSubtypeCount() == 0) { return null; } - if (!subtypeIsSelected || mCurrentSubtype == null - || !SubtypeUtils.isValidSubtypeId(imi, mCurrentSubtype.hashCode())) { - int subtypeId = settings.getSelectedInputMethodSubtypeId(selectedMethodId); - if (subtypeId == NOT_A_SUBTYPE_ID) { - // If there are no selected subtypes, the framework will try to find - // the most applicable subtype from explicitly or implicitly enabled - // subtypes. - List<InputMethodSubtype> explicitlyOrImplicitlyEnabledSubtypes = - settings.getEnabledInputMethodSubtypeList(imi, true); - // If there is only one explicitly or implicitly enabled subtype, - // just returns it. - if (explicitlyOrImplicitlyEnabledSubtypes.size() == 1) { - mCurrentSubtype = explicitlyOrImplicitlyEnabledSubtypes.get(0); - } else if (explicitlyOrImplicitlyEnabledSubtypes.size() > 1) { - final String locale = SystemLocaleWrapper.get(settings.getUserId()) - .get(0).toString(); - mCurrentSubtype = SubtypeUtils.findLastResortApplicableSubtype( - explicitlyOrImplicitlyEnabledSubtypes, - SubtypeUtils.SUBTYPE_MODE_KEYBOARD, locale, true); - if (mCurrentSubtype == null) { - mCurrentSubtype = SubtypeUtils.findLastResortApplicableSubtype( - explicitlyOrImplicitlyEnabledSubtypes, null, locale, true); - } - } - } else { - mCurrentSubtype = SubtypeUtils.getSubtypes(imi).get(subtypeId); - } - } + mCurrentSubtype = SubtypeUtils.getCurrentInputMethodSubtype(imi, settings, + mCurrentSubtype); return mCurrentSubtype; } @@ -5922,7 +6048,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. p.println("Current Input Method Manager state:"); final List<InputMethodInfo> methodList = settings.getMethodList(); int numImes = methodList.size(); - p.println(" Input Methods: mMethodMapUpdateCount=" + mMethodMapUpdateCount); + p.println(" Input Methods:"); for (int i = 0; i < numImes; i++) { InputMethodInfo info = methodList.get(i); p.println(" InputMethod #" + i + ":"); @@ -5989,7 +6115,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. p.println(" mStylusIds=" + (mStylusIds != null ? Arrays.toString(mStylusIds.toArray()) : "")); p.println(" mSwitchingController:"); - mSwitchingController.dump(p); + mSwitchingController.dump(p, " "); p.println(" mStartInputHistory:"); mStartInputHistory.dump(pw, " "); @@ -6466,8 +6592,19 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. final List<InputMethodInfo> nextEnabledImes; final InputMethodSettings settings = InputMethodSettingsRepository.get(userId); if (userId == mCurrentUserId) { - hideCurrentInputLocked(mImeBindingState.mFocusedWindow, 0 /* flags */, - SoftInputShowHideReason.HIDE_RESET_SHELL_COMMAND); + if (Flags.refactorInsetsController()) { + if (mImeBindingState != null + && mImeBindingState.mFocusedWindowClient != null + && mImeBindingState.mFocusedWindowClient.mClient != null) { + mImeBindingState.mFocusedWindowClient.mClient.setImeVisibility( + false); + } else { + // TODO(b329229469): ImeTracker? + } + } else { + hideCurrentInputLocked(mImeBindingState.mFocusedWindow, 0 /* flags */, + SoftInputShowHideReason.HIDE_RESET_SHELL_COMMAND); + } final var bindingController = getInputMethodBindingController(userId); bindingController.unbindCurrentMethod(); diff --git a/services/core/java/com/android/server/inputmethod/InputMethodSettings.java b/services/core/java/com/android/server/inputmethod/InputMethodSettings.java index a558838172f8..7ce4074bb1d0 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodSettings.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodSettings.java @@ -51,8 +51,23 @@ final class InputMethodSettings { public static final boolean DEBUG = false; private static final String TAG = "InputMethodSettings"; - private static final int NOT_A_SUBTYPE_ID = InputMethodUtils.NOT_A_SUBTYPE_ID; - private static final String NOT_A_SUBTYPE_ID_STR = String.valueOf(NOT_A_SUBTYPE_ID); + /** + * An integer code that represents "no subtype" when a subtype hashcode is used. + * + * <p>Due to historical confusions with {@link InputMethodUtils#NOT_A_SUBTYPE_ID}, we have + * used {@code -1} here. We cannot change this value as it's already saved into secure settings. + * </p> + */ + private static final int INVALID_SUBTYPE_HASHCODE = -1; + /** + * A string code that represents "no subtype" when a subtype hashcode is used. + * + * <p>Due to historical confusions with {@link InputMethodUtils#NOT_A_SUBTYPE_ID}, we have + * used {@code "-1"} here. We cannot change this value as it's already saved into secure + * settings.</p> + */ + private static final String INVALID_SUBTYPE_HASHCODE_STR = + String.valueOf(INVALID_SUBTYPE_HASHCODE); private static final char INPUT_METHOD_SEPARATOR = InputMethodUtils.INPUT_METHOD_SEPARATOR; private static final char INPUT_METHOD_SUBTYPE_SEPARATOR = InputMethodUtils.INPUT_METHOD_SUBTYPE_SEPARATOR; @@ -259,34 +274,33 @@ final class InputMethodSettings { } private void saveSubtypeHistory( - List<Pair<String, String>> savedImes, String newImeId, String newSubtypeId) { + List<Pair<String, String>> savedImes, String newImeId, String newSubtypeHashCodeStr) { final StringBuilder builder = new StringBuilder(); boolean isImeAdded = false; - if (!TextUtils.isEmpty(newImeId) && !TextUtils.isEmpty(newSubtypeId)) { + if (!TextUtils.isEmpty(newImeId) && !TextUtils.isEmpty(newSubtypeHashCodeStr)) { builder.append(newImeId).append(INPUT_METHOD_SUBTYPE_SEPARATOR).append( - newSubtypeId); + newSubtypeHashCodeStr); isImeAdded = true; } for (int i = 0; i < savedImes.size(); ++i) { final Pair<String, String> ime = savedImes.get(i); final String imeId = ime.first; - String subtypeId = ime.second; - if (TextUtils.isEmpty(subtypeId)) { - subtypeId = NOT_A_SUBTYPE_ID_STR; + String subtypeHashCodeStr = ime.second; + if (TextUtils.isEmpty(subtypeHashCodeStr)) { + subtypeHashCodeStr = INVALID_SUBTYPE_HASHCODE_STR; } if (isImeAdded) { builder.append(INPUT_METHOD_SEPARATOR); } else { isImeAdded = true; } - builder.append(imeId).append(INPUT_METHOD_SUBTYPE_SEPARATOR).append( - subtypeId); + builder.append(imeId).append(INPUT_METHOD_SUBTYPE_SEPARATOR).append(subtypeHashCodeStr); } // Remove the last INPUT_METHOD_SEPARATOR putSubtypeHistoryStr(builder.toString()); } - private void addSubtypeToHistory(String imeId, String subtypeId) { + private void addSubtypeToHistory(String imeId, String subtypeHashCodeStr) { final List<Pair<String, String>> subtypeHistory = loadInputMethodAndSubtypeHistory(); for (int i = 0; i < subtypeHistory.size(); ++i) { final Pair<String, String> ime = subtypeHistory.get(i); @@ -301,9 +315,9 @@ final class InputMethodSettings { } } if (DEBUG) { - Slog.v(TAG, "Add subtype to the history: " + imeId + ", " + subtypeId); + Slog.v(TAG, "Add subtype to the history: " + imeId + ", " + subtypeHashCodeStr); } - saveSubtypeHistory(subtypeHistory, imeId, subtypeId); + saveSubtypeHistory(subtypeHistory, imeId, subtypeHashCodeStr); } private void putSubtypeHistoryStr(@NonNull String str) { @@ -413,26 +427,26 @@ final class InputMethodSettings { for (int j = 0; j < explicitlyEnabledSubtypes.size(); ++j) { final String s = explicitlyEnabledSubtypes.get(j); if (s.equals(subtypeHashCode)) { - // If both imeId and subtypeId are enabled, return subtypeId. + // If both imeId and subtype are enabled, return subtypeId. try { final int hashCode = Integer.parseInt(subtypeHashCode); - // Check whether the subtype id is valid or not - if (SubtypeUtils.isValidSubtypeId(imi, hashCode)) { + // Check whether the subtype is valid or not + if (SubtypeUtils.isValidSubtypeHashCode(imi, hashCode)) { return s; } else { - return NOT_A_SUBTYPE_ID_STR; + return INVALID_SUBTYPE_HASHCODE_STR; } } catch (NumberFormatException e) { - return NOT_A_SUBTYPE_ID_STR; + return INVALID_SUBTYPE_HASHCODE_STR; } } } } - // If imeId was enabled but subtypeId was disabled. - return NOT_A_SUBTYPE_ID_STR; + // If imeId was enabled but subtype was disabled. + return INVALID_SUBTYPE_HASHCODE_STR; } } - // If both imeId and subtypeId are disabled, return null + // If both imeId and subtype are disabled, return null return null; } @@ -451,14 +465,14 @@ final class InputMethodSettings { String nextImsStr = inputMethodSplitter.next(); subtypeSplitter.setString(nextImsStr); if (subtypeSplitter.hasNext()) { - String subtypeId = NOT_A_SUBTYPE_ID_STR; + String subtypeHashCodeStr = INVALID_SUBTYPE_HASHCODE_STR; // The first element is ime id. String imeId = subtypeSplitter.next(); while (subtypeSplitter.hasNext()) { - subtypeId = subtypeSplitter.next(); + subtypeHashCodeStr = subtypeSplitter.next(); break; } - imsList.add(new Pair<>(imeId, subtypeId)); + imsList.add(new Pair<>(imeId, subtypeHashCodeStr)); } } return imsList; @@ -528,13 +542,8 @@ final class InputMethodSettings { return imi; } - boolean isSubtypeSelected() { - return getSelectedInputMethodSubtypeHashCode() != NOT_A_SUBTYPE_ID; - } - private int getSelectedInputMethodSubtypeHashCode() { - return getInt(Settings.Secure.SELECTED_INPUT_METHOD_SUBTYPE, - NOT_A_SUBTYPE_ID); + return getInt(Settings.Secure.SELECTED_INPUT_METHOD_SUBTYPE, INVALID_SUBTYPE_HASHCODE); } @UserIdInt @@ -545,7 +554,7 @@ final class InputMethodSettings { int getSelectedInputMethodSubtypeId(String selectedImiId) { final InputMethodInfo imi = mMethodMap.get(selectedImiId); if (imi == null) { - return NOT_A_SUBTYPE_ID; + return InputMethodUtils.NOT_A_SUBTYPE_ID; } final int subtypeHashCode = getSelectedInputMethodSubtypeHashCode(); return SubtypeUtils.getSubtypeIdFromHashCode(imi, subtypeHashCode); @@ -553,12 +562,12 @@ final class InputMethodSettings { void saveCurrentInputMethodAndSubtypeToHistory(String curMethodId, InputMethodSubtype currentSubtype) { - String subtypeId = NOT_A_SUBTYPE_ID_STR; + String subtypeHashCodeStr = INVALID_SUBTYPE_HASHCODE_STR; if (currentSubtype != null) { - subtypeId = String.valueOf(currentSubtype.hashCode()); + subtypeHashCodeStr = String.valueOf(currentSubtype.hashCode()); } if (InputMethodUtils.canAddToLastInputMethod(currentSubtype)) { - addSubtypeToHistory(curMethodId, subtypeId); + addSubtypeToHistory(curMethodId, subtypeHashCodeStr); } } @@ -583,7 +592,7 @@ final class InputMethodSettings { } final int subtypeHashCode = getSelectedInputMethodSubtypeHashCode(); - if (subtypeHashCode != NOT_A_SUBTYPE_ID) { + if (subtypeHashCode != INVALID_SUBTYPE_HASHCODE) { final int subtypeIndex = SubtypeUtils.getSubtypeIdFromHashCode(imi, subtypeHashCode); if (subtypeIndex >= 0) { @@ -645,10 +654,10 @@ final class InputMethodSettings { final IntArray validSubtypeHashCodes = new IntArray(subtypeHashCodes.length); for (int subtypeHashCode : subtypeHashCodes) { - if (subtypeHashCode == NOT_A_SUBTYPE_ID) { - continue; // NOT_A_SUBTYPE_ID must not be saved + if (subtypeHashCode == INVALID_SUBTYPE_HASHCODE) { + continue; // INVALID_SUBTYPE_HASHCODE must not be saved } - if (!SubtypeUtils.isValidSubtypeId(imi, subtypeHashCode)) { + if (!SubtypeUtils.isValidSubtypeHashCode(imi, subtypeHashCode)) { continue; // this subtype does not exist in InputMethodInfo. } if (validSubtypeHashCodes.indexOf(subtypeHashCode) >= 0) { diff --git a/services/core/java/com/android/server/inputmethod/InputMethodSubtypeSwitchingController.java b/services/core/java/com/android/server/inputmethod/InputMethodSubtypeSwitchingController.java index 23f947cc8452..770e3b267145 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodSubtypeSwitchingController.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodSubtypeSwitchingController.java @@ -465,11 +465,11 @@ final class InputMethodSubtypeSwitchingController { return result; } - protected void dump(final Printer pw) { - pw.println(" mSwitchingAwareRotationList:"); - mSwitchingAwareRotationList.dump(pw, " "); - pw.println(" mSwitchingUnawareRotationList:"); - mSwitchingUnawareRotationList.dump(pw, " "); + protected void dump(@NonNull Printer pw, @NonNull String prefix) { + pw.println(prefix + "mSwitchingAwareRotationList:"); + mSwitchingAwareRotationList.dump(pw, prefix + " "); + pw.println(prefix + "mSwitchingUnawareRotationList:"); + mSwitchingUnawareRotationList.dump(pw, prefix + " "); } } @@ -529,11 +529,11 @@ final class InputMethodSubtypeSwitchingController { return mController.getNextInputMethod(onlyCurrentIme, imi, subtype); } - public void dump(final Printer pw) { + public void dump(@NonNull Printer pw, @NonNull String prefix) { if (mController != null) { - mController.dump(pw); + mController.dump(pw, prefix); } else { - pw.println(" mController=null"); + pw.println(prefix + "mController=null"); } } } diff --git a/services/core/java/com/android/server/inputmethod/SubtypeUtils.java b/services/core/java/com/android/server/inputmethod/SubtypeUtils.java index 3d5c867768ac..1b4c0d6ef4d5 100644 --- a/services/core/java/com/android/server/inputmethod/SubtypeUtils.java +++ b/services/core/java/com/android/server/inputmethod/SubtypeUtils.java @@ -16,9 +16,11 @@ package com.android.server.inputmethod; +import android.annotation.AnyThread; import android.annotation.NonNull; import android.annotation.Nullable; import android.os.LocaleList; +import android.provider.Settings; import android.text.TextUtils; import android.util.ArrayMap; import android.util.Slog; @@ -100,7 +102,7 @@ final class SubtypeUtils { return subtypes; } - static boolean isValidSubtypeId(InputMethodInfo imi, int subtypeHashCode) { + static boolean isValidSubtypeHashCode(InputMethodInfo imi, int subtypeHashCode) { return getSubtypeIdFromHashCode(imi, subtypeHashCode) != NOT_A_SUBTYPE_ID; } @@ -289,4 +291,54 @@ final class SubtypeUtils { } return applicableSubtype; } + + /** + * Returns a {@link InputMethodSubtype} available in {@code imi} based on + * {@link Settings.Secure#SELECTED_INPUT_METHOD_SUBTYPE}. + * + * @param imi {@link InputMethodInfo} to find out the current + * {@link InputMethodSubtype} + * @param settings {@link InputMethodSettings} to be used to find out the current + * {@link InputMethodSubtype} + * @param currentSubtype the current value that will be used as fallback + * @return {@link InputMethodSubtype} to be used as the current {@link InputMethodSubtype} + */ + @AnyThread + @Nullable + static InputMethodSubtype getCurrentInputMethodSubtype( + @NonNull InputMethodInfo imi, @NonNull InputMethodSettings settings, + @Nullable InputMethodSubtype currentSubtype) { + final int userId = settings.getUserId(); + final int selectedSubtypeHashCode = SecureSettingsWrapper.getInt( + Settings.Secure.SELECTED_INPUT_METHOD_SUBTYPE, NOT_A_SUBTYPE_ID, userId); + if (selectedSubtypeHashCode != NOT_A_SUBTYPE_ID && currentSubtype != null + && isValidSubtypeHashCode(imi, currentSubtype.hashCode())) { + return currentSubtype; + } + + final int subtypeId = settings.getSelectedInputMethodSubtypeId(imi.getId()); + if (subtypeId != NOT_A_SUBTYPE_ID) { + return imi.getSubtypeAt(subtypeId); + } + + // If there are no selected subtypes, the framework will try to find the most applicable + // subtype from explicitly or implicitly enabled subtypes. + final List<InputMethodSubtype> subtypes = settings.getEnabledInputMethodSubtypeList(imi, + true); + if (subtypes.isEmpty()) { + return currentSubtype; + } + // If there is only one explicitly or implicitly enabled subtype, + // just returns it. + if (subtypes.size() == 1) { + return subtypes.get(0); + } + final String locale = SystemLocaleWrapper.get(userId).get(0).toString(); + final var subtype = findLastResortApplicableSubtype(subtypes, SUBTYPE_MODE_KEYBOARD, locale, + true); + if (subtype != null) { + return subtype; + } + return findLastResortApplicableSubtype(subtypes, null, locale, true); + } } diff --git a/services/core/java/com/android/server/inputmethod/ZeroJankProxy.java b/services/core/java/com/android/server/inputmethod/ZeroJankProxy.java index 189c1a7eb7a0..757c07c0b683 100644 --- a/services/core/java/com/android/server/inputmethod/ZeroJankProxy.java +++ b/services/core/java/com/android/server/inputmethod/ZeroJankProxy.java @@ -248,6 +248,17 @@ final class ZeroJankProxy implements IInputMethodManagerImpl.Callback { unverifiedTargetSdkVersion, userId, imeDispatcher); sendOnStartInputResult(client, result, startInputSeq); + // For first-time client bind, MSG_BIND should arrive after MSG_START_INPUT_RESULT. + if (result.result == InputBindResult.ResultCode.SUCCESS_WAITING_IME_SESSION) { + InputMethodManagerService imms = ((InputMethodManagerService) mInner); + synchronized (ImfLock.class) { + ClientState cs = imms.getClientStateLocked(client); + if (cs != null) { + imms.requestClientSessionLocked(cs); + imms.requestClientSessionForAccessibilityLocked(cs); + } + } + } }); } diff --git a/services/core/java/com/android/server/locales/OWNERS b/services/core/java/com/android/server/locales/OWNERS index e1e946b1c51d..7e35dacd758e 100644 --- a/services/core/java/com/android/server/locales/OWNERS +++ b/services/core/java/com/android/server/locales/OWNERS @@ -1,5 +1,4 @@ roosa@google.com -pratyushmore@google.com goldmanj@google.com ankitavyas@google.com allenwtsu@google.com diff --git a/services/core/java/com/android/server/locksettings/BiometricDeferredQueue.java b/services/core/java/com/android/server/locksettings/BiometricDeferredQueue.java index f572845dc214..966be5318973 100644 --- a/services/core/java/com/android/server/locksettings/BiometricDeferredQueue.java +++ b/services/core/java/com/android/server/locksettings/BiometricDeferredQueue.java @@ -32,6 +32,7 @@ import android.util.ArraySet; import android.util.Slog; import com.android.internal.widget.VerifyCredentialResponse; +import com.android.server.biometrics.BiometricHandlerProvider; import java.util.ArrayList; import java.util.List; @@ -132,9 +133,11 @@ public class BiometricDeferredQueue { mFaceResetLockoutTask = null; }; - BiometricDeferredQueue(@NonNull SyntheticPasswordManager spManager, @NonNull Handler handler) { + BiometricDeferredQueue(@NonNull SyntheticPasswordManager spManager) { mSpManager = spManager; - mHandler = handler; + + //Using a higher priority thread to avoid any delays and interruption of clients + mHandler = BiometricHandlerProvider.getInstance().getBiometricCallbackHandler(); mPendingResetLockoutsForFingerprint = new ArrayList<>(); mPendingResetLockoutsForFace = new ArrayList<>(); mPendingResetLockouts = new ArrayList<>(); diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java index ae3d36acdb7f..22b33ddcfa2b 100644 --- a/services/core/java/com/android/server/locksettings/LockSettingsService.java +++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java @@ -687,7 +687,7 @@ public class LockSettingsService extends ILockSettings.Stub { mSpManager = injector.getSyntheticPasswordManager(mStorage); mUnifiedProfilePasswordCache = injector.getUnifiedProfilePasswordCache(mKeyStore); - mBiometricDeferredQueue = new BiometricDeferredQueue(mSpManager, mHandler); + mBiometricDeferredQueue = new BiometricDeferredQueue(mSpManager); mRebootEscrowManager = injector.getRebootEscrowManager(new RebootEscrowCallbacks(), mStorage); diff --git a/services/core/java/com/android/server/media/MediaRoute2Provider.java b/services/core/java/com/android/server/media/MediaRoute2Provider.java index 09605fefe80e..b0fa523da959 100644 --- a/services/core/java/com/android/server/media/MediaRoute2Provider.java +++ b/services/core/java/com/android/server/media/MediaRoute2Provider.java @@ -22,6 +22,7 @@ import android.content.ComponentName; import android.media.MediaRoute2Info; import android.media.MediaRoute2ProviderInfo; import android.media.MediaRouter2; +import android.media.MediaRouter2Utils; import android.media.RouteDiscoveryPreference; import android.media.RoutingSessionInfo; import android.os.Bundle; @@ -226,8 +227,23 @@ abstract class MediaRoute2Provider { return route2Info != null && mTargetOriginalRouteId.equals(route2Info.getOriginalId()); } - public boolean isTargetRouteIdInList(@NonNull List<String> routeOriginalIdList) { - return routeOriginalIdList.stream().anyMatch(mTargetOriginalRouteId::equals); + /** + * Returns whether the given list of {@link MediaRoute2Info#getOriginalId() original ids} + * contains the {@link #mTargetOriginalRouteId target route id}. + */ + public boolean isTargetRouteIdInRouteOriginalIdList( + @NonNull List<String> originalRouteIdList) { + return originalRouteIdList.stream().anyMatch(mTargetOriginalRouteId::equals); + } + + /** + * Returns whether the given list of {@link MediaRoute2Info#getId() unique ids} contains the + * {@link #mTargetOriginalRouteId target route id}. + */ + public boolean isTargetRouteIdInRouteUniqueIdList(@NonNull List<String> uniqueRouteIdList) { + return uniqueRouteIdList.stream() + .map(MediaRouter2Utils::getOriginalId) + .anyMatch(mTargetOriginalRouteId::equals); } } } diff --git a/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java b/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java index 71cbcb91100f..3673eb027096 100644 --- a/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java +++ b/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java @@ -21,6 +21,7 @@ import static android.media.MediaRoute2ProviderService.REQUEST_ID_NONE; import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage; import android.annotation.NonNull; +import android.annotation.Nullable; import android.content.ComponentName; import android.content.Context; import android.content.Intent; @@ -41,6 +42,7 @@ import android.os.RemoteException; import android.os.UserHandle; import android.text.TextUtils; import android.util.Log; +import android.util.LongSparseArray; import android.util.Slog; import com.android.internal.annotations.GuardedBy; @@ -49,15 +51,14 @@ import com.android.media.flags.Flags; import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.Collections; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.Objects; import java.util.Set; -/** - * Maintains a connection to a particular {@link MediaRoute2ProviderService}. - */ -final class MediaRoute2ProviderServiceProxy extends MediaRoute2Provider - implements ServiceConnection { +/** Maintains a connection to a particular {@link MediaRoute2ProviderService}. */ +final class MediaRoute2ProviderServiceProxy extends MediaRoute2Provider { private static final String TAG = "MR2ProviderSvcProxy"; private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); @@ -65,6 +66,7 @@ final class MediaRoute2ProviderServiceProxy extends MediaRoute2Provider private final int mUserId; private final Handler mHandler; private final boolean mIsSelfScanOnlyProvider; + private final ServiceConnection mServiceConnection = new ServiceConnectionImpl(); // Connection state private boolean mRunning; @@ -77,7 +79,16 @@ final class MediaRoute2ProviderServiceProxy extends MediaRoute2Provider private boolean mLastDiscoveryPreferenceIncludesThisPackage = false; @GuardedBy("mLock") - final List<RoutingSessionInfo> mReleasingSessions = new ArrayList<>(); + private final List<RoutingSessionInfo> mReleasingSessions = new ArrayList<>(); + + // We keep pending requests for transfers and sessions creation separately because transfers + // don't have an associated request id and session creations don't have a session id. + @GuardedBy("mLock") + private final LongSparseArray<SessionCreationOrTransferRequest> + mRequestIdToSessionCreationRequest; + + @GuardedBy("mLock") + private final Map<String, SessionCreationOrTransferRequest> mSessionOriginalIdToTransferRequest; MediaRoute2ProviderServiceProxy( @NonNull Context context, @@ -87,6 +98,8 @@ final class MediaRoute2ProviderServiceProxy extends MediaRoute2Provider int userId) { super(componentName); mContext = Objects.requireNonNull(context, "Context must not be null."); + mRequestIdToSessionCreationRequest = new LongSparseArray<>(); + mSessionOriginalIdToTransferRequest = new HashMap<>(); mIsSelfScanOnlyProvider = isSelfScanOnlyProvider; mUserId = userId; mHandler = new Handler(looper); @@ -109,6 +122,18 @@ final class MediaRoute2ProviderServiceProxy extends MediaRoute2Provider @NonNull UserHandle transferInitiatorUserHandle, @NonNull String transferInitiatorPackageName) { if (mConnectionReady) { + if (Flags.enableBuiltInSpeakerRouteSuitabilityStatuses()) { + synchronized (mLock) { + mRequestIdToSessionCreationRequest.put( + requestId, + new SessionCreationOrTransferRequest( + requestId, + routeOriginalId, + transferReason, + transferInitiatorUserHandle, + transferInitiatorPackageName)); + } + } mActiveConnection.requestCreateSession( requestId, packageName, routeOriginalId, sessionHints); updateBinding(); @@ -118,6 +143,11 @@ final class MediaRoute2ProviderServiceProxy extends MediaRoute2Provider @Override public void releaseSession(long requestId, String sessionId) { if (mConnectionReady) { + if (Flags.enableBuiltInSpeakerRouteSuitabilityStatuses()) { + synchronized (mLock) { + mSessionOriginalIdToTransferRequest.remove(sessionId); + } + } mActiveConnection.releaseSession(requestId, sessionId); updateBinding(); } @@ -158,6 +188,18 @@ final class MediaRoute2ProviderServiceProxy extends MediaRoute2Provider String routeOriginalId, @RoutingSessionInfo.TransferReason int transferReason) { if (mConnectionReady) { + if (Flags.enableBuiltInSpeakerRouteSuitabilityStatuses()) { + synchronized (mLock) { + mSessionOriginalIdToTransferRequest.put( + sessionOriginalId, + new SessionCreationOrTransferRequest( + requestId, + routeOriginalId, + transferReason, + transferInitiatorUserHandle, + transferInitiatorPackageName)); + } + } mActiveConnection.transferToRoute(requestId, sessionOriginalId, routeOriginalId); } } @@ -259,9 +301,12 @@ final class MediaRoute2ProviderServiceProxy extends MediaRoute2Provider Intent service = new Intent(MediaRoute2ProviderService.SERVICE_INTERFACE); service.setComponent(mComponentName); try { - mBound = mContext.bindServiceAsUser(service, this, - Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE, - new UserHandle(mUserId)); + mBound = + mContext.bindServiceAsUser( + service, + mServiceConnection, + Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE, + new UserHandle(mUserId)); if (!mBound && DEBUG) { Slog.d(TAG, this + ": Bind failed"); } @@ -281,12 +326,11 @@ final class MediaRoute2ProviderServiceProxy extends MediaRoute2Provider mBound = false; disconnect(); - mContext.unbindService(this); + mContext.unbindService(mServiceConnection); } } - @Override - public void onServiceConnected(ComponentName name, IBinder service) { + private void onServiceConnectedInternal(IBinder service) { if (DEBUG) { Slog.d(TAG, this + ": Connected"); } @@ -310,16 +354,14 @@ final class MediaRoute2ProviderServiceProxy extends MediaRoute2Provider } } - @Override - public void onServiceDisconnected(ComponentName name) { + private void onServiceDisconnectedInternal() { if (DEBUG) { Slog.d(TAG, this + ": Service disconnected"); } disconnect(); } - @Override - public void onBindingDied(ComponentName name) { + private void onBindingDiedInternal(ComponentName name) { unbind(); if (Flags.enablePreventionOfKeepAliveRouteProviders()) { Slog.w( @@ -384,6 +426,11 @@ final class MediaRoute2ProviderServiceProxy extends MediaRoute2Provider String newSessionId = newSession.getId(); synchronized (mLock) { + if (Flags.enableBuiltInSpeakerRouteSuitabilityStatuses()) { + newSession = + createSessionWithPopulatedTransferInitiationDataLocked( + requestId, /* oldSessionInfo= */ null, newSession); + } if (mSessionInfos.stream() .anyMatch(session -> TextUtils.equals(session.getId(), newSessionId)) || mReleasingSessions.stream() @@ -397,6 +444,7 @@ final class MediaRoute2ProviderServiceProxy extends MediaRoute2Provider mCallback.onSessionCreated(this, requestId, newSession); } + @GuardedBy("mLock") private int findSessionByIdLocked(RoutingSessionInfo session) { for (int i = 0; i < mSessionInfos.size(); i++) { if (TextUtils.equals(mSessionInfos.get(i).getId(), session.getId())) { @@ -417,7 +465,6 @@ final class MediaRoute2ProviderServiceProxy extends MediaRoute2Provider for (RoutingSessionInfo session : sessions) { if (session == null) continue; session = assignProviderIdForSession(session); - int sourceIndex = findSessionByIdLocked(session); if (sourceIndex < 0) { mSessionInfos.add(targetIndex++, session); @@ -425,6 +472,12 @@ final class MediaRoute2ProviderServiceProxy extends MediaRoute2Provider } else if (sourceIndex < targetIndex) { Slog.w(TAG, "Ignoring duplicate session ID: " + session.getId()); } else { + if (Flags.enableBuiltInSpeakerRouteSuitabilityStatuses()) { + RoutingSessionInfo oldSessionInfo = mSessionInfos.get(sourceIndex); + session = + createSessionWithPopulatedTransferInitiationDataLocked( + REQUEST_ID_NONE, oldSessionInfo, session); + } mSessionInfos.set(sourceIndex, session); Collections.swap(mSessionInfos, sourceIndex, targetIndex++); dispatchSessionUpdated(session); @@ -432,11 +485,65 @@ final class MediaRoute2ProviderServiceProxy extends MediaRoute2Provider } for (int i = mSessionInfos.size() - 1; i >= targetIndex; i--) { RoutingSessionInfo releasedSession = mSessionInfos.remove(i); + mSessionOriginalIdToTransferRequest.remove(releasedSession.getId()); dispatchSessionReleased(releasedSession); } } } + /** + * Returns a {@link RoutingSessionInfo} with transfer initiation data from the given {@code + * oldSessionInfo}, and any pending transfer or session creation requests. + */ + @GuardedBy("mLock") + private RoutingSessionInfo createSessionWithPopulatedTransferInitiationDataLocked( + long requestId, + @Nullable RoutingSessionInfo oldSessionInfo, + @NonNull RoutingSessionInfo newSessionInfo) { + SessionCreationOrTransferRequest pendingRequest = + oldSessionInfo != null + ? mSessionOriginalIdToTransferRequest.get(newSessionInfo.getOriginalId()) + : mRequestIdToSessionCreationRequest.get(requestId); + boolean pendingTargetRouteInSelectedRoutes = + pendingRequest != null + && pendingRequest.isTargetRouteIdInRouteUniqueIdList( + newSessionInfo.getSelectedRoutes()); + boolean pendingTargetRouteInTransferableRoutes = + pendingRequest != null + && pendingRequest.isTargetRouteIdInRouteUniqueIdList( + newSessionInfo.getTransferableRoutes()); + + int transferReason; + UserHandle transferInitiatorUserHandle; + String transferInitiatorPackageName; + if (pendingTargetRouteInSelectedRoutes) { // The pending request has been satisfied. + transferReason = pendingRequest.mTransferReason; + transferInitiatorUserHandle = pendingRequest.mTransferInitiatorUserHandle; + transferInitiatorPackageName = pendingRequest.mTransferInitiatorPackageName; + } else if (oldSessionInfo != null) { + // No pending request, we copy the values from the old session object. + transferReason = oldSessionInfo.getTransferReason(); + transferInitiatorUserHandle = oldSessionInfo.getTransferInitiatorUserHandle(); + transferInitiatorPackageName = oldSessionInfo.getTransferInitiatorPackageName(); + } else { // There's a new session with no associated creation request, we use defaults. + transferReason = RoutingSessionInfo.TRANSFER_REASON_FALLBACK; + transferInitiatorUserHandle = UserHandle.of(mUserId); + transferInitiatorPackageName = newSessionInfo.getClientPackageName(); + } + if (pendingTargetRouteInSelectedRoutes || !pendingTargetRouteInTransferableRoutes) { + // The pending request has been satisfied, or the target route is no longer available. + if (oldSessionInfo != null) { + mSessionOriginalIdToTransferRequest.remove(newSessionInfo.getId()); + } else if (pendingRequest != null) { + mRequestIdToSessionCreationRequest.remove(pendingRequest.mRequestId); + } + } + return new RoutingSessionInfo.Builder(newSessionInfo) + .setTransferInitiator(transferInitiatorUserHandle, transferInitiatorPackageName) + .setTransferReason(transferReason) + .build(); + } + private void onSessionReleased(Connection connection, RoutingSessionInfo releasedSession) { if (mActiveConnection != connection) { return; @@ -450,6 +557,7 @@ final class MediaRoute2ProviderServiceProxy extends MediaRoute2Provider boolean found = false; synchronized (mLock) { + mSessionOriginalIdToTransferRequest.remove(releasedSession.getId()); for (RoutingSessionInfo session : mSessionInfos) { if (TextUtils.equals(session.getId(), releasedSession.getId())) { mSessionInfos.remove(session); @@ -498,6 +606,11 @@ final class MediaRoute2ProviderServiceProxy extends MediaRoute2Provider } private void onRequestFailed(Connection connection, long requestId, int reason) { + if (Flags.enableBuiltInSpeakerRouteSuitabilityStatuses()) { + synchronized (mLock) { + mRequestIdToSessionCreationRequest.remove(requestId); + } + } if (mActiveConnection != connection) { return; } @@ -522,18 +635,60 @@ final class MediaRoute2ProviderServiceProxy extends MediaRoute2Provider } mSessionInfos.clear(); mReleasingSessions.clear(); + mRequestIdToSessionCreationRequest.clear(); + mSessionOriginalIdToTransferRequest.clear(); } } } @Override protected String getDebugString() { + int pendingSessionCreationCount; + int pendingTransferCount; + synchronized (mLock) { + pendingSessionCreationCount = mRequestIdToSessionCreationRequest.size(); + pendingTransferCount = mSessionOriginalIdToTransferRequest.size(); + } return TextUtils.formatSimple( - "ProviderServiceProxy - package: %s, bound: %b, connection (active:%b, ready:%b)", + "ProviderServiceProxy - package: %s, bound: %b, connection (active:%b, ready:%b), " + + "pending (session creations: %d, transfers: %d)", mComponentName.getPackageName(), mBound, mActiveConnection != null, - mConnectionReady); + mConnectionReady, + pendingSessionCreationCount, + pendingTransferCount); + } + + // All methods in this class are called on the main thread. + private final class ServiceConnectionImpl implements ServiceConnection { + + @Override + public void onServiceConnected(ComponentName name, IBinder service) { + if (Flags.enableMr2ServiceNonMainBgThread()) { + mHandler.post(() -> onServiceConnectedInternal(service)); + } else { + onServiceConnectedInternal(service); + } + } + + @Override + public void onServiceDisconnected(ComponentName name) { + if (Flags.enableMr2ServiceNonMainBgThread()) { + mHandler.post(() -> onServiceDisconnectedInternal()); + } else { + onServiceDisconnectedInternal(); + } + } + + @Override + public void onBindingDied(ComponentName name) { + if (Flags.enableMr2ServiceNonMainBgThread()) { + mHandler.post(() -> onBindingDiedInternal(name)); + } else { + onBindingDiedInternal(name); + } + } } private final class Connection implements DeathRecipient { diff --git a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java index c03497e629f0..ba7d3b8c76d2 100644 --- a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java +++ b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java @@ -70,6 +70,7 @@ import com.android.internal.util.function.pooled.PooledLambda; import com.android.media.flags.Flags; import com.android.server.LocalServices; import com.android.server.pm.UserManagerInternal; +import com.android.server.statusbar.StatusBarManagerInternal; import java.io.PrintWriter; import java.lang.ref.WeakReference; @@ -118,6 +119,7 @@ class MediaRouter2ServiceImpl { private final UserManagerInternal mUserManagerInternal; private final Object mLock = new Object(); private final AppOpsManager mAppOpsManager; + private final StatusBarManagerInternal mStatusBarManagerInternal; final AtomicInteger mNextRouterOrManagerId = new AtomicInteger(1); final ActivityManager mActivityManager; final PowerManager mPowerManager; @@ -188,6 +190,7 @@ class MediaRouter2ServiceImpl { mPowerManager = mContext.getSystemService(PowerManager.class); mUserManagerInternal = LocalServices.getService(UserManagerInternal.class); mAppOpsManager = mContext.getSystemService(AppOpsManager.class); + mStatusBarManagerInternal = LocalServices.getService(StatusBarManagerInternal.class); IntentFilter screenOnOffIntentFilter = new IntentFilter(); screenOnOffIntentFilter.addAction(ACTION_SCREEN_ON); @@ -260,6 +263,17 @@ class MediaRouter2ServiceImpl { } } + @RequiresPermission(Manifest.permission.PACKAGE_USAGE_STATS) + public boolean showMediaOutputSwitcherWithRouter2(@NonNull String packageName) { + UserHandle userHandle = Binder.getCallingUserHandle(); + final long token = Binder.clearCallingIdentity(); + try { + return showOutputSwitcher(packageName, userHandle); + } finally { + Binder.restoreCallingIdentity(token); + } + } + public void registerRouter2(@NonNull IMediaRouter2 router, @NonNull String packageName) { Objects.requireNonNull(router, "router must not be null"); if (TextUtils.isEmpty(packageName)) { @@ -778,6 +792,31 @@ class MediaRouter2ServiceImpl { } } + @RequiresPermission(Manifest.permission.PACKAGE_USAGE_STATS) + public boolean showMediaOutputSwitcherWithProxyRouter( + @NonNull IMediaRouter2Manager proxyRouter) { + Objects.requireNonNull(proxyRouter, "Proxy router must not be null"); + + final long token = Binder.clearCallingIdentity(); + try { + synchronized (mLock) { + final IBinder binder = proxyRouter.asBinder(); + ManagerRecord proxyRouterRecord = mAllManagerRecords.get(binder); + + if (proxyRouterRecord.mTargetPackageName == null) { + throw new UnsupportedOperationException( + "Only proxy routers can show the Output Switcher."); + } + + return showOutputSwitcher( + proxyRouterRecord.mTargetPackageName, + UserHandle.of(proxyRouterRecord.mUserRecord.mUserId)); + } + } finally { + Binder.restoreCallingIdentity(token); + } + } + // End of methods that implement MediaRouter2Manager operations. // Start of methods that implements operations for both MediaRouter2 and MediaRouter2Manager. @@ -934,6 +973,19 @@ class MediaRouter2ServiceImpl { } } + @RequiresPermission(Manifest.permission.PACKAGE_USAGE_STATS) + private boolean showOutputSwitcher( + @NonNull String packageName, @NonNull UserHandle userHandle) { + if (mActivityManager.getPackageImportance(packageName) > IMPORTANCE_FOREGROUND) { + Slog.w(TAG, "showMediaOutputSwitcher only works when called from foreground"); + return false; + } + synchronized (mLock) { + mStatusBarManagerInternal.showMediaOutputSwitcher(packageName, userHandle); + } + return true; + } + // End of methods that implements operations for both MediaRouter2 and MediaRouter2Manager. public void dump(@NonNull PrintWriter pw, @NonNull String prefix) { diff --git a/services/core/java/com/android/server/media/MediaRouterService.java b/services/core/java/com/android/server/media/MediaRouterService.java index 192ac6287884..1188a0764051 100644 --- a/services/core/java/com/android/server/media/MediaRouterService.java +++ b/services/core/java/com/android/server/media/MediaRouterService.java @@ -16,7 +16,6 @@ package com.android.server.media; -import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND; import android.Manifest; import android.annotation.NonNull; @@ -75,7 +74,6 @@ import com.android.media.flags.Flags; import com.android.server.LocalServices; import com.android.server.Watchdog; import com.android.server.pm.UserManagerInternal; -import com.android.server.statusbar.StatusBarManagerInternal; import java.io.FileDescriptor; import java.io.PrintWriter; @@ -266,32 +264,6 @@ public final class MediaRouterService extends IMediaRouterService.Stub // Binder call @Override - public boolean showMediaOutputSwitcher(String packageName) { - int uid = Binder.getCallingUid(); - if (!validatePackageName(uid, packageName)) { - throw new SecurityException("packageName must match the calling identity"); - } - UserHandle userHandle = UserHandle.getUserHandleForUid(uid); - final long token = Binder.clearCallingIdentity(); - try { - if (mContext.getSystemService(ActivityManager.class).getPackageImportance(packageName) - > IMPORTANCE_FOREGROUND) { - Slog.w(TAG, "showMediaOutputSwitcher only works when called from foreground"); - return false; - } - synchronized (mLock) { - StatusBarManagerInternal statusBar = - LocalServices.getService(StatusBarManagerInternal.class); - statusBar.showMediaOutputSwitcher(packageName, userHandle); - } - } finally { - Binder.restoreCallingIdentity(token); - } - return true; - } - - // Binder call - @Override public MediaRouterClientState getState(IMediaRouterClient client) { final long token = Binder.clearCallingIdentity(); try { @@ -443,6 +415,17 @@ public final class MediaRouterService extends IMediaRouterService.Stub } // Binder call + @RequiresPermission(Manifest.permission.PACKAGE_USAGE_STATS) + @Override + public boolean showMediaOutputSwitcherWithRouter2(@NonNull String packageName) { + int uid = Binder.getCallingUid(); + if (!validatePackageName(uid, packageName)) { + throw new SecurityException("packageName must match the calling identity"); + } + return mService2.showMediaOutputSwitcherWithRouter2(packageName); + } + + // Binder call @Override public void registerRouter2(IMediaRouter2 router, String packageName) { final int uid = Binder.getCallingUid(); @@ -676,6 +659,13 @@ public final class MediaRouterService extends IMediaRouterService.Stub mService2.releaseSessionWithManager(manager, requestId, sessionId); } + @RequiresPermission(Manifest.permission.PACKAGE_USAGE_STATS) + @Override + public boolean showMediaOutputSwitcherWithProxyRouter( + @NonNull IMediaRouter2Manager proxyRouter) { + return mService2.showMediaOutputSwitcherWithProxyRouter(proxyRouter); + } + void restoreBluetoothA2dp() { try { boolean a2dpOn; diff --git a/services/core/java/com/android/server/media/MediaSession2Record.java b/services/core/java/com/android/server/media/MediaSession2Record.java index dfb2b0a750e3..89555a9f1de4 100644 --- a/services/core/java/com/android/server/media/MediaSession2Record.java +++ b/services/core/java/com/android/server/media/MediaSession2Record.java @@ -67,7 +67,6 @@ public class MediaSession2Record extends MediaSessionRecordImpl { // The lock is required to prevent `Controller2Callback` from using partially initialized // `MediaSession2Record.this`. synchronized (mLock) { - mUniqueId = sNextMediaSessionRecordId.getAndIncrement(); mSessionToken = sessionToken; mService = service; mHandlerExecutor = new HandlerExecutor(new Handler(handlerLooper)); diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java index 69f07d5c5f7b..0a9109b3e98c 100644 --- a/services/core/java/com/android/server/media/MediaSessionRecord.java +++ b/services/core/java/com/android/server/media/MediaSessionRecord.java @@ -81,6 +81,8 @@ import android.util.Log; import android.util.Slog; import android.view.KeyEvent; +import com.android.internal.annotations.GuardedBy; +import com.android.media.flags.Flags; import com.android.server.LocalServices; import com.android.server.uri.UriGrantsManagerInternal; @@ -229,6 +231,14 @@ public class MediaSessionRecord extends MediaSessionRecordImpl implements IBinde private int mPolicies; + private final Runnable mUserEngagementTimeoutExpirationRunnable = + () -> { + synchronized (mLock) { + updateUserEngagedStateIfNeededLocked(/* isTimeoutExpired= */ true); + } + }; + + @GuardedBy("mLock") private @UserEngagementState int mUserEngagementState = USER_DISENGAGED; @IntDef({USER_PERMANENTLY_ENGAGED, USER_TEMPORARY_ENGAGED, USER_DISENGAGED}) @@ -238,26 +248,26 @@ public class MediaSessionRecord extends MediaSessionRecordImpl implements IBinde /** * Indicates that the session is active and in one of the user engaged states. * - * @see #updateUserEngagedStateIfNeededLocked(boolean) () + * @see #updateUserEngagedStateIfNeededLocked(boolean) */ private static final int USER_PERMANENTLY_ENGAGED = 0; /** * Indicates that the session is active and in {@link PlaybackState#STATE_PAUSED} state. * - * @see #updateUserEngagedStateIfNeededLocked(boolean) () + * @see #updateUserEngagedStateIfNeededLocked(boolean) */ private static final int USER_TEMPORARY_ENGAGED = 1; /** * Indicates that the session is either not active or in one of the user disengaged states * - * @see #updateUserEngagedStateIfNeededLocked(boolean) () + * @see #updateUserEngagedStateIfNeededLocked(boolean) */ private static final int USER_DISENGAGED = 2; /** - * Indicates the duration of the temporary engaged states. + * Indicates the duration of the temporary engaged states, 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 @@ -270,7 +280,7 @@ public class MediaSessionRecord extends MediaSessionRecordImpl implements IBinde * user-engaged state is not considered user-engaged when transitioning from a non-user engaged * state {@link PlaybackState#STATE_STOPPED}. */ - private static final int TEMP_USER_ENGAGED_TIMEOUT = 600000; + private static final int TEMP_USER_ENGAGED_TIMEOUT_MS = 600000; public MediaSessionRecord( int ownerPid, @@ -284,7 +294,6 @@ public class MediaSessionRecord extends MediaSessionRecordImpl implements IBinde Looper handlerLooper, int policies) throws RemoteException { - mUniqueId = sNextMediaSessionRecordId.getAndIncrement(); mOwnerPid = ownerPid; mOwnerUid = ownerUid; mUserId = userId; @@ -609,8 +618,7 @@ public class MediaSessionRecord extends MediaSessionRecordImpl implements IBinde @Override public void expireTempEngaged() { - mHandler.removeCallbacks(mHandleTempEngagedSessionTimeout); - updateUserEngagedStateIfNeededLocked(/* isTimeoutExpired= */ true); + mHandler.post(mUserEngagementTimeoutExpirationRunnable); } /** @@ -1086,11 +1094,6 @@ public class MediaSessionRecord extends MediaSessionRecordImpl implements IBinde } }; - private final Runnable mHandleTempEngagedSessionTimeout = - () -> { - updateUserEngagedStateIfNeededLocked(/* isTimeoutExpired= */ true); - }; - @RequiresPermission(Manifest.permission.INTERACT_ACROSS_USERS) private static boolean componentNameExists( @NonNull ComponentName componentName, @NonNull Context context, int userId) { @@ -1107,10 +1110,14 @@ public class MediaSessionRecord extends MediaSessionRecordImpl implements IBinde return !resolveInfos.isEmpty(); } + @GuardedBy("mLock") private void updateUserEngagedStateIfNeededLocked(boolean isTimeoutExpired) { + if (!Flags.enableNotifyingActivityManagerWithMediaSessionStatusChange()) { + return; + } int oldUserEngagedState = mUserEngagementState; int newUserEngagedState; - if (!isActive() || mPlaybackState == null) { + if (!isActive() || mPlaybackState == null || mDestroyed) { newUserEngagedState = USER_DISENGAGED; } else if (isActive() && mPlaybackState.isActive()) { newUserEngagedState = USER_PERMANENTLY_ENGAGED; @@ -1126,18 +1133,22 @@ public class MediaSessionRecord extends MediaSessionRecordImpl implements IBinde return; } + mUserEngagementState = newUserEngagedState; if (newUserEngagedState == USER_TEMPORARY_ENGAGED) { - mHandler.postDelayed(mHandleTempEngagedSessionTimeout, TEMP_USER_ENGAGED_TIMEOUT); - } else if (oldUserEngagedState == USER_TEMPORARY_ENGAGED) { - mHandler.removeCallbacks(mHandleTempEngagedSessionTimeout); + mHandler.postDelayed( + mUserEngagementTimeoutExpirationRunnable, TEMP_USER_ENGAGED_TIMEOUT_MS); + } else { + mHandler.removeCallbacks(mUserEngagementTimeoutExpirationRunnable); } boolean wasUserEngaged = oldUserEngagedState != USER_DISENGAGED; boolean isNowUserEngaged = newUserEngagedState != USER_DISENGAGED; - mUserEngagementState = newUserEngagedState; if (wasUserEngaged != isNowUserEngaged) { - mService.onSessionUserEngagementStateChange( - /* mediaSessionRecord= */ this, /* isUserEngaged= */ isNowUserEngaged); + mHandler.post( + () -> + mService.onSessionUserEngagementStateChange( + /* mediaSessionRecord= */ this, + /* isUserEngaged= */ isNowUserEngaged)); } } diff --git a/services/core/java/com/android/server/media/MediaSessionRecordImpl.java b/services/core/java/com/android/server/media/MediaSessionRecordImpl.java index b57b14835987..15f90d4fdd0e 100644 --- a/services/core/java/com/android/server/media/MediaSessionRecordImpl.java +++ b/services/core/java/com/android/server/media/MediaSessionRecordImpl.java @@ -34,8 +34,12 @@ import java.util.concurrent.atomic.AtomicInteger; */ public abstract class MediaSessionRecordImpl { - static final AtomicInteger sNextMediaSessionRecordId = new AtomicInteger(1); - int mUniqueId; + private static final AtomicInteger sNextMediaSessionRecordId = new AtomicInteger(1); + private final int mUniqueId; + + protected MediaSessionRecordImpl() { + mUniqueId = sNextMediaSessionRecordId.getAndIncrement(); + } /** * Get the info for this session. diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java index 399866728770..1ebc856af2d8 100644 --- a/services/core/java/com/android/server/media/MediaSessionService.java +++ b/services/core/java/com/android/server/media/MediaSessionService.java @@ -197,6 +197,16 @@ public class MediaSessionService extends SystemService implements Monitor { @GuardedBy("mLock") private final Map<Integer, Set<Notification>> mMediaNotifications = new HashMap<>(); + /** + * Holds all {@link MediaSessionRecordImpl} which we've reported as being {@link + * ActivityManagerInternal#startForegroundServiceDelegate user engaged}. + * + * <p>This map simply prevents invoking {@link + * ActivityManagerInternal#startForegroundServiceDelegate} more than once per session. + */ + @GuardedBy("mLock") + private final Set<MediaSessionRecordImpl> mFgsAllowedMediaSessionRecords = new HashSet<>(); + // The FullUserRecord of the current users. (i.e. The foreground user that isn't a profile) // It's always not null after the MediaSessionService is started. private FullUserRecord mCurrentFullUserRecord; @@ -704,17 +714,31 @@ public class MediaSessionService extends SystemService implements Monitor { int uid = mediaSessionRecord.getUid(); for (Notification mediaNotification : mMediaNotifications.getOrDefault(uid, Set.of())) { if (mediaSessionRecord.isLinkedToNotification(mediaNotification)) { - startFgsDelegate(mediaSessionRecord.getForegroundServiceDelegationOptions()); + startFgsDelegateLocked(mediaSessionRecord); return; } } } } - private void startFgsDelegate( - ForegroundServiceDelegationOptions foregroundServiceDelegationOptions) { + @GuardedBy("mLock") + private void startFgsDelegateLocked(MediaSessionRecordImpl mediaSessionRecord) { + ForegroundServiceDelegationOptions foregroundServiceDelegationOptions = + mediaSessionRecord.getForegroundServiceDelegationOptions(); + if (foregroundServiceDelegationOptions == null) { + return; // This record doesn't support FGS. Typically a MediaSession2 record. + } + if (!mFgsAllowedMediaSessionRecords.add(mediaSessionRecord)) { + return; // This record is already FGS-started. + } final long token = Binder.clearCallingIdentity(); try { + Log.i( + TAG, + TextUtils.formatSimple( + "startFgsDelegate: pkg=%s uid=%d", + foregroundServiceDelegationOptions.mClientPackageName, + foregroundServiceDelegationOptions.mClientUid)); mActivityManagerInternal.startForegroundServiceDelegate( foregroundServiceDelegationOptions, /* connection= */ null); } finally { @@ -748,14 +772,29 @@ public class MediaSessionService extends SystemService implements Monitor { } } - stopFgsDelegate(foregroundServiceDelegationOptions); + stopFgsDelegateLocked(mediaSessionRecord); } } - private void stopFgsDelegate( - ForegroundServiceDelegationOptions foregroundServiceDelegationOptions) { + @GuardedBy("mLock") + private void stopFgsDelegateLocked(MediaSessionRecordImpl mediaSessionRecord) { + ForegroundServiceDelegationOptions foregroundServiceDelegationOptions = + mediaSessionRecord.getForegroundServiceDelegationOptions(); + if (foregroundServiceDelegationOptions == null) { + return; // This record doesn't support FGS. Typically a MediaSession2 record. + } + if (!mFgsAllowedMediaSessionRecords.remove(mediaSessionRecord)) { + return; // This record is not FGS-started. No need to stop it. + } + final long token = Binder.clearCallingIdentity(); try { + Log.i( + TAG, + TextUtils.formatSimple( + "stopFgsDelegate: pkg=%s uid=%d", + foregroundServiceDelegationOptions.mClientPackageName, + foregroundServiceDelegationOptions.mClientUid)); mActivityManagerInternal.stopForegroundServiceDelegate( foregroundServiceDelegationOptions); } finally { @@ -2679,6 +2718,9 @@ public class MediaSessionService extends SystemService implements Monitor { @Override public void expireTempEngagedSessions() { + if (!Flags.enableNotifyingActivityManagerWithMediaSessionStatusChange()) { + return; + } synchronized (mLock) { for (Set<MediaSessionRecordImpl> uidSessions : mUserEngagedSessionsForFgs.values()) { @@ -3194,11 +3236,8 @@ public class MediaSessionService extends SystemService implements Monitor { mMediaNotifications.get(uid).add(postedNotification); for (MediaSessionRecordImpl mediaSessionRecord : mUserEngagedSessionsForFgs.getOrDefault(uid, Set.of())) { - ForegroundServiceDelegationOptions foregroundServiceDelegationOptions = - mediaSessionRecord.getForegroundServiceDelegationOptions(); - if (foregroundServiceDelegationOptions != null - && mediaSessionRecord.isLinkedToNotification(postedNotification)) { - startFgsDelegate(foregroundServiceDelegationOptions); + if (mediaSessionRecord.isLinkedToNotification(postedNotification)) { + startFgsDelegateLocked(mediaSessionRecord); return; } } diff --git a/services/core/java/com/android/server/media/MediaShellCommand.java b/services/core/java/com/android/server/media/MediaShellCommand.java index bea71dc5cedb..19f16cc38000 100644 --- a/services/core/java/com/android/server/media/MediaShellCommand.java +++ b/services/core/java/com/android/server/media/MediaShellCommand.java @@ -113,6 +113,7 @@ public class MediaShellCommand extends ShellCommand { mWriter.println(" media_session list-sessions"); mWriter.println(" media_session monitor <tag>"); mWriter.println(" media_session volume [options]"); + mWriter.println(" media_session expire-temp-engaged-sessions"); mWriter.println(); mWriter.println("media_session dispatch: dispatch a media key to the system."); mWriter.println(" KEY may be: play, pause, play-pause, mute, headsethook,"); @@ -121,6 +122,9 @@ public class MediaShellCommand extends ShellCommand { mWriter.println("media_session monitor: monitor updates to the specified session."); mWriter.println(" Use the tag from list-sessions."); mWriter.println("media_session volume: " + VolumeCtrl.USAGE); + mWriter.println("media_session expire-temp-engaged-sessions: Expires any ongoing"); + mWriter.println(" timers for media sessions in a temporary user-engaged"); + mWriter.println(" state."); mWriter.println(); } diff --git a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java index 6b409ee6f482..8c6273ce959f 100644 --- a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java +++ b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java @@ -443,7 +443,8 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider { boolean isTransferringToTheSelectedRoute = mPendingTransferRequest.isTargetRoute(selectedRoute); boolean canBePotentiallyTransferred = - mPendingTransferRequest.isTargetRouteIdInList(transferableRoutes); + mPendingTransferRequest.isTargetRouteIdInRouteOriginalIdList( + transferableRoutes); if (isTransferringToTheSelectedRoute) { transferReason = mPendingTransferRequest.mTransferReason; diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java index c60ac3a74ebd..d9e22c5a270f 100644 --- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java +++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java @@ -28,6 +28,7 @@ import static android.Manifest.permission.READ_PHONE_STATE; import static android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE; import static android.app.ActivityManager.PROCESS_CAPABILITY_POWER_RESTRICTED_NETWORK; import static android.app.ActivityManager.PROCESS_CAPABILITY_USER_RESTRICTED_NETWORK; +import static android.app.ActivityManager.PROCESS_STATE_LAST_ACTIVITY; import static android.app.ActivityManager.PROCESS_STATE_UNKNOWN; import static android.app.ActivityManager.isProcStateConsideredInteraction; import static android.app.ActivityManager.printCapabilitiesSummary; @@ -523,6 +524,12 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { */ private boolean mUseMeteredFirewallChains; + /** + * Whether or not sensitive process states and non-sensitive process-states have different + * delays before network is blocked after transitioning to background. + */ + private boolean mUseDifferentDelaysForBackgroundChain; + // See main javadoc for instructions on how to use these locks. final Object mUidRulesFirstLock = new Object(); final Object mNetworkPoliciesSecondLock = new Object(); @@ -552,10 +559,43 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { * {@link NetworkPolicyManager#BACKGROUND_THRESHOLD_STATE} will lose network access. * The delay is meant to prevent churn due to quick process-state changes. * Note that there is no delay while granting network access. + * + * This is only used when the flag {@link #mUseDifferentDelaysForBackgroundChain} is disabled. */ @VisibleForTesting long mBackgroundRestrictionDelayMs = TimeUnit.SECONDS.toMillis(5); + /** + * Short delay after which a uid going into a process state having priority equal to + * {@link NetworkPolicyManager#BACKGROUND_THRESHOLD_STATE} or lower will lose network access. + * + * This will apply to apps that should be fine with losing network access immediately. + * It is only meant as a debounce to prevent churn due to quick process-state changes. + * Note that there is no delay while granting network access. + * + * This is only used when the flag {@link #mUseDifferentDelaysForBackgroundChain} is enabled. + */ + @VisibleForTesting + long mBackgroundRestrictionShortDelayMs = TimeUnit.SECONDS.toMillis(2); + + /** + * Long delay after which a uid going into a process state having priority equal to + * {@link NetworkPolicyManager#BACKGROUND_THRESHOLD_STATE} or lower will lose network access. + * + * Unlike {@link #mBackgroundRestrictionShortDelayMs}, this is meant to be applied to apps + * in sensitive proc-states like {@link ActivityManager#PROCESS_STATE_TOP_SLEEPING} and + * {@link ActivityManager#PROCESS_STATE_LAST_ACTIVITY}, where the user may switch to this app + * before this period and any latency in granting network access before resuming app activities + * may degrade experience. + * + * This is only used when the flag {@link #mUseDifferentDelaysForBackgroundChain} is enabled. + */ + @VisibleForTesting + long mBackgroundRestrictionLongDelayMs = TimeUnit.SECONDS.toMillis(20); + + @GuardedBy("mUidRulesFirstLock") + private long mNextProcessBackgroundUidsTime = Long.MAX_VALUE; + /** Defined network policies. */ @GuardedBy("mNetworkPoliciesSecondLock") final ArrayMap<NetworkTemplate, NetworkPolicy> mNetworkPolicy = new ArrayMap<>(); @@ -1007,6 +1047,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class); mUseMeteredFirewallChains = Flags.useMeteredFirewallChains(); + mUseDifferentDelaysForBackgroundChain = Flags.useDifferentDelaysForBackgroundChain(); synchronized (mUidRulesFirstLock) { synchronized (mNetworkPoliciesSecondLock) { @@ -1241,11 +1282,21 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { // different chains may change. return true; } - if (mBackgroundNetworkRestricted && (previousProcState >= BACKGROUND_THRESHOLD_STATE) + if (mBackgroundNetworkRestricted) { + if ((previousProcState >= BACKGROUND_THRESHOLD_STATE) != (newProcState >= BACKGROUND_THRESHOLD_STATE)) { - // Proc-state change crossed BACKGROUND_THRESHOLD_STATE: Network rules for the - // BACKGROUND chain may change. - 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; @@ -3965,8 +4016,8 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { // allow override without having plans defined. synchronized (mNetworkPoliciesSecondLock) { final SubscriptionPlan plan = getPrimarySubscriptionPlanLocked(subId); - if (overrideMask != SUBSCRIPTION_OVERRIDE_UNMETERED && plan == null - || plan.getDataLimitBehavior() == SubscriptionPlan.LIMIT_BEHAVIOR_UNKNOWN) { + if (overrideMask != SUBSCRIPTION_OVERRIDE_UNMETERED && (plan == null + || plan.getDataLimitBehavior() == SubscriptionPlan.LIMIT_BEHAVIOR_UNKNOWN)) { throw new IllegalStateException( "Must provide valid SubscriptionPlan to enable overriding"); } @@ -4045,6 +4096,8 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { + mBackgroundNetworkRestricted); fout.println(Flags.FLAG_USE_METERED_FIREWALL_CHAINS + ": " + mUseMeteredFirewallChains); + fout.println(Flags.FLAG_USE_DIFFERENT_DELAYS_FOR_BACKGROUND_CHAIN + ": " + + mUseDifferentDelaysForBackgroundChain); fout.println(); fout.println("mRestrictBackgroundLowPowerMode: " + mRestrictBackgroundLowPowerMode); @@ -4188,20 +4241,34 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { 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); + if (mBackgroundNetworkRestricted) { + 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.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.decreaseIndent(); + } + fout.println(); } final SparseBooleanArray knownUids = new SparseBooleanArray(); @@ -4337,6 +4404,15 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { || isProcStateAllowedNetworkWhileBackground(mUidState.get(uid)); } + private long getBackgroundTransitioningDelay(int procState) { + if (mUseDifferentDelaysForBackgroundChain) { + return procState <= PROCESS_STATE_LAST_ACTIVITY ? mBackgroundRestrictionLongDelayMs + : mBackgroundRestrictionShortDelayMs; + } else { + return mBackgroundRestrictionDelayMs; + } + } + /** * Process state of UID changed; if needed, will trigger * {@link #updateRulesForDataUsageRestrictionsUL(int)} and @@ -4387,19 +4463,41 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { mBackgroundTransitioningUids.delete(uid); updateRuleForBackgroundUL(uid); updatePowerRestrictionRules = true; - } else if (wasAllowed && !isAllowed) { + } else if (!isAllowed) { + final int transitionIdx = mBackgroundTransitioningUids.indexOfKey(uid); final long completionTimeMs = SystemClock.uptimeMillis() - + mBackgroundRestrictionDelayMs; - if (mBackgroundTransitioningUids.indexOfKey(uid) < 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); + + 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; + } } - if (!mHandler.hasMessages(MSG_PROCESS_BACKGROUND_TRANSITIONING_UIDS)) { - // Many uids may be in this "transitioning" state at the same time, so - // using one message at a time to avoid congestion in the MessageQueue. + 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; } } } @@ -5750,10 +5848,11 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { updateRuleForBackgroundUL(uid); updateRulesForPowerRestrictionsUL(uid, false); } - } - if (nextCheckTime < Long.MAX_VALUE) { - mHandler.sendEmptyMessageAtTime(MSG_PROCESS_BACKGROUND_TRANSITIONING_UIDS, - nextCheckTime); + mNextProcessBackgroundUidsTime = nextCheckTime; + if (nextCheckTime < Long.MAX_VALUE) { + mHandler.sendEmptyMessageAtTime( + MSG_PROCESS_BACKGROUND_TRANSITIONING_UIDS, nextCheckTime); + } } return true; } diff --git a/services/core/java/com/android/server/net/OWNERS b/services/core/java/com/android/server/net/OWNERS index 669cdaaf3ab5..bbc7c013f170 100644 --- a/services/core/java/com/android/server/net/OWNERS +++ b/services/core/java/com/android/server/net/OWNERS @@ -1,5 +1,6 @@ set noparent file:platform/packages/modules/Connectivity:main:/OWNERS_core_networking +per-file NetworkPolicyManagerService.java=jackyu@google.com, sarahchin@google.com jsharkey@android.com sudheersai@google.com diff --git a/services/core/java/com/android/server/net/flags.aconfig b/services/core/java/com/android/server/net/flags.aconfig index e986dd81b94b..586baf022897 100644 --- a/services/core/java/com/android/server/net/flags.aconfig +++ b/services/core/java/com/android/server/net/flags.aconfig @@ -17,3 +17,13 @@ flag { purpose: PURPOSE_BUGFIX } } + +flag { + name: "use_different_delays_for_background_chain" + namespace: "backstage_power" + description: "Grant longer grace periods for sensitive process-states before blocking network using the background chain" + bug: "323963467" + metadata { + purpose: PURPOSE_BUGFIX + } +} diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index c7b0f7dd3ad7..a8adc06f1860 100755..100644 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -284,6 +284,7 @@ import android.service.notification.NotificationRecordProto; import android.service.notification.NotificationServiceDumpProto; import android.service.notification.NotificationStats; import android.service.notification.StatusBarNotification; +import android.service.notification.ZenDeviceEffects; import android.service.notification.ZenModeConfig; import android.service.notification.ZenModeProto; import android.service.notification.ZenPolicy; @@ -2311,7 +2312,7 @@ public class NotificationManagerService extends SystemService { @VisibleForTesting void clearNotifications() { - synchronized (mNotificationList) { + synchronized (mNotificationLock) { mEnqueuedNotifications.clear(); mNotificationList.clear(); mNotificationsByKey.clear(); @@ -2321,21 +2322,27 @@ public class NotificationManagerService extends SystemService { @VisibleForTesting void addNotification(NotificationRecord r) { - mNotificationList.add(r); - mNotificationsByKey.put(r.getSbn().getKey(), r); - if (r.getSbn().isGroup()) { - mSummaryByGroupKey.put(r.getGroupKey(), r); + synchronized (mNotificationLock) { + mNotificationList.add(r); + mNotificationsByKey.put(r.getSbn().getKey(), r); + if (r.getSbn().isGroup()) { + mSummaryByGroupKey.put(r.getGroupKey(), r); + } } } @VisibleForTesting void addEnqueuedNotification(NotificationRecord r) { - mEnqueuedNotifications.add(r); + synchronized (mNotificationLock) { + mEnqueuedNotifications.add(r); + } } @VisibleForTesting NotificationRecord getNotificationRecord(String key) { - return mNotificationsByKey.get(key); + synchronized (mNotificationLock) { + return mNotificationsByKey.get(key); + } } @VisibleForTesting @@ -5502,6 +5509,14 @@ public class NotificationManagerService extends SystemService { } @Override + public void setManualZenRuleDeviceEffects(ZenDeviceEffects effects) throws RemoteException { + checkCallerIsSystem(); + + mZenModeHelper.setManualZenRuleDeviceEffects(effects, computeZenOrigin(true), + "Update manual mode non-policy settings", Binder.getCallingUid()); + } + + @Override public boolean updateAutomaticZenRule(String id, AutomaticZenRule automaticZenRule, boolean fromUser) throws RemoteException { validateAutomaticZenRule(id, automaticZenRule); diff --git a/services/core/java/com/android/server/notification/ZenModeConditions.java b/services/core/java/com/android/server/notification/ZenModeConditions.java index 86aa2d8b0a10..02b5f973d6b1 100644 --- a/services/core/java/com/android/server/notification/ZenModeConditions.java +++ b/services/core/java/com/android/server/notification/ZenModeConditions.java @@ -68,16 +68,17 @@ public class ZenModeConditions implements ConditionProviders.Callback { public void evaluateConfig(ZenModeConfig config, ComponentName trigger, boolean processSubscriptions) { if (config == null) return; - if (config.manualRule != null && config.manualRule.condition != null + if (!android.app.Flags.modesUi() && config.manualRule != null + && config.manualRule.condition != null && !config.manualRule.isTrueOrUnknown()) { if (DEBUG) Log.d(TAG, "evaluateConfig: clearing manual rule"); config.manualRule = null; } final ArraySet<Uri> current = new ArraySet<>(); - evaluateRule(config.manualRule, current, null, processSubscriptions); + evaluateRule(config.manualRule, current, null, processSubscriptions, true); for (ZenRule automaticRule : config.automaticRules.values()) { if (automaticRule.component != null) { - evaluateRule(automaticRule, current, trigger, processSubscriptions); + evaluateRule(automaticRule, current, trigger, processSubscriptions, false); updateSnoozing(automaticRule); } } @@ -131,7 +132,7 @@ public class ZenModeConditions implements ConditionProviders.Callback { // Only valid for CPS backed rules private void evaluateRule(ZenRule rule, ArraySet<Uri> current, ComponentName trigger, - boolean processSubscriptions) { + boolean processSubscriptions, boolean isManual) { if (rule == null || rule.conditionId == null) return; if (rule.configurationActivity != null) return; final Uri id = rule.conditionId; @@ -153,8 +154,10 @@ public class ZenModeConditions implements ConditionProviders.Callback { } // empty rule? disable and bail early if (rule.component == null && rule.enabler == null) { - Log.w(TAG, "No component found for automatic rule: " + rule.conditionId); - rule.enabled = false; + if (!android.app.Flags.modesUi() || (android.app.Flags.modesUi() && !isManual)) { + Log.w(TAG, "No component found for automatic rule: " + rule.conditionId); + rule.enabled = false; + } return; } if (current != null) { diff --git a/services/core/java/com/android/server/notification/ZenModeEventLogger.java b/services/core/java/com/android/server/notification/ZenModeEventLogger.java index 418eacc7ca65..ec5d96df3430 100644 --- a/services/core/java/com/android/server/notification/ZenModeEventLogger.java +++ b/services/core/java/com/android/server/notification/ZenModeEventLogger.java @@ -18,8 +18,8 @@ package com.android.server.notification; import static android.app.NotificationManager.Policy.STATE_CHANNELS_BYPASSING_DND; import static android.provider.Settings.Global.ZEN_MODE_OFF; -import static android.service.notification.NotificationServiceProto.CHANNEL_POLICY_PRIORITY; import static android.service.notification.NotificationServiceProto.CHANNEL_POLICY_NONE; +import static android.service.notification.NotificationServiceProto.CHANNEL_POLICY_PRIORITY; import static android.service.notification.NotificationServiceProto.RULE_TYPE_AUTOMATIC; import static android.service.notification.NotificationServiceProto.RULE_TYPE_MANUAL; import static android.service.notification.NotificationServiceProto.RULE_TYPE_UNKNOWN; @@ -352,8 +352,10 @@ class ZenModeEventLogger { ZenModeDiff.RuleDiff manualDiff = diff.getManualRuleDiff(); if (manualDiff != null && manualDiff.hasDiff()) { // a diff in the manual rule doesn't *necessarily* mean that it's responsible for - // the change -- only if it's been added or removed. - if (manualDiff.wasAdded() || manualDiff.wasRemoved()) { + // the change -- only if it's been added or removed or updated. + if (manualDiff.wasAdded() || manualDiff.wasRemoved() + || (Flags.modesUi() + && (manualDiff.becameActive() || manualDiff.becameInactive()))) { return RULE_TYPE_MANUAL; } } @@ -391,10 +393,8 @@ class ZenModeEventLogger { if (config == null) { return rules; } - - if (config.manualRule != null) { - // If the manual rule is non-null, then it's active. We make a copy and set the rule - // type so that the correct value gets logged. + if (config.isManualActive()) { + // We make a copy and set the rule type so that the correct value gets logged. ZenRule rule = config.manualRule.copy(); rule.type = ACTIVE_RULE_TYPE_MANUAL; rules.add(rule); @@ -592,10 +592,10 @@ class ZenModeEventLogger { // This applies to both call and message senders, but not conversation senders, // where they use the same enum values. proto.write(DNDPolicyProto.ALLOW_CALLS_FROM, - ZenAdapters.notificationPolicySendersToZenPolicyPeopleType( + ZenAdapters.prioritySendersToPeopleType( mNewPolicy.allowCallsFrom())); proto.write(DNDPolicyProto.ALLOW_MESSAGES_FROM, - ZenAdapters.notificationPolicySendersToZenPolicyPeopleType( + ZenAdapters.prioritySendersToPeopleType( mNewPolicy.allowMessagesFrom())); proto.write(DNDPolicyProto.ALLOW_CONVERSATIONS_FROM, mNewPolicy.allowConversationsFrom()); diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java index 454bd20c59e2..267291c1a970 100644 --- a/services/core/java/com/android/server/notification/ZenModeHelper.java +++ b/services/core/java/com/android/server/notification/ZenModeHelper.java @@ -24,6 +24,10 @@ import static android.app.NotificationManager.AUTOMATIC_RULE_STATUS_ENABLED; import static android.app.NotificationManager.AUTOMATIC_RULE_STATUS_REMOVED; import static android.app.NotificationManager.AUTOMATIC_RULE_STATUS_UNKNOWN; import static android.app.NotificationManager.Policy.PRIORITY_SENDERS_ANY; +import static android.service.notification.Condition.SOURCE_UNKNOWN; +import static android.service.notification.Condition.SOURCE_USER_ACTION; +import static android.service.notification.Condition.STATE_FALSE; +import static android.service.notification.Condition.STATE_TRUE; import static android.service.notification.NotificationServiceProto.ROOT_CONFIG; import static android.service.notification.ZenModeConfig.UPDATE_ORIGIN_APP; import static android.service.notification.ZenModeConfig.UPDATE_ORIGIN_INIT; @@ -216,7 +220,9 @@ public class ZenModeHelper { mAppOps = context.getSystemService(AppOpsManager.class); mNotificationManager = context.getSystemService(NotificationManager.class); - mDefaultConfig = readDefaultConfig(mContext.getResources()); + mDefaultConfig = Flags.modesUi() + ? ZenModeConfig.getDefaultConfig() + : readDefaultConfig(mContext.getResources()); updateDefaultConfigAutomaticRules(); if (Flags.modesApi()) { updateDefaultAutomaticRulePolicies(); @@ -612,7 +618,7 @@ public class ZenModeHelper { if (rule != null) { Condition deactivated = new Condition(rule.conditionId, mContext.getString(R.string.zen_mode_implicit_deactivated), - Condition.STATE_FALSE); + STATE_FALSE); setAutomaticZenRuleStateLocked(newConfig, Collections.singletonList(rule), deactivated, UPDATE_ORIGIN_APP, callingUid); } @@ -627,8 +633,7 @@ public class ZenModeHelper { // would apply if changing the global interruption filter. We only do this // for newly created rules, as existing rules have a pre-existing policy // (whether initialized here or set via app or user). - rule.zenPolicy = mConfig.toZenPolicy(); - + rule.zenPolicy = mConfig.getZenPolicy().copy(); newConfig.automaticRules.put(rule.id, rule); } // If the user has changed the rule's *zenMode*, then don't let app overwrite it. @@ -639,7 +644,7 @@ public class ZenModeHelper { rule.snoozing = false; rule.condition = new Condition(rule.conditionId, mContext.getString(R.string.zen_mode_implicit_activated), - Condition.STATE_TRUE); + STATE_TRUE); setConfigLocked(newConfig, /* triggeringComponent= */ null, UPDATE_ORIGIN_APP, "applyGlobalZenModeAsImplicitZenRule", callingUid); @@ -687,7 +692,7 @@ public class ZenModeHelper { // would take effect if changing the global policy. // Note that NotificationManager.Policy cannot have any unset priority // categories, but *can* have unset visual effects, which is why we do this. - newZenPolicy = mConfig.toZenPolicy().overwrittenWith(newZenPolicy); + newZenPolicy = mConfig.getZenPolicy().overwrittenWith(newZenPolicy); } updatePolicy( rule, @@ -878,7 +883,7 @@ public class ZenModeHelper { if (rule == null || !canManageAutomaticZenRule(rule)) { return Condition.STATE_UNKNOWN; } - return rule.condition != null ? rule.condition.state : Condition.STATE_FALSE; + return rule.condition != null ? rule.condition.state : STATE_FALSE; } } @@ -929,7 +934,7 @@ public class ZenModeHelper { Condition condition, @ConfigChangeOrigin int origin, int callingUid) { if (rules == null || rules.isEmpty()) return; - if (Flags.modesApi() && condition.source == Condition.SOURCE_USER_ACTION) { + if (Flags.modesApi() && condition.source == SOURCE_USER_ACTION) { origin = UPDATE_ORIGIN_USER; // Although coming from app, it's actually a user action. } @@ -1285,7 +1290,7 @@ public class ZenModeHelper { if (isNew) { // Newly created rule with no provided policy; fill in with the default. zenRule.zenPolicy = - Flags.modesUi() ? mDefaultConfig.toZenPolicy() : mConfig.toZenPolicy(); + Flags.modesUi() ? mDefaultConfig.getZenPolicy() : mConfig.getZenPolicy(); return true; } // Otherwise, a null policy means no policy changes, so we can stop here. @@ -1296,7 +1301,7 @@ public class ZenModeHelper { // fields in the bitmask should be marked as updated. ZenPolicy oldPolicy = zenRule.zenPolicy != null ? zenRule.zenPolicy - : (Flags.modesUi() ? mDefaultConfig.toZenPolicy() : mConfig.toZenPolicy()); + : (Flags.modesUi() ? mDefaultConfig.getZenPolicy() : mConfig.getZenPolicy()); // If this is updating a rule rather than creating a new one, keep any fields from the // old policy if they are unspecified in the new policy. For newly created rules, oldPolicy @@ -1524,9 +1529,15 @@ public class ZenModeHelper { + " conditionId=" + conditionId + " reason=" + reason + " setRingerMode=" + setRingerMode); newConfig = mConfig.copy(); - if (zenMode == Global.ZEN_MODE_OFF) { - newConfig.manualRule = null; - if (!Flags.modesUi() || origin != UPDATE_ORIGIN_USER) { + if (Flags.modesUi()) { + newConfig.manualRule.enabler = caller; + newConfig.manualRule.conditionId = conditionId != null ? conditionId : Uri.EMPTY; + newConfig.manualRule.pkg = PACKAGE_ANDROID; + newConfig.manualRule.zenMode = zenMode; + newConfig.manualRule.condition = new Condition(newConfig.manualRule.conditionId, "", + zenMode == Global.ZEN_MODE_OFF ? STATE_FALSE : STATE_TRUE, + origin == UPDATE_ORIGIN_USER ? SOURCE_USER_ACTION : SOURCE_UNKNOWN); + if (zenMode == Global.ZEN_MODE_OFF && origin != UPDATE_ORIGIN_USER) { // User deactivation of DND means just turning off the manual DND rule. // For API calls (different origin) keep old behavior of snoozing all rules. for (ZenRule automaticRule : newConfig.automaticRules.values()) { @@ -1536,20 +1547,51 @@ public class ZenModeHelper { } } } else { - final ZenRule newRule = new ZenRule(); - newRule.enabled = true; - newRule.zenMode = zenMode; - newRule.conditionId = conditionId; - newRule.enabler = caller; - if (Flags.modesApi()) { - newRule.allowManualInvocation = true; + if (zenMode == Global.ZEN_MODE_OFF) { + newConfig.manualRule = null; + // User deactivation of DND means just turning off the manual DND rule. + // For API calls (different origin) keep old behavior of snoozing all rules. + for (ZenRule automaticRule : newConfig.automaticRules.values()) { + if (automaticRule.isAutomaticActive()) { + automaticRule.snoozing = true; + } + } + + } else { + final ZenRule newRule = new ZenRule(); + newRule.enabled = true; + newRule.zenMode = zenMode; + newRule.conditionId = conditionId; + newRule.enabler = caller; + if (Flags.modesApi()) { + newRule.allowManualInvocation = true; + } + newConfig.manualRule = newRule; } - newConfig.manualRule = newRule; } setConfigLocked(newConfig, origin, reason, null, setRingerMode, callingUid); } } + public void setManualZenRuleDeviceEffects(ZenDeviceEffects deviceEffects, + @ConfigChangeOrigin int origin, String reason, int callingUid) { + if (!Flags.modesUi()) { + return; + } + ZenModeConfig newConfig; + synchronized (mConfigLock) { + if (mConfig == null) return; + if (DEBUG) Log.d(TAG, "updateManualRule " + deviceEffects + + " reason=" + reason + + " callingUid=" + callingUid); + newConfig = mConfig.copy(); + + newConfig.manualRule.pkg = PACKAGE_ANDROID; + newConfig.manualRule.zenDeviceEffects = deviceEffects; + setConfigLocked(newConfig, origin, reason, null, true, callingUid); + } + } + void dump(ProtoOutputStream proto) { proto.write(ZenModeProto.ZEN_MODE, mZenMode); synchronized (mConfigLock) { @@ -1558,7 +1600,7 @@ public class ZenModeHelper { } for (ZenRule rule : mConfig.automaticRules.values()) { if (rule.enabled && rule.condition != null - && rule.condition.state == Condition.STATE_TRUE + && rule.condition.state == STATE_TRUE && !rule.snoozing) { rule.dumpDebug(proto, ZenModeProto.ENABLED_ACTIVE_CONDITIONS); } @@ -1592,33 +1634,7 @@ public class ZenModeHelper { private static void dump(PrintWriter pw, String prefix, String var, ZenModeConfig config) { pw.print(prefix); pw.print(var); pw.print('='); - if (config == null) { - pw.println(config); - return; - } - pw.printf("allow(alarms=%b,media=%b,system=%b,calls=%b,callsFrom=%s,repeatCallers=%b," - + "messages=%b,messagesFrom=%s,conversations=%b,conversationsFrom=%s," - + "events=%b,reminders=%b", - config.allowAlarms, config.allowMedia, config.allowSystem, - config.allowCalls, ZenModeConfig.sourceToString(config.allowCallsFrom), - config.allowRepeatCallers, config.allowMessages, - ZenModeConfig.sourceToString(config.allowMessagesFrom), - config.allowConversations, - ZenPolicy.conversationTypeToString(config.allowConversationsFrom), - config.allowEvents, config.allowReminders); - if (Flags.modesApi()) { - pw.printf(",priorityChannels=%b", config.allowPriorityChannels); - } - pw.printf(")\n"); - pw.print(prefix); - pw.printf(" disallow(visualEffects=%s)\n", config.suppressedVisualEffects); - pw.print(prefix); pw.print(" manualRule="); pw.println(config.manualRule); - if (config.automaticRules.isEmpty()) return; - final int N = config.automaticRules.size(); - for (int i = 0; i < N; i++) { - pw.print(prefix); pw.print(i == 0 ? " automaticRules=" : " "); - pw.println(config.automaticRules.valueAt(i)); - } + pw.println(config); } public void readXml(TypedXmlPullParser parser, boolean forRestore, int userId) @@ -1629,7 +1645,9 @@ public class ZenModeHelper { if (config != null) { if (forRestore) { config.user = userId; - config.manualRule = null; // don't restore the manual rule + if (!Flags.modesUi()) { + config.manualRule = null; // don't restore the manual rule + } } // booleans to determine whether to reset the rules to the default rules @@ -1653,7 +1671,7 @@ public class ZenModeHelper { // rules with null ZenPolicies explicitly as a copy of the global policy. if (Flags.modesApi() && config.version < ZenModeConfig.XML_VERSION_MODES_API) { // Keep the manual ("global") policy that from config. - ZenPolicy manualRulePolicy = config.toZenPolicy(); + ZenPolicy manualRulePolicy = config.getZenPolicy(); if (automaticRule.zenPolicy == null) { automaticRule.zenPolicy = manualRulePolicy; } else { @@ -1842,7 +1860,7 @@ public class ZenModeHelper { */ @VisibleForTesting protected ZenPolicy getDefaultZenPolicy() { - return mDefaultConfig.toZenPolicy(); + return mDefaultConfig.getZenPolicy(); } @GuardedBy("mConfigLock") @@ -2008,7 +2026,7 @@ public class ZenModeHelper { private int computeZenMode() { synchronized (mConfigLock) { if (mConfig == null) return Global.ZEN_MODE_OFF; - if (mConfig.manualRule != null) return mConfig.manualRule.zenMode; + if (mConfig.isManualActive()) return mConfig.manualRule.zenMode; int zen = Global.ZEN_MODE_OFF; for (ZenRule automaticRule : mConfig.automaticRules.values()) { if (automaticRule.isAutomaticActive()) { @@ -2047,19 +2065,19 @@ public class ZenModeHelper { if (Flags.modesApi()) { if (useManualConfig) { // manual rule is configured using the settings stored directly in mConfig - policy.apply(mConfig.toZenPolicy()); + policy.apply(mConfig.getZenPolicy()); } else { // under modes_api flag, an active automatic rule with no specified policy // inherits the device default settings as stored in mDefaultConfig. While the // rule's policy fields should be set upon creation, this is a fallback to // catch any that may have fallen through the cracks. Log.wtf(TAG, "active automatic rule found with no specified policy: " + rule); - policy.apply( - Flags.modesUi() ? mDefaultConfig.toZenPolicy() : mConfig.toZenPolicy()); + policy.apply(Flags.modesUi() + ? mDefaultConfig.getZenPolicy() : mConfig.getZenPolicy()); } } else { - // active rule with no specified policy inherits the global config settings - policy.apply(mConfig.toZenPolicy()); + // active rule with no specified policy inherits the manual rule config settings + policy.apply(mConfig.getZenPolicy()); } } } @@ -2071,7 +2089,7 @@ public class ZenModeHelper { if (mConfig == null) return; ZenPolicy policy = new ZenPolicy(); ZenDeviceEffects.Builder deviceEffectsBuilder = new ZenDeviceEffects.Builder(); - if (mConfig.manualRule != null) { + if (mConfig.isManualActive()) { applyCustomPolicy(policy, mConfig.manualRule, true); if (Flags.modesApi()) { deviceEffectsBuilder.add(mConfig.manualRule.zenDeviceEffects); @@ -2154,7 +2172,7 @@ public class ZenModeHelper { // Should be checked before calling, but just in case. return; } - ZenPolicy defaultPolicy = mDefaultConfig.toZenPolicy(); + ZenPolicy defaultPolicy = mDefaultConfig.getZenPolicy(); for (ZenRule rule : mDefaultConfig.automaticRules.values()) { if (ZenModeConfig.DEFAULT_RULE_IDS.contains(rule.id) && rule.zenPolicy == null) { rule.zenPolicy = defaultPolicy.copy(); @@ -2335,17 +2353,17 @@ public class ZenModeHelper { final ZenModeConfig config = mConfigs.valueAt(i); events.add(FrameworkStatsLog.buildStatsEvent(DND_MODE_RULE, /* optional int32 user = 1 */ user, - /* optional bool enabled = 2 */ config.manualRule != null, + /* optional bool enabled = 2 */ config.isManualActive(), /* optional bool channels_bypassing = 3 */ config.areChannelsBypassingDnd, /* optional LoggedZenMode zen_mode = 4 */ ROOT_CONFIG, /* optional string id = 5 */ "", // empty for root config /* optional int32 uid = 6 */ Process.SYSTEM_UID, // system owns root config - /* optional DNDPolicyProto policy = 7 */ config.toZenPolicy().toProto(), + /* optional DNDPolicyProto policy = 7 */ config.getZenPolicy().toProto(), /* optional int32 rule_modified_fields = 8 */ 0, /* optional int32 policy_modified_fields = 9 */ 0, /* optional int32 device_effects_modified_fields = 10 */ 0, /* optional ActiveRuleType rule_type = 11 */ TYPE_UNKNOWN)); - if (config.manualRule != null) { + if (config.isManualActive()) { ruleToProtoLocked(user, config.manualRule, true, events); } for (ZenRule rule : config.automaticRules.values()) { diff --git a/services/core/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceManagerInternal.java b/services/core/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceManagerInternal.java index 81f11b52dcb7..07af8d042420 100644 --- a/services/core/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceManagerInternal.java +++ b/services/core/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceManagerInternal.java @@ -17,5 +17,5 @@ package com.android.server.ondeviceintelligence; public interface OnDeviceIntelligenceManagerInternal { - String getRemoteServicePackageName(); + int getInferenceServiceUid(); }
\ No newline at end of file diff --git a/services/core/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceManagerService.java b/services/core/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceManagerService.java index f540f1db6952..1a43bc27bdfc 100644 --- a/services/core/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceManagerService.java +++ b/services/core/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceManagerService.java @@ -56,6 +56,7 @@ import android.os.Bundle; import android.os.Handler; import android.os.IBinder; import android.os.ICancellationSignal; +import android.os.IRemoteCallback; import android.os.Looper; import android.os.Message; import android.os.ParcelFileDescriptor; @@ -143,6 +144,9 @@ public class OnDeviceIntelligenceManagerService extends SystemService { volatile boolean mIsServiceEnabled; @GuardedBy("mLock") + private int remoteInferenceServiceUid = -1; + + @GuardedBy("mLock") private String[] mTemporaryServiceNames; @GuardedBy("mLock") private String[] mTemporaryBroadcastKeys; @@ -174,7 +178,7 @@ public class OnDeviceIntelligenceManagerService extends SystemService { Context.ON_DEVICE_INTELLIGENCE_SERVICE, getOnDeviceIntelligenceManagerService(), /* allowIsolated = */true); LocalServices.addService(OnDeviceIntelligenceManagerInternal.class, - OnDeviceIntelligenceManagerService.this::getRemoteConfiguredPackageName); + this::getRemoteInferenceServiceUid); } @Override @@ -187,6 +191,16 @@ public class OnDeviceIntelligenceManagerService extends SystemService { mIsServiceEnabled = isServiceEnabled(); } + + //connect to remote services(if available) during boot phase. + if (phase == SystemService.PHASE_THIRD_PARTY_APPS_CAN_START) { + try { + ensureRemoteInferenceServiceInitialized(); + ensureRemoteIntelligenceServiceInitialized(); + } catch (Exception e) { + Slog.w(TAG, "Couldn't pre-start remote ondeviceintelligence services.", e); + } + } } private void onDeviceConfigChange(@NonNull Set<String> keys) { @@ -212,7 +226,7 @@ public class OnDeviceIntelligenceManagerService extends SystemService { public void getVersion(RemoteCallback remoteCallback) { Slog.i(TAG, "OnDeviceIntelligenceManagerInternal getVersion"); Objects.requireNonNull(remoteCallback); - mContext.enforceCallingOrSelfPermission( + mContext.enforceCallingPermission( Manifest.permission.USE_ON_DEVICE_INTELLIGENCE, TAG); if (!mIsServiceEnabled) { Slog.w(TAG, "Service not available"); @@ -237,7 +251,7 @@ public class OnDeviceIntelligenceManagerService extends SystemService { throws RemoteException { Slog.i(TAG, "OnDeviceIntelligenceManagerInternal getFeatures"); Objects.requireNonNull(featureCallback); - mContext.enforceCallingOrSelfPermission( + mContext.enforceCallingPermission( Manifest.permission.USE_ON_DEVICE_INTELLIGENCE, TAG); if (!mIsServiceEnabled) { Slog.w(TAG, "Service not available"); @@ -275,7 +289,7 @@ public class OnDeviceIntelligenceManagerService extends SystemService { throws RemoteException { Slog.i(TAG, "OnDeviceIntelligenceManagerInternal getFeatures"); Objects.requireNonNull(listFeaturesCallback); - mContext.enforceCallingOrSelfPermission( + mContext.enforceCallingPermission( Manifest.permission.USE_ON_DEVICE_INTELLIGENCE, TAG); if (!mIsServiceEnabled) { Slog.w(TAG, "Service not available"); @@ -319,7 +333,7 @@ public class OnDeviceIntelligenceManagerService extends SystemService { Slog.i(TAG, "OnDeviceIntelligenceManagerInternal getFeatureStatus"); Objects.requireNonNull(feature); Objects.requireNonNull(featureDetailsCallback); - mContext.enforceCallingOrSelfPermission( + mContext.enforceCallingPermission( Manifest.permission.USE_ON_DEVICE_INTELLIGENCE, TAG); if (!mIsServiceEnabled) { Slog.w(TAG, "Service not available"); @@ -363,7 +377,7 @@ public class OnDeviceIntelligenceManagerService extends SystemService { Slog.i(TAG, "OnDeviceIntelligenceManagerInternal requestFeatureDownload"); Objects.requireNonNull(feature); Objects.requireNonNull(downloadCallback); - mContext.enforceCallingOrSelfPermission( + mContext.enforceCallingPermission( Manifest.permission.USE_ON_DEVICE_INTELLIGENCE, TAG); if (!mIsServiceEnabled) { Slog.w(TAG, "Service not available"); @@ -403,7 +417,7 @@ public class OnDeviceIntelligenceManagerService extends SystemService { sanitizeInferenceParams(request); Objects.requireNonNull(tokenInfoCallback); - mContext.enforceCallingOrSelfPermission( + mContext.enforceCallingPermission( Manifest.permission.USE_ON_DEVICE_INTELLIGENCE, TAG); if (!mIsServiceEnabled) { Slog.w(TAG, "Service not available"); @@ -446,7 +460,7 @@ public class OnDeviceIntelligenceManagerService extends SystemService { Objects.requireNonNull(feature); sanitizeInferenceParams(request); Objects.requireNonNull(responseCallback); - mContext.enforceCallingOrSelfPermission( + mContext.enforceCallingPermission( Manifest.permission.USE_ON_DEVICE_INTELLIGENCE, TAG); if (!mIsServiceEnabled) { Slog.w(TAG, "Service not available"); @@ -491,7 +505,7 @@ public class OnDeviceIntelligenceManagerService extends SystemService { Objects.requireNonNull(feature); sanitizeInferenceParams(request); Objects.requireNonNull(streamingCallback); - mContext.enforceCallingOrSelfPermission( + mContext.enforceCallingPermission( Manifest.permission.USE_ON_DEVICE_INTELLIGENCE, TAG); if (!mIsServiceEnabled) { Slog.w(TAG, "Service not available"); @@ -603,7 +617,13 @@ public class OnDeviceIntelligenceManagerService extends SystemService { try { ensureRemoteIntelligenceServiceInitialized(); service.registerRemoteStorageService( - getIRemoteStorageService()); + getIRemoteStorageService(), new IRemoteCallback.Stub() { + @Override + public void sendResult(Bundle bundle) { + final int uid = Binder.getCallingUid(); + setRemoteInferenceServiceUid(uid); + } + }); mRemoteOnDeviceIntelligenceService.run( IOnDeviceIntelligenceService::notifyInferenceServiceConnected); broadcastExecutor.execute( @@ -1038,4 +1058,16 @@ public class OnDeviceIntelligenceManagerService extends SystemService { Settings.Secure.ON_DEVICE_INTELLIGENCE_IDLE_TIMEOUT_MS, TimeUnit.HOURS.toMillis(1), mContext.getUserId()); } + + private int getRemoteInferenceServiceUid() { + synchronized (mLock) { + return remoteInferenceServiceUid; + } + } + + private void setRemoteInferenceServiceUid(int remoteInferenceServiceUid) { + synchronized (mLock){ + this.remoteInferenceServiceUid = remoteInferenceServiceUid; + } + } } diff --git a/services/core/java/com/android/server/os/OWNERS b/services/core/java/com/android/server/os/OWNERS index 70be161ba9a8..e130d9c0344a 100644 --- a/services/core/java/com/android/server/os/OWNERS +++ b/services/core/java/com/android/server/os/OWNERS @@ -2,4 +2,4 @@ per-file Bugreport* = file:/platform/frameworks/native:/cmds/dumpstate/OWNERS # NativeTombstone -per-file NativeTombstone* = gaillard@google.com +per-file NativeTombstone* = benmiles@google.com diff --git a/services/core/java/com/android/server/pm/AppDataHelper.java b/services/core/java/com/android/server/pm/AppDataHelper.java index fe774aa75efc..8b72138898ad 100644 --- a/services/core/java/com/android/server/pm/AppDataHelper.java +++ b/services/core/java/com/android/server/pm/AppDataHelper.java @@ -345,7 +345,7 @@ public class AppDataHelper { final StorageManager storage = mInjector.getSystemService(StorageManager.class); for (VolumeInfo vol : storage.getWritablePrivateVolumes()) { final String volumeUuid = vol.getFsUuid(); - synchronized (mPm.mInstallLock) { + try (PackageManagerTracedLock installLock = mPm.mInstallLock.acquireLock()) { reconcileAppsDataLI(volumeUuid, userId, flags, migrateAppsData); } } @@ -505,8 +505,8 @@ public class AppDataHelper { storageFlags = StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE; } final List<String> deferPackages; - synchronized (mPm.mInstallLock) { - deferPackages = reconcileAppsDataLI(StorageManager.UUID_PRIVATE_INTERNAL, + try (PackageManagerTracedLock installLock = mPm.mInstallLock.acquireLock()) { + deferPackages = reconcileAppsDataLI(StorageManager.UUID_PRIVATE_INTERNAL, UserHandle.USER_SYSTEM, storageFlags, true /* migrateAppData */, true /* onlyCoreApps */); } @@ -541,7 +541,7 @@ public class AppDataHelper { count++; } } - synchronized (mPm.mInstallLock) { + try (PackageManagerTracedLock installLock = mPm.mInstallLock.acquireLock()) { executeBatchLI(batch); } traceLog.traceEnd(); diff --git a/services/core/java/com/android/server/pm/Computer.java b/services/core/java/com/android/server/pm/Computer.java index 482807c397ea..3528d3d96c2b 100644 --- a/services/core/java/com/android/server/pm/Computer.java +++ b/services/core/java/com/android/server/pm/Computer.java @@ -108,16 +108,20 @@ public interface Computer extends PackageDataSnapshot { default int getUsed() { return 0; } - @NonNull List<ResolveInfo> queryIntentActivitiesInternal(Intent intent, String resolvedType, + @NonNull List<ResolveInfo> queryIntentActivitiesInternal( + Intent intent, String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags, @PackageManagerInternal.PrivateResolveFlags long privateResolveFlags, - int filterCallingUid, int userId, boolean resolveForStart, boolean allowDynamicSplits); + int filterCallingUid, int callingPid, int userId, + boolean resolveForStart, boolean allowDynamicSplits); @NonNull List<ResolveInfo> queryIntentActivitiesInternal(Intent intent, String resolvedType, long flags, int filterCallingUid, int userId); @NonNull List<ResolveInfo> queryIntentActivitiesInternal(Intent intent, String resolvedType, long flags, int userId); - @NonNull List<ResolveInfo> queryIntentServicesInternal(Intent intent, String resolvedType, - long flags, int userId, int callingUid, boolean includeInstantApps); + @NonNull List<ResolveInfo> queryIntentServicesInternal( + Intent intent, String resolvedType, long flags, + int userId, int callingUid, int callingPid, + boolean includeInstantApps, boolean resolveForStart); @NonNull QueryIntentActivitiesResult queryIntentActivitiesInternalBody(Intent intent, String resolvedType, long flags, int filterCallingUid, int userId, boolean resolveForStart, boolean allowDynamicSplits, String pkgName, diff --git a/services/core/java/com/android/server/pm/ComputerEngine.java b/services/core/java/com/android/server/pm/ComputerEngine.java index 6a25f64b697d..f59ae168bdc3 100644 --- a/services/core/java/com/android/server/pm/ComputerEngine.java +++ b/services/core/java/com/android/server/pm/ComputerEngine.java @@ -500,10 +500,10 @@ public class ComputerEngine implements Computer { return mUsed; } - public final @NonNull List<ResolveInfo> queryIntentActivitiesInternal(Intent intent, - String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags, + public final @NonNull List<ResolveInfo> queryIntentActivitiesInternal( + Intent intent, String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags, @PackageManagerInternal.PrivateResolveFlags long privateResolveFlags, - int filterCallingUid, int userId, boolean resolveForStart, + int filterCallingUid, int callingPid, int userId, boolean resolveForStart, boolean allowDynamicSplits) { if (!mUserManager.exists(userId)) return Collections.emptyList(); @@ -530,6 +530,11 @@ public class ComputerEngine implements Computer { isImplicitImageCaptureIntentAndNotSetByDpc(intent, userId, resolvedType, flags)); + var args = new SaferIntentUtils.IntentArgs(intent, resolvedType, + false /* isReceiver */, resolveForStart, filterCallingUid, callingPid); + args.platformCompat = mInjector.getCompatibility(); + args.snapshot = this; + List<ResolveInfo> list = Collections.emptyList(); boolean skipPostResolution = false; if (comp != null) { @@ -583,9 +588,7 @@ public class ComputerEngine implements Computer { ri.userHandle = UserHandle.of(userId); list = new ArrayList<>(1); list.add(ri); - PackageManagerServiceUtils.applyEnforceIntentFilterMatching( - mInjector.getCompatibility(), this, list, false, intent, - resolvedType, filterCallingUid); + SaferIntentUtils.enforceIntentFilterMatching(args, list); } } } else { @@ -609,15 +612,13 @@ public class ComputerEngine implements Computer { } list = lockedResult.result; } - PackageManagerServiceUtils.applyNullActionBlocking( - mInjector.getCompatibility(), this, list, false, intent, filterCallingUid); + SaferIntentUtils.blockNullAction(args, list); } if (originalIntent != null) { // We also have to ensure all components match the original intent - PackageManagerServiceUtils.applyEnforceIntentFilterMatching( - mInjector.getCompatibility(), this, list, false, originalIntent, - resolvedType, filterCallingUid); + args.intent = originalIntent; + SaferIntentUtils.enforceIntentFilterMatching(args, list); } return skipPostResolution ? list : applyPostResolutionFilter( @@ -631,19 +632,22 @@ public class ComputerEngine implements Computer { @PackageManager.ResolveInfoFlagsBits long flags, int filterCallingUid, int userId) { return queryIntentActivitiesInternal( intent, resolvedType, flags, 0 /*privateResolveFlags*/, filterCallingUid, - userId, false /*resolveForStart*/, true /*allowDynamicSplits*/); + Process.INVALID_PID, userId, + /*resolveForStart*/ false, /*allowDynamicSplits*/ true); } public final @NonNull List<ResolveInfo> queryIntentActivitiesInternal(Intent intent, String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags, int userId) { return queryIntentActivitiesInternal( - intent, resolvedType, flags, 0 /*privateResolveFlags*/, Binder.getCallingUid(), - userId, false /*resolveForStart*/, true /*allowDynamicSplits*/); + intent, resolvedType, flags, 0 /*privateResolveFlags*/, + Binder.getCallingUid(), Process.INVALID_PID, userId, + /*resolveForStart*/ false, /*allowDynamicSplits*/ true); } - public final @NonNull List<ResolveInfo> queryIntentServicesInternal(Intent intent, - String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags, int userId, - int callingUid, boolean includeInstantApps) { + public final @NonNull List<ResolveInfo> queryIntentServicesInternal( + Intent intent, String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags, + int userId, int callingUid, int callingPid, + boolean includeInstantApps, boolean resolveForStart) { if (!mUserManager.exists(userId)) return Collections.emptyList(); enforceCrossUserOrProfilePermission(callingUid, userId, @@ -654,6 +658,11 @@ public class ComputerEngine implements Computer { flags = updateFlagsForResolve(flags, userId, callingUid, includeInstantApps, false /* isImplicitImageCaptureIntentAndNotSetByDpc */); + var args = new SaferIntentUtils.IntentArgs(intent, resolvedType, + false /* isReceiver */, resolveForStart, callingUid, callingPid); + args.platformCompat = mInjector.getCompatibility(); + args.snapshot = this; + Intent originalIntent = null; ComponentName comp = intent.getComponent(); if (comp == null) { @@ -699,23 +708,19 @@ public class ComputerEngine implements Computer { ri.serviceInfo = si; list = new ArrayList<>(1); list.add(ri); - PackageManagerServiceUtils.applyEnforceIntentFilterMatching( - mInjector.getCompatibility(), this, list, false, intent, - resolvedType, callingUid); + SaferIntentUtils.enforceIntentFilterMatching(args, list); } } } else { list = queryIntentServicesInternalBody(intent, resolvedType, flags, userId, callingUid, instantAppPkgName); - PackageManagerServiceUtils.applyNullActionBlocking( - mInjector.getCompatibility(), this, list, false, intent, callingUid); + SaferIntentUtils.blockNullAction(args, list); } if (originalIntent != null) { // We also have to ensure all components match the original intent - PackageManagerServiceUtils.applyEnforceIntentFilterMatching( - mInjector.getCompatibility(), this, list, false, originalIntent, - resolvedType, callingUid); + args.intent = originalIntent; + SaferIntentUtils.enforceIntentFilterMatching(args, list); } return list; @@ -847,8 +852,8 @@ public class ComputerEngine implements Computer { // IMPORTANT: disallow dynamic splits to avoid an infinite loop final List<ResolveInfo> result = queryIntentActivitiesInternal( failureActivityIntent, null /*resolvedType*/, 0 /*flags*/, - 0 /*privateResolveFlags*/, filterCallingUid, userId, false /*resolveForStart*/, - false /*allowDynamicSplits*/); + 0 /*privateResolveFlags*/, filterCallingUid, Process.INVALID_PID, userId, + /*resolveForStart*/ false, /*allowDynamicSplits*/ false); final int numResults = result.size(); if (numResults > 0) { for (int i = 0; i < numResults; i++) { @@ -4359,7 +4364,7 @@ public class ComputerEngine implements Computer { uid = getBaseSdkSandboxUid(); } final int callingUserId = UserHandle.getUserId(callingUid); - if (isKnownIsolatedComputeApp(uid, callingUserId)) { + if (isKnownIsolatedComputeApp(uid)) { try { uid = getIsolatedOwner(uid); } catch (IllegalStateException e) { @@ -4402,7 +4407,7 @@ public class ComputerEngine implements Computer { if (Process.isSdkSandboxUid(uid)) { uid = getBaseSdkSandboxUid(); } - if (isKnownIsolatedComputeApp(uid, callingUserId)) { + if (isKnownIsolatedComputeApp(uid)) { try { uid = getIsolatedOwner(uid); } catch (IllegalStateException e) { @@ -5804,7 +5809,7 @@ public class ComputerEngine implements Computer { } - private boolean isKnownIsolatedComputeApp(int uid, int callingUserId) { + private boolean isKnownIsolatedComputeApp(int uid) { if (!Process.isIsolatedUid(uid)) { return false; } @@ -5817,27 +5822,8 @@ public class ComputerEngine implements Computer { } OnDeviceIntelligenceManagerInternal onDeviceIntelligenceManagerInternal = mInjector.getLocalService(OnDeviceIntelligenceManagerInternal.class); - if (onDeviceIntelligenceManagerInternal == null) { - return false; - } - - String onDeviceIntelligencePackage = - onDeviceIntelligenceManagerInternal.getRemoteServicePackageName(); - if (onDeviceIntelligencePackage == null) { - return false; - } - - try { - if (getIsolatedOwner(uid) == getPackageUid(onDeviceIntelligencePackage, 0, - callingUserId)) { - return true; - } - } catch (IllegalStateException e) { - // If the owner uid doesn't exist, just use the current uid - Slog.wtf(TAG, "Expected isolated uid " + uid + " to have an owner", e); - } - - return false; + return onDeviceIntelligenceManagerInternal != null + && uid == onDeviceIntelligenceManagerInternal.getInferenceServiceUid(); } @Nullable diff --git a/services/core/java/com/android/server/pm/DeletePackageHelper.java b/services/core/java/com/android/server/pm/DeletePackageHelper.java index 47ee1d08780f..b56e119d0a19 100644 --- a/services/core/java/com/android/server/pm/DeletePackageHelper.java +++ b/services/core/java/com/android/server/pm/DeletePackageHelper.java @@ -241,7 +241,7 @@ final class DeletePackageHelper { isInstallerPackage = mPm.mSettings.isInstallerPackage(packageName); } - synchronized (mPm.mInstallLock) { + try (PackageManagerTracedLock installLock = mPm.mInstallLock.acquireLock()) { if (DEBUG_REMOVE) Slog.d(TAG, "deletePackageX: pkg=" + packageName + " user=" + userId); try (PackageFreezer freezer = mPm.freezePackageForDelete(packageName, freezeUser, deleteFlags, "deletePackageX", ApplicationExitInfo.REASON_OTHER)) { @@ -280,7 +280,7 @@ final class DeletePackageHelper { // Delete the resources here after sending the broadcast to let // other processes clean up before deleting resources. - synchronized (mPm.mInstallLock) { + try (PackageManagerTracedLock installLock = mPm.mInstallLock.acquireLock()) { if (info.mArgs != null) { mRemovePackageHelper.cleanUpResources(info.mArgs.getPackageName(), info.mArgs.getCodeFile(), info.mArgs.getInstructionSets()); @@ -435,7 +435,7 @@ final class DeletePackageHelper { public void executeDeletePackage(DeletePackageAction action, String packageName, boolean deleteCodeAndResources, @NonNull int[] allUserHandles, boolean writeSettings) throws SystemDeleteException { - synchronized (mPm.mInstallLock) { + try (PackageManagerTracedLock installLock = mPm.mInstallLock.acquireLock()) { executeDeletePackageLIF(action, packageName, deleteCodeAndResources, allUserHandles, writeSettings); } @@ -681,7 +681,7 @@ final class DeletePackageHelper { // Preserve data by setting flag flags |= PackageManager.DELETE_KEEP_DATA; } - synchronized (mPm.mInstallLock) { + try (PackageManagerTracedLock installLock = mPm.mInstallLock.acquireLock()) { deleteInstalledPackageLIF(deletedPs, UserHandle.USER_ALL, true, flags, allUserHandles, outInfo, writeSettings); } diff --git a/services/core/java/com/android/server/pm/DexOptHelper.java b/services/core/java/com/android/server/pm/DexOptHelper.java index 209cbb7f591e..e34bdc60cfdb 100644 --- a/services/core/java/com/android/server/pm/DexOptHelper.java +++ b/services/core/java/com/android/server/pm/DexOptHelper.java @@ -728,7 +728,14 @@ public final class DexOptHelper { final int compilationReason = dexManager.getCompilationReasonForInstallScenario( installRequest.getInstallScenario()); - return new DexoptOptions(packageName, compilationReason, dexoptFlags); + final AndroidPackage pkg = ps.getPkg(); + var options = new DexoptOptions(packageName, compilationReason, dexoptFlags); + if (installRequest.getDexoptCompilerFilter() != null) { + options = options.overrideCompilerFilter(installRequest.getDexoptCompilerFilter()); + } else if (pkg != null && pkg.isDebuggable()) { + options = options.overrideCompilerFilter(DexoptParams.COMPILER_FILTER_NOOP); + } + return options; } /** @@ -772,12 +779,12 @@ public final class DexOptHelper { && installRequest.getInstallSource().mInitiatingPackageName.equals("android")) : true; + // Don't skip the dexopt call if the compiler filter is "skip". Instead, call dexopt with + // the "skip" filter so that ART Service gets notified and skips dexopt itself. return (!instantApp || Global.getInt(context.getContentResolver(), Global.INSTANT_APP_DEXOPT_ENABLED, 0) != 0) && pkg != null - && !pkg.isDebuggable() && (!onIncremental) - && dexoptOptions.isCompilationEnabled() && !isApex && performDexOptForRollback; } diff --git a/services/core/java/com/android/server/pm/FreeStorageHelper.java b/services/core/java/com/android/server/pm/FreeStorageHelper.java index 66ec80f69bd1..6e84a5bbe1be 100644 --- a/services/core/java/com/android/server/pm/FreeStorageHelper.java +++ b/services/core/java/com/android/server/pm/FreeStorageHelper.java @@ -107,11 +107,9 @@ final class FreeStorageHelper { } // 4. Consider cached app data (above quotas) - synchronized (mPm.mInstallLock) { - try { - mPm.mInstaller.freeCache(volumeUuid, bytes, Installer.FLAG_FREE_CACHE_V2); - } catch (Installer.InstallerException ignored) { - } + try (PackageManagerTracedLock installLock = mPm.mInstallLock.acquireLock()) { + mPm.mInstaller.freeCache(volumeUuid, bytes, Installer.FLAG_FREE_CACHE_V2); + } catch (Installer.InstallerException ignored) { } if (file.getUsableSpace() >= bytes) return; @@ -141,12 +139,10 @@ final class FreeStorageHelper { } // 8. Consider cached app data (below quotas) - synchronized (mPm.mInstallLock) { - try { - mPm.mInstaller.freeCache(volumeUuid, bytes, - Installer.FLAG_FREE_CACHE_V2 | Installer.FLAG_FREE_CACHE_V2_DEFY_QUOTA); - } catch (Installer.InstallerException ignored) { - } + try (PackageManagerTracedLock installLock = mPm.mInstallLock.acquireLock()) { + mPm.mInstaller.freeCache(volumeUuid, bytes, + Installer.FLAG_FREE_CACHE_V2 | Installer.FLAG_FREE_CACHE_V2_DEFY_QUOTA); + } catch (Installer.InstallerException ignored) { } if (file.getUsableSpace() >= bytes) return; @@ -176,11 +172,9 @@ final class FreeStorageHelper { // 12. Clear temp install session files mPm.mInstallerService.freeStageDirs(volumeUuid); } else { - synchronized (mPm.mInstallLock) { - try { - mPm.mInstaller.freeCache(volumeUuid, bytes, 0); - } catch (Installer.InstallerException ignored) { - } + try (PackageManagerTracedLock installLock = mPm.mInstallLock.acquireLock()) { + mPm.mInstaller.freeCache(volumeUuid, bytes, 0); + } catch (Installer.InstallerException ignored) { } } if (file.getUsableSpace() >= bytes) return; @@ -197,22 +191,20 @@ final class FreeStorageHelper { final long sizeBytes = PackageManagerServiceUtils.calculateInstalledSize(resolvedPath, mPackageAbiOverride); if (sizeBytes >= 0) { - synchronized (mPm.mInstallLock) { - try { - mPm.mInstaller.freeCache(null, sizeBytes + lowThreshold, 0); - PackageInfoLite pkgInfoLite = PackageManagerServiceUtils.getMinimalPackageInfo( - mContext, pkgLite, resolvedPath, installFlags, - mPackageAbiOverride); - // The cache free must have deleted the file we downloaded to install. - if (pkgInfoLite.recommendedInstallLocation - == InstallLocationUtils.RECOMMEND_FAILED_INVALID_URI) { - pkgInfoLite.recommendedInstallLocation = - InstallLocationUtils.RECOMMEND_FAILED_INSUFFICIENT_STORAGE; - } - return pkgInfoLite.recommendedInstallLocation; - } catch (Installer.InstallerException e) { - Slog.w(TAG, "Failed to free cache", e); + try (PackageManagerTracedLock installLock = mPm.mInstallLock.acquireLock()) { + mPm.mInstaller.freeCache(null, sizeBytes + lowThreshold, 0); + PackageInfoLite pkgInfoLite = PackageManagerServiceUtils.getMinimalPackageInfo( + mContext, pkgLite, resolvedPath, installFlags, + mPackageAbiOverride); + // The cache free must have deleted the file we downloaded to install. + if (pkgInfoLite.recommendedInstallLocation + == InstallLocationUtils.RECOMMEND_FAILED_INVALID_URI) { + pkgInfoLite.recommendedInstallLocation = + InstallLocationUtils.RECOMMEND_FAILED_INSUFFICIENT_STORAGE; } + return pkgInfoLite.recommendedInstallLocation; + } catch (Installer.InstallerException e) { + Slog.w(TAG, "Failed to free cache", e); } } return recommendedInstallLocation; diff --git a/services/core/java/com/android/server/pm/IPackageManagerBase.java b/services/core/java/com/android/server/pm/IPackageManagerBase.java index f987d4ae8999..f05c54d666df 100644 --- a/services/core/java/com/android/server/pm/IPackageManagerBase.java +++ b/services/core/java/com/android/server/pm/IPackageManagerBase.java @@ -1095,7 +1095,8 @@ public abstract class IPackageManagerBase extends IPackageManager.Stub { String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags, int userId) { final int callingUid = Binder.getCallingUid(); return new ParceledListSlice<>(snapshot().queryIntentServicesInternal( - intent, resolvedType, flags, userId, callingUid, false /*includeInstantApps*/)); + intent, resolvedType, flags, userId, callingUid, Process.INVALID_PID, + /*includeInstantApps*/ false, /*resolveForStart*/ false)); } @Override @@ -1139,7 +1140,7 @@ public abstract class IPackageManagerBase extends IPackageManager.Stub { @PackageManager.ResolveInfoFlagsBits long flags, int userId) { return mResolveIntentHelper.resolveIntentInternal(snapshot(), intent, resolvedType, flags, 0 /*privateResolveFlags*/, userId, false, - Binder.getCallingUid()); + Binder.getCallingUid(), Binder.getCallingPid()); } @Override @@ -1148,7 +1149,8 @@ public abstract class IPackageManagerBase extends IPackageManager.Stub { @PackageManager.ResolveInfoFlagsBits long flags, int userId) { final int callingUid = Binder.getCallingUid(); return mResolveIntentHelper.resolveServiceInternal(snapshot(), intent, - resolvedType, flags, userId, callingUid); + resolvedType, flags, userId, callingUid, Process.INVALID_PID, + /*resolveForStart*/ false); } @Override diff --git a/services/core/java/com/android/server/pm/InstallArgs.java b/services/core/java/com/android/server/pm/InstallArgs.java index 46f9732a88e1..80016151a42e 100644 --- a/services/core/java/com/android/server/pm/InstallArgs.java +++ b/services/core/java/com/android/server/pm/InstallArgs.java @@ -58,6 +58,8 @@ final class InstallArgs { final int mDataLoaderType; final int mPackageSource; final boolean mApplicationEnabledSettingPersistent; + @Nullable + final String mDexoptCompilerFilter; // The list of instruction sets supported by this app. This is currently // only used during the rmdex() phase to clean up resources. We can get rid of this @@ -73,7 +75,7 @@ final class InstallArgs { int autoRevokePermissionsMode, String traceMethod, int traceCookie, SigningDetails signingDetails, int installReason, int installScenario, boolean forceQueryableOverride, int dataLoaderType, int packageSource, - boolean applicationEnabledSettingPersistent) { + boolean applicationEnabledSettingPersistent, String dexoptCompilerFilter) { mOriginInfo = originInfo; mMoveInfo = moveInfo; mInstallFlags = installFlags; @@ -96,5 +98,6 @@ final class InstallArgs { mDataLoaderType = dataLoaderType; mPackageSource = packageSource; mApplicationEnabledSettingPersistent = applicationEnabledSettingPersistent; + mDexoptCompilerFilter = dexoptCompilerFilter; } } diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java index 6cfa09f9adf1..6eac72d84fd4 100644 --- a/services/core/java/com/android/server/pm/InstallPackageHelper.java +++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java @@ -756,7 +756,7 @@ final class InstallPackageHelper { Process.INVALID_UID /* previousAppId */, permissionParamsBuilder.build(), userId); - synchronized (mPm.mInstallLock) { + try (PackageManagerTracedLock installLock = mPm.mInstallLock.acquireLock()) { // We don't need to freeze for a brand new install mAppDataHelper.prepareAppDataPostCommitLIF( pkgSetting, /* previousAppId= */0, new int[] { userId }); @@ -985,13 +985,11 @@ final class InstallPackageHelper { } void installPackagesTraced(List<InstallRequest> requests) { - mPm.mInstallLock.lock(); - try { + try (PackageManagerTracedLock installLock = mPm.mInstallLock.acquireLock()) { Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "installPackages"); installPackagesLI(requests); } finally { Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); - mPm.mInstallLock.unlock(); } } @@ -2592,7 +2590,8 @@ final class InstallPackageHelper { if (performDexopt) { // dexopt can take long, and ArtService doesn't require installd, so we release // the lock here and re-acquire the lock after dexopt is finished. - mPm.mInstallLock.unlock(); + PackageManagerTracedLock.RawLock installLock = mPm.mInstallLock.getRawLock(); + installLock.unlock(); try { Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "dexopt"); @@ -2612,7 +2611,7 @@ final class InstallPackageHelper { installRequest.onDexoptFinished(dexOptResult); Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); } finally { - mPm.mInstallLock.lock(); + installLock.lock(); } } } @@ -2921,7 +2920,7 @@ final class InstallPackageHelper { // propagated to all application threads. mPm.scheduleDeferredNoKillPostDelete(args); if (Flags.improveInstallDontKill()) { - synchronized (mPm.mInstallLock) { + try (var installLock = mPm.mInstallLock.acquireLock()) { PackageManagerServiceUtils.linkFilesToOldDirs(mPm.mInstaller, packageName, pkgSetting.getPath(), pkgSetting.getOldPaths()); } @@ -3068,7 +3067,7 @@ final class InstallPackageHelper { @NonNull PackageSetting stubPkgSetting) { final int parseFlags = mPm.getDefParseFlags() | ParsingPackageUtils.PARSE_CHATTY | ParsingPackageUtils.PARSE_ENFORCE_CODE; - synchronized (mPm.mInstallLock) { + try (PackageManagerTracedLock installLock = mPm.mInstallLock.acquireLock()) { final AndroidPackage pkg; try (PackageFreezer freezer = mPm.freezePackage(stubPkg.getPackageName(), UserHandle.USER_ALL, @@ -3231,12 +3230,10 @@ final class InstallPackageHelper { } // Install the system package if (DEBUG_REMOVE) Slog.d(TAG, "Re-installing system package: " + disabledPs); - try { - synchronized (mPm.mInstallLock) { - final int[] origUsers = outInfo == null ? null : outInfo.mOrigUsers; - installPackageFromSystemLIF(disabledPs.getPathString(), allUserHandles, - origUsers, writeSettings); - } + try (PackageManagerTracedLock installLock = mPm.mInstallLock.acquireLock()) { + final int[] origUsers = outInfo == null ? null : outInfo.mOrigUsers; + installPackageFromSystemLIF(disabledPs.getPathString(), allUserHandles, + origUsers, writeSettings); } catch (PackageManagerException e) { Slog.w(TAG, "Failed to restore system package:" + deletedPs.getPackageName() + ": " + e.getMessage()); @@ -3466,12 +3463,9 @@ final class InstallPackageHelper { if (ps != null) { ps.getPkgState().setUpdatedSystemApp(false); } - - try { - final File codePath = new File(pkg.getPath()); - synchronized (mPm.mInstallLock) { - initPackageTracedLI(codePath, 0, scanFlags); - } + final File codePath = new File(pkg.getPath()); + try (PackageManagerTracedLock installLock = mPm.mInstallLock.acquireLock()) { + initPackageTracedLI(codePath, 0, scanFlags); } catch (PackageManagerException e) { Slog.e(TAG, "Failed to parse updated, ex-system package: " + e.getMessage()); @@ -3679,14 +3673,12 @@ final class InstallPackageHelper { } mPm.mSettings.enableSystemPackageLPw(packageName); - try { - synchronized (mPm.mInstallLock) { - final AndroidPackage newPkg = initPackageTracedLI( - scanFile, reparseFlags, rescanFlags); - // We rescanned a stub, add it to the list of stubbed system packages - if (newPkg.isStub()) { - stubSystemApps.add(packageName); - } + try (PackageManagerTracedLock installLock = mPm.mInstallLock.acquireLock()) { + final AndroidPackage newPkg = initPackageTracedLI( + scanFile, reparseFlags, rescanFlags); + // We rescanned a stub, add it to the list of stubbed system packages + if (newPkg.isStub()) { + stubSystemApps.add(packageName); } } catch (PackageManagerException e) { Slog.e(TAG, "Failed to parse original system package: " diff --git a/services/core/java/com/android/server/pm/InstallRequest.java b/services/core/java/com/android/server/pm/InstallRequest.java index 8f51e3696108..dd2583a0db1d 100644 --- a/services/core/java/com/android/server/pm/InstallRequest.java +++ b/services/core/java/com/android/server/pm/InstallRequest.java @@ -174,13 +174,13 @@ final class InstallRequest { mUserId = params.getUser().getIdentifier(); mInstallArgs = new InstallArgs(params.mOriginInfo, params.mMoveInfo, params.mObserver, params.mInstallFlags, params.mDevelopmentInstallFlags, params.mInstallSource, - params.mVolumeUuid, params.getUser(), null /*instructionSets*/, + params.mVolumeUuid, params.getUser(), null /*instructionSets*/, params.mPackageAbiOverride, params.mPermissionStates, params.mAllowlistedRestrictedPermissions, params.mAutoRevokePermissionsMode, params.mTraceMethod, params.mTraceCookie, params.mSigningDetails, params.mInstallReason, params.mInstallScenario, params.mForceQueryableOverride, params.mDataLoaderType, params.mPackageSource, - params.mApplicationEnabledSettingPersistent); + params.mApplicationEnabledSettingPersistent, params.mDexoptCompilerFilter); mPackageLite = params.mPackageLite; mPackageMetrics = new PackageMetrics(this); mIsInstallInherit = params.mIsInherit; @@ -709,6 +709,11 @@ final class InstallRequest { return mWarnings; } + @Nullable + public String getDexoptCompilerFilter() { + return mInstallArgs != null ? mInstallArgs.mDexoptCompilerFilter : null; + } + public void setScanFlags(int scanFlags) { mScanFlags = scanFlags; } diff --git a/services/core/java/com/android/server/pm/InstallingSession.java b/services/core/java/com/android/server/pm/InstallingSession.java index b06c7cb4ac33..ccc117566989 100644 --- a/services/core/java/com/android/server/pm/InstallingSession.java +++ b/services/core/java/com/android/server/pm/InstallingSession.java @@ -102,6 +102,7 @@ class InstallingSession { @Nullable final DomainSet mPreVerifiedDomains; final boolean mHasAppMetadataFile; + @Nullable final String mDexoptCompilerFilter; // For move install InstallingSession(OriginInfo originInfo, MoveInfo moveInfo, IPackageInstallObserver2 observer, @@ -136,6 +137,7 @@ class InstallingSession { mApplicationEnabledSettingPersistent = false; mPreVerifiedDomains = null; mHasAppMetadataFile = false; + mDexoptCompilerFilter = null; } InstallingSession(int sessionId, File stagedDir, IPackageInstallObserver2 observer, @@ -172,6 +174,7 @@ class InstallingSession { mApplicationEnabledSettingPersistent = sessionParams.applicationEnabledSettingPersistent; mPreVerifiedDomains = preVerifiedDomains; mHasAppMetadataFile = hasAppMetadatafile; + mDexoptCompilerFilter = sessionParams.dexoptCompilerFilter; } @Override @@ -370,18 +373,16 @@ class InstallingSession { Slog.d(TAG, "Moving " + mMoveInfo.mPackageName + " from " + mMoveInfo.mFromUuid + " to " + mMoveInfo.mToUuid); } - synchronized (mPm.mInstallLock) { - try { - mPm.mInstaller.moveCompleteApp(mMoveInfo.mFromUuid, mMoveInfo.mToUuid, - mMoveInfo.mPackageName, mMoveInfo.mAppId, mMoveInfo.mSeInfo, - mMoveInfo.mTargetSdkVersion, mMoveInfo.mFromCodePath); - } catch (Installer.InstallerException e) { - final String errorMessage = "Failed to move app"; - request.setError(PackageManagerException.ofInternalError(errorMessage, - PackageManagerException.INTERNAL_ERROR_MOVE)); - Slog.w(TAG, errorMessage, e); - return PackageManager.INSTALL_FAILED_INTERNAL_ERROR; - } + try (PackageManagerTracedLock installLock = mPm.mInstallLock.acquireLock()) { + mPm.mInstaller.moveCompleteApp(mMoveInfo.mFromUuid, mMoveInfo.mToUuid, + mMoveInfo.mPackageName, mMoveInfo.mAppId, mMoveInfo.mSeInfo, + mMoveInfo.mTargetSdkVersion, mMoveInfo.mFromCodePath); + } catch (Installer.InstallerException e) { + final String errorMessage = "Failed to move app"; + request.setError(PackageManagerException.ofInternalError(errorMessage, + PackageManagerException.INTERNAL_ERROR_MOVE)); + Slog.w(TAG, errorMessage, e); + return PackageManager.INSTALL_FAILED_INTERNAL_ERROR; } final String toPathName = new File(mMoveInfo.mFromCodePath).getName(); diff --git a/services/core/java/com/android/server/pm/KillAppBlocker.java b/services/core/java/com/android/server/pm/KillAppBlocker.java index e2901c39e612..7f5ad9d32e6b 100644 --- a/services/core/java/com/android/server/pm/KillAppBlocker.java +++ b/services/core/java/com/android/server/pm/KillAppBlocker.java @@ -83,13 +83,13 @@ final class KillAppBlocker { } } - void waitAppProcessGone(ActivityManagerInternal mAmi, Computer snapshot, + void waitAppProcessGone(ActivityManagerInternal ami, Computer snapshot, UserManagerService userManager, String packageName) { if (!mRegistered) { return; } synchronized (this) { - if (mAmi != null) { + if (ami != null) { int[] users = userManager.getUserIds(); for (int i = 0; i < users.length; i++) { @@ -97,12 +97,16 @@ final class KillAppBlocker { final int uid = snapshot.getPackageUidInternal( packageName, MATCH_ALL, userId, Process.SYSTEM_UID); if (uid != INVALID_UID) { - if (mAmi.getUidProcessState(uid) != PROCESS_STATE_NONEXISTENT) { + if (ami.getUidProcessState(uid) != PROCESS_STATE_NONEXISTENT) { mActiveUids.add(uid); } } } } + if (mActiveUids.size() == 0) { + // no active uid + return; + } } try { diff --git a/services/core/java/com/android/server/pm/MovePackageHelper.java b/services/core/java/com/android/server/pm/MovePackageHelper.java index f5f5577329e6..f01b508ca246 100644 --- a/services/core/java/com/android/server/pm/MovePackageHelper.java +++ b/services/core/java/com/android/server/pm/MovePackageHelper.java @@ -206,7 +206,7 @@ public final class MovePackageHelper { } final PackageStats stats = new PackageStats(null, -1); - synchronized (mPm.mInstallLock) { + try (PackageManagerTracedLock installLock = mPm.mInstallLock.acquireLock()) { for (int userId : installedUserIds) { if (!getPackageSizeInfoLI(packageName, userId, stats)) { freezer.close(); diff --git a/services/core/java/com/android/server/pm/OWNERS b/services/core/java/com/android/server/pm/OWNERS index 85aee8606bc2..c10dfcb5e9bf 100644 --- a/services/core/java/com/android/server/pm/OWNERS +++ b/services/core/java/com/android/server/pm/OWNERS @@ -34,6 +34,7 @@ per-file KeySetManagerService.java = cbrubaker@google.com, nnk@google.com per-file PackageKeySetData.java = cbrubaker@google.com, nnk@google.com per-file PackageSignatures.java = cbrubaker@google.com, nnk@google.com per-file SELinuxMMAC* = alanstokes@google.com, cbrubaker@google.com, jeffv@google.com +per-file SaferIntentUtils.java = topjohnwu@google.com # shortcuts per-file LauncherAppsService.java = omakoto@google.com, yamasani@google.com, sunnygoyal@google.com, mett@google.com, pinyaoting@google.com diff --git a/services/core/java/com/android/server/pm/OtaDexoptService.java b/services/core/java/com/android/server/pm/OtaDexoptService.java index 5b326fd297cb..087b17aeadee 100644 --- a/services/core/java/com/android/server/pm/OtaDexoptService.java +++ b/services/core/java/com/android/server/pm/OtaDexoptService.java @@ -506,8 +506,8 @@ public class OtaDexoptService extends IOtaDexopt.Stub { private static class OTADexoptPackageDexOptimizer extends PackageDexOptimizer.ForcedUpdatePackageDexOptimizer { - public OTADexoptPackageDexOptimizer(Installer installer, Object installLock, - Context context) { + OTADexoptPackageDexOptimizer(Installer installer, + PackageManagerTracedLock installLock, Context context) { super(installer, installLock, context, "*otadexopt*"); } } diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java index 396fa22393e4..72bacfd784b9 100644 --- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java +++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java @@ -123,7 +123,7 @@ public class PackageDexOptimizer { // One minute over PM WATCHDOG_TIMEOUT private static final long WAKELOCK_TIMEOUT_MS = WATCHDOG_TIMEOUT + 1000 * 60; - private final Object mInstallLock; + private final PackageManagerTracedLock mInstallLock; /** * This should be accessed only through {@link #getInstallerLI()} with @@ -142,7 +142,7 @@ public class PackageDexOptimizer { private final Context mContext; private static final Random sRandom = new Random(); - PackageDexOptimizer(Installer installer, Object installLock, Context context, + PackageDexOptimizer(Installer installer, PackageManagerTracedLock installLock, Context context, String wakeLockTag) { this(new Injector() { @Override @@ -167,8 +167,8 @@ public class PackageDexOptimizer { } @VisibleForTesting - PackageDexOptimizer(@NonNull Injector injector, Installer installer, Object installLock, - Context context, String wakeLockTag) { + PackageDexOptimizer(@NonNull Injector injector, Installer installer, + PackageManagerTracedLock installLock, Context context, String wakeLockTag) { this.mContext = context; this.mInstaller = installer; this.mInstallLock = installLock; @@ -231,7 +231,7 @@ public class PackageDexOptimizer { if (!canOptimizePackage(pkg)) { return DEX_OPT_SKIPPED; } - synchronized (mInstallLock) { + try (PackageManagerTracedLock installLock = mInstallLock.acquireLock()) { final long acquireTime = acquireWakeLockLI(pkg.getUid()); try { return performDexOptLI(pkg, pkgSetting, instructionSets, @@ -868,8 +868,8 @@ public class PackageDexOptimizer { */ public static class ForcedUpdatePackageDexOptimizer extends PackageDexOptimizer { - public ForcedUpdatePackageDexOptimizer(Installer installer, Object installLock, - Context context, String wakeLockTag) { + public ForcedUpdatePackageDexOptimizer(Installer installer, + PackageManagerTracedLock installLock, Context context, String wakeLockTag) { super(installer, installLock, context, wakeLockTag); } diff --git a/services/core/java/com/android/server/pm/PackageFreezer.java b/services/core/java/com/android/server/pm/PackageFreezer.java index 0afda4598bcb..11f2059c4267 100644 --- a/services/core/java/com/android/server/pm/PackageFreezer.java +++ b/services/core/java/com/android/server/pm/PackageFreezer.java @@ -62,6 +62,12 @@ final class PackageFreezer implements AutoCloseable { PackageFreezer(String packageName, int userId, String killReason, PackageManagerService pm, int exitInfoReason, @Nullable InstallRequest request) { + this(packageName, userId, killReason, pm, exitInfoReason, request, false); + } + + PackageFreezer(String packageName, int userId, String killReason, + PackageManagerService pm, int exitInfoReason, @Nullable InstallRequest request, + boolean waitAppKilled) { mPm = pm; mPackageName = packageName; mInstallRequest = request; @@ -77,7 +83,7 @@ final class PackageFreezer implements AutoCloseable { ps = mPm.mSettings.getPackageLPr(mPackageName); } if (ps != null) { - if (Flags.waitApplicationKilled()) { + if (waitAppKilled && Flags.waitApplicationKilled()) { mPm.killApplicationSync(ps.getPackageName(), ps.getAppId(), userId, killReason, exitInfoReason); } else { diff --git a/services/core/java/com/android/server/pm/PackageHandler.java b/services/core/java/com/android/server/pm/PackageHandler.java index 68f6ca1c019f..0a0882d80cc1 100644 --- a/services/core/java/com/android/server/pm/PackageHandler.java +++ b/services/core/java/com/android/server/pm/PackageHandler.java @@ -123,19 +123,10 @@ final class PackageHandler extends Handler { } } break; case WRITE_SETTINGS: { - if (!mPm.tryWriteSettings(/*sync=*/false)) { - // Failed to write. - this.removeMessages(WRITE_SETTINGS); - mPm.scheduleWriteSettings(); - } + mPm.writeSettings(/*sync=*/false); } break; case WRITE_PACKAGE_LIST: { - int userId = msg.arg1; - if (!mPm.tryWritePackageList(userId)) { - // Failed to write. - this.removeMessages(WRITE_PACKAGE_LIST); - mPm.scheduleWritePackageList(userId); - } + mPm.writePackageList(msg.arg1); } break; case CHECK_PENDING_VERIFICATION: { final int verificationId = msg.arg1; diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java index 050d44eed2ea..b93dcdc93a82 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerService.java +++ b/services/core/java/com/android/server/pm/PackageInstallerService.java @@ -1423,7 +1423,9 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements DevicePolicyManagerInternal dpmi = LocalServices.getService(DevicePolicyManagerInternal.class); final boolean canSilentlyInstallPackage = - dpmi != null && dpmi.canSilentlyInstallPackage(callerPackageName, callingUid); + (dpmi != null && dpmi.canSilentlyInstallPackage(callerPackageName, callingUid)) + || PackageInstallerSession.isEmergencyInstallerEnabled( + versionedPackage.getPackageName(), snapshot, userId, callingUid); final PackageDeleteObserverAdapter adapter = new PackageDeleteObserverAdapter(mContext, statusReceiver, versionedPackage.getPackageName(), @@ -1445,15 +1447,6 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements .createEvent(DevicePolicyEnums.UNINSTALL_PACKAGE) .setAdmin(callerPackageName) .write(); - } else if (PackageInstallerSession.isEmergencyInstallerEnabled(callerPackageName, snapshot, - userId, callingUid)) { - // Need to clear the calling identity to get DELETE_PACKAGES permission - final long ident = Binder.clearCallingIdentity(); - try { - mPm.deletePackageVersioned(versionedPackage, adapter.getBinder(), userId, flags); - } finally { - Binder.restoreCallingIdentity(ident); - } } else { ApplicationInfo appInfo = snapshot.getApplicationInfo(callerPackageName, 0, userId); if (appInfo.targetSdkVersion >= Build.VERSION_CODES.P) { diff --git a/services/core/java/com/android/server/pm/PackageManagerInternalBase.java b/services/core/java/com/android/server/pm/PackageManagerInternalBase.java index d2b60a489de3..ed568b823159 100644 --- a/services/core/java/com/android/server/pm/PackageManagerInternalBase.java +++ b/services/core/java/com/android/server/pm/PackageManagerInternalBase.java @@ -327,11 +327,11 @@ abstract class PackageManagerInternalBase extends PackageManagerInternal { @Override @Deprecated - public final List<ResolveInfo> queryIntentReceivers(Intent intent, - String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags, - int filterCallingUid, int userId, boolean forSend) { + public final List<ResolveInfo> queryIntentReceivers( + Intent intent, String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags, + int filterCallingUid, int callingPid, int userId, boolean forSend) { return getResolveIntentHelper().queryIntentReceiversInternal(snapshot(), intent, - resolvedType, flags, userId, filterCallingUid, forSend); + resolvedType, flags, userId, filterCallingUid, callingPid, forSend); } @Override @@ -341,7 +341,7 @@ abstract class PackageManagerInternalBase extends PackageManagerInternal { int userId) { final String resolvedType = intent.resolveTypeIfNeeded(getContext().getContentResolver()); return snapshot().queryIntentServicesInternal(intent, resolvedType, flags, userId, - callingUid, false); + callingUid, Process.INVALID_PID, false, /*resolveForStart*/ false); } @Override @@ -472,32 +472,29 @@ abstract class PackageManagerInternalBase extends PackageManagerInternal { public final ResolveInfo resolveIntent(Intent intent, String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags, @PackageManagerInternal.PrivateResolveFlags long privateResolveFlags, int userId, - boolean resolveForStart, int filterCallingUid) { + boolean resolveForStart, int filterCallingUid, int callingPid) { return getResolveIntentHelper().resolveIntentInternal(snapshot(), intent, resolvedType, flags, privateResolveFlags, userId, resolveForStart, - filterCallingUid); + filterCallingUid, callingPid); } - /** - * @deprecated similar to {@link resolveIntent} but limits the matches to exported components. - */ @Override @Deprecated - public final ResolveInfo resolveIntentExported(Intent intent, String resolvedType, - @PackageManager.ResolveInfoFlagsBits long flags, - @PackageManagerInternal.PrivateResolveFlags long privateResolveFlags, int userId, - boolean resolveForStart, int filterCallingUid, int callingPid) { - return getResolveIntentHelper().resolveIntentInternal(snapshot(), - intent, resolvedType, flags, privateResolveFlags, userId, resolveForStart, - filterCallingUid, true, callingPid); + public final ResolveInfo resolveService(Intent intent, String resolvedType, + @PackageManager.ResolveInfoFlagsBits long flags, int userId, int callingUid) { + return getResolveIntentHelper().resolveServiceInternal(snapshot(), intent, + resolvedType, flags, userId, callingUid, Process.INVALID_PID, + /*resolveForStart*/ false); } @Override @Deprecated - public final ResolveInfo resolveService(Intent intent, String resolvedType, - @PackageManager.ResolveInfoFlagsBits long flags, int userId, int callingUid) { + public final ResolveInfo resolveService( + Intent intent, String resolvedType, + @PackageManager.ResolveInfoFlagsBits long flags, int userId, + int callingUid, int callingPid) { return getResolveIntentHelper().resolveServiceInternal(snapshot(), intent, - resolvedType, flags, userId, callingUid); + resolvedType, flags, userId, callingUid, callingPid, /*resolveForStart*/ true); } @Override diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 679ab65380af..66a93d7ffc47 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -485,9 +485,6 @@ public class PackageManagerService implements PackageSender, TestUtilityService */ static final long WATCHDOG_TIMEOUT = 1000*60*10; // ten minutes - // How long to wait for Lock in async writeSettings and writePackageList. - private static final long WRITE_LOCK_TIMEOUT_MS = 1000 * 10; // 10 seconds - /** * Default IncFs timeouts. Maximum values in IncFs is 1hr. * @@ -1576,7 +1573,7 @@ public class PackageManagerService implements PackageSender, TestUtilityService } } - void scheduleWritePackageList(int userId) { + void scheduleWritePackageListLocked(int userId) { invalidatePackageInfoCache(); if (!mHandler.hasMessages(WRITE_PACKAGE_LIST)) { Message msg = mHandler.obtainMessage(WRITE_PACKAGE_LIST); @@ -1628,41 +1625,22 @@ public class PackageManagerService implements PackageSender, TestUtilityService mSettings.writePackageRestrictions(dirtyUsers); } - private boolean tryUnderLock(boolean sync, long timeoutMs, Runnable runnable) { - try { - if (sync) { - mLock.lock(); - } else if (!mLock.tryLock(timeoutMs, TimeUnit.MILLISECONDS)) { - return false; - } - try { - runnable.run(); - return true; - } finally { - mLock.unlock(); - } - } catch (InterruptedException e) { - Slog.e(TAG, "Failed to obtain mLock", e); - } - return false; - } - - boolean tryWriteSettings(boolean sync) { - return tryUnderLock(sync, WRITE_LOCK_TIMEOUT_MS, () -> { + void writeSettings(boolean sync) { + synchronized (mLock) { mHandler.removeMessages(WRITE_SETTINGS); mBackgroundHandler.removeMessages(WRITE_DIRTY_PACKAGE_RESTRICTIONS); writeSettingsLPrTEMP(sync); synchronized (mDirtyUsers) { mDirtyUsers.clear(); } - }); + } } - boolean tryWritePackageList(int userId) { - return tryUnderLock(/*sync=*/false, WRITE_LOCK_TIMEOUT_MS, () -> { + void writePackageList(int userId) { + synchronized (mLock) { mHandler.removeMessages(WRITE_PACKAGE_LIST); mSettings.writePackageListLPr(userId); - }); + } } private static final Handler.Callback BACKGROUND_HANDLER_CALLBACK = new Handler.Callback() { @@ -1723,7 +1701,7 @@ public class PackageManagerService implements PackageSender, TestUtilityService (i, pm) -> new PackageDexOptimizer(i.getInstaller(), i.getInstallLock(), i.getContext(), "*dexopt*"), (i, pm) -> new DexManager(i.getContext(), i.getPackageDexOptimizer(), - i.getInstaller(), i.getInstallLock(), i.getDynamicCodeLogger()), + i.getDynamicCodeLogger()), (i, pm) -> new DynamicCodeLogger(i.getInstaller()), (i, pm) -> new ArtManagerService(i.getContext(), i.getInstaller(), i.getInstallLock()), @@ -2139,8 +2117,7 @@ public class PackageManagerService implements PackageSender, TestUtilityService mPreferredActivityHelper = new PreferredActivityHelper(this, mBroadcastHelper); mResolveIntentHelper = new ResolveIntentHelper(mContext, mPreferredActivityHelper, injector.getCompatibility(), mUserManager, mDomainVerificationManager, - mUserNeedsBadging, () -> mResolveInfo, () -> mInstantAppInstallerActivity, - injector.getBackgroundHandler()); + mUserNeedsBadging, () -> mResolveInfo, () -> mInstantAppInstallerActivity); mDexOptHelper = new DexOptHelper(this); mSuspendPackageHelper = new SuspendPackageHelper(this, mInjector, mBroadcastHelper, mProtectedPackages); @@ -2161,7 +2138,7 @@ public class PackageManagerService implements PackageSender, TestUtilityService Computer computer = mLiveComputer; // CHECKSTYLE:OFF IndentationCheck - synchronized (mInstallLock) { + try (PackageManagerTracedLock installLock = mInstallLock.acquireLock()) { // writer synchronized (mLock) { mHandler = injector.getHandler(); @@ -2695,7 +2672,8 @@ public class PackageManagerService implements PackageSender, TestUtilityService final ResolveInfo resolveInfo = mResolveIntentHelper.resolveIntentInternal(computer, intent, null, MATCH_SYSTEM_ONLY | MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE, - 0 /*privateResolveFlags*/, UserHandle.USER_SYSTEM, false, Binder.getCallingUid()); + 0 /*privateResolveFlags*/, UserHandle.USER_SYSTEM, false, + Binder.getCallingUid(), Binder.getCallingPid()); if (resolveInfo == null || mResolveActivity.name.equals(resolveInfo.getComponentInfo().name)) { throw new RuntimeException("There must be exactly one uninstaller; found " @@ -2810,7 +2788,8 @@ public class PackageManagerService implements PackageSender, TestUtilityService | (!Build.IS_DEBUGGABLE ? MATCH_SYSTEM_ONLY : 0); final Intent resolverIntent = new Intent(Intent.ACTION_RESOLVE_INSTANT_APP_PACKAGE); List<ResolveInfo> resolvers = snapshot.queryIntentServicesInternal(resolverIntent, null, - resolveFlags, UserHandle.USER_SYSTEM, callingUid, false /*includeInstantApps*/); + resolveFlags, UserHandle.USER_SYSTEM, callingUid, Process.INVALID_PID, + /*includeInstantApps*/ false, /*resolveForStart*/ false); final int N = resolvers.size(); if (N == 0) { if (DEBUG_INSTANT) { @@ -2924,17 +2903,14 @@ public class PackageManagerService implements PackageSender, TestUtilityService * Blocking call to clear all cached app data above quota. */ public void freeAllAppCacheAboveQuota(String volumeUuid) throws IOException { - synchronized (mInstallLock) { + try (PackageManagerTracedLock installLock = mInstallLock.acquireLock()) { // To avoid refactoring Installer.freeCache() and InstalldNativeService.freeCache(), // Long.MAX_VALUE is passed as an argument which is used in neither of two methods // when FLAG_FREE_CACHE_DEFY_TARGET_FREE_BYTES is set - try { - mInstaller.freeCache(volumeUuid, Long.MAX_VALUE, Installer.FLAG_FREE_CACHE_V2 - | Installer.FLAG_FREE_CACHE_DEFY_TARGET_FREE_BYTES); - } catch (InstallerException ignored) { - } + mInstaller.freeCache(volumeUuid, Long.MAX_VALUE, Installer.FLAG_FREE_CACHE_V2 + | Installer.FLAG_FREE_CACHE_DEFY_TARGET_FREE_BYTES); + } catch (InstallerException ignored) { } - return; } /** @@ -3069,9 +3045,7 @@ public class PackageManagerService implements PackageSender, TestUtilityService if (mHandler.hasMessages(WRITE_SETTINGS) || mBackgroundHandler.hasMessages(WRITE_DIRTY_PACKAGE_RESTRICTIONS) || mHandler.hasMessages(WRITE_PACKAGE_LIST)) { - while (!tryWriteSettings(/*sync=*/true)) { - Slog.wtf(TAG, "Failed to write settings on shutdown"); - } + writeSettings(/*sync=*/true); } } } @@ -3671,7 +3645,9 @@ public class PackageManagerService implements PackageSender, TestUtilityService MATCH_SYSTEM_ONLY | MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE, UserHandle.USER_SYSTEM, /* callingUid= */ Process.myUid(), - /* includeInstantApps= */ false); + Process.INVALID_PID, + /* includeInstantApps= */ false, + /* resolveForStart */ false); if (matches.size() == 1) { return matches.get(0).getComponentInfo().packageName; } else { @@ -4371,7 +4347,14 @@ public class PackageManagerService implements PackageSender, TestUtilityService public PackageFreezer freezePackage(String packageName, int userId, String killReason, int exitInfoReason, InstallRequest request) { - return new PackageFreezer(packageName, userId, killReason, this, exitInfoReason, request); + return freezePackage(packageName, userId, killReason, exitInfoReason, request, + /* waitAppKilled= */ false); + } + + private PackageFreezer freezePackage(String packageName, int userId, String killReason, + int exitInfoReason, InstallRequest request, boolean waitAppKilled) { + return new PackageFreezer(packageName, userId, killReason, this, exitInfoReason, request, + waitAppKilled); } public PackageFreezer freezePackageForDelete(String packageName, int userId, int deleteFlags, @@ -4425,13 +4408,13 @@ public class PackageManagerService implements PackageSender, TestUtilityService */ void createNewUser(int userId, @Nullable Set<String> userTypeInstallablePackages, String[] disallowedPackages) { - synchronized (mInstallLock) { + try (PackageManagerTracedLock installLock = mInstallLock.acquireLock()) { mSettings.createNewUserLI(this, mInstaller, userId, userTypeInstallablePackages, disallowedPackages); } synchronized (mLock) { scheduleWritePackageRestrictions(userId); - scheduleWritePackageList(userId); + scheduleWritePackageListLocked(userId); mAppsFilter.onUserCreated(snapshotComputer(), userId); } } @@ -4751,7 +4734,7 @@ public class PackageManagerService implements PackageSender, TestUtilityService freezePackage(packageName, UserHandle.USER_ALL, "clearApplicationProfileData", ApplicationExitInfo.REASON_OTHER, null /* request */)) { - synchronized (mInstallLock) { + try (PackageManagerTracedLock installLock = mInstallLock.acquireLock()) { mAppDataHelper.clearAppProfilesLIF(pkg); } } @@ -4796,8 +4779,9 @@ public class PackageManagerService implements PackageSender, TestUtilityService final boolean succeeded; try (PackageFreezer freezer = freezePackage(packageName, UserHandle.USER_ALL, "clearApplicationUserData", - ApplicationExitInfo.REASON_USER_REQUESTED, null /* request */)) { - synchronized (mInstallLock) { + ApplicationExitInfo.REASON_USER_REQUESTED, null /* request */, + /* waitAppKilled= */ true)) { + try (PackageManagerTracedLock installLock = mInstallLock.acquireLock()) { succeeded = clearApplicationUserDataLIF(snapshotComputer(), packageName, userId); } @@ -4940,7 +4924,7 @@ public class PackageManagerService implements PackageSender, TestUtilityService || hasAccessInstantApps == PackageManager.PERMISSION_GRANTED; } if (doClearData) { - synchronized (mInstallLock) { + try (PackageManagerTracedLock installLock = mInstallLock.acquireLock()) { final int flags = FLAG_STORAGE_DE | FLAG_STORAGE_CE | FLAG_STORAGE_EXTERNAL; // Snapshot again after mInstallLock? final AndroidPackage pkg = snapshotComputer().getPackage(packageName); @@ -8055,16 +8039,14 @@ public class PackageManagerService implements PackageSender, TestUtilityService public void reconcileSdkData(@Nullable String volumeUuid, @NonNull String packageName, @NonNull List<String> subDirNames, int userId, int appId, int previousAppId, @NonNull String seInfo, int flags) throws IOException { - synchronized (mInstallLock) { - ReconcileSdkDataArgs args = mInstaller.buildReconcileSdkDataArgs(volumeUuid, - packageName, subDirNames, userId, appId, seInfo, - flags); - args.previousAppId = previousAppId; - try { - mInstaller.reconcileSdkData(args); - } catch (InstallerException e) { - throw new IOException(e.getMessage()); - } + ReconcileSdkDataArgs args = mInstaller.buildReconcileSdkDataArgs(volumeUuid, + packageName, subDirNames, userId, appId, seInfo, + flags); + args.previousAppId = previousAppId; + try (PackageManagerTracedLock installLock = mInstallLock.acquireLock()) { + mInstaller.reconcileSdkData(args); + } catch (InstallerException e) { + throw new IOException(e.getMessage()); } } diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceCompilerMapping.java b/services/core/java/com/android/server/pm/PackageManagerServiceCompilerMapping.java index e2ddba5188fa..819a75c8c128 100644 --- a/services/core/java/com/android/server/pm/PackageManagerServiceCompilerMapping.java +++ b/services/core/java/com/android/server/pm/PackageManagerServiceCompilerMapping.java @@ -18,7 +18,7 @@ package com.android.server.pm; import android.os.SystemProperties; -import com.android.server.pm.dex.DexoptOptions; +import com.android.server.art.model.DexoptParams; import dalvik.system.DexFile; @@ -71,7 +71,7 @@ public class PackageManagerServiceCompilerMapping { private static String getAndCheckValidity(int reason) { String sysPropValue = SystemProperties.get(getSystemPropertyName(reason)); if (sysPropValue == null || sysPropValue.isEmpty() - || !(sysPropValue.equals(DexoptOptions.COMPILER_FILTER_NOOP) + || !(sysPropValue.equals(DexoptParams.COMPILER_FILTER_NOOP) || DexFile.isValidCompilerFilter(sysPropValue))) { throw new IllegalStateException("Value \"" + sysPropValue +"\" not valid " + "(reason " + REASON_STRINGS[reason] + ")"); diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java index 6700f00a8856..ff8abf879487 100644 --- a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java +++ b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java @@ -26,14 +26,11 @@ import static android.system.OsConstants.O_RDWR; import static com.android.internal.content.NativeLibraryHelper.LIB64_DIR_NAME; import static com.android.internal.content.NativeLibraryHelper.LIB_DIR_NAME; -import static com.android.internal.util.FrameworkStatsLog.UNSAFE_INTENT_EVENT_REPORTED__EVENT_TYPE__EXPLICIT_INTENT_FILTER_UNMATCH; -import static com.android.internal.util.FrameworkStatsLog.UNSAFE_INTENT_EVENT_REPORTED__EVENT_TYPE__NULL_ACTION_MATCH; import static com.android.server.LocalManagerRegistry.ManagerNotFoundException; import static com.android.server.pm.PackageInstallerSession.APP_METADATA_FILE_ACCESS_MODE; import static com.android.server.pm.PackageInstallerSession.getAppMetadataSizeLimit; import static com.android.server.pm.PackageManagerService.COMPRESSED_EXTENSION; import static com.android.server.pm.PackageManagerService.DEBUG_COMPRESSION; -import static com.android.server.pm.PackageManagerService.DEBUG_INTENT_MATCHING; import static com.android.server.pm.PackageManagerService.DEBUG_PREFERRED; import static com.android.server.pm.PackageManagerService.DEFAULT_FILE_ACCESS_MODE; import static com.android.server.pm.PackageManagerService.DEFAULT_NATIVE_LIBRARY_FILE_ACCESS_MODE; @@ -48,23 +45,15 @@ import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UserIdInt; -import android.app.ActivityManager; -import android.compat.annotation.ChangeId; -import android.compat.annotation.Disabled; -import android.compat.annotation.Overridable; import android.content.Context; import android.content.Intent; -import android.content.IntentFilter; -import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; -import android.content.pm.ComponentInfo; import android.content.pm.PackageInfoLite; import android.content.pm.PackageInstaller; import android.content.pm.PackageManager; import android.content.pm.PackageManager.Property; import android.content.pm.PackagePartitions; import android.content.pm.ResolveInfo; -import android.content.pm.ServiceInfo; import android.content.pm.Signature; import android.content.pm.SigningDetails; import android.content.pm.parsing.ApkLiteParseUtils; @@ -83,7 +72,6 @@ import android.os.FileUtils; import android.os.Process; import android.os.SELinux; import android.os.SystemProperties; -import android.os.UserHandle; import android.os.incremental.IncrementalManager; import android.os.incremental.IncrementalStorage; import android.os.incremental.V4Signature; @@ -99,29 +87,21 @@ import android.util.AtomicFile; import android.util.Base64; import android.util.DisplayMetrics; import android.util.Log; -import android.util.LogPrinter; -import android.util.Printer; import android.util.Slog; import android.util.proto.ProtoOutputStream; import com.android.internal.content.InstallLocationUtils; import com.android.internal.content.NativeLibraryHelper; -import com.android.internal.pm.pkg.component.ParsedMainComponent; import com.android.internal.util.ArrayUtils; import com.android.internal.util.FastPrintWriter; import com.android.internal.util.HexDump; import com.android.server.EventLogTags; -import com.android.server.IntentResolver; import com.android.server.LocalManagerRegistry; import com.android.server.Watchdog; -import com.android.server.am.ActivityManagerUtils; -import com.android.server.compat.PlatformCompat; import com.android.server.pm.dex.PackageDexUsage; import com.android.server.pm.pkg.AndroidPackage; import com.android.server.pm.pkg.AndroidPackageSplit; import com.android.server.pm.pkg.PackageStateInternal; -import com.android.server.pm.resolution.ComponentResolverApi; -import com.android.server.pm.snapshot.PackageDataSnapshot; import com.android.server.pm.verify.domain.DomainVerificationManagerInternal; import dalvik.system.VMRuntime; @@ -173,11 +153,6 @@ public class PackageManagerServiceUtils { public static final Predicate<PackageStateInternal> REMOVE_IF_NULL_PKG = pkgSetting -> pkgSetting.getPkg() == null; - // This is a horrible hack to workaround b/240373119, specifically for fixing the T branch. - // A proper fix should be implemented in master instead. - public static final ThreadLocal<Boolean> DISABLE_ENFORCE_INTENTS_TO_MATCH_INTENT_FILTERS = - ThreadLocal.withInitial(() -> false); - /** * Type used with {@link #canJoinSharedUserId(String, SigningDetails, SharedUserSetting, int)} * when the package attempting to join the sharedUserId is a new install. @@ -202,22 +177,6 @@ public class PackageManagerServiceUtils { public @interface SharedUserIdJoinType {} /** - * Intents sent from apps targeting Android V and above will stop resolving to components with - * non matching intent filters, even when explicitly setting a component name, unless the - * target components are in the same app as the calling app. - * - * When an app registers an exported component in its manifest and adds an <intent-filter>, - * the component can be started by any intent - even those that do not match the intent filter. - * This has proven to be something that many developers find counterintuitive. - * Without checking the intent when the component is started, in some circumstances this can - * allow 3P apps to trigger internal-only functionality. - */ - @Overridable - @ChangeId - @Disabled - private static final long ENFORCE_INTENTS_TO_MATCH_INTENT_FILTERS = 161252188; - - /** * The initial enabled state of the cache before other checks are done. */ private static final boolean DEFAULT_PACKAGE_PARSER_CACHE_ENABLED = true; @@ -1204,166 +1163,6 @@ public class PackageManagerServiceUtils { return (ps.getFlags() & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0; } - private static ParsedMainComponent componentInfoToComponent( - ComponentInfo info, ComponentResolverApi resolver, boolean isReceiver) { - if (info instanceof ActivityInfo) { - if (isReceiver) { - return resolver.getReceiver(info.getComponentName()); - } else { - return resolver.getActivity(info.getComponentName()); - } - } else if (info instanceof ServiceInfo) { - return resolver.getService(info.getComponentName()); - } else { - // This shall never happen - throw new IllegalArgumentException("Unsupported component type"); - } - } - - /** - * Under the correct conditions, remove components if the intent has null action. - * - * `compat` and `snapshot` may be null when this method is called in ActivityManagerService - * CTS tests. The code in this method will properly avoid control flows using these arguments. - */ - public static void applyNullActionBlocking( - @Nullable PlatformCompat compat, @Nullable PackageDataSnapshot snapshot, - List componentList, boolean isReceiver, Intent intent, int filterCallingUid) { - if (ActivityManager.canAccessUnexportedComponents(filterCallingUid)) return; - - final Computer computer = (Computer) snapshot; - ComponentResolverApi resolver = null; - - final boolean enforce = android.security.Flags.blockNullActionIntents() - && (compat == null || compat.isChangeEnabledByUidInternal( - IntentFilter.BLOCK_NULL_ACTION_INTENTS, filterCallingUid)); - - for (int i = componentList.size() - 1; i >= 0; --i) { - boolean match = true; - - Object c = componentList.get(i); - if (c instanceof ResolveInfo resolveInfo) { - if (computer == null) { - // PackageManagerService is not started - return; - } - if (resolver == null) { - resolver = computer.getComponentResolver(); - } - final ParsedMainComponent comp = componentInfoToComponent( - resolveInfo.getComponentInfo(), resolver, isReceiver); - if (comp != null && !comp.getIntents().isEmpty() && intent.getAction() == null) { - match = false; - } - } else if (c instanceof IntentFilter) { - if (intent.getAction() == null) { - match = false; - } - } - - if (!match) { - ActivityManagerUtils.logUnsafeIntentEvent( - UNSAFE_INTENT_EVENT_REPORTED__EVENT_TYPE__NULL_ACTION_MATCH, - filterCallingUid, intent, null, enforce); - if (enforce) { - Slog.w(TAG, "Blocking intent with null action: " + intent); - componentList.remove(i); - } - } - } - } - - public static void applyEnforceIntentFilterMatching( - PlatformCompat compat, PackageDataSnapshot snapshot, - List<ResolveInfo> resolveInfos, boolean isReceiver, - Intent intent, String resolvedType, int filterCallingUid) { - if (DISABLE_ENFORCE_INTENTS_TO_MATCH_INTENT_FILTERS.get()) return; - - // Do not enforce filter matching when the caller is system or root - if (ActivityManager.canAccessUnexportedComponents(filterCallingUid)) return; - - final Computer computer = (Computer) snapshot; - final ComponentResolverApi resolver = computer.getComponentResolver(); - - final Printer logPrinter = DEBUG_INTENT_MATCHING - ? new LogPrinter(Log.VERBOSE, TAG, Log.LOG_ID_SYSTEM) - : null; - - final boolean enforceMatch = android.security.Flags.enforceIntentFilterMatch() - && compat.isChangeEnabledByUidInternal( - ENFORCE_INTENTS_TO_MATCH_INTENT_FILTERS, filterCallingUid); - final boolean blockNullAction = android.security.Flags.blockNullActionIntents() - && compat.isChangeEnabledByUidInternal( - IntentFilter.BLOCK_NULL_ACTION_INTENTS, filterCallingUid); - - for (int i = resolveInfos.size() - 1; i >= 0; --i) { - final ComponentInfo info = resolveInfos.get(i).getComponentInfo(); - - // Skip filter matching when the caller is targeting the same app - if (UserHandle.isSameApp(filterCallingUid, info.applicationInfo.uid)) { - continue; - } - - final ParsedMainComponent comp = componentInfoToComponent(info, resolver, isReceiver); - - if (comp == null || comp.getIntents().isEmpty()) { - continue; - } - - Boolean match = null; - - if (intent.getAction() == null) { - ActivityManagerUtils.logUnsafeIntentEvent( - UNSAFE_INTENT_EVENT_REPORTED__EVENT_TYPE__NULL_ACTION_MATCH, - filterCallingUid, intent, resolvedType, enforceMatch && blockNullAction); - if (blockNullAction) { - // Skip intent filter matching if blocking null action - match = false; - } - } - - if (match == null) { - // Check if any intent filter matches - for (int j = 0, size = comp.getIntents().size(); j < size; ++j) { - IntentFilter intentFilter = comp.getIntents().get(j).getIntentFilter(); - if (IntentResolver.intentMatchesFilter(intentFilter, intent, resolvedType)) { - match = true; - break; - } - } - } - - // At this point, the value `match` has the following states: - // null : Intent does not match any intent filter - // false: Null action intent detected AND blockNullAction == true - // true : The intent matches at least one intent filter - - if (match == null) { - ActivityManagerUtils.logUnsafeIntentEvent( - UNSAFE_INTENT_EVENT_REPORTED__EVENT_TYPE__EXPLICIT_INTENT_FILTER_UNMATCH, - filterCallingUid, intent, resolvedType, enforceMatch); - match = false; - } - - if (!match) { - // All non-matching intents has to be marked accordingly - if (android.security.Flags.enforceIntentFilterMatch()) { - intent.addExtendedFlags(Intent.EXTENDED_FLAG_FILTER_MISMATCH); - } - if (enforceMatch) { - Slog.w(TAG, "Intent does not match component's intent filter: " + intent); - Slog.w(TAG, "Access blocked: " + comp.getComponentName()); - if (DEBUG_INTENT_MATCHING) { - Slog.v(TAG, "Component intent filters:"); - comp.getIntents().forEach(f -> f.getIntentFilter().dump(logPrinter, " ")); - Slog.v(TAG, "-----------------------------"); - } - resolveInfos.remove(i); - } - } - } - } - /** * Do NOT use for intent resolution filtering. That should be done with * {@link DomainVerificationManagerInternal#filterToApprovedApp(Intent, List, int, Function)}. diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java index c40563f2abeb..7a53fe78c1bf 100644 --- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java +++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java @@ -121,6 +121,8 @@ import com.android.server.LocalManagerRegistry; import com.android.server.LocalServices; import com.android.server.SystemConfig; import com.android.server.art.ArtManagerLocal; +import com.android.server.art.ReasonMapping; +import com.android.server.art.model.DexoptParams; import com.android.server.pm.PackageManagerShellCommandDataLoader.Metadata; import com.android.server.pm.permission.LegacyPermissionManagerInternal; import com.android.server.pm.permission.PermissionAllowlist; @@ -330,6 +332,8 @@ class PackageManagerShellCommand extends ShellCommand { return runGetOemPermissions(); case "get-signature-permission-allowlist": return runGetSignaturePermissionAllowlist(); + case "get-shared-uid-allowlist": + return runGetSharedUidAllowlist(); case "trim-caches": return runTrimCaches(); case "create-user": @@ -2944,6 +2948,9 @@ class PackageManagerShellCommand extends ShellCommand { case "system-ext": allowlist = permissionAllowlist.getSystemExtSignatureAppAllowlist(); break; + case "apex": + allowlist = permissionAllowlist.getApexSignatureAppAllowlist(); + break; default: getErrPrintWriter().println("Error: unknown partition: " + partition); return 1; @@ -2970,6 +2977,20 @@ class PackageManagerShellCommand extends ShellCommand { return 0; } + private int runGetSharedUidAllowlist() { + final var allowlist = SystemConfig.getInstance().getPackageToSharedUidAllowList(); + final var pw = getOutPrintWriter(); + final var allowlistSize = allowlist.size(); + for (var allowlistIndex = 0; allowlistIndex < allowlistSize; allowlistIndex++) { + final var packageName = allowlist.keyAt(allowlistIndex); + final var sharedUserName = allowlist.valueAt(allowlistIndex); + pw.print(packageName); + pw.print(" "); + pw.println(sharedUserName); + } + return 0; + } + private int runTrimCaches() throws RemoteException { String size = getNextArg(); if (size == null) { @@ -3570,6 +3591,14 @@ class PackageManagerShellCommand extends ShellCommand { case "--package-source": sessionParams.setPackageSource(Integer.parseInt(getNextArg())); break; + case "--dexopt-compiler-filter": + sessionParams.dexoptCompilerFilter = getNextArgRequired(); + // An early check that throws IllegalArgumentException if the compiler filter is + // invalid. + new DexoptParams.Builder(ReasonMapping.REASON_INSTALL) + .setCompilerFilter(sessionParams.dexoptCompilerFilter) + .build(); + break; default: throw new IllegalArgumentException("Unknown option " + opt); } @@ -4716,6 +4745,7 @@ class PackageManagerShellCommand extends ShellCommand { pw.println(" [--force-uuid internal|UUID] [--pkg PACKAGE] [-S BYTES]"); pw.println(" [--apex] [--non-staged] [--force-non-staged]"); pw.println(" [--staged-ready-timeout TIMEOUT] [--ignore-dexopt-profile]"); + pw.println(" [--dexopt-compiler-filter FILTER]"); pw.println(" [PATH [SPLIT...]|-]"); pw.println(" Install an application. Must provide the apk data to install, either as"); pw.println(" file path(s) or '-' to read from stdin. Options are:"); @@ -4762,13 +4792,19 @@ class PackageManagerShellCommand extends ShellCommand { pw.println(" milliseconds for pre-reboot verification to complete when"); pw.println(" performing staged install. This flag is used to alter the waiting"); pw.println(" time. You can skip the waiting time by specifying a TIMEOUT of '0'"); - pw.println(" --ignore-dexopt-profile: If set, all profiles are ignored by dexopt"); + pw.println(" --ignore-dexopt-profile: if set, all profiles are ignored by dexopt"); pw.println(" during the installation, including the profile in the DM file and"); pw.println(" the profile embedded in the APK file. If an invalid profile is"); pw.println(" provided during installation, no warning will be reported by `adb"); pw.println(" install`."); pw.println(" This option does not affect later dexopt operations (e.g.,"); pw.println(" background dexopt and manual `pm compile` invocations)."); + pw.println(" --dexopt-compiler-filter: the target compiler filter for dexopt during"); + pw.println(" the installation. The filter actually used may be different."); + pw.println(" Valid values: one of the values documented in"); + pw.println(" https://source.android.com/docs/core/runtime/configure" + + "#compiler_filters"); + pw.println(" or 'skip'"); pw.println(""); pw.println(" install-existing [--user USER_ID|all|current]"); pw.println(" [--instant] [--full] [--wait] [--restrict-permissions] PACKAGE"); @@ -4907,7 +4943,10 @@ class PackageManagerShellCommand extends ShellCommand { pw.println(""); pw.println(" get-signature-permission-allowlist PARTITION"); pw.println(" Prints the signature permission allowlist for a partition."); - pw.println(" PARTITION is one of system, vendor, product and system-ext"); + pw.println(" PARTITION is one of system, vendor, product, system-ext and apex"); + pw.println(""); + pw.println(" get-shared-uid-allowlist"); + pw.println(" Prints the shared UID allowlist."); pw.println(""); pw.println(" trim-caches DESIRED_FREE_SPACE [internal|UUID]"); pw.println(" Trim cache files to reach the given free space."); diff --git a/services/core/java/com/android/server/pm/PackageManagerTracedLock.java b/services/core/java/com/android/server/pm/PackageManagerTracedLock.java index 303b8b9dd817..d9604b3c396b 100644 --- a/services/core/java/com/android/server/pm/PackageManagerTracedLock.java +++ b/services/core/java/com/android/server/pm/PackageManagerTracedLock.java @@ -16,6 +16,7 @@ package com.android.server.pm; +import android.annotation.NonNull; import android.annotation.Nullable; import android.util.Slog; @@ -25,32 +26,77 @@ import java.util.concurrent.locks.ReentrantLock; * This is a unique class that is used as the PackageManager lock. It can be targeted for lock * injection, similar to {@link ActivityManagerGlobalLock}. */ -public class PackageManagerTracedLock extends ReentrantLock { +public class PackageManagerTracedLock implements AutoCloseable { private static final String TAG = "PackageManagerTracedLock"; private static final boolean DEBUG = false; - @Nullable private final String mLockName; + private @NonNull final RawLock mLock; public PackageManagerTracedLock(@Nullable String lockName) { - mLockName = lockName; + mLock = new RawLock(lockName); } public PackageManagerTracedLock() { this(null); } - @Override - public void lock() { - super.lock(); - if (DEBUG && mLockName != null) { - Slog.i(TAG, "locked " + mLockName); - } + /** + * Use this method to acquire the lock. Use it with try-with-resources to make sure the lock is + * released afterwards. Example usage: + * <pre> + * PackageManagerTracedLock myInstallLock = new PackageManagerTracedLock(); + * try (PackageManagerTracedLock installLock = myInstallLock.acquireLock()) { + * // do stuff under lock + * } + * </pre> + */ + public PackageManagerTracedLock acquireLock() { + mLock.lock(); + return this; } + /** + * Obtain the raw lock for fine control of lock state. Example usage: + * <pre> + * PackageManagerTracedLock myInstallLock = new PackageManagerTracedLock(); + * PackageManagerTracedLock.RawLock rawLock = myInstallLock.getRawLock(); + * rawLock.lock(); + * // do stuff under lock + * rawLock.unlock(); + * </pre> + */ + public RawLock getRawLock() { + return mLock; + } + + /** + * Release the lock if it's held by the current thread. + * If you use {@link #acquireLock()} using try-with-resources, there's no need to call this + * method explicitly. + */ @Override - public void unlock() { - super.unlock(); - if (DEBUG && mLockName != null) { - Slog.i(TAG, "unlocked " + mLockName); + public void close() { + mLock.unlock(); + } + + public static class RawLock extends ReentrantLock { + @Nullable private final String mLockName; + RawLock(@Nullable String lockName) { + mLockName = lockName; + } + @Override + public void lock() { + super.lock(); + if (DEBUG && mLockName != null) { + Slog.i(TAG, "locked " + mLockName); + } + } + + @Override + public void unlock() { + super.unlock(); + if (DEBUG && mLockName != null) { + Slog.i(TAG, "unlocked " + mLockName); + } } } } diff --git a/services/core/java/com/android/server/pm/PackageMetrics.java b/services/core/java/com/android/server/pm/PackageMetrics.java index 2081f73e7336..0acadb129f2b 100644 --- a/services/core/java/com/android/server/pm/PackageMetrics.java +++ b/services/core/java/com/android/server/pm/PackageMetrics.java @@ -16,7 +16,12 @@ package com.android.server.pm; +import static android.content.pm.PackageManager.GET_RESOLVED_FILTER; +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_DISABLED_COMPONENTS; import static android.os.Process.INVALID_UID; +import static android.os.Process.SYSTEM_UID; import android.annotation.IntDef; import android.annotation.NonNull; @@ -25,6 +30,7 @@ import android.annotation.UserIdInt; import android.app.ActivityManager; import android.app.admin.SecurityLog; import android.content.ComponentName; +import android.content.Intent; import android.content.pm.ActivityInfo; import android.content.pm.Flags; import android.content.pm.PackageManager; @@ -376,7 +382,30 @@ final class PackageMetrics { mCallingUid = callingUid; } - public boolean isSameComponent(ActivityInfo activityInfo) { + public boolean isLauncherActivity(@NonNull Computer computer, @UserIdInt int userId) { + if (mIsForWholeApp) { + return false; + } + // Query the launcher activities with the package name. + final Intent intent = new Intent(Intent.ACTION_MAIN); + intent.addCategory(Intent.CATEGORY_LAUNCHER); + intent.setPackage(mPackageName); + List<ResolveInfo> launcherActivities = computer.queryIntentActivitiesInternal( + intent, null, + MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE | GET_RESOLVED_FILTER + | MATCH_DISABLED_COMPONENTS, SYSTEM_UID, userId); + final int launcherActivitiesSize = + launcherActivities != null ? launcherActivities.size() : 0; + for (int i = 0; i < launcherActivitiesSize; i++) { + ResolveInfo resolveInfo = launcherActivities.get(i); + if (isSameComponent(resolveInfo.activityInfo)) { + return true; + } + } + return false; + } + + private boolean isSameComponent(ActivityInfo activityInfo) { if (activityInfo == null) { return false; } @@ -395,25 +424,13 @@ final class PackageMetrics { Slog.d(TAG, "Fail to report component state due to metrics is empty"); return; } - boolean isLauncher = false; - final List<ResolveInfo> resolveInfosForLauncher = getHomeActivitiesResolveInfoAsUser( - computer, userId); - final int resolveInfosForLauncherSize = - resolveInfosForLauncher != null ? resolveInfosForLauncher.size() : 0; final int metricsSize = componentStateMetricsList.size(); for (int i = 0; i < metricsSize; i++) { final ComponentStateMetrics componentStateMetrics = componentStateMetricsList.get(i); - for (int j = 0; j < resolveInfosForLauncherSize; j++) { - ResolveInfo resolveInfo = resolveInfosForLauncher.get(j); - if (componentStateMetrics.isSameComponent(resolveInfo.activityInfo)) { - isLauncher = true; - break; - } - } reportComponentStateChanged(componentStateMetrics.mUid, componentStateMetrics.mComponentOldState, componentStateMetrics.mComponentNewState, - isLauncher, + componentStateMetrics.isLauncherActivity(computer, userId), componentStateMetrics.mIsForWholeApp, componentStateMetrics.mCallingUid); } @@ -424,10 +441,4 @@ final class PackageMetrics { FrameworkStatsLog.write(FrameworkStatsLog.COMPONENT_STATE_CHANGED_REPORTED, uid, componentOldState, componentNewState, isLauncher, isForWholeApp, callingUid); } - - private static List<ResolveInfo> getHomeActivitiesResolveInfoAsUser(@NonNull Computer computer, - @UserIdInt int userId) { - return computer.queryIntentActivitiesInternal(computer.getHomeIntent(), /* resolvedType */ - null, /* flags */ 0, userId); - } } diff --git a/services/core/java/com/android/server/pm/RemovePackageHelper.java b/services/core/java/com/android/server/pm/RemovePackageHelper.java index 02bd3cca5eb4..2f2c451ca7f3 100644 --- a/services/core/java/com/android/server/pm/RemovePackageHelper.java +++ b/services/core/java/com/android/server/pm/RemovePackageHelper.java @@ -83,7 +83,7 @@ final class RemovePackageHelper { } public void removeCodePath(File codePath) { - synchronized (mPm.mInstallLock) { + try (PackageManagerTracedLock installLock = mPm.mInstallLock.acquireLock()) { removeCodePathLI(codePath); } } @@ -133,7 +133,7 @@ final class RemovePackageHelper { // Used for system apps only public void removePackage(AndroidPackage pkg, boolean chatty) { - synchronized (mPm.mInstallLock) { + try (PackageManagerTracedLock installLock = mPm.mInstallLock.acquireLock()) { removePackageLI(pkg, chatty); } } @@ -356,7 +356,7 @@ final class RemovePackageHelper { // Called to clean up disabled system packages public void removePackageData(final PackageSetting deletedPs, @NonNull int[] allUserHandles) { - synchronized (mPm.mInstallLock) { + try (PackageManagerTracedLock installLock = mPm.mInstallLock.acquireLock()) { removePackageDataLIF(deletedPs, UserHandle.USER_ALL, allUserHandles, new PackageRemovedInfo(), /* flags= */ 0, /* writeSettings= */ false); } @@ -488,7 +488,7 @@ final class RemovePackageHelper { void cleanUpResources(@Nullable String packageName, @Nullable File codeFile, @Nullable String[] instructionSets) { - synchronized (mPm.mInstallLock) { + try (PackageManagerTracedLock installLock = mPm.mInstallLock.acquireLock()) { cleanUpResourcesLI(codeFile, instructionSets); } if (packageName == null) { @@ -528,7 +528,7 @@ final class RemovePackageHelper { final File codeFile = new File(Environment.getDataAppDirectory(volumeUuid), toPathName); Slog.d(TAG, "Cleaning up " + packageName + " on " + volumeUuid); final int[] userIds = mPm.mUserManager.getUserIds(); - synchronized (mPm.mInstallLock) { + try (PackageManagerTracedLock installLock = mPm.mInstallLock.acquireLock()) { // Clean up both app data and code // All package moves are frozen until finished diff --git a/services/core/java/com/android/server/pm/ResolveIntentHelper.java b/services/core/java/com/android/server/pm/ResolveIntentHelper.java index 309a4481e9de..69490a81fc86 100644 --- a/services/core/java/com/android/server/pm/ResolveIntentHelper.java +++ b/services/core/java/com/android/server/pm/ResolveIntentHelper.java @@ -18,7 +18,6 @@ package com.android.server.pm; import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER; -import static com.android.internal.util.FrameworkStatsLog.UNSAFE_INTENT_EVENT_REPORTED__EVENT_TYPE__INTERNAL_NON_EXPORTED_COMPONENT_MATCH; import static com.android.server.pm.PackageManagerService.DEBUG_INSTANT; import static com.android.server.pm.PackageManagerService.DEBUG_INTENT_MATCHING; import static com.android.server.pm.PackageManagerService.TAG; @@ -26,8 +25,6 @@ import static com.android.server.pm.PackageManagerService.TAG; import android.annotation.NonNull; import android.annotation.UserIdInt; import android.app.ActivityManager; -import android.app.ActivityManagerInternal; -import android.app.IUnsafeIntentStrictModeCallback; import android.app.PendingIntent; import android.content.ComponentName; import android.content.ContentResolver; @@ -45,7 +42,6 @@ import android.content.pm.ProviderInfo; import android.content.pm.ResolveInfo; import android.os.Binder; import android.os.Bundle; -import android.os.Handler; import android.os.Process; import android.os.RemoteException; import android.os.Trace; @@ -56,9 +52,6 @@ import android.util.Slog; import com.android.internal.app.ResolverActivity; import com.android.internal.util.ArrayUtils; -import com.android.server.LocalServices; -import com.android.server.am.ActivityManagerService; -import com.android.server.am.ActivityManagerUtils; import com.android.server.compat.PlatformCompat; import com.android.server.pm.pkg.AndroidPackage; import com.android.server.pm.pkg.PackageStateInternal; @@ -89,8 +82,6 @@ final class ResolveIntentHelper { private final Supplier<ResolveInfo> mResolveInfoSupplier; @NonNull private final Supplier<ActivityInfo> mInstantAppInstallerActivitySupplier; - @NonNull - private final Handler mHandler; ResolveIntentHelper(@NonNull Context context, @NonNull PreferredActivityHelper preferredActivityHelper, @@ -98,8 +89,7 @@ final class ResolveIntentHelper { @NonNull DomainVerificationManagerInternal domainVerificationManager, @NonNull UserNeedsBadgingCache userNeedsBadgingCache, @NonNull Supplier<ResolveInfo> resolveInfoSupplier, - @NonNull Supplier<ActivityInfo> instantAppInstallerActivitySupplier, - @NonNull Handler handler) { + @NonNull Supplier<ActivityInfo> instantAppInstallerActivitySupplier) { mContext = context; mPreferredActivityHelper = preferredActivityHelper; mPlatformCompat = platformCompat; @@ -108,47 +98,6 @@ final class ResolveIntentHelper { mUserNeedsBadging = userNeedsBadgingCache; mResolveInfoSupplier = resolveInfoSupplier; mInstantAppInstallerActivitySupplier = instantAppInstallerActivitySupplier; - mHandler = handler; - } - - private static void filterNonExportedComponents(Intent intent, int filterCallingUid, - int callingPid, List<ResolveInfo> query, PlatformCompat platformCompat, - String resolvedType, Computer computer, Handler handler) { - if (query == null - || intent.getPackage() != null - || intent.getComponent() != null - || ActivityManager.canAccessUnexportedComponents(filterCallingUid)) { - return; - } - AndroidPackage caller = computer.getPackage(filterCallingUid); - String callerPackage = caller == null ? "Not specified" : caller.getPackageName(); - ActivityManagerInternal activityManagerInternal = LocalServices - .getService(ActivityManagerInternal.class); - final IUnsafeIntentStrictModeCallback callback = activityManagerInternal - .getRegisteredStrictModeCallback(callingPid); - for (int i = query.size() - 1; i >= 0; i--) { - if (!query.get(i).getComponentInfo().exported) { - boolean hasToBeExportedToMatch = platformCompat.isChangeEnabledByUid( - ActivityManagerService.IMPLICIT_INTENTS_ONLY_MATCH_EXPORTED_COMPONENTS, - filterCallingUid); - ActivityManagerUtils.logUnsafeIntentEvent( - UNSAFE_INTENT_EVENT_REPORTED__EVENT_TYPE__INTERNAL_NON_EXPORTED_COMPONENT_MATCH, - filterCallingUid, intent, resolvedType, hasToBeExportedToMatch); - if (callback != null) { - handler.post(() -> { - try { - callback.onImplicitIntentMatchedInternalComponent(intent.cloneFilter()); - } catch (RemoteException e) { - activityManagerInternal.unregisterStrictModeCallback(callingPid); - } - }); - } - if (!hasToBeExportedToMatch) { - return; - } - query.remove(i); - } - } } /** @@ -159,22 +108,7 @@ final class ResolveIntentHelper { public ResolveInfo resolveIntentInternal(Computer computer, Intent intent, String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags, @PackageManagerInternal.PrivateResolveFlags long privateResolveFlags, int userId, - boolean resolveForStart, int filterCallingUid) { - return resolveIntentInternal(computer, intent, resolvedType, flags, - privateResolveFlags, userId, resolveForStart, filterCallingUid, false, 0); - } - - /** - * Normally instant apps can only be resolved when they're visible to the caller. - * However, if {@code resolveForStart} is {@code true}, all instant apps are visible - * since we need to allow the system to start any installed application. - * Allows picking exported components only. - */ - public ResolveInfo resolveIntentInternal(Computer computer, Intent intent, String resolvedType, - @PackageManager.ResolveInfoFlagsBits long flags, - @PackageManagerInternal.PrivateResolveFlags long privateResolveFlags, int userId, - boolean resolveForStart, int filterCallingUid, boolean exportedComponentsOnly, - int callingPid) { + boolean resolveForStart, int filterCallingUid, int callingPid) { try { Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "resolveIntent"); @@ -188,14 +122,15 @@ final class ResolveIntentHelper { Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "queryIntentActivities"); final List<ResolveInfo> query = computer.queryIntentActivitiesInternal(intent, - resolvedType, flags, privateResolveFlags, filterCallingUid, userId, - resolveForStart, true /*allowDynamicSplits*/); - if (exportedComponentsOnly) { - filterNonExportedComponents(intent, filterCallingUid, callingPid, query, - mPlatformCompat, resolvedType, computer, mHandler); - } + resolvedType, flags, privateResolveFlags, filterCallingUid, callingPid, + userId, resolveForStart, /*allowDynamicSplits*/ true); Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); + var args = new SaferIntentUtils.IntentArgs(intent, resolvedType, + false /* isReceiver */, resolveForStart, filterCallingUid, callingPid); + args.platformCompat = mPlatformCompat; + SaferIntentUtils.filterNonExportedComponents(args, query); + final boolean queryMayBeFiltered = UserHandle.getAppId(filterCallingUid) >= Process.FIRST_APPLICATION_UID && !resolveForStart; @@ -331,6 +266,7 @@ final class ResolveIntentHelper { throws RemoteException { Objects.requireNonNull(packageName); final int callingUid = Binder.getCallingUid(); + final int callingPid = Binder.getCallingPid(); computer.enforceCrossUserPermission(callingUid, userId, false /* requireFullPermission */, false /* checkShell */, "get launch intent sender for package"); final int packageUid = computer.getPackageUid(callingPackage, 0 /* flags */, userId); @@ -346,17 +282,17 @@ final class ResolveIntentHelper { intentToResolve.setPackage(packageName); final ContentResolver contentResolver = mContext.getContentResolver(); String resolvedType = intentToResolve.resolveTypeIfNeeded(contentResolver); - List<ResolveInfo> ris = computer.queryIntentActivitiesInternal(intentToResolve, resolvedType, - 0 /* flags */, 0 /* privateResolveFlags */, callingUid, userId, - true /* resolveForStart */, false /* allowDynamicSplits */); + List<ResolveInfo> ris = computer.queryIntentActivitiesInternal(intentToResolve, + resolvedType, 0 /* flags */, 0 /* privateResolveFlags */, callingUid, callingPid, + userId, /* resolveForStart */ true, /* allowDynamicSplits */false); if (ris == null || ris.size() <= 0) { intentToResolve.removeCategory(Intent.CATEGORY_INFO); intentToResolve.addCategory(Intent.CATEGORY_LAUNCHER); intentToResolve.setPackage(packageName); resolvedType = intentToResolve.resolveTypeIfNeeded(contentResolver); ris = computer.queryIntentActivitiesInternal(intentToResolve, resolvedType, - 0 /* flags */, 0 /* privateResolveFlags */, callingUid, userId, - true /* resolveForStart */, false /* allowDynamicSplits */); + 0 /* flags */, 0 /* privateResolveFlags */, callingUid, callingPid, + userId, /* resolveForStart */ true, /* allowDynamicSplits */false); } final Intent intent = new Intent(intentToResolve); @@ -390,16 +326,17 @@ final class ResolveIntentHelper { String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags, int userId, int queryingUid) { return queryIntentReceiversInternal(computer, intent, resolvedType, flags, userId, - queryingUid, false); + queryingUid, Process.INVALID_PID, false); } /** - * @see PackageManagerInternal#queryIntentReceivers(Intent, String, long, int, int, boolean) + * @see PackageManagerInternal#queryIntentReceivers */ @NonNull - public List<ResolveInfo> queryIntentReceiversInternal(Computer computer, Intent intent, - String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags, int userId, - int filterCallingUid, boolean forSend) { + public List<ResolveInfo> queryIntentReceiversInternal( + Computer computer, Intent intent, String resolvedType, + @PackageManager.ResolveInfoFlagsBits long flags, int userId, + int filterCallingUid, int callingPid, boolean forSend) { if (!mUserManager.exists(userId)) return Collections.emptyList(); // The identity used to filter the receiver components final int queryingUid = forSend ? Process.SYSTEM_UID : filterCallingUid; @@ -421,6 +358,12 @@ final class ResolveIntentHelper { } final ComponentResolverApi componentResolver = computer.getComponentResolver(); List<ResolveInfo> list = Collections.emptyList(); + + var args = new SaferIntentUtils.IntentArgs(intent, resolvedType, + true /* isReceiver */, forSend, filterCallingUid, callingPid); + args.platformCompat = mPlatformCompat; + args.snapshot = computer; + if (comp != null) { final ActivityInfo ai = computer.getReceiverInfo(comp, flags, userId); if (ai != null) { @@ -457,9 +400,7 @@ final class ResolveIntentHelper { ri.activityInfo = ai; list = new ArrayList<>(1); list.add(ri); - PackageManagerServiceUtils.applyEnforceIntentFilterMatching( - mPlatformCompat, computer, list, true, intent, - resolvedType, filterCallingUid); + SaferIntentUtils.enforceIntentFilterMatching(args, list); } } } else { @@ -479,15 +420,13 @@ final class ResolveIntentHelper { list = result; } } - PackageManagerServiceUtils.applyNullActionBlocking( - mPlatformCompat, computer, list, true, intent, filterCallingUid); + SaferIntentUtils.blockNullAction(args, list); } if (originalIntent != null) { // We also have to ensure all components match the original intent - PackageManagerServiceUtils.applyEnforceIntentFilterMatching( - mPlatformCompat, computer, - list, true, originalIntent, resolvedType, filterCallingUid); + args.intent = originalIntent; + SaferIntentUtils.enforceIntentFilterMatching(args, list); } return computer.applyPostResolutionFilter(list, instantAppPkgName, false, queryingUid, @@ -495,14 +434,16 @@ final class ResolveIntentHelper { } - public ResolveInfo resolveServiceInternal(@NonNull Computer computer, Intent intent, + public ResolveInfo resolveServiceInternal( + @NonNull Computer computer, Intent intent, String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags, int userId, - int callingUid) { + int callingUid, int callingPid, boolean resolveForStart) { if (!mUserManager.exists(userId)) return null; flags = computer.updateFlagsForResolve(flags, userId, callingUid, false /*includeInstantApps*/, false /* isImplicitImageCaptureIntentAndNotSetByDpc */); List<ResolveInfo> query = computer.queryIntentServicesInternal( - intent, resolvedType, flags, userId, callingUid, false /*includeInstantApps*/); + intent, resolvedType, flags, userId, callingUid, callingPid, + /*includeInstantApps*/ false, resolveForStart); if (query != null) { if (query.size() >= 1) { // If there is more than one service with the same priority, @@ -705,7 +646,8 @@ final class ResolveIntentHelper { if (comp == null) { ri = resolveIntentInternal(computer, sintent, specificTypes != null ? specificTypes[i] : null, flags, - 0 /*privateResolveFlags*/, userId, false, Binder.getCallingUid()); + 0 /*privateResolveFlags*/, userId, false, + Binder.getCallingUid(), Binder.getCallingPid()); if (ri == null) { continue; } diff --git a/services/core/java/com/android/server/pm/SaferIntentUtils.java b/services/core/java/com/android/server/pm/SaferIntentUtils.java new file mode 100644 index 000000000000..8175321ea293 --- /dev/null +++ b/services/core/java/com/android/server/pm/SaferIntentUtils.java @@ -0,0 +1,397 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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 com.android.internal.util.FrameworkStatsLog.UNSAFE_INTENT_EVENT_REPORTED__EVENT_TYPE__EXPLICIT_INTENT_FILTER_UNMATCH; +import static com.android.internal.util.FrameworkStatsLog.UNSAFE_INTENT_EVENT_REPORTED__EVENT_TYPE__INTERNAL_NON_EXPORTED_COMPONENT_MATCH; +import static com.android.internal.util.FrameworkStatsLog.UNSAFE_INTENT_EVENT_REPORTED__EVENT_TYPE__NULL_ACTION_MATCH; +import static com.android.server.pm.PackageManagerService.DEBUG_INTENT_MATCHING; +import static com.android.server.pm.PackageManagerService.TAG; + +import android.annotation.Nullable; +import android.app.ActivityManager; +import android.app.ActivityManagerInternal; +import android.compat.annotation.ChangeId; +import android.compat.annotation.Disabled; +import android.compat.annotation.EnabledAfter; +import android.compat.annotation.Overridable; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.pm.ActivityInfo; +import android.content.pm.ComponentInfo; +import android.content.pm.ResolveInfo; +import android.content.pm.ServiceInfo; +import android.os.Build; +import android.os.Process; +import android.os.UserHandle; +import android.security.Flags; +import android.util.Log; +import android.util.LogPrinter; +import android.util.Printer; +import android.util.Slog; + +import com.android.internal.pm.pkg.component.ParsedMainComponent; +import com.android.internal.util.FrameworkStatsLog; +import com.android.server.IntentResolver; +import com.android.server.LocalServices; +import com.android.server.am.BroadcastFilter; +import com.android.server.compat.PlatformCompat; +import com.android.server.pm.resolution.ComponentResolverApi; +import com.android.server.pm.snapshot.PackageDataSnapshot; + +import java.util.List; + +/** + * The way Safer Intent is implemented is to add several "hooks" into PMS's intent + * resolution process, and in some cases, AMS's runtime receiver resolution. Think of + * these methods as resolution "passes", where they post-process the resolved component list. + * <p> + * Here are the 4 main hooking entry points for each component type: + * <ul> + * <li>Activity: {@link ComputerEngine#queryIntentActivitiesInternal} or + * {@link ResolveIntentHelper#resolveIntentInternal}</li> + * <li>Service: {@link Computer#queryIntentServicesInternal}</li> + * <li>Static BroadcastReceivers: {@link ResolveIntentHelper#queryIntentReceiversInternal}</li> + * <li>Runtime BroadcastReceivers: + * {@link com.android.server.am.ActivityManagerService#broadcastIntentLockedTraced}</li> + * </ul> + */ +public class SaferIntentUtils { + + // This is a hack to workaround b/240373119; a proper fix should be implemented instead. + public static final ThreadLocal<Boolean> DISABLE_ENFORCE_INTENTS_TO_MATCH_INTENT_FILTERS = + ThreadLocal.withInitial(() -> false); + + /** + * Apps targeting Android U and above will need to export components in order to invoke them + * through implicit intents. + * <p> + * If a component is not exported and invoked, it will be removed from the list of receivers. + * This applies specifically to activities and broadcasts. + */ + @ChangeId + @Overridable + @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.TIRAMISU) + private static final long IMPLICIT_INTENTS_ONLY_MATCH_EXPORTED_COMPONENTS = 229362273; + + /** + * Intents sent from apps enabling this feature will stop resolving to components with + * non matching intent filters, even when explicitly setting a component name, unless the + * target components are in the same app as the calling app. + * <p> + * When an app registers an exported component in its manifest and adds <intent-filter>s, + * the component can be started by any intent - even those that do not match the intent filter. + * This has proven to be something that many developers find counterintuitive. + * Without checking the intent when the component is started, in some circumstances this can + * allow 3P apps to trigger internal-only functionality. + */ + @ChangeId + @Overridable + @Disabled + private static final long ENFORCE_INTENTS_TO_MATCH_INTENT_FILTERS = 161252188; + + private static ParsedMainComponent infoToComponent( + ComponentInfo info, ComponentResolverApi resolver, boolean isReceiver) { + if (info instanceof ActivityInfo) { + if (isReceiver) { + return resolver.getReceiver(info.getComponentName()); + } else { + return resolver.getActivity(info.getComponentName()); + } + } else if (info instanceof ServiceInfo) { + return resolver.getService(info.getComponentName()); + } else { + // This shall never happen + throw new IllegalArgumentException("Unsupported component type"); + } + } + + /** + * Helper method to report an unsafe intent event. + */ + public static void reportUnsafeIntentEvent( + int event, int callingUid, int callingPid, + Intent intent, String resolvedType, boolean blocked) { + String[] categories = intent.getCategories() == null ? new String[0] + : intent.getCategories().toArray(String[]::new); + String component = intent.getComponent() == null ? null + : intent.getComponent().flattenToString(); + FrameworkStatsLog.write(FrameworkStatsLog.UNSAFE_INTENT_EVENT_REPORTED, + event, + callingUid, + component, + intent.getPackage(), + intent.getAction(), + categories, + resolvedType, + intent.getScheme(), + blocked); + LocalServices.getService(ActivityManagerInternal.class) + .triggerUnsafeIntentStrictMode(callingPid, event, intent); + } + + /** + * All the relevant information about an intent resolution transaction. + */ + public static class IntentArgs { + + /* Several system_server components */ + + @Nullable + public PlatformCompat platformCompat; + @Nullable + public PackageDataSnapshot snapshot; + + /* Information about the intent itself */ + + public Intent intent; + public String resolvedType; + public boolean isReceiver; + + /* Information about the caller */ + + // Whether this intent resolution transaction is actually for starting a component and + // not only for querying matching components. + // This information is required because we only want to log and trigger strict mode + // violations on unsafe intent events when the caller actually wants to start something. + public boolean resolveForStart; + public int callingUid; + // When resolveForStart is false, callingPid does not matter as this is only used + // to lookup the strict mode violation callback. + public int callingPid; + + public IntentArgs( + Intent intent, String resolvedType, boolean isReceiver, + boolean resolveForStart, int callingUid, int callingPid) { + this.isReceiver = isReceiver; + this.intent = intent; + this.resolvedType = resolvedType; + this.resolveForStart = resolveForStart; + this.callingUid = callingUid; + this.callingPid = resolveForStart ? callingPid : Process.INVALID_PID; + } + + boolean isChangeEnabled(long changeId) { + return platformCompat == null || platformCompat.isChangeEnabledByUidInternal( + changeId, callingUid); + } + + void reportEvent(int event, boolean blocked) { + if (resolveForStart) { + SaferIntentUtils.reportUnsafeIntentEvent( + event, callingUid, callingPid, intent, resolvedType, blocked); + } + } + } + + /** + * Remove components if the intent has null action. + * <p> + * Because blocking null action applies to all resolution cases, it has to be hooked + * in all 4 locations. Note, for component intent resolution in Activity, Service, + * and static BroadcastReceivers, null action blocking is actually handled within + * {@link #enforceIntentFilterMatching}; we only need to handle it in this method when + * the intent does not specify an explicit component name. + * <p> + * `compat` and `snapshot` may be null when this method is called in ActivityManagerService + * CTS tests. The code in this method shall properly avoid control flows using these arguments. + */ + public static void blockNullAction(IntentArgs args, List componentList) { + if (ActivityManager.canAccessUnexportedComponents(args.callingUid)) return; + + final Computer computer = (Computer) args.snapshot; + ComponentResolverApi resolver = null; + + final boolean enforce = Flags.blockNullActionIntents() + && args.isChangeEnabled(IntentFilter.BLOCK_NULL_ACTION_INTENTS); + + for (int i = componentList.size() - 1; i >= 0; --i) { + boolean match = true; + + Object c = componentList.get(i); + if (c instanceof ResolveInfo resolveInfo) { + if (computer == null) { + // PackageManagerService is not started + return; + } + if (resolver == null) { + resolver = computer.getComponentResolver(); + } + final ParsedMainComponent comp = infoToComponent( + resolveInfo.getComponentInfo(), resolver, args.isReceiver); + if (!comp.getIntents().isEmpty() && args.intent.getAction() == null) { + match = false; + } + } else if (c instanceof IntentFilter) { + if (args.intent.getAction() == null) { + match = false; + } + } + + if (!match) { + args.reportEvent( + UNSAFE_INTENT_EVENT_REPORTED__EVENT_TYPE__NULL_ACTION_MATCH, enforce); + if (enforce) { + Slog.w(TAG, "Blocking intent with null action: " + args.intent); + componentList.remove(i); + } + } + } + } + + /** + * Remove ResolveInfos that does not match the provided component intent. + * <p> + * Component intents cannot refer to a runtime registered BroadcastReceiver, so we only + * need to hook into the rest of the 3 entry points. Please note, this method also + * handles null action blocking for all component intents; do not go through an additional + * {@link #blockNullAction} pass! + */ + public static void enforceIntentFilterMatching( + IntentArgs args, List<ResolveInfo> resolveInfos) { + if (DISABLE_ENFORCE_INTENTS_TO_MATCH_INTENT_FILTERS.get()) return; + + // Do not enforce filter matching when the caller is system or root + if (ActivityManager.canAccessUnexportedComponents(args.callingUid)) return; + + final Computer computer = (Computer) args.snapshot; + final ComponentResolverApi resolver = computer.getComponentResolver(); + + final Printer logPrinter = DEBUG_INTENT_MATCHING + ? new LogPrinter(Log.VERBOSE, TAG, Log.LOG_ID_SYSTEM) + : null; + + final boolean enforceMatch = Flags.enforceIntentFilterMatch() + && args.isChangeEnabled(ENFORCE_INTENTS_TO_MATCH_INTENT_FILTERS); + final boolean blockNullAction = Flags.blockNullActionIntents() + && args.isChangeEnabled(IntentFilter.BLOCK_NULL_ACTION_INTENTS); + + for (int i = resolveInfos.size() - 1; i >= 0; --i) { + final ComponentInfo info = resolveInfos.get(i).getComponentInfo(); + + // Skip filter matching when the caller is targeting the same app + if (UserHandle.isSameApp(args.callingUid, info.applicationInfo.uid)) { + continue; + } + + final ParsedMainComponent comp = infoToComponent(info, resolver, args.isReceiver); + + if (comp == null || comp.getIntents().isEmpty()) { + continue; + } + + Boolean match = null; + + if (args.intent.getAction() == null) { + args.reportEvent( + UNSAFE_INTENT_EVENT_REPORTED__EVENT_TYPE__NULL_ACTION_MATCH, + enforceMatch && blockNullAction); + if (blockNullAction) { + // Skip intent filter matching if blocking null action + match = false; + } + } + + if (match == null) { + // Check if any intent filter matches + for (int j = 0, size = comp.getIntents().size(); j < size; ++j) { + IntentFilter intentFilter = comp.getIntents().get(j).getIntentFilter(); + if (IntentResolver.intentMatchesFilter( + intentFilter, args.intent, args.resolvedType)) { + match = true; + break; + } + } + } + + // At this point, the value `match` has the following states: + // null : Intent does not match any intent filter + // false: Null action intent detected AND blockNullAction == true + // true : The intent matches at least one intent filter + + if (match == null) { + args.reportEvent( + UNSAFE_INTENT_EVENT_REPORTED__EVENT_TYPE__EXPLICIT_INTENT_FILTER_UNMATCH, + enforceMatch); + match = false; + } + + if (!match) { + // All non-matching intents has to be marked accordingly + if (Flags.enforceIntentFilterMatch()) { + args.intent.addExtendedFlags(Intent.EXTENDED_FLAG_FILTER_MISMATCH); + } + if (enforceMatch) { + Slog.w(TAG, "Intent does not match component's intent filter: " + args.intent); + Slog.w(TAG, "Access blocked: " + comp.getComponentName()); + if (DEBUG_INTENT_MATCHING) { + Slog.v(TAG, "Component intent filters:"); + comp.getIntents().forEach(f -> f.getIntentFilter().dump(logPrinter, " ")); + Slog.v(TAG, "-----------------------------"); + } + resolveInfos.remove(i); + } + } + } + } + + /** + * Filter non-exported components from the componentList if intent is implicit. + * <p> + * Implicit intents cannot be used to start Services since API 21+. + * Implicit broadcasts cannot be delivered to static BroadcastReceivers since API 25+. + * So we only need to hook into Activity and runtime BroadcastReceiver intent resolution. + */ + public static void filterNonExportedComponents(IntentArgs args, List componentList) { + if (componentList == null + || args.intent.getPackage() != null + || args.intent.getComponent() != null + || ActivityManager.canAccessUnexportedComponents(args.callingUid)) { + return; + } + + final boolean enforce = + args.isChangeEnabled(IMPLICIT_INTENTS_ONLY_MATCH_EXPORTED_COMPONENTS); + boolean violated = false; + + for (int i = componentList.size() - 1; i >= 0; i--) { + Object c = componentList.get(i); + if (c instanceof ResolveInfo resolveInfo) { + if (resolveInfo.getComponentInfo().exported) { + continue; + } + } else if (c instanceof BroadcastFilter broadcastFilter) { + if (broadcastFilter.exported) { + continue; + } + } else { + continue; + } + violated = true; + if (!enforce) { + break; + } + componentList.remove(i); + } + + if (violated) { + args.reportEvent( + UNSAFE_INTENT_EVENT_REPORTED__EVENT_TYPE__INTERNAL_NON_EXPORTED_COMPONENT_MATCH, + enforce); + } + } +} diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java index 8d6d774a9959..39565526f33e 100644 --- a/services/core/java/com/android/server/pm/Settings.java +++ b/services/core/java/com/android/server/pm/Settings.java @@ -934,6 +934,7 @@ public final class Settings implements Watchable, Snappable, ResilientAtomicFile ret.setTargetSdkVersion(p.getTargetSdkVersion()); ret.setRestrictUpdateHash(p.getRestrictUpdateHash()); ret.setScannedAsStoppedSystemApp(p.isScannedAsStoppedSystemApp()); + ret.setInstallSource(p.getInstallSource()); } mDisabledSysPackages.remove(name); return ret; diff --git a/services/core/java/com/android/server/pm/SharedLibrariesImpl.java b/services/core/java/com/android/server/pm/SharedLibrariesImpl.java index ec8af2ecd070..929fccce5265 100644 --- a/services/core/java/com/android/server/pm/SharedLibrariesImpl.java +++ b/services/core/java/com/android/server/pm/SharedLibrariesImpl.java @@ -729,7 +729,7 @@ public final class SharedLibrariesImpl implements SharedLibrariesRead, Watchable if (!pkgSetting.isSystem() || pkgSetting.isUpdatedSystemApp()) { final int flags = pkgSetting.isUpdatedSystemApp() ? PackageManager.DELETE_KEEP_DATA : 0; - synchronized (mPm.mInstallLock) { + try (var installLock = mPm.mInstallLock.acquireLock()) { mDeletePackageHelper.deletePackageLIF(pkg.getPackageName(), null, true, mPm.mUserManager.getUserIds(), flags, new PackageRemovedInfo(), true); diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java index 1cd77ffcedaa..78d8002f185a 100644 --- a/services/core/java/com/android/server/pm/ShortcutService.java +++ b/services/core/java/com/android/server/pm/ShortcutService.java @@ -320,10 +320,10 @@ public class ShortcutService extends IShortcutService.Stub { private final Handler mHandler; - @GuardedBy("mServiceLock") + @GuardedBy("itself") private final ArrayList<ShortcutChangeListener> mListeners = new ArrayList<>(1); - @GuardedBy("mServiceLock") + @GuardedBy("itself") private final ArrayList<LauncherApps.ShortcutChangeCallback> mShortcutChangeCallbacks = new ArrayList<>(1); @@ -1847,7 +1847,9 @@ public class ShortcutService extends IShortcutService.Stub { return; } - copy = new ArrayList<>(mListeners); + synchronized (mListeners) { + copy = new ArrayList<>(mListeners); + } } // Note onShortcutChanged() needs to be called with the system service permissions. for (int i = copy.size() - 1; i >= 0; i--) { @@ -1872,8 +1874,9 @@ public class ShortcutService extends IShortcutService.Stub { if (!isUserUnlockedL(userId)) { return; } - - copy = new ArrayList<>(mShortcutChangeCallbacks); + synchronized (mShortcutChangeCallbacks) { + copy = new ArrayList<>(mShortcutChangeCallbacks); + } } for (int i = copy.size() - 1; i >= 0; i--) { if (!CollectionUtils.isEmpty(changedList)) { @@ -3429,7 +3432,7 @@ public class ShortcutService extends IShortcutService.Stub { @Override public void addListener(@NonNull ShortcutChangeListener listener) { - synchronized (mServiceLock) { + synchronized (mListeners) { mListeners.add(Objects.requireNonNull(listener)); } } @@ -3437,7 +3440,7 @@ public class ShortcutService extends IShortcutService.Stub { @Override public void addShortcutChangeCallback( @NonNull LauncherApps.ShortcutChangeCallback callback) { - synchronized (mServiceLock) { + synchronized (mShortcutChangeCallbacks) { mShortcutChangeCallbacks.add(Objects.requireNonNull(callback)); } } diff --git a/services/core/java/com/android/server/pm/StorageEventHelper.java b/services/core/java/com/android/server/pm/StorageEventHelper.java index cef3244c9068..951986fbd71a 100644 --- a/services/core/java/com/android/server/pm/StorageEventHelper.java +++ b/services/core/java/com/android/server/pm/StorageEventHelper.java @@ -156,7 +156,7 @@ public final class StorageEventHelper extends StorageEventListener { freezers.add(mPm.freezePackage(ps.getPackageName(), UserHandle.USER_ALL, "loadPrivatePackagesInner", ApplicationExitInfo.REASON_OTHER, null /* request */)); - synchronized (mPm.mInstallLock) { + try (PackageManagerTracedLock installLock = mPm.mInstallLock.acquireLock()) { final AndroidPackage pkg; try { pkg = mPm.initPackageTracedLI( @@ -194,7 +194,7 @@ public final class StorageEventHelper extends StorageEventListener { try { sm.prepareUserStorage(volumeUuid, user.id, flags); - synchronized (mPm.mInstallLock) { + try (PackageManagerTracedLock installLock = mPm.mInstallLock.acquireLock()) { appDataHelper.reconcileAppsDataLI(volumeUuid, user.id, flags, true /* migrateAppData */); } @@ -247,7 +247,7 @@ public final class StorageEventHelper extends StorageEventListener { final int[] userIds = mPm.mUserManager.getUserIds(); final ArrayList<AndroidPackage> unloaded = new ArrayList<>(); - synchronized (mPm.mInstallLock) { + try (PackageManagerTracedLock installLock = mPm.mInstallLock.acquireLock()) { synchronized (mPm.mLock) { final List<? extends PackageStateInternal> packages = mPm.mSettings.getVolumePackagesLPr(volumeUuid); diff --git a/services/core/java/com/android/server/pm/UserDataPreparer.java b/services/core/java/com/android/server/pm/UserDataPreparer.java index ef32485567f8..041f2d3a459d 100644 --- a/services/core/java/com/android/server/pm/UserDataPreparer.java +++ b/services/core/java/com/android/server/pm/UserDataPreparer.java @@ -66,7 +66,7 @@ class UserDataPreparer { * Prepare storage areas for given user on all mounted devices. */ void prepareUserData(UserInfo userInfo, int flags) { - synchronized (mInstallLock) { + try (PackageManagerTracedLock installLock = mInstallLock.acquireLock()) { final StorageManager storage = mContext.getSystemService(StorageManager.class); /* * Internal storage must be prepared before adoptable storage, since the user's volume @@ -157,7 +157,7 @@ class UserDataPreparer { * Destroy storage areas for given user on all mounted devices. */ void destroyUserData(int userId, int flags) { - synchronized (mInstallLock) { + try (PackageManagerTracedLock installLock = mInstallLock.acquireLock()) { final StorageManager storage = mContext.getSystemService(StorageManager.class); /* * Volume destruction order isn't really important, but to avoid any weird issues we @@ -262,7 +262,7 @@ class UserDataPreparer { } if (destroyUser) { - synchronized (mInstallLock) { + try (PackageManagerTracedLock installLock = mInstallLock.acquireLock()) { destroyUserDataLI(volumeUuid, userId, StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE); } diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java index 29acbcda7c4f..db94d0e8b0ba 100644 --- a/services/core/java/com/android/server/pm/UserManagerService.java +++ b/services/core/java/com/android/server/pm/UserManagerService.java @@ -281,6 +281,10 @@ public class UserManagerService extends IUserManager.Stub { private static final String RESTRICTIONS_FILE_PREFIX = "res_"; private static final String XML_SUFFIX = ".xml"; + private static final String CUSTOM_BIOMETRIC_PROMPT_LOGO_RES_ID_KEY = "custom_logo_res_id"; + private static final String CUSTOM_BIOMETRIC_PROMPT_LOGO_DESCRIPTION_KEY = + "custom_logo_description"; + private static final int ALLOWED_FLAGS_FOR_CREATE_USERS_PERMISSION = UserInfo.FLAG_MANAGED_PROFILE | UserInfo.FLAG_PROFILE @@ -1970,6 +1974,14 @@ public class UserManagerService extends IUserManager.Stub { // intentSender unlockIntent.putExtra(Intent.EXTRA_INTENT, pendingIntent.getIntentSender()); unlockIntent.setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS); + + if (Flags.enablePrivateSpaceFeatures() && Flags.usePrivateSpaceIconInBiometricPrompt() + && getUserInfo(userId).isPrivateProfile()) { + unlockIntent.putExtra(CUSTOM_BIOMETRIC_PROMPT_LOGO_RES_ID_KEY, + com.android.internal.R.drawable.stat_sys_private_profile_status); + unlockIntent.putExtra(CUSTOM_BIOMETRIC_PROMPT_LOGO_DESCRIPTION_KEY, + mContext.getString(R.string.private_space_biometric_prompt_title)); + } mContext.startActivityAsUser( unlockIntent, UserHandle.of(getProfileParentIdUnchecked(userId))); } diff --git a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java index 483d308ae8ad..95e5b84a8ed3 100644 --- a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java +++ b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java @@ -156,7 +156,8 @@ public class UserRestrictionsUtils { UserManager.DISALLOW_NEAR_FIELD_COMMUNICATION_RADIO, UserManager.DISALLOW_SIM_GLOBALLY, UserManager.DISALLOW_ASSIST_CONTENT, - UserManager.DISALLOW_THREAD_NETWORK + UserManager.DISALLOW_THREAD_NETWORK, + UserManager.DISALLOW_CHANGE_NEAR_FIELD_COMMUNICATION_RADIO }); public static final Set<String> DEPRECATED_USER_RESTRICTIONS = Sets.newArraySet( @@ -208,7 +209,8 @@ public class UserRestrictionsUtils { UserManager.DISALLOW_CELLULAR_2G, UserManager.DISALLOW_ULTRA_WIDEBAND_RADIO, UserManager.DISALLOW_NEAR_FIELD_COMMUNICATION_RADIO, - UserManager.DISALLOW_THREAD_NETWORK + UserManager.DISALLOW_THREAD_NETWORK, + UserManager.DISALLOW_CHANGE_NEAR_FIELD_COMMUNICATION_RADIO ); /** @@ -254,7 +256,8 @@ public class UserRestrictionsUtils { UserManager.DISALLOW_CELLULAR_2G, UserManager.DISALLOW_ULTRA_WIDEBAND_RADIO, UserManager.DISALLOW_NEAR_FIELD_COMMUNICATION_RADIO, - UserManager.DISALLOW_THREAD_NETWORK + UserManager.DISALLOW_THREAD_NETWORK, + UserManager.DISALLOW_CHANGE_NEAR_FIELD_COMMUNICATION_RADIO ); /** diff --git a/services/core/java/com/android/server/pm/dex/DexManager.java b/services/core/java/com/android/server/pm/dex/DexManager.java index e93d3206a4f1..ffb23e6ad1db 100644 --- a/services/core/java/com/android/server/pm/dex/DexManager.java +++ b/services/core/java/com/android/server/pm/dex/DexManager.java @@ -39,7 +39,6 @@ import android.util.jar.StrictJarFile; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; -import com.android.server.pm.Installer; import com.android.server.pm.PackageDexOptimizer; import com.android.server.pm.PackageManagerService; import com.android.server.pm.PackageManagerServiceUtils; @@ -99,9 +98,6 @@ public class DexManager { private IPackageManager mPackageManager; private final PackageDexOptimizer mPackageDexOptimizer; - private final Object mInstallLock; - @GuardedBy("mInstallLock") - private final Installer mInstaller; private BatteryManager mBatteryManager = null; private PowerManager mPowerManager = null; @@ -116,21 +112,18 @@ public class DexManager { private static final int DEX_SEARCH_FOUND_SPLIT = 2; // dex file is a split apk private static final int DEX_SEARCH_FOUND_SECONDARY = 3; // dex file is a secondary dex - public DexManager(Context context, PackageDexOptimizer pdo, Installer installer, - Object installLock, DynamicCodeLogger dynamicCodeLogger) { - this(context, pdo, installer, installLock, dynamicCodeLogger, null); + public DexManager(Context context, PackageDexOptimizer pdo, + DynamicCodeLogger dynamicCodeLogger) { + this(context, pdo, dynamicCodeLogger, null); } @VisibleForTesting - public DexManager(Context context, PackageDexOptimizer pdo, Installer installer, - Object installLock, DynamicCodeLogger dynamicCodeLogger, - @Nullable IPackageManager packageManager) { + public DexManager(Context context, PackageDexOptimizer pdo, + DynamicCodeLogger dynamicCodeLogger, @Nullable IPackageManager packageManager) { mContext = context; mPackageCodeLocationsCache = new HashMap<>(); mPackageDexUsage = new PackageDexUsage(); mPackageDexOptimizer = pdo; - mInstaller = installer; - mInstallLock = installLock; mDynamicCodeLogger = dynamicCodeLogger; mPackageManager = packageManager; diff --git a/services/core/java/com/android/server/pm/dex/DexoptOptions.java b/services/core/java/com/android/server/pm/dex/DexoptOptions.java index fcdc0080811b..8cf248d9b19b 100644 --- a/services/core/java/com/android/server/pm/dex/DexoptOptions.java +++ b/services/core/java/com/android/server/pm/dex/DexoptOptions.java @@ -73,12 +73,6 @@ public final class DexoptOptions { // or device setup and should be scheduled appropriately. public static final int DEXOPT_FOR_RESTORE = 1 << 11; // TODO(b/135202722): remove - /** - * A value indicating that dexopt shouldn't be run. This string is only used when loading - * filters from the `pm.dexopt.install*` properties and is not propagated to dex2oat. - */ - public static final String COMPILER_FILTER_NOOP = "skip"; - // The name of package to optimize. private final String mPackageName; @@ -186,10 +180,6 @@ public final class DexoptOptions { return mCompilationReason; } - public boolean isCompilationEnabled() { - return !mCompilerFilter.equals(COMPILER_FILTER_NOOP); - } - /** * Creates a new set of DexoptOptions which are the same with the exception of the compiler * filter (set to the given value). diff --git a/services/core/java/com/android/server/pm/permission/AccessCheckDelegate.java b/services/core/java/com/android/server/pm/permission/AccessCheckDelegate.java index 3edd6975ab7d..f518769bdb35 100644 --- a/services/core/java/com/android/server/pm/permission/AccessCheckDelegate.java +++ b/services/core/java/com/android/server/pm/permission/AccessCheckDelegate.java @@ -38,6 +38,7 @@ import android.util.SparseArray; import com.android.internal.util.ArrayUtils; import com.android.internal.util.function.DodecFunction; +import com.android.internal.util.function.HexConsumer; import com.android.internal.util.function.HexFunction; import com.android.internal.util.function.OctFunction; import com.android.internal.util.function.QuadFunction; @@ -269,8 +270,8 @@ public interface AccessCheckDelegate extends CheckPermissionDelegate, CheckOpsDe if (isDelegatePermission(permissionName)) { final long identity = Binder.clearCallingIdentity(); try { - return checkPermission(SHELL_PKG, permissionName, - persistentDeviceId, userId, superImpl); + return checkPermission(SHELL_PKG, permissionName, persistentDeviceId, + userId, superImpl); } finally { Binder.restoreCallingIdentity(identity); } @@ -323,8 +324,7 @@ public interface AccessCheckDelegate extends CheckPermissionDelegate, CheckOpsDe Process.SHELL_UID); final long identity = Binder.clearCallingIdentity(); try { - return superImpl.apply(code, shellUid, "com.android.shell", null, - virtualDeviceId, raw); + return superImpl.apply(code, shellUid, SHELL_PKG, null, virtualDeviceId, raw); } finally { Binder.restoreCallingIdentity(identity); } @@ -340,7 +340,7 @@ public interface AccessCheckDelegate extends CheckPermissionDelegate, CheckOpsDe Process.SHELL_UID); final long identity = Binder.clearCallingIdentity(); try { - return superImpl.apply(code, usage, shellUid, "com.android.shell"); + return superImpl.apply(code, usage, shellUid, SHELL_PKG); } finally { Binder.restoreCallingIdentity(identity); } @@ -359,9 +359,8 @@ public interface AccessCheckDelegate extends CheckPermissionDelegate, CheckOpsDe Process.SHELL_UID); final long identity = Binder.clearCallingIdentity(); try { - return superImpl.apply(code, shellUid, "com.android.shell", featureId, - virtualDeviceId, shouldCollectAsyncNotedOp, message, - shouldCollectMessage); + return superImpl.apply(code, shellUid, SHELL_PKG, featureId, virtualDeviceId, + shouldCollectAsyncNotedOp, message, shouldCollectMessage); } finally { Binder.restoreCallingIdentity(identity); } @@ -382,8 +381,8 @@ public interface AccessCheckDelegate extends CheckPermissionDelegate, CheckOpsDe final long identity = Binder.clearCallingIdentity(); try { return superImpl.apply(code, - new AttributionSource(shellUid, Process.INVALID_PID, - "com.android.shell", attributionSource.getAttributionTag(), + new AttributionSource(shellUid, Process.INVALID_PID, SHELL_PKG, + attributionSource.getAttributionTag(), attributionSource.getToken(), /*renouncedPermissions*/ null, attributionSource.getDeviceId(), attributionSource.getNext()), shouldCollectAsyncNotedOp, message, shouldCollectMessage, @@ -409,10 +408,9 @@ public interface AccessCheckDelegate extends CheckPermissionDelegate, CheckOpsDe Process.SHELL_UID); final long identity = Binder.clearCallingIdentity(); try { - return superImpl.apply(token, code, shellUid, "com.android.shell", - attributionTag, virtualDeviceId, startIfModeDefault, - shouldCollectAsyncNotedOp, message, shouldCollectMessage, - attributionFlags, attributionChainId); + return superImpl.apply(token, code, shellUid, SHELL_PKG, attributionTag, + virtualDeviceId, startIfModeDefault, shouldCollectAsyncNotedOp, message, + shouldCollectMessage, attributionFlags, attributionChainId); } finally { Binder.restoreCallingIdentity(identity); } @@ -438,8 +436,8 @@ public interface AccessCheckDelegate extends CheckPermissionDelegate, CheckOpsDe final long identity = Binder.clearCallingIdentity(); try { return superImpl.apply(clientId, code, - new AttributionSource(shellUid, Process.INVALID_PID, - "com.android.shell", attributionSource.getAttributionTag(), + new AttributionSource(shellUid, Process.INVALID_PID, SHELL_PKG, + attributionSource.getAttributionTag(), attributionSource.getToken(), /*renouncedPermissions*/ null, attributionSource.getDeviceId(), attributionSource.getNext()), startIfModeDefault, shouldCollectAsyncNotedOp, message, @@ -465,11 +463,12 @@ public interface AccessCheckDelegate extends CheckPermissionDelegate, CheckOpsDe final long identity = Binder.clearCallingIdentity(); try { superImpl.apply(clientId, code, - new AttributionSource(shellUid, Process.INVALID_PID, - "com.android.shell", attributionSource.getAttributionTag(), + new AttributionSource(shellUid, Process.INVALID_PID, SHELL_PKG, + attributionSource.getAttributionTag(), attributionSource.getToken(), /*renouncedPermissions*/ null, attributionSource.getDeviceId(), attributionSource.getNext()), skipProxyOperation); + return; } finally { Binder.restoreCallingIdentity(identity); } @@ -477,6 +476,26 @@ public interface AccessCheckDelegate extends CheckPermissionDelegate, CheckOpsDe superImpl.apply(clientId, code, attributionSource, skipProxyOperation); } + @Override + public void finishOperation(IBinder clientId, int code, int uid, String packageName, + String attributionTag, int virtualDeviceId, @NonNull HexConsumer<IBinder, Integer, + Integer, String, String, Integer> superImpl) { + if (uid == mDelegateAndOwnerUid && isDelegateOp(code)) { + final int shellUid = + UserHandle.getUid(UserHandle.getUserId(uid), Process.SHELL_UID); + final long identity = Binder.clearCallingIdentity(); + try { + superImpl.accept(clientId, code, shellUid, SHELL_PKG, attributionTag, + virtualDeviceId); + return; + } finally { + Binder.restoreCallingIdentity(identity); + } + } + superImpl.accept(clientId, code, uid, packageName, attributionTag, + virtualDeviceId); + } + private boolean isDelegatePermission(@NonNull String permission) { // null permissions means all permissions are delegated return mDelegateAndOwnerUid != INVALID_UID diff --git a/services/core/java/com/android/server/pm/permission/LegacyPermissionManagerService.java b/services/core/java/com/android/server/pm/permission/LegacyPermissionManagerService.java index 23872d4fe634..119b659dc15a 100644 --- a/services/core/java/com/android/server/pm/permission/LegacyPermissionManagerService.java +++ b/services/core/java/com/android/server/pm/permission/LegacyPermissionManagerService.java @@ -336,8 +336,13 @@ public class LegacyPermissionManagerService extends ILegacyPermissionManager.Stu final PermissionManagerServiceInternal permissionManagerInternal = LocalServices.getService(PermissionManagerServiceInternal.class); for (final int userId : UserManagerService.getInstance().getUserIds()) { - packageManagerInternal.forEachPackage(pkg -> - permissionManagerInternal.resetRuntimePermissions(pkg, userId)); + packageManagerInternal.forEachPackage(pkg -> { + // Filter out packages that don't have app IDs which means they don't have + // permission states either. + if (pkg.getUid() != -1) { + permissionManagerInternal.resetRuntimePermissions(pkg, userId); + } + }); } } diff --git a/services/core/java/com/android/server/pm/permission/PermissionAllowlist.java b/services/core/java/com/android/server/pm/permission/PermissionAllowlist.java index d138606369b9..6b99cbb2cba9 100644 --- a/services/core/java/com/android/server/pm/permission/PermissionAllowlist.java +++ b/services/core/java/com/android/server/pm/permission/PermissionAllowlist.java @@ -55,6 +55,9 @@ public final class PermissionAllowlist { @NonNull private final ArrayMap<String, ArrayMap<String, Boolean>> mSystemExtSignatureAppAllowlist = new ArrayMap<>(); + @NonNull + private final ArrayMap<String, ArrayMap<String, Boolean>> mApexSignatureAppAllowlist = + new ArrayMap<>(); @NonNull public ArrayMap<String, ArrayMap<String, Boolean>> getOemAppAllowlist() { @@ -107,6 +110,11 @@ public final class PermissionAllowlist { return mSystemExtSignatureAppAllowlist; } + @NonNull + public ArrayMap<String, ArrayMap<String, Boolean>> getApexSignatureAppAllowlist() { + return mApexSignatureAppAllowlist; + } + @Nullable public Boolean getOemAppAllowlistState(@NonNull String packageName, @NonNull String permissionName) { @@ -211,4 +219,14 @@ public final class PermissionAllowlist { } return permissions.get(permissionName); } + + @Nullable + public Boolean getApexSignatureAppAllowlistState(@NonNull String packageName, + @NonNull String permissionName) { + ArrayMap<String, Boolean> permissions = mApexSignatureAppAllowlist.get(packageName); + if (permissions == null) { + return null; + } + return permissions.get(permissionName); + } } diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java index cd1d7996fbac..ea71953d7cb3 100644 --- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java +++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java @@ -165,6 +165,7 @@ import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.Optional; import java.util.Set; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; @@ -368,18 +369,32 @@ public class PermissionManagerServiceImpl implements PermissionManagerServiceInt return false; } + int userId = UserHandle.getUserId(uid); + + boolean isInSetup = + getSecureInt(Settings.Secure.USER_SETUP_COMPLETE, userId) + .map(setupState -> setupState == 0) + .orElse(false); + if (isInSetup) { + return true; + } + + boolean isInDeferredSetup = + getSecureInt(Settings.Secure.USER_SETUP_PERSONALIZATION_STATE, userId) + .map(state -> + state == Settings.Secure.USER_SETUP_PERSONALIZATION_STARTED) + .orElse(false); + return isInDeferredSetup; + } + + private Optional<Integer> getSecureInt(String settingName, int userId) { try { - int userId = UserHandle.getUserId(uid); - boolean isInSetup = Settings.Secure.getIntForUser(mContext.getContentResolver(), - Settings.Secure.USER_SETUP_COMPLETE, userId) == 0; - boolean isInDeferredSetup = Settings.Secure.getIntForUser( - mContext.getContentResolver(), - Settings.Secure.USER_SETUP_PERSONALIZATION_STATE, userId) - == Settings.Secure.USER_SETUP_PERSONALIZATION_STARTED; - return isInSetup || isInDeferredSetup; + return Optional.of( + Settings.Secure.getIntForUser( + mContext.getContentResolver(), settingName, userId)); } catch (Settings.SettingNotFoundException e) { - Slog.w(LOG_TAG, "Failed to check if the user is in restore: " + e); - return false; + Slog.i(LOG_TAG, "Setting " + settingName + " not found", e); + return Optional.empty(); } } diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index 12e51809fabf..4e224a3cb38a 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -2882,6 +2882,8 @@ public class PhoneWindowManager implements WindowManagerPolicy { } ContentResolver resolver = mContext.getContentResolver(); boolean updateRotation = false; + boolean updateKidsModeSettings = false; + final boolean kidsModeEnabled; synchronized (mLock) { mEndcallBehavior = Settings.System.getIntForUser(resolver, Settings.System.END_BUTTON_BEHAVIOR, @@ -2995,20 +2997,23 @@ public class PhoneWindowManager implements WindowManagerPolicy { Secure.STYLUS_BUTTONS_ENABLED, 1, UserHandle.USER_CURRENT) == 1; mInputManagerInternal.setStylusButtonMotionEventsEnabled(mStylusButtonsEnabled); - final boolean kidsModeEnabled = Settings.Secure.getIntForUser(resolver, + kidsModeEnabled = Settings.Secure.getIntForUser(resolver, Settings.Secure.NAV_BAR_KIDS_MODE, 0, UserHandle.USER_CURRENT) == 1; if (mKidsModeEnabled != kidsModeEnabled) { mKidsModeEnabled = kidsModeEnabled; - updateKidsModeSettings(); + updateKidsModeSettings = true; } } + if (updateKidsModeSettings) { + updateKidsModeSettings(kidsModeEnabled); + } if (updateRotation) { updateRotation(true); } } - private void updateKidsModeSettings() { - if (mKidsModeEnabled) { + private void updateKidsModeSettings(boolean kidsModeEnabled) { + if (kidsModeEnabled) { // Needed since many Kids apps aren't optimised to support both orientations and it // will be hard for kids to understand the app compat mode. // TODO(229961548): Remove ignoreOrientationRequest exception for Kids Mode once diff --git a/services/core/java/com/android/server/policy/TalkbackShortcutController.java b/services/core/java/com/android/server/policy/TalkbackShortcutController.java index b05a421e6e87..e544ae64521c 100644 --- a/services/core/java/com/android/server/policy/TalkbackShortcutController.java +++ b/services/core/java/com/android/server/policy/TalkbackShortcutController.java @@ -117,6 +117,7 @@ class TalkbackShortcutController { } private boolean isTalkback(ServiceInfo info) { - return TALKBACK_LABEL.equals(info.loadLabel(mPackageManager).toString()); + return TALKBACK_LABEL.equals(info.loadLabel(mPackageManager).toString()) + && (info.applicationInfo.isSystemApp() || info.applicationInfo.isUpdatedSystemApp()); } } diff --git a/services/core/java/com/android/server/power/ThermalManagerService.java b/services/core/java/com/android/server/power/ThermalManagerService.java index 53607880ff48..0052d4f28b93 100644 --- a/services/core/java/com/android/server/power/ThermalManagerService.java +++ b/services/core/java/com/android/server/power/ThermalManagerService.java @@ -1709,7 +1709,7 @@ public class ThermalManagerService extends SystemService { ArrayList<Sample> samples = mSamples.computeIfAbsent(temperature.getName(), k -> new ArrayList<>(RING_BUFFER_SIZE)); if (samples.size() == RING_BUFFER_SIZE) { - samples.remove(0); + samples.removeFirst(); } samples.add(new Sample(now, temperature.getValue())); } @@ -1806,7 +1806,7 @@ public class ThermalManagerService extends SystemService { continue; } - float currentTemperature = samples.get(0).temperature; + float currentTemperature = samples.getLast().temperature; if (samples.size() < MINIMUM_SAMPLE_COUNT) { // Don't try to forecast, just use the latest one we have diff --git a/services/core/java/com/android/server/power/hint/HintManagerService.java b/services/core/java/com/android/server/power/hint/HintManagerService.java index df502eb08be2..06595acfd012 100644 --- a/services/core/java/com/android/server/power/hint/HintManagerService.java +++ b/services/core/java/com/android/server/power/hint/HintManagerService.java @@ -19,6 +19,7 @@ package com.android.server.power.hint; import static android.os.Flags.adpfUseFmqChannel; import static com.android.internal.util.ConcurrentUtils.DIRECT_EXECUTOR; +import static com.android.server.power.hint.Flags.adpfSessionTag; import static com.android.server.power.hint.Flags.powerhintThreadCleanup; import android.annotation.NonNull; @@ -28,6 +29,8 @@ import android.app.ActivityManagerInternal; import android.app.StatsManager; import android.app.UidObserver; import android.content.Context; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; import android.hardware.power.ChannelConfig; import android.hardware.power.IPower; import android.hardware.power.SessionConfig; @@ -130,6 +133,7 @@ public final class HintManagerService extends SystemService { private final IPower mPowerHal; private int mPowerHalVersion; + private final PackageManager mPackageManager; private static final String PROPERTY_SF_ENABLE_CPU_HINT = "debug.sf.enable_adpf_cpu_hint"; private static final String PROPERTY_HWUI_ENABLE_HINT_MANAGER = "debug.hwui.use_hint_manager"; @@ -151,6 +155,11 @@ public final class HintManagerService extends SystemService { mCleanUpHandler = null; mNonIsolatedTids = null; } + if (adpfSessionTag()) { + mPackageManager = mContext.getPackageManager(); + } else { + mPackageManager = null; + } mActiveSessions = new ArrayMap<>(); mChannelMap = new ArrayMap<>(); mNativeWrapper = injector.createNativeWrapper(); @@ -819,6 +828,25 @@ public final class HintManagerService extends SystemService { throw new SecurityException(errMsg); } + if (adpfSessionTag() && tag == SessionTag.APP) { + // If the category of the app is a game, + // we change the session tag to SessionTag.GAME + // as it was not previously classified + switch (getUidApplicationCategory(callingUid)) { + case ApplicationInfo.CATEGORY_GAME: + tag = SessionTag.GAME; + break; + case ApplicationInfo.CATEGORY_UNDEFINED: + // We use CATEGORY_UNDEFINED to filter the case when + // PackageManager.NameNotFoundException is caught, + // which should not happen. + tag = SessionTag.APP; + break; + default: + tag = SessionTag.APP; + } + } + Long halSessionPtr = null; if (mConfigCreationSupport.get()) { try { @@ -857,7 +885,10 @@ public final class HintManagerService extends SystemService { } } - logPerformanceHintSessionAtom(callingUid, halSessionPtr, durationNanos, tids); + final long sessionId = config != null ? config.id : halSessionPtr; + logPerformanceHintSessionAtom( + callingUid, sessionId, durationNanos, tids, tag); + synchronized (mLock) { AppHintSession hs = new AppHintSession(callingUid, callingTgid, tids, token, halSessionPtr, durationNanos); @@ -944,9 +975,20 @@ public final class HintManagerService extends SystemService { } private void logPerformanceHintSessionAtom(int uid, long sessionId, - long targetDuration, int[] tids) { + long targetDuration, int[] tids, @SessionTag int sessionTag) { FrameworkStatsLog.write(FrameworkStatsLog.PERFORMANCE_HINT_SESSION_REPORTED, uid, - sessionId, targetDuration, tids.length); + sessionId, targetDuration, tids.length, sessionTag); + } + + private int getUidApplicationCategory(int uid) { + try { + final String packageName = mPackageManager.getNameForUid(uid); + final ApplicationInfo applicationInfo = + mPackageManager.getApplicationInfo(packageName, PackageManager.MATCH_ALL); + return applicationInfo.category; + } catch (PackageManager.NameNotFoundException e) { + return ApplicationInfo.CATEGORY_UNDEFINED; + } } } diff --git a/services/core/java/com/android/server/power/hint/TEST_MAPPING b/services/core/java/com/android/server/power/hint/TEST_MAPPING index ce6277d2f490..34c25c6eb360 100644 --- a/services/core/java/com/android/server/power/hint/TEST_MAPPING +++ b/services/core/java/com/android/server/power/hint/TEST_MAPPING @@ -10,16 +10,18 @@ "exclude-annotation": "androidx.test.filters.FlakyTest" } ] - }, + } + ], + "postsubmit": [ { "name": "CtsStatsdAtomHostTestCases", "options": [ {"exclude-annotation": "androidx.test.filters.FlakyTest"}, {"exclude-annotation": "org.junit.Ignore"}, - {"include-filter": "android.cts.statsdatom.powermanager"} + {"include-filter": "android.cts.statsdatom.performancehintmanager"} ], "file_patterns": [ - "(/|^)ThermalManagerService.java" + "(/|^)HintManagerService.java" ] } ] diff --git a/services/core/java/com/android/server/power/hint/flags.aconfig b/services/core/java/com/android/server/power/hint/flags.aconfig index 099774420d23..55afa05f66fe 100644 --- a/services/core/java/com/android/server/power/hint/flags.aconfig +++ b/services/core/java/com/android/server/power/hint/flags.aconfig @@ -7,3 +7,10 @@ flag { description: "Feature flag for auto PowerHintSession dead thread cleanup" bug: "296160319" } + +flag { + name: "adpf_session_tag" + namespace: "game" + description: "Feature flag for adding session tag to hint session atom" + bug: "345011125" +} diff --git a/services/core/java/com/android/server/power/stats/AudioPowerStatsProcessor.java b/services/core/java/com/android/server/power/stats/AudioPowerStatsProcessor.java new file mode 100644 index 000000000000..a48f162321dd --- /dev/null +++ b/services/core/java/com/android/server/power/stats/AudioPowerStatsProcessor.java @@ -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.server.power.stats; + +import android.os.BatteryConsumer; +import android.os.BatteryStats; + +import com.android.internal.os.PowerProfile; + +public class AudioPowerStatsProcessor extends BinaryStatePowerStatsProcessor { + public AudioPowerStatsProcessor(PowerProfile powerProfile, + PowerStatsUidResolver uidResolver) { + super(BatteryConsumer.POWER_COMPONENT_AUDIO, uidResolver, + powerProfile.getAveragePower(PowerProfile.POWER_AUDIO)); + } + + @Override + protected @BinaryState int getBinaryState(BatteryStats.HistoryItem item) { + return (item.states & BatteryStats.HistoryItem.STATE_AUDIO_ON_FLAG) != 0 + ? STATE_ON + : STATE_OFF; + } +} 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 efaa7a8598c0..322ed864fca6 100644 --- a/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java +++ b/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java @@ -298,6 +298,8 @@ public class BatteryStatsImpl extends BatteryStats { private final MobileRadioPowerStatsCollector mMobileRadioPowerStatsCollector; private final WifiPowerStatsCollector mWifiPowerStatsCollector; private final BluetoothPowerStatsCollector mBluetoothPowerStatsCollector; + private final CameraPowerStatsCollector mCameraPowerStatsCollector; + private final GnssPowerStatsCollector mGnssPowerStatsCollector; private final SparseBooleanArray mPowerStatsCollectorEnabled = new SparseBooleanArray(); private final WifiPowerStatsCollector.WifiStatsRetriever mWifiStatsRetriever = new WifiPowerStatsCollector.WifiStatsRetriever() { @@ -1963,7 +1965,7 @@ public class BatteryStatsImpl extends BatteryStats { private class PowerStatsCollectorInjector implements CpuPowerStatsCollector.Injector, MobileRadioPowerStatsCollector.Injector, WifiPowerStatsCollector.Injector, - BluetoothPowerStatsCollector.Injector { + BluetoothPowerStatsCollector.Injector, EnergyConsumerPowerStatsCollector.Injector { private PackageManager mPackageManager; private PowerStatsCollector.ConsumedEnergyRetriever mConsumedEnergyRetriever; private NetworkStatsManager mNetworkStatsManager; @@ -5446,7 +5448,10 @@ public class BatteryStatsImpl extends BatteryStats { final int mappedUid = mapUid(uid); if (mGpsNesting == 0) { mHistory.recordStateStartEvent(elapsedRealtimeMs, uptimeMs, - HistoryItem.STATE_GPS_ON_FLAG); + HistoryItem.STATE_GPS_ON_FLAG, uid, "gnss"); + if (mPowerStatsCollectorEnabled.get(BatteryConsumer.POWER_COMPONENT_GNSS)) { + mGnssPowerStatsCollector.schedule(); + } } mGpsNesting++; @@ -5465,11 +5470,14 @@ public class BatteryStatsImpl extends BatteryStats { mGpsNesting--; if (mGpsNesting == 0) { mHistory.recordStateStopEvent(elapsedRealtimeMs, uptimeMs, - HistoryItem.STATE_GPS_ON_FLAG); + HistoryItem.STATE_GPS_ON_FLAG, uid, "gnss"); mHistory.recordGpsSignalQualityEvent(elapsedRealtimeMs, uptimeMs, GPS_SIGNAL_QUALITY_NONE); stopAllGpsSignalQualityTimersLocked(-1, elapsedRealtimeMs); mGpsSignalQualityBin = -1; + if (mPowerStatsCollectorEnabled.get(BatteryConsumer.POWER_COMPONENT_GNSS)) { + mGnssPowerStatsCollector.schedule(); + } } mFrameworkStatsLogger.gpsScanStateChanged(mapIsolatedUid(uid), workChain, /* on */ false); @@ -6490,12 +6498,14 @@ public class BatteryStatsImpl extends BatteryStats { uid = mapUid(uid); if (mAudioOnNesting == 0) { mHistory.recordStateStartEvent(elapsedRealtimeMs, uptimeMs, - HistoryItem.STATE_AUDIO_ON_FLAG); + HistoryItem.STATE_AUDIO_ON_FLAG, uid, "audio"); mAudioOnTimer.startRunningLocked(elapsedRealtimeMs); } mAudioOnNesting++; - getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs) - .noteAudioTurnedOnLocked(elapsedRealtimeMs); + if (!mPowerStatsCollectorEnabled.get(BatteryConsumer.POWER_COMPONENT_AUDIO)) { + getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs) + .noteAudioTurnedOnLocked(elapsedRealtimeMs); + } } @GuardedBy("this") @@ -6506,11 +6516,13 @@ public class BatteryStatsImpl extends BatteryStats { uid = mapUid(uid); if (--mAudioOnNesting == 0) { mHistory.recordStateStopEvent(elapsedRealtimeMs, uptimeMs, - HistoryItem.STATE_AUDIO_ON_FLAG); + HistoryItem.STATE_AUDIO_ON_FLAG, uid, "audio"); mAudioOnTimer.stopRunningLocked(elapsedRealtimeMs); } - getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs) - .noteAudioTurnedOffLocked(elapsedRealtimeMs); + if (!mPowerStatsCollectorEnabled.get(BatteryConsumer.POWER_COMPONENT_AUDIO)) { + getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs) + .noteAudioTurnedOffLocked(elapsedRealtimeMs); + } } @GuardedBy("this") @@ -6518,12 +6530,14 @@ public class BatteryStatsImpl extends BatteryStats { uid = mapUid(uid); if (mVideoOnNesting == 0) { mHistory.recordState2StartEvent(elapsedRealtimeMs, uptimeMs, - HistoryItem.STATE2_VIDEO_ON_FLAG); + HistoryItem.STATE2_VIDEO_ON_FLAG, uid, "video"); mVideoOnTimer.startRunningLocked(elapsedRealtimeMs); } mVideoOnNesting++; - getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs) - .noteVideoTurnedOnLocked(elapsedRealtimeMs); + if (!mPowerStatsCollectorEnabled.get(BatteryConsumer.POWER_COMPONENT_VIDEO)) { + getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs) + .noteVideoTurnedOnLocked(elapsedRealtimeMs); + } } @GuardedBy("this") @@ -6534,11 +6548,13 @@ public class BatteryStatsImpl extends BatteryStats { uid = mapUid(uid); if (--mVideoOnNesting == 0) { mHistory.recordState2StopEvent(elapsedRealtimeMs, uptimeMs, - HistoryItem.STATE2_VIDEO_ON_FLAG); + HistoryItem.STATE2_VIDEO_ON_FLAG, uid, "video"); mVideoOnTimer.stopRunningLocked(elapsedRealtimeMs); } - getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs) - .noteVideoTurnedOffLocked(elapsedRealtimeMs); + if (!mPowerStatsCollectorEnabled.get(BatteryConsumer.POWER_COMPONENT_VIDEO)) { + getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs) + .noteVideoTurnedOffLocked(elapsedRealtimeMs); + } } @GuardedBy("this") @@ -6613,11 +6629,13 @@ public class BatteryStatsImpl extends BatteryStats { uid = mapUid(uid); if (mFlashlightOnNesting++ == 0) { mHistory.recordState2StartEvent(elapsedRealtimeMs, uptimeMs, - HistoryItem.STATE2_FLASHLIGHT_FLAG); + HistoryItem.STATE2_FLASHLIGHT_FLAG, uid, "flashlight"); mFlashlightOnTimer.startRunningLocked(elapsedRealtimeMs); } - getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs) - .noteFlashlightTurnedOnLocked(elapsedRealtimeMs); + if (!mPowerStatsCollectorEnabled.get(BatteryConsumer.POWER_COMPONENT_FLASHLIGHT)) { + getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs) + .noteFlashlightTurnedOnLocked(elapsedRealtimeMs); + } } @GuardedBy("this") @@ -6628,11 +6646,13 @@ public class BatteryStatsImpl extends BatteryStats { uid = mapUid(uid); if (--mFlashlightOnNesting == 0) { mHistory.recordState2StopEvent(elapsedRealtimeMs, uptimeMs, - HistoryItem.STATE2_FLASHLIGHT_FLAG); + HistoryItem.STATE2_FLASHLIGHT_FLAG, uid, "flashlight"); mFlashlightOnTimer.stopRunningLocked(elapsedRealtimeMs); } - getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs) - .noteFlashlightTurnedOffLocked(elapsedRealtimeMs); + if (!mPowerStatsCollectorEnabled.get(BatteryConsumer.POWER_COMPONENT_FLASHLIGHT)) { + getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs) + .noteFlashlightTurnedOffLocked(elapsedRealtimeMs); + } } @GuardedBy("this") @@ -6640,13 +6660,17 @@ public class BatteryStatsImpl extends BatteryStats { uid = mapUid(uid); if (mCameraOnNesting++ == 0) { mHistory.recordState2StartEvent(elapsedRealtimeMs, uptimeMs, - HistoryItem.STATE2_CAMERA_FLAG); + HistoryItem.STATE2_CAMERA_FLAG, uid, "camera"); mCameraOnTimer.startRunningLocked(elapsedRealtimeMs); } getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs) .noteCameraTurnedOnLocked(elapsedRealtimeMs); - scheduleSyncExternalStatsLocked("camera-on", ExternalStatsSync.UPDATE_CAMERA); + if (mPowerStatsCollectorEnabled.get(BatteryConsumer.POWER_COMPONENT_CAMERA)) { + mCameraPowerStatsCollector.schedule(); + } else { + scheduleSyncExternalStatsLocked("camera-on", ExternalStatsSync.UPDATE_CAMERA); + } } @GuardedBy("this") @@ -6657,13 +6681,17 @@ public class BatteryStatsImpl extends BatteryStats { uid = mapUid(uid); if (--mCameraOnNesting == 0) { mHistory.recordState2StopEvent(elapsedRealtimeMs, uptimeMs, - HistoryItem.STATE2_CAMERA_FLAG); + HistoryItem.STATE2_CAMERA_FLAG, uid, "camera"); mCameraOnTimer.stopRunningLocked(elapsedRealtimeMs); } getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs) .noteCameraTurnedOffLocked(elapsedRealtimeMs); - scheduleSyncExternalStatsLocked("camera-off", ExternalStatsSync.UPDATE_CAMERA); + if (mPowerStatsCollectorEnabled.get(BatteryConsumer.POWER_COMPONENT_CAMERA)) { + mCameraPowerStatsCollector.schedule(); + } else { + scheduleSyncExternalStatsLocked("camera-off", ExternalStatsSync.UPDATE_CAMERA); + } } @GuardedBy("this") @@ -11269,6 +11297,12 @@ public class BatteryStatsImpl extends BatteryStats { mPowerStatsCollectorInjector); mBluetoothPowerStatsCollector.addConsumer(this::recordPowerStats); + mCameraPowerStatsCollector = new CameraPowerStatsCollector(mPowerStatsCollectorInjector); + mCameraPowerStatsCollector.addConsumer(this::recordPowerStats); + + mGnssPowerStatsCollector = new GnssPowerStatsCollector(mPowerStatsCollectorInjector); + mGnssPowerStatsCollector.addConsumer(this::recordPowerStats); + mStartCount++; initTimersAndCounters(); mOnBattery = mOnBatteryInternal = false; @@ -14691,6 +14725,14 @@ public class BatteryStatsImpl extends BatteryStats { mPowerStatsCollectorEnabled.get(BatteryConsumer.POWER_COMPONENT_BLUETOOTH)); mBluetoothPowerStatsCollector.schedule(); + mCameraPowerStatsCollector.setEnabled( + mPowerStatsCollectorEnabled.get(BatteryConsumer.POWER_COMPONENT_CAMERA)); + mCameraPowerStatsCollector.schedule(); + + mGnssPowerStatsCollector.setEnabled( + mPowerStatsCollectorEnabled.get(BatteryConsumer.POWER_COMPONENT_GNSS)); + mGnssPowerStatsCollector.schedule(); + mSystemReady = true; } @@ -14709,6 +14751,10 @@ public class BatteryStatsImpl extends BatteryStats { return mWifiPowerStatsCollector; case BatteryConsumer.POWER_COMPONENT_BLUETOOTH: return mBluetoothPowerStatsCollector; + case BatteryConsumer.POWER_COMPONENT_CAMERA: + return mCameraPowerStatsCollector; + case BatteryConsumer.POWER_COMPONENT_GNSS: + return mGnssPowerStatsCollector; } return null; } @@ -16246,6 +16292,8 @@ public class BatteryStatsImpl extends BatteryStats { mMobileRadioPowerStatsCollector.forceSchedule(); mWifiPowerStatsCollector.forceSchedule(); mBluetoothPowerStatsCollector.forceSchedule(); + mCameraPowerStatsCollector.forceSchedule(); + mGnssPowerStatsCollector.forceSchedule(); } /** @@ -16266,6 +16314,8 @@ public class BatteryStatsImpl extends BatteryStats { mMobileRadioPowerStatsCollector.collectAndDump(pw); mWifiPowerStatsCollector.collectAndDump(pw); mBluetoothPowerStatsCollector.collectAndDump(pw); + mCameraPowerStatsCollector.collectAndDump(pw); + mGnssPowerStatsCollector.collectAndDump(pw); } private final Runnable mWriteAsyncRunnable = () -> { 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 b25239574071..ce0ee39d0a53 100644 --- a/services/core/java/com/android/server/power/stats/BatteryUsageStatsProvider.java +++ b/services/core/java/com/android/server/power/stats/BatteryUsageStatsProvider.java @@ -95,11 +95,21 @@ public class BatteryUsageStatsProvider { } mPowerCalculators.add(new SensorPowerCalculator( mContext.getSystemService(SensorManager.class))); - mPowerCalculators.add(new GnssPowerCalculator(mPowerProfile)); - mPowerCalculators.add(new CameraPowerCalculator(mPowerProfile)); - mPowerCalculators.add(new FlashlightPowerCalculator(mPowerProfile)); - mPowerCalculators.add(new AudioPowerCalculator(mPowerProfile)); - mPowerCalculators.add(new VideoPowerCalculator(mPowerProfile)); + if (!mPowerStatsExporterEnabled.get(BatteryConsumer.POWER_COMPONENT_GNSS)) { + mPowerCalculators.add(new GnssPowerCalculator(mPowerProfile)); + } + if (!mPowerStatsExporterEnabled.get(BatteryConsumer.POWER_COMPONENT_CAMERA)) { + mPowerCalculators.add(new CameraPowerCalculator(mPowerProfile)); + } + if (!mPowerStatsExporterEnabled.get(BatteryConsumer.POWER_COMPONENT_FLASHLIGHT)) { + mPowerCalculators.add(new FlashlightPowerCalculator(mPowerProfile)); + } + if (!mPowerStatsExporterEnabled.get(BatteryConsumer.POWER_COMPONENT_AUDIO)) { + mPowerCalculators.add(new AudioPowerCalculator(mPowerProfile)); + } + if (!mPowerStatsExporterEnabled.get(BatteryConsumer.POWER_COMPONENT_VIDEO)) { + mPowerCalculators.add(new VideoPowerCalculator(mPowerProfile)); + } mPowerCalculators.add(new ScreenPowerCalculator(mPowerProfile)); mPowerCalculators.add(new AmbientDisplayPowerCalculator(mPowerProfile)); mPowerCalculators.add(new IdlePowerCalculator(mPowerProfile)); diff --git a/services/core/java/com/android/server/power/stats/BinaryStatePowerStatsProcessor.java b/services/core/java/com/android/server/power/stats/BinaryStatePowerStatsProcessor.java index 490bd5e77c7b..599e63d12906 100644 --- a/services/core/java/com/android/server/power/stats/BinaryStatePowerStatsProcessor.java +++ b/services/core/java/com/android/server/power/stats/BinaryStatePowerStatsProcessor.java @@ -51,7 +51,7 @@ abstract class BinaryStatePowerStatsProcessor extends PowerStatsProcessor { private long mLastUpdateTimestamp; private PowerStats.Descriptor mDescriptor; - private final BinaryStatePowerStatsLayout mStatsLayout = new BinaryStatePowerStatsLayout(); + private final BinaryStatePowerStatsLayout mStatsLayout; private PowerStats mPowerStats; private PowerEstimationPlan mPlan; private long[] mTmpDeviceStatsArray; @@ -59,9 +59,17 @@ abstract class BinaryStatePowerStatsProcessor extends PowerStatsProcessor { BinaryStatePowerStatsProcessor(int powerComponentId, PowerStatsUidResolver uidResolver, double averagePowerMilliAmp) { + this(powerComponentId, uidResolver, averagePowerMilliAmp, + new BinaryStatePowerStatsLayout()); + } + + BinaryStatePowerStatsProcessor(int powerComponentId, + PowerStatsUidResolver uidResolver, double averagePowerMilliAmp, + BinaryStatePowerStatsLayout statsLayout) { mPowerComponentId = powerComponentId; mUsageBasedPowerEstimator = new UsageBasedPowerEstimator(averagePowerMilliAmp); mUidResolver = uidResolver; + mStatsLayout = statsLayout; } protected abstract @BinaryState int getBinaryState(BatteryStats.HistoryItem item); @@ -107,7 +115,7 @@ abstract class BinaryStatePowerStatsProcessor extends PowerStatsProcessor { mInitiatingUid = mUidResolver.mapUid(item.eventTag.uid); } } else { - recordUsageDuration(item.time); + recordUsageDuration(mPowerStats, mInitiatingUid, item.time); mInitiatingUid = Process.INVALID_UID; if (!mEnergyConsumerSupported) { flushPowerStats(stats, item.time); @@ -117,20 +125,16 @@ abstract class BinaryStatePowerStatsProcessor extends PowerStatsProcessor { mLastState = state; } - private void recordUsageDuration(long time) { - if (mLastState == STATE_OFF) { - return; - } - + protected void recordUsageDuration(PowerStats powerStats, int uid, long time) { long durationMs = time - mLastStateTimestamp; mStatsLayout.setUsageDuration(mPowerStats.stats, mStatsLayout.getUsageDuration(mPowerStats.stats) + durationMs); - if (mInitiatingUid != Process.INVALID_UID) { - long[] uidStats = mPowerStats.uidStats.get(mInitiatingUid); + if (uid != Process.INVALID_UID) { + long[] uidStats = mPowerStats.uidStats.get(uid); if (uidStats == null) { uidStats = new long[mDescriptor.uidStatsArrayLength]; - mPowerStats.uidStats.put(mInitiatingUid, uidStats); + mPowerStats.uidStats.put(uid, uidStats); mStatsLayout.setUidUsageDuration(uidStats, durationMs); } else { mStatsLayout.setUsageDuration(mPowerStats.stats, @@ -143,7 +147,11 @@ abstract class BinaryStatePowerStatsProcessor extends PowerStatsProcessor { void addPowerStats(PowerComponentAggregatedPowerStats stats, PowerStats powerStats, long timestampMs) { ensureInitialized(); - recordUsageDuration(timestampMs); + + if (mLastState == STATE_ON) { + recordUsageDuration(mPowerStats, mInitiatingUid, timestampMs); + } + long consumedEnergy = mStatsLayout.getConsumedEnergy(powerStats.stats, 0); if (consumedEnergy != BatteryStats.POWER_DATA_UNAVAILABLE) { mEnergyConsumerSupported = true; @@ -169,14 +177,16 @@ abstract class BinaryStatePowerStatsProcessor extends PowerStatsProcessor { @Override void finish(PowerComponentAggregatedPowerStats stats, long timestampMs) { - recordUsageDuration(timestampMs); + if (mLastState == STATE_ON) { + recordUsageDuration(mPowerStats, mInitiatingUid, timestampMs); + } flushPowerStats(stats, timestampMs); if (mPlan == null) { mPlan = new PowerEstimationPlan(stats.getConfig()); } - computeDevicePowerEstimates(stats); + computeDevicePowerEstimates(stats, mPlan, mEnergyConsumerSupported); combineDevicePowerEstimates(stats); List<Integer> uids = new ArrayList<>(); @@ -186,9 +196,10 @@ abstract class BinaryStatePowerStatsProcessor extends PowerStatsProcessor { computeUidPowerEstimates(stats, uids); } - private void computeDevicePowerEstimates(PowerComponentAggregatedPowerStats stats) { - for (int i = mPlan.deviceStateEstimations.size() - 1; i >= 0; i--) { - DeviceStateEstimation estimation = mPlan.deviceStateEstimations.get(i); + protected void computeDevicePowerEstimates(PowerComponentAggregatedPowerStats stats, + PowerEstimationPlan plan, boolean energyConsumerSupported) { + for (int i = plan.deviceStateEstimations.size() - 1; i >= 0; i--) { + DeviceStateEstimation estimation = plan.deviceStateEstimations.get(i); if (!stats.getDeviceStats(mTmpDeviceStatsArray, estimation.stateValues)) { continue; } @@ -196,7 +207,7 @@ abstract class BinaryStatePowerStatsProcessor extends PowerStatsProcessor { long duration = mStatsLayout.getUsageDuration(mTmpDeviceStatsArray); if (duration > 0) { double power; - if (mEnergyConsumerSupported) { + if (energyConsumerSupported) { power = uCtoMah(mStatsLayout.getConsumedEnergy(mTmpDeviceStatsArray, 0)); } else { power = mUsageBasedPowerEstimator.calculatePower(duration); diff --git a/services/core/java/com/android/server/power/stats/CameraPowerStatsCollector.java b/services/core/java/com/android/server/power/stats/CameraPowerStatsCollector.java new file mode 100644 index 000000000000..8705bd53a3b7 --- /dev/null +++ b/services/core/java/com/android/server/power/stats/CameraPowerStatsCollector.java @@ -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.server.power.stats; + +import android.hardware.power.stats.EnergyConsumerType; +import android.os.BatteryConsumer; + +public class CameraPowerStatsCollector extends EnergyConsumerPowerStatsCollector { + + CameraPowerStatsCollector(Injector injector) { + super(injector, BatteryConsumer.POWER_COMPONENT_CAMERA, + BatteryConsumer.powerComponentIdToString(BatteryConsumer.POWER_COMPONENT_CAMERA), + EnergyConsumerType.CAMERA, /* energy consumer name */ null, + new BinaryStatePowerStatsLayout()); + } +} diff --git a/services/core/java/com/android/server/power/stats/CameraPowerStatsProcessor.java b/services/core/java/com/android/server/power/stats/CameraPowerStatsProcessor.java new file mode 100644 index 000000000000..15c3eb8c0063 --- /dev/null +++ b/services/core/java/com/android/server/power/stats/CameraPowerStatsProcessor.java @@ -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.server.power.stats; + +import android.os.BatteryConsumer; +import android.os.BatteryStats; + +import com.android.internal.os.PowerProfile; + +public class CameraPowerStatsProcessor extends BinaryStatePowerStatsProcessor { + public CameraPowerStatsProcessor(PowerProfile powerProfile, + PowerStatsUidResolver uidResolver) { + super(BatteryConsumer.POWER_COMPONENT_CAMERA, uidResolver, + powerProfile.getAveragePower(PowerProfile.POWER_CAMERA)); + } + + @Override + protected @BinaryState int getBinaryState(BatteryStats.HistoryItem item) { + return (item.states2 & BatteryStats.HistoryItem.STATE2_CAMERA_FLAG) != 0 + ? STATE_ON + : STATE_OFF; + } +} diff --git a/services/core/java/com/android/server/power/stats/EnergyConsumerPowerStatsCollector.java b/services/core/java/com/android/server/power/stats/EnergyConsumerPowerStatsCollector.java new file mode 100644 index 000000000000..2021f85b0210 --- /dev/null +++ b/services/core/java/com/android/server/power/stats/EnergyConsumerPowerStatsCollector.java @@ -0,0 +1,150 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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.stats; + +import android.hardware.power.stats.EnergyConsumerType; +import android.os.Handler; +import android.os.PersistableBundle; +import android.util.Slog; + +import com.android.internal.os.Clock; +import com.android.internal.os.PowerStats; + +import java.util.function.IntSupplier; + +public class EnergyConsumerPowerStatsCollector extends PowerStatsCollector { + private static final String TAG = "CameraPowerStatsCollector"; + + private static final long CAMERA_ACTIVITY_REQUEST_TIMEOUT = 20000; + + private static final long ENERGY_UNSPECIFIED = -1; + + interface Injector { + Handler getHandler(); + Clock getClock(); + PowerStatsUidResolver getUidResolver(); + long getPowerStatsCollectionThrottlePeriod(String powerComponentName); + ConsumedEnergyRetriever getConsumedEnergyRetriever(); + IntSupplier getVoltageSupplier(); + } + + private final Injector mInjector; + private final int mPowerComponentId; + private final String mPowerComponentName; + private final int mEnergyConsumerType; + private final String mEnergyConsumerName; + + private final BinaryStatePowerStatsLayout mLayout; + private boolean mIsInitialized; + + private PowerStats mPowerStats; + private ConsumedEnergyRetriever mConsumedEnergyRetriever; + private IntSupplier mVoltageSupplier; + private int[] mEnergyConsumerIds = new int[0]; + private long mLastConsumedEnergyUws = ENERGY_UNSPECIFIED; + private int mLastVoltageMv; + private long mLastUpdateTimestamp; + private boolean mFirstCollection = true; + + EnergyConsumerPowerStatsCollector(Injector injector, int powerComponentId, + String powerComponentName, @EnergyConsumerType int energyConsumerType, + String energyConsumerName, BinaryStatePowerStatsLayout statsLayout) { + super(injector.getHandler(), + injector.getPowerStatsCollectionThrottlePeriod(powerComponentName), + injector.getUidResolver(), injector.getClock()); + mInjector = injector; + mPowerComponentId = powerComponentId; + mPowerComponentName = powerComponentName; + mEnergyConsumerType = energyConsumerType; + mEnergyConsumerName = energyConsumerName; + mLayout = statsLayout; + } + + private boolean ensureInitialized() { + if (mIsInitialized) { + return true; + } + + if (!isEnabled()) { + return false; + } + + mConsumedEnergyRetriever = mInjector.getConsumedEnergyRetriever(); + mVoltageSupplier = mInjector.getVoltageSupplier(); + mEnergyConsumerIds = mConsumedEnergyRetriever.getEnergyConsumerIds(mEnergyConsumerType, + mEnergyConsumerName); + + PersistableBundle extras = new PersistableBundle(); + mLayout.toExtras(extras); + PowerStats.Descriptor powerStatsDescriptor = new PowerStats.Descriptor( + mPowerComponentId, mPowerComponentName, mLayout.getDeviceStatsArrayLength(), + null, 0, mLayout.getUidStatsArrayLength(), + extras); + mPowerStats = new PowerStats(powerStatsDescriptor); + + mIsInitialized = true; + return true; + } + + @Override + protected PowerStats collectStats() { + if (!ensureInitialized()) { + return null; + } + + if (mEnergyConsumerIds.length == 0) { + return null; + } + + long consumedEnergy = 0; + int voltageMv = mVoltageSupplier.getAsInt(); + if (voltageMv <= 0) { + Slog.wtf(TAG, "Unexpected battery voltage (" + voltageMv + + " mV) when querying energy consumers"); + } else { + long[] energyUws = mConsumedEnergyRetriever.getConsumedEnergyUws(mEnergyConsumerIds); + if (energyUws != null) { + for (int i = energyUws.length - 1; i >= 0; i--) { + if (energyUws[i] != ENERGY_UNSPECIFIED) { + consumedEnergy += energyUws[i]; + } + } + } + } + + long energyDelta = mLastConsumedEnergyUws != ENERGY_UNSPECIFIED + ? consumedEnergy - mLastConsumedEnergyUws : 0; + mLastConsumedEnergyUws = consumedEnergy; + if (energyDelta < 0) { + // Likely, restart of powerstats HAL + energyDelta = 0; + } + + if (energyDelta == 0 && !mFirstCollection) { + return null; + } + + int averageVoltage = mLastVoltageMv != 0 ? (mLastVoltageMv + voltageMv) / 2 : voltageMv; + mLastVoltageMv = voltageMv; + mLayout.setConsumedEnergy(mPowerStats.stats, 0, uJtoUc(energyDelta, averageVoltage)); + long timestamp = mClock.elapsedRealtime(); + mPowerStats.durationMs = timestamp - mLastUpdateTimestamp; + mLastUpdateTimestamp = timestamp; + mFirstCollection = false; + return mPowerStats; + } +} diff --git a/services/core/java/com/android/server/power/stats/FlashlightPowerStatsProcessor.java b/services/core/java/com/android/server/power/stats/FlashlightPowerStatsProcessor.java new file mode 100644 index 000000000000..f7216c9af9d6 --- /dev/null +++ b/services/core/java/com/android/server/power/stats/FlashlightPowerStatsProcessor.java @@ -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.server.power.stats; + +import android.os.BatteryConsumer; +import android.os.BatteryStats; + +import com.android.internal.os.PowerProfile; + +public class FlashlightPowerStatsProcessor extends BinaryStatePowerStatsProcessor { + public FlashlightPowerStatsProcessor(PowerProfile powerProfile, + PowerStatsUidResolver uidResolver) { + super(BatteryConsumer.POWER_COMPONENT_FLASHLIGHT, uidResolver, + powerProfile.getAveragePower(PowerProfile.POWER_FLASHLIGHT)); + } + + @Override + protected @BinaryState int getBinaryState(BatteryStats.HistoryItem item) { + return (item.states2 & BatteryStats.HistoryItem.STATE2_FLASHLIGHT_FLAG) != 0 + ? STATE_ON + : STATE_OFF; + } +} diff --git a/services/core/java/com/android/server/power/stats/GnssPowerStatsCollector.java b/services/core/java/com/android/server/power/stats/GnssPowerStatsCollector.java new file mode 100644 index 000000000000..168a8749b34c --- /dev/null +++ b/services/core/java/com/android/server/power/stats/GnssPowerStatsCollector.java @@ -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.server.power.stats; + +import android.hardware.power.stats.EnergyConsumerType; +import android.os.BatteryConsumer; + +public class GnssPowerStatsCollector extends EnergyConsumerPowerStatsCollector { + + GnssPowerStatsCollector(Injector injector) { + super(injector, BatteryConsumer.POWER_COMPONENT_GNSS, + BatteryConsumer.powerComponentIdToString(BatteryConsumer.POWER_COMPONENT_GNSS), + EnergyConsumerType.GNSS, /* energy consumer name */ null, + new GnssPowerStatsLayout()); + } +} diff --git a/services/core/java/com/android/server/power/stats/GnssPowerStatsLayout.java b/services/core/java/com/android/server/power/stats/GnssPowerStatsLayout.java new file mode 100644 index 000000000000..9a1317d2420c --- /dev/null +++ b/services/core/java/com/android/server/power/stats/GnssPowerStatsLayout.java @@ -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.server.power.stats; + +import android.location.GnssSignalQuality; +import android.os.PersistableBundle; + +class GnssPowerStatsLayout extends BinaryStatePowerStatsLayout { + private static final String EXTRA_DEVICE_TIME_SIGNAL_LEVEL_POSITION = "dt-sig"; + private static final String EXTRA_UID_TIME_SIGNAL_LEVEL_POSITION = "ut-sig"; + + private int mDeviceSignalLevelTimePosition; + private int mUidSignalLevelTimePosition; + + GnssPowerStatsLayout() { + mDeviceSignalLevelTimePosition = addDeviceSection( + GnssSignalQuality.NUM_GNSS_SIGNAL_QUALITY_LEVELS, "level"); + mUidSignalLevelTimePosition = addUidSection( + GnssSignalQuality.NUM_GNSS_SIGNAL_QUALITY_LEVELS, "level"); + } + + @Override + public void fromExtras(PersistableBundle extras) { + super.fromExtras(extras); + mDeviceSignalLevelTimePosition = extras.getInt(EXTRA_DEVICE_TIME_SIGNAL_LEVEL_POSITION); + mUidSignalLevelTimePosition = extras.getInt(EXTRA_UID_TIME_SIGNAL_LEVEL_POSITION); + } + + @Override + public void toExtras(PersistableBundle extras) { + super.toExtras(extras); + extras.putInt(EXTRA_DEVICE_TIME_SIGNAL_LEVEL_POSITION, mDeviceSignalLevelTimePosition); + extras.putInt(EXTRA_UID_TIME_SIGNAL_LEVEL_POSITION, mUidSignalLevelTimePosition); + } + + public void setDeviceSignalLevelTime(long[] stats, int signalLevel, long durationMillis) { + stats[mDeviceSignalLevelTimePosition + signalLevel] = durationMillis; + } + + public long getDeviceSignalLevelTime(long[] stats, int signalLevel) { + return stats[mDeviceSignalLevelTimePosition + signalLevel]; + } + + public void setUidSignalLevelTime(long[] stats, int signalLevel, long durationMillis) { + stats[mUidSignalLevelTimePosition + signalLevel] = durationMillis; + } + + public long getUidSignalLevelTime(long[] stats, int signalLevel) { + return stats[mUidSignalLevelTimePosition + signalLevel]; + } +} diff --git a/services/core/java/com/android/server/power/stats/GnssPowerStatsProcessor.java b/services/core/java/com/android/server/power/stats/GnssPowerStatsProcessor.java new file mode 100644 index 000000000000..572bde9b9266 --- /dev/null +++ b/services/core/java/com/android/server/power/stats/GnssPowerStatsProcessor.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.power.stats; + +import android.location.GnssSignalQuality; +import android.os.BatteryConsumer; +import android.os.BatteryStats; +import android.os.Process; + +import com.android.internal.os.PowerProfile; +import com.android.internal.os.PowerStats; + +import java.util.Arrays; + +public class GnssPowerStatsProcessor extends BinaryStatePowerStatsProcessor { + private int mGnssSignalLevel = GnssSignalQuality.GNSS_SIGNAL_QUALITY_UNKNOWN; + private long mGnssSignalLevelTimestamp; + private final long[] mGnssSignalDurations = + new long[GnssSignalQuality.NUM_GNSS_SIGNAL_QUALITY_LEVELS]; + private static final GnssPowerStatsLayout sStatsLayout = new GnssPowerStatsLayout(); + private final UsageBasedPowerEstimator[] mSignalLevelEstimators = + new UsageBasedPowerEstimator[GnssSignalQuality.NUM_GNSS_SIGNAL_QUALITY_LEVELS]; + private final boolean mUseSignalLevelEstimators; + private long[] mTmpDeviceStatsArray; + + public GnssPowerStatsProcessor(PowerProfile powerProfile, PowerStatsUidResolver uidResolver) { + super(BatteryConsumer.POWER_COMPONENT_GNSS, uidResolver, + powerProfile.getAveragePower(PowerProfile.POWER_GPS_ON), + sStatsLayout); + + boolean useSignalLevelEstimators = false; + for (int level = 0; level < GnssSignalQuality.NUM_GNSS_SIGNAL_QUALITY_LEVELS; level++) { + double power = powerProfile.getAveragePower( + PowerProfile.POWER_GPS_SIGNAL_QUALITY_BASED, level); + if (power != 0) { + useSignalLevelEstimators = true; + } + mSignalLevelEstimators[level] = new UsageBasedPowerEstimator(power); + } + mUseSignalLevelEstimators = useSignalLevelEstimators; + } + + @Override + protected @BinaryState int getBinaryState(BatteryStats.HistoryItem item) { + if ((item.states & BatteryStats.HistoryItem.STATE_GPS_ON_FLAG) == 0) { + mGnssSignalLevel = GnssSignalQuality.GNSS_SIGNAL_QUALITY_UNKNOWN; + return STATE_OFF; + } + + noteGnssSignalLevel(item); + return STATE_ON; + } + + private void noteGnssSignalLevel(BatteryStats.HistoryItem item) { + int signalLevel = (item.states2 & BatteryStats.HistoryItem.STATE2_GPS_SIGNAL_QUALITY_MASK) + >> BatteryStats.HistoryItem.STATE2_GPS_SIGNAL_QUALITY_SHIFT; + if (signalLevel >= GnssSignalQuality.NUM_GNSS_SIGNAL_QUALITY_LEVELS) { + signalLevel = GnssSignalQuality.GNSS_SIGNAL_QUALITY_UNKNOWN; + } + if (signalLevel == mGnssSignalLevel) { + return; + } + + if (mGnssSignalLevel != GnssSignalQuality.GNSS_SIGNAL_QUALITY_UNKNOWN) { + mGnssSignalDurations[mGnssSignalLevel] += item.time - mGnssSignalLevelTimestamp; + } + mGnssSignalLevel = signalLevel; + mGnssSignalLevelTimestamp = item.time; + } + + @Override + protected void recordUsageDuration(PowerStats powerStats, int uid, long time) { + super.recordUsageDuration(powerStats, uid, time); + + if (mGnssSignalLevel != GnssSignalQuality.GNSS_SIGNAL_QUALITY_UNKNOWN) { + mGnssSignalDurations[mGnssSignalLevel] += time - mGnssSignalLevelTimestamp; + } else if (mUseSignalLevelEstimators) { + // Default GNSS signal quality to GOOD for the purposes of power attribution + mGnssSignalDurations[GnssSignalQuality.GNSS_SIGNAL_QUALITY_GOOD] += + time - mGnssSignalLevelTimestamp; + } + + for (int level = 0; level < GnssSignalQuality.NUM_GNSS_SIGNAL_QUALITY_LEVELS; level++) { + long duration = mGnssSignalDurations[level]; + sStatsLayout.setDeviceSignalLevelTime(powerStats.stats, level, duration); + if (uid != Process.INVALID_UID) { + long[] uidStats = powerStats.uidStats.get(uid); + if (uidStats == null) { + uidStats = new long[powerStats.descriptor.uidStatsArrayLength]; + powerStats.uidStats.put(uid, uidStats); + sStatsLayout.setUidSignalLevelTime(uidStats, level, duration); + } else { + sStatsLayout.setUidSignalLevelTime(uidStats, level, + sStatsLayout.getUidSignalLevelTime(uidStats, level) + duration); + } + } + } + + mGnssSignalLevelTimestamp = time; + Arrays.fill(mGnssSignalDurations, 0); + } + + protected void computeDevicePowerEstimates(PowerComponentAggregatedPowerStats stats, + PowerEstimationPlan plan, boolean energyConsumerSupported) { + if (!mUseSignalLevelEstimators || energyConsumerSupported) { + super.computeDevicePowerEstimates(stats, plan, energyConsumerSupported); + return; + } + + if (mTmpDeviceStatsArray == null) { + mTmpDeviceStatsArray = new long[stats.getPowerStatsDescriptor().statsArrayLength]; + } + + for (int i = plan.deviceStateEstimations.size() - 1; i >= 0; i--) { + DeviceStateEstimation estimation = plan.deviceStateEstimations.get(i); + if (!stats.getDeviceStats(mTmpDeviceStatsArray, estimation.stateValues)) { + continue; + } + + double power = 0; + for (int level = 0; level < GnssSignalQuality.NUM_GNSS_SIGNAL_QUALITY_LEVELS; level++) { + long duration = sStatsLayout.getDeviceSignalLevelTime(mTmpDeviceStatsArray, level); + power += mSignalLevelEstimators[level].calculatePower(duration); + } + sStatsLayout.setDevicePowerEstimate(mTmpDeviceStatsArray, power); + stats.setDeviceStats(estimation.stateValues, mTmpDeviceStatsArray); + } + } +} diff --git a/services/core/java/com/android/server/power/stats/PowerStatsCollector.java b/services/core/java/com/android/server/power/stats/PowerStatsCollector.java index b82c0215013c..d442c61ee923 100644 --- a/services/core/java/com/android/server/power/stats/PowerStatsCollector.java +++ b/services/core/java/com/android/server/power/stats/PowerStatsCollector.java @@ -231,10 +231,14 @@ public abstract class PowerStatsCollector { } interface ConsumedEnergyRetriever { - int[] getEnergyConsumerIds(@EnergyConsumerType int energyConsumerType); + int[] getEnergyConsumerIds(@EnergyConsumerType int energyConsumerType, String name); @Nullable long[] getConsumedEnergyUws(int[] energyConsumerIds); + + default int[] getEnergyConsumerIds(@EnergyConsumerType int energyConsumerType) { + return getEnergyConsumerIds(energyConsumerType, null); + } } static class ConsumedEnergyRetrieverImpl implements ConsumedEnergyRetriever { @@ -245,7 +249,7 @@ public abstract class PowerStatsCollector { } @Override - public int[] getEnergyConsumerIds(int energyConsumerType) { + public int[] getEnergyConsumerIds(int energyConsumerType, String name) { if (mPowerStatsInternal == null) { return new int[0]; } @@ -257,7 +261,8 @@ public abstract class PowerStatsCollector { List<EnergyConsumer> energyConsumers = new ArrayList<>(); for (EnergyConsumer energyConsumer : energyConsumerInfo) { - if (energyConsumer.type == energyConsumerType) { + if (energyConsumer.type == energyConsumerType + && (name == null || name.equals(energyConsumer.name))) { energyConsumers.add(energyConsumer); } } diff --git a/services/core/java/com/android/server/power/stats/VideoPowerStatsProcessor.java b/services/core/java/com/android/server/power/stats/VideoPowerStatsProcessor.java new file mode 100644 index 000000000000..48dac8a8a970 --- /dev/null +++ b/services/core/java/com/android/server/power/stats/VideoPowerStatsProcessor.java @@ -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.server.power.stats; + +import android.os.BatteryConsumer; +import android.os.BatteryStats; + +import com.android.internal.os.PowerProfile; + +public class VideoPowerStatsProcessor extends BinaryStatePowerStatsProcessor { + public VideoPowerStatsProcessor(PowerProfile powerProfile, + PowerStatsUidResolver uidResolver) { + super(BatteryConsumer.POWER_COMPONENT_VIDEO, uidResolver, + powerProfile.getAveragePower(PowerProfile.POWER_VIDEO)); + } + + @Override + protected @BinaryState int getBinaryState(BatteryStats.HistoryItem item) { + return (item.states2 & BatteryStats.HistoryItem.STATE2_VIDEO_ON_FLAG) != 0 + ? STATE_ON + : STATE_OFF; + } +} diff --git a/services/core/java/com/android/server/power/stats/flags.aconfig b/services/core/java/com/android/server/power/stats/flags.aconfig index 9283fe9bb7b0..6a5a7ac8d240 100644 --- a/services/core/java/com/android/server/power/stats/flags.aconfig +++ b/services/core/java/com/android/server/power/stats/flags.aconfig @@ -37,3 +37,13 @@ flag { description: "Feature flag for streamlined misc (excluding CPU, Cell, Wifi, BT) battery stats" bug: "333941740" } + +flag { + name: "oneway_battery_stats_service" + namespace: "backstage_power" + description: "Bugfix flag for locking issues and watchdog kills in BatteryStatsService" + bug: "330792526" + metadata { + purpose: PURPOSE_BUGFIX + } +} diff --git a/services/core/java/com/android/server/powerstats/Android.bp b/services/core/java/com/android/server/powerstats/Android.bp new file mode 100644 index 000000000000..7f3b091f8969 --- /dev/null +++ b/services/core/java/com/android/server/powerstats/Android.bp @@ -0,0 +1,11 @@ +aconfig_declarations { + name: "powerstats_flags", + package: "com.android.server.powerstats", + container: "system", + srcs: ["*.aconfig"], +} + +java_aconfig_library { + name: "powerstats_flags_lib", + aconfig_declarations: "powerstats_flags", +} diff --git a/services/core/java/com/android/server/powerstats/TimerTrigger.java b/services/core/java/com/android/server/powerstats/TimerTrigger.java index f8a4135b9d66..817a40d80d04 100644 --- a/services/core/java/com/android/server/powerstats/TimerTrigger.java +++ b/services/core/java/com/android/server/powerstats/TimerTrigger.java @@ -16,8 +16,10 @@ package com.android.server.powerstats; +import android.app.AlarmManager; import android.content.Context; import android.os.Handler; +import android.os.SystemClock; import android.util.Slog; /** @@ -33,37 +35,53 @@ public final class TimerTrigger extends PowerStatsLogTrigger { private static final long LOG_PERIOD_MS_HIGH_FREQUENCY = 2 * 60 * 1000; // 2 minutes private final Handler mHandler; + private final AlarmManager mAlarmManager; + + class PeriodicTimer implements Runnable, AlarmManager.OnAlarmListener { + private final String mName; + private final long mPeriodMs; + private final int mMsgType; + + PeriodicTimer(String name, long periodMs, int msgType) { + mName = name; + mPeriodMs = periodMs; + mMsgType = msgType; + } - private Runnable mLogDataLowFrequency = new Runnable() { @Override - public void run() { - // Do not wake the device for these messages. Opportunistically log rail data every - // LOG_PERIOD_MS_LOW_FREQUENCY. - mHandler.postDelayed(mLogDataLowFrequency, LOG_PERIOD_MS_LOW_FREQUENCY); - if (DEBUG) Slog.d(TAG, "Received delayed message. Log rail data low frequency"); - logPowerStatsData(PowerStatsLogger.MSG_LOG_TO_DATA_STORAGE_LOW_FREQUENCY); + public void onAlarm() { + run(); } - }; - private Runnable mLogDataHighFrequency = new Runnable() { @Override public void run() { - // Do not wake the device for these messages. Opportunistically log rail data every - // LOG_PERIOD_MS_HIGH_FREQUENCY. - mHandler.postDelayed(mLogDataHighFrequency, LOG_PERIOD_MS_HIGH_FREQUENCY); - if (DEBUG) Slog.d(TAG, "Received delayed message. Log rail data high frequency"); - logPowerStatsData(PowerStatsLogger.MSG_LOG_TO_DATA_STORAGE_HIGH_FREQUENCY); + if (Flags.alarmBasedPowerstatsLogging()) { + final long nextAlarmMs = SystemClock.elapsedRealtime() + mPeriodMs; + mAlarmManager.set(AlarmManager.ELAPSED_REALTIME, nextAlarmMs, + AlarmManager.WINDOW_EXACT, 0, mName, this, mHandler, null); + } else { + mHandler.postDelayed(this, mPeriodMs); + } + if (DEBUG) Slog.d(TAG, "Received delayed message (" + mName + "). Logging rail data"); + logPowerStatsData(mMsgType); } - }; + } public TimerTrigger(Context context, PowerStatsLogger powerStatsLogger, boolean triggerEnabled) { super(context, powerStatsLogger); mHandler = mContext.getMainThreadHandler(); + mAlarmManager = mContext.getSystemService(AlarmManager.class); if (triggerEnabled) { - mLogDataLowFrequency.run(); - mLogDataHighFrequency.run(); + final PeriodicTimer logDataLowFrequency = new PeriodicTimer("PowerStatsLowFreqLog", + LOG_PERIOD_MS_LOW_FREQUENCY, + PowerStatsLogger.MSG_LOG_TO_DATA_STORAGE_LOW_FREQUENCY); + final PeriodicTimer logDataHighFrequency = new PeriodicTimer("PowerStatsHighFreqLog", + LOG_PERIOD_MS_HIGH_FREQUENCY, + PowerStatsLogger.MSG_LOG_TO_DATA_STORAGE_HIGH_FREQUENCY); + logDataLowFrequency.run(); + logDataHighFrequency.run(); } } } diff --git a/services/core/java/com/android/server/powerstats/flags.aconfig b/services/core/java/com/android/server/powerstats/flags.aconfig new file mode 100644 index 000000000000..0a4a7510aab8 --- /dev/null +++ b/services/core/java/com/android/server/powerstats/flags.aconfig @@ -0,0 +1,13 @@ + +package: "com.android.server.powerstats" +container: "system" + +flag { + name: "alarm_based_powerstats_logging" + namespace: "backstage_power" + description: "Utilize new OomAdjuster implementation" + bug: "294598168" + metadata { + purpose: PURPOSE_BUGFIX + } +}
\ No newline at end of file diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java index 4264e912f25a..85c8900555d9 100644 --- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java +++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java @@ -1296,7 +1296,7 @@ public class StatusBarManagerService extends IStatusBarService.Stub implements D synchronized (mIcons) { StatusBarIcon icon = new StatusBarIcon(iconPackage, UserHandle.SYSTEM, iconId, - iconLevel, 0, contentDescription); + iconLevel, 0, contentDescription, StatusBarIcon.Type.SystemIcon); //Slog.d(TAG, "setIcon slot=" + slot + " index=" + index + " icon=" + icon); mIcons.put(slot, icon); diff --git a/services/core/java/com/android/server/trust/TrustManagerService.java b/services/core/java/com/android/server/trust/TrustManagerService.java index 04db3e83ac71..3138a9ea9426 100644 --- a/services/core/java/com/android/server/trust/TrustManagerService.java +++ b/services/core/java/com/android/server/trust/TrustManagerService.java @@ -1870,6 +1870,11 @@ public class TrustManagerService extends SystemService { @Override public boolean isInSignificantPlace() { + if (android.security.Flags.significantPlaces()) { + mSignificantPlaceServiceWatcher.runOnBinder( + binder -> ISignificantPlaceProvider.Stub.asInterface(binder) + .onSignificantPlaceCheck()); + } return mIsInSignificantPlace; } diff --git a/services/core/java/com/android/server/tv/TvInputHardwareManager.java b/services/core/java/com/android/server/tv/TvInputHardwareManager.java index 92b57645b9a3..92b57645b9a3 100755..100644 --- a/services/core/java/com/android/server/tv/TvInputHardwareManager.java +++ b/services/core/java/com/android/server/tv/TvInputHardwareManager.java diff --git a/services/core/java/com/android/server/vibrator/VibratorController.java b/services/core/java/com/android/server/vibrator/VibratorController.java index 6710d02bee90..988e8fea70b9 100644 --- a/services/core/java/com/android/server/vibrator/VibratorController.java +++ b/services/core/java/com/android/server/vibrator/VibratorController.java @@ -56,10 +56,17 @@ final class VibratorController { private volatile boolean mIsUnderExternalControl; private volatile float mCurrentAmplitude; - /** Listener for vibration completion callbacks from native. */ + /** + * Listener for vibration completion callbacks from native. + * + * <p>Only the latest active native call to {@link VibratorController#on} will ever trigger this + * completion callback, to avoid race conditions during a vibration playback. If a new call to + * {@link #on} or {@link #off} happens before a previous callback was triggered then the + * previous callback will be disabled, even if the new command fails. + */ public interface OnVibrationCompleteListener { - /** Callback triggered when vibration is complete. */ + /** Callback triggered when an active vibration command is complete. */ void onComplete(int vibratorId, long vibrationId); } @@ -235,7 +242,7 @@ final class VibratorController { } /** - * Turn on the vibrator for {@code milliseconds} time, using {@code vibrationId} or completion + * Turn on the vibrator for {@code milliseconds} time, using {@code vibrationId} for completion * callback to {@link OnVibrationCompleteListener}. * * <p>This will affect the state of {@link #isVibrating()}. @@ -255,7 +262,7 @@ final class VibratorController { } /** - * Plays predefined vibration effect, using {@code vibrationId} or completion callback to + * Plays predefined vibration effect, using {@code vibrationId} for completion callback to * {@link OnVibrationCompleteListener}. * * <p>This will affect the state of {@link #isVibrating()}. @@ -276,8 +283,8 @@ final class VibratorController { } /** - * Plays a composition of vibration primitives, using {@code vibrationId} or completion callback - * to {@link OnVibrationCompleteListener}. + * Plays a composition of vibration primitives, using {@code vibrationId} for completion + * callback to {@link OnVibrationCompleteListener}. * * <p>This will affect the state of {@link #isVibrating()}. * @@ -299,7 +306,7 @@ final class VibratorController { } /** - * Plays a composition of pwle primitives, using {@code vibrationId} or completion callback + * Plays a composition of pwle primitives, using {@code vibrationId} for completion callback * to {@link OnVibrationCompleteListener}. * * <p>This will affect the state of {@link #isVibrating()}. @@ -321,7 +328,11 @@ final class VibratorController { } } - /** Turns off the vibrator. This will affect the state of {@link #isVibrating()}. */ + /** + * Turns off the vibrator and disables completion callback to any pending vibration. + * + * <p>This will affect the state of {@link #isVibrating()}. + */ public void off() { synchronized (mLock) { mNativeWrapper.off(); diff --git a/services/core/java/com/android/server/wallpaper/WallpaperCropper.java b/services/core/java/com/android/server/wallpaper/WallpaperCropper.java index 80f1125a4ecf..f70a3ba107e1 100644 --- a/services/core/java/com/android/server/wallpaper/WallpaperCropper.java +++ b/services/core/java/com/android/server/wallpaper/WallpaperCropper.java @@ -150,7 +150,7 @@ public class WallpaperCropper { Rect landscapeCrop = getCrop(rotatedDisplaySize, bitmapSize, suggestedCrops, rtl); landscapeCrop = noParallax(landscapeCrop, rotatedDisplaySize, bitmapSize, rtl); // compute the crop on portrait at the center of the landscape crop - crop = getAdjustedCrop(landscapeCrop, bitmapSize, displaySize, false, ADD); + crop = getAdjustedCrop(landscapeCrop, bitmapSize, displaySize, false, rtl, ADD); // add some parallax (until the border of the landscape crop without parallax) if (rtl) { @@ -160,7 +160,7 @@ public class WallpaperCropper { } } - return getAdjustedCrop(crop, bitmapSize, displaySize, true, ADD); + return getAdjustedCrop(crop, bitmapSize, displaySize, true, rtl, ADD); } // If any suggested crop is invalid, fallback to case 1 @@ -176,7 +176,7 @@ public class WallpaperCropper { // Case 2: if the orientation exists in the suggested crops, adjust the suggested crop Rect suggestedCrop = suggestedCrops.get(orientation); if (suggestedCrop != null) { - return getAdjustedCrop(suggestedCrop, bitmapSize, displaySize, true, ADD); + return getAdjustedCrop(suggestedCrop, bitmapSize, displaySize, true, rtl, ADD); } // Case 3: if we have the 90° rotated orientation in the suggested crops, reuse it and @@ -188,7 +188,7 @@ public class WallpaperCropper { if (suggestedCrop != null) { // only keep the visible part (without parallax) Rect adjustedCrop = noParallax(suggestedCrop, suggestedDisplaySize, bitmapSize, rtl); - return getAdjustedCrop(adjustedCrop, bitmapSize, displaySize, false, BALANCE); + return getAdjustedCrop(adjustedCrop, bitmapSize, displaySize, false, rtl, BALANCE); } // Case 4: if the device is a foldable, if we're looking for a folded orientation and have @@ -200,13 +200,13 @@ public class WallpaperCropper { // compute the visible part (without parallax) of the unfolded screen Rect adjustedCrop = noParallax(suggestedCrop, suggestedDisplaySize, bitmapSize, rtl); // compute the folded crop, at the center of the crop of the unfolded screen - Rect res = getAdjustedCrop(adjustedCrop, bitmapSize, displaySize, false, REMOVE); + Rect res = getAdjustedCrop(adjustedCrop, bitmapSize, displaySize, false, rtl, REMOVE); // if we removed some width, add it back to add a parallax effect if (res.width() < adjustedCrop.width()) { if (rtl) res.left = Math.min(res.left, adjustedCrop.left); else res.right = Math.max(res.right, adjustedCrop.right); // use getAdjustedCrop(parallax=true) to make sure we don't exceed MAX_PARALLAX - res = getAdjustedCrop(res, bitmapSize, displaySize, true, ADD); + res = getAdjustedCrop(res, bitmapSize, displaySize, true, rtl, ADD); } return res; } @@ -220,7 +220,7 @@ public class WallpaperCropper { if (suggestedCrop != null) { // only keep the visible part (without parallax) Rect adjustedCrop = noParallax(suggestedCrop, suggestedDisplaySize, bitmapSize, rtl); - return getAdjustedCrop(adjustedCrop, bitmapSize, displaySize, false, ADD); + return getAdjustedCrop(adjustedCrop, bitmapSize, displaySize, false, rtl, ADD); } // Case 6: for a foldable device, try to combine case 3 + case 4 or 5: @@ -255,7 +255,7 @@ public class WallpaperCropper { @VisibleForTesting static Rect noParallax(Rect crop, Point displaySize, Point bitmapSize, boolean rtl) { if (displaySize == null) return crop; - Rect adjustedCrop = getAdjustedCrop(crop, bitmapSize, displaySize, true, ADD); + Rect adjustedCrop = getAdjustedCrop(crop, bitmapSize, displaySize, true, rtl, ADD); // only keep the visible part (without parallax) float suggestedDisplayRatio = 1f * displaySize.x / displaySize.y; int widthToRemove = (int) (adjustedCrop.width() @@ -272,7 +272,7 @@ public class WallpaperCropper { * Adjust a given crop: * <ul> * <li>If parallax = true, make sure we have a parallax of at most {@link #MAX_PARALLAX}, - * by removing content from both sides if necessary. + * by removing content from the right (or left if RTL) if necessary. * <li>If parallax = false, make sure we do not have additional width for parallax. If we * have additional width for parallax, remove half of the additional width on both sides. * <li>Make sure the crop fills the screen, i.e. that the width/height ratio of the crop @@ -282,7 +282,7 @@ public class WallpaperCropper { */ @VisibleForTesting static Rect getAdjustedCrop(Rect crop, Point bitmapSize, Point screenSize, - boolean parallax, int mode) { + boolean parallax, boolean rtl, int mode) { Rect adjustedCrop = new Rect(crop); float cropRatio = ((float) crop.width()) / crop.height(); float screenRatio = ((float) screenSize.x) / screenSize.y; @@ -297,7 +297,8 @@ public class WallpaperCropper { Rect rotatedCrop = new Rect(newLeft, newTop, newRight, newBottom); Point rotatedBitmap = new Point(bitmapSize.y, bitmapSize.x); Point rotatedScreen = new Point(screenSize.y, screenSize.x); - Rect rect = getAdjustedCrop(rotatedCrop, rotatedBitmap, rotatedScreen, false, mode); + Rect rect = getAdjustedCrop( + rotatedCrop, rotatedBitmap, rotatedScreen, false, rtl, mode); int resultLeft = rect.top; int resultRight = resultLeft + rect.height(); int resultTop = rotatedBitmap.x - rect.right; @@ -308,8 +309,11 @@ public class WallpaperCropper { if (additionalWidthForParallax > MAX_PARALLAX) { int widthToRemove = (int) Math.ceil( (additionalWidthForParallax - MAX_PARALLAX) * screenRatio * crop.height()); - adjustedCrop.left += widthToRemove / 2; - adjustedCrop.right -= widthToRemove / 2 + widthToRemove % 2; + if (rtl) { + adjustedCrop.left += widthToRemove; + } else { + adjustedCrop.right -= widthToRemove; + } } } else { // Note: the third case when MODE == BALANCE, -W + sqrt(W * H * R), is the width to add diff --git a/services/core/java/com/android/server/wm/ActivityClientController.java b/services/core/java/com/android/server/wm/ActivityClientController.java index c9395daff974..3e177c9fe8c6 100644 --- a/services/core/java/com/android/server/wm/ActivityClientController.java +++ b/services/core/java/com/android/server/wm/ActivityClientController.java @@ -1299,9 +1299,11 @@ class ActivityClientController extends IActivityClientController.Stub { // The restore windowing mode must be set after the windowing mode is set since // Task#setWindowingMode resets the restore windowing mode to WINDOWING_MODE_INVALID. requester.mMultiWindowRestoreWindowingMode = restoreWindowingMode; + requester.mMultiWindowRestoreParent = + requester.getParent().mRemoteToken.toWindowContainerToken(); } else { targetWindowingMode = requester.mMultiWindowRestoreWindowingMode; - requester.setWindowingMode(targetWindowingMode); + requester.restoreWindowingMode(); } if (targetWindowingMode == WINDOWING_MODE_FULLSCREEN) { requester.setBounds(null); diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index 2d2a88a866ba..5159fc4009f7 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -156,6 +156,7 @@ import static com.android.internal.util.FrameworkStatsLog.APP_COMPAT_STATE_CHANG import static com.android.internal.util.FrameworkStatsLog.APP_COMPAT_STATE_CHANGED__STATE__LETTERBOXED_FOR_SIZE_COMPAT_MODE; import static com.android.internal.util.FrameworkStatsLog.APP_COMPAT_STATE_CHANGED__STATE__NOT_LETTERBOXED; import static com.android.internal.util.FrameworkStatsLog.APP_COMPAT_STATE_CHANGED__STATE__NOT_VISIBLE; +import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage; import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM; import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER; import static com.android.server.wm.ActivityRecord.State.DESTROYED; @@ -661,7 +662,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A */ private CompatDisplayInsets mCompatDisplayInsets; - private final TaskFragment.ConfigOverrideHint mResolveConfigHint; + @VisibleForTesting + final TaskFragment.ConfigOverrideHint mResolveConfigHint; private final boolean mOptOutEdgeToEdge; @@ -803,6 +805,11 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A final LetterboxUiController mLetterboxUiController; /** + * The policy for transparent activities + */ + final TransparentPolicy mTransparentPolicy; + + /** * The scale to fit at least one side of the activity to its parent. If the activity uses * 1920x1080, and the actually size on the screen is 960x540, then the scale is 0.5. */ @@ -1698,7 +1705,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A if (isState(RESUMED)) { newParent.setResumedActivity(this, "onParentChanged"); } - mLetterboxUiController.updateInheritedLetterbox(); + mTransparentPolicy.start(); } if (rootTask != null && rootTask.topRunningActivity() == this) { @@ -2136,6 +2143,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // Don't move below setOrientation(info.screenOrientation) since it triggers // getOverrideOrientation that requires having mLetterboxUiController // initialised. + mTransparentPolicy = new TransparentPolicy(this, mWmService.mLetterboxConfiguration); mLetterboxUiController = new LetterboxUiController(mWmService, this); mCameraCompatControlEnabled = mWmService.mContext.getResources() .getBoolean(R.bool.config_isCameraCompatControlForStretchedIssuesEnabled); @@ -2975,7 +2983,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A void removeStartingWindowAnimation(boolean prepareAnimation) { mTransferringSplashScreenState = TRANSFER_SPLASH_SCREEN_IDLE; - if (task != null) { + if (mStartingData != null && task != null) { task.mSharedStartingData = null; } if (mStartingWindow == null) { @@ -4026,6 +4034,10 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A if (next == null) { mRootWindowContainer.ensureVisibilityAndConfig(null /* starting */, mDisplayContent, true /* deferResume */); + if (mDisplayContent.topRunningActivity() == null) { + // The transition is ready on a display with no running activities. + mTransitionController.setReady(mDisplayContent); + } } if (activityRemoved) { mRootWindowContainer.resumeFocusedTasksTopActivities(); @@ -6021,14 +6033,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A true /* activityChange */, true /* updateOomAdj */, true /* addPendingTopUid */); } - final ContentCaptureManagerInternal contentCaptureService = - LocalServices.getService(ContentCaptureManagerInternal.class); - if (contentCaptureService != null) { - contentCaptureService.notifyActivityEvent(mUserId, mActivityComponent, - ActivityEvent.TYPE_ACTIVITY_STARTED, - new ActivityId(getTask() != null ? getTask().mTaskId : INVALID_TASK_ID, - shareableActivityToken)); - } + mAtmService.mH.post(this::notifyActivityStartedToContentCaptureService); break; case PAUSED: mAtmService.updateBatteryStats(this, false); @@ -6060,6 +6065,22 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } } + private void notifyActivityStartedToContentCaptureService() { + final ContentCaptureManagerInternal contentCaptureService = + LocalServices.getService(ContentCaptureManagerInternal.class); + if (contentCaptureService != null) { + // For ACTIVITY_STARTED content capture is directly invoked to avoid persisting + // to UsageStats. + contentCaptureService.notifyActivityEvent(mUserId, mActivityComponent, + ActivityEvent.TYPE_ACTIVITY_STARTED, + new ActivityId(getTask() != null ? getTask().mTaskId : INVALID_TASK_ID, + shareableActivityToken)); + + contentCaptureService.sendActivityStartAssistData(mUserId, + shareableActivityToken, intent); + } + } + State getState() { return mState; } @@ -6541,7 +6562,10 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // Schedule an idle timeout in case the app doesn't do it for us. mTaskSupervisor.scheduleIdleTimeout(this); - mTaskSupervisor.reportResumedActivityLocked(this); + mTaskSupervisor.mStoppingActivities.remove(this); + if (getDisplayArea().allResumedActivitiesComplete()) { + mRootWindowContainer.executeAppTransitionForAllDisplay(); + } resumeKeyDispatchingLocked(); final Task rootTask = getRootTask(); @@ -8080,13 +8104,13 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A @Configuration.Orientation int getRequestedConfigurationOrientation(boolean forDisplay, @ActivityInfo.ScreenOrientation int requestedOrientation) { - if (mLetterboxUiController.hasInheritedOrientation()) { + if (mTransparentPolicy.hasInheritedOrientation()) { final RootDisplayArea root = getRootDisplayArea(); if (forDisplay && root != null && root.isOrientationDifferentFromDisplay()) { return reverseConfigurationOrientation( - mLetterboxUiController.getInheritedOrientation()); + mTransparentPolicy.getInheritedOrientation()); } else { - return mLetterboxUiController.getInheritedOrientation(); + return mTransparentPolicy.getInheritedOrientation(); } } if (task != null && requestedOrientation == SCREEN_ORIENTATION_BEHIND) { @@ -8302,8 +8326,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A @Nullable CompatDisplayInsets getCompatDisplayInsets() { - if (mLetterboxUiController.hasInheritedLetterboxBehavior()) { - return mLetterboxUiController.getInheritedCompatDisplayInsets(); + if (mTransparentPolicy.isRunning()) { + return mTransparentPolicy.getInheritedCompatDisplayInsets(); } return mCompatDisplayInsets; } @@ -8466,7 +8490,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } mSizeCompatBounds = null; mCompatDisplayInsets = null; - mLetterboxUiController.clearInheritedCompatDisplayInsets(); + mTransparentPolicy.clearInheritedCompatDisplayInsets(); } @VisibleForTesting @@ -8527,6 +8551,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A mIsEligibleForFixedOrientationLetterbox = false; mLetterboxBoundsForFixedOrientationAndAspectRatio = null; mLetterboxBoundsForAspectRatio = null; + mResolveConfigHint.resolveTmpOverrides(mDisplayContent, newParentConfiguration, + isFixedRotationTransforming()); // Can't use resolvedConfig.windowConfiguration.getWindowingMode() because it can be // different from windowing mode of the task (PiP) during transition from fullscreen to PiP @@ -8641,10 +8667,15 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } applySizeOverrideIfNeeded(newParentConfiguration, parentWindowingMode, resolvedConfig); + mResolveConfigHint.resetTmpOverrides(); logAppCompatState(); } + @Nullable Rect getParentAppBoundsOverride() { + return Rect.copyOrNull(mResolveConfigHint.mTmpParentAppBoundsOverride); + } + /** * If necessary, override configuration fields related to app bounds. * This will happen when the app is targeting SDK earlier than 35. @@ -8668,8 +8699,12 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A rotation = mDisplayContent.getRotation(); } if (!mOptOutEdgeToEdge && (!mResolveConfigHint.mUseOverrideInsetsForConfig - || getCompatDisplayInsets() != null || shouldCreateCompatDisplayInsets() - || isFloating(parentWindowingMode) || rotation == ROTATION_UNDEFINED)) { + || getCompatDisplayInsets() != null + || (isFloating(parentWindowingMode) + // Check the windowing mode of activity as well in case it is switching + // between PiP and fullscreen. + && isFloating(inOutConfig.windowConfiguration.getWindowingMode())) + || rotation == ROTATION_UNDEFINED)) { // If the insets configuration decoupled logic is not enabled for the app, or the app // already has a compat override, or the context doesn't contain enough info to // calculate the override, skip the override. @@ -8691,7 +8726,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A : mDisplayContent.mBaseDisplayWidth; final int dh = rotated ? mDisplayContent.mBaseDisplayWidth : mDisplayContent.mBaseDisplayHeight; - final Rect nonDecorInsets = mDisplayContent.getDisplayPolicy() + final Rect nonDecorInsets = mDisplayContent.getDisplayPolicy() .getDecorInsetsInfo(rotation, dw, dh).mOverrideNonDecorInsets; // This should be the only place override the configuration for ActivityRecord. Override // the value if not calculated yet. @@ -8707,12 +8742,10 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } density *= DisplayMetrics.DENSITY_DEFAULT_SCALE; if (inOutConfig.screenWidthDp == Configuration.SCREEN_WIDTH_DP_UNDEFINED) { - final int overrideScreenWidthDp = (int) (outAppBounds.width() / density + 0.5f); - inOutConfig.screenWidthDp = overrideScreenWidthDp; + inOutConfig.screenWidthDp = (int) (outAppBounds.width() / density + 0.5f); } if (inOutConfig.screenHeightDp == Configuration.SCREEN_HEIGHT_DP_UNDEFINED) { - final int overrideScreenHeightDp = (int) (outAppBounds.height() / density + 0.5f); - inOutConfig.screenHeightDp = overrideScreenHeightDp; + inOutConfig.screenHeightDp = (int) (outAppBounds.height() / density + 0.5f); } if (inOutConfig.smallestScreenWidthDp == Configuration.SMALLEST_SCREEN_WIDTH_DP_UNDEFINED @@ -8784,8 +8817,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A return APP_COMPAT_STATE_CHANGED__STATE__NOT_VISIBLE; } // TODO(b/256564921): Investigate if we need new metrics for translucent activities - if (mLetterboxUiController.hasInheritedLetterboxBehavior()) { - return mLetterboxUiController.getInheritedAppCompatState(); + if (mTransparentPolicy.isRunning()) { + return mTransparentPolicy.getInheritedAppCompatState(); } if (mInSizeCompatModeForBounds) { return APP_COMPAT_STATE_CHANGED__STATE__LETTERBOXED_FOR_SIZE_COMPAT_MODE; @@ -8823,7 +8856,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } final Rect screenResolvedBounds = mSizeCompatBounds != null ? mSizeCompatBounds : resolvedBounds; - final Rect parentAppBounds = newParentConfiguration.windowConfiguration.getAppBounds(); + final Rect parentAppBounds = mResolveConfigHint.mTmpParentAppBoundsOverride; final Rect parentBounds = newParentConfiguration.windowConfiguration.getBounds(); final float screenResolvedBoundsWidth = screenResolvedBounds.width(); final float parentAppBoundsWidth = parentAppBounds.width(); @@ -8938,7 +8971,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // We check if the current activity is transparent. In that case we need to // recomputeConfiguration of the first opaque activity beneath, to allow a // proper computation of the new bounds. - if (!mLetterboxUiController.applyOnOpaqueActivityBelow( + if (!mTransparentPolicy.applyOnOpaqueActivityBelow( ActivityRecord::recomputeConfiguration)) { onRequestedOverrideConfigurationChanged(getRequestedOverrideConfiguration()); } @@ -9232,7 +9265,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A */ private void resolveAspectRatioRestriction(Configuration newParentConfiguration) { final Configuration resolvedConfig = getResolvedOverrideConfiguration(); - final Rect parentAppBounds = newParentConfiguration.windowConfiguration.getAppBounds(); + final Rect parentAppBounds = mResolveConfigHint.mTmpParentAppBoundsOverride; final Rect parentBounds = newParentConfiguration.windowConfiguration.getBounds(); final Rect resolvedBounds = resolvedConfig.windowConfiguration.getBounds(); // Use tmp bounds to calculate aspect ratio so we can know whether the activity should use @@ -9261,19 +9294,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A @NonNull CompatDisplayInsets compatDisplayInsets) { final Configuration resolvedConfig = getResolvedOverrideConfiguration(); final Rect resolvedBounds = resolvedConfig.windowConfiguration.getBounds(); - final Insets insets; - if (mResolveConfigHint.mUseOverrideInsetsForConfig) { - // TODO(b/343197837): Add test to verify SCM behaviour with new bound configuration - // Insets are decoupled from configuration by default from V+, use legacy - // compatibility behaviour for apps targeting SDK earlier than 35 - // (see applySizeOverrideIfNeeded). - insets = Insets.of(mDisplayContent.getDisplayPolicy() - .getDecorInsetsInfo(mDisplayContent.mDisplayFrames.mRotation, - mDisplayContent.mDisplayFrames.mWidth, - mDisplayContent.mDisplayFrames.mHeight).mOverrideNonDecorInsets); - } else { - insets = Insets.NONE; - } // When an activity needs to be letterboxed because of fixed orientation, use fixed // orientation bounds (stored in resolved bounds) instead of parent bounds since the @@ -9284,22 +9304,22 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A final Rect containerBounds = useResolvedBounds ? new Rect(resolvedBounds) : newParentConfiguration.windowConfiguration.getBounds(); - final Rect parentAppBounds = - newParentConfiguration.windowConfiguration.getAppBounds(); - parentAppBounds.inset(insets); final Rect containerAppBounds = useResolvedBounds ? new Rect(resolvedConfig.windowConfiguration.getAppBounds()) - : parentAppBounds; + : mResolveConfigHint.mTmpParentAppBoundsOverride; final int requestedOrientation = getRequestedConfigurationOrientation(); final boolean orientationRequested = requestedOrientation != ORIENTATION_UNDEFINED; + final int parentOrientation = mResolveConfigHint.mUseOverrideInsetsForConfig + ? mResolveConfigHint.mTmpOverrideConfigOrientation + : newParentConfiguration.orientation; final int orientation = orientationRequested ? requestedOrientation // We should use the original orientation of the activity when possible to avoid // forcing the activity in the opposite orientation. : compatDisplayInsets.mOriginalRequestedOrientation != ORIENTATION_UNDEFINED ? compatDisplayInsets.mOriginalRequestedOrientation - : newParentConfiguration.orientation; + : parentOrientation; int rotation = newParentConfiguration.windowConfiguration.getRotation(); final boolean isFixedToUserRotation = mDisplayContent == null || mDisplayContent.getDisplayRotation().isFixedToUserRotation(); @@ -9341,7 +9361,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // Use parent orientation if it cannot be decided by bounds, so the activity can fit inside // the parent bounds appropriately. if (resolvedConfig.screenWidthDp == resolvedConfig.screenHeightDp) { - resolvedConfig.orientation = newParentConfiguration.orientation; + resolvedConfig.orientation = parentOrientation; } // Below figure is an example that puts an activity which was launched in a larger container @@ -9411,7 +9431,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A void updateSizeCompatScale(Rect resolvedAppBounds, Rect containerAppBounds) { // Only allow to scale down. - mSizeCompatScale = mLetterboxUiController.findOpaqueNotFinishingActivityBelow() + mSizeCompatScale = mTransparentPolicy.findOpaqueNotFinishingActivityBelow() .map(activityRecord -> activityRecord.mSizeCompatScale) .orElseGet(() -> { final int contentW = resolvedAppBounds.width(); @@ -9424,7 +9444,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } private boolean isInSizeCompatModeForBounds(final Rect appBounds, final Rect containerBounds) { - if (mLetterboxUiController.hasInheritedLetterboxBehavior()) { + if (mTransparentPolicy.isRunning()) { // To avoid wrong app behaviour, we decided to disable SCM when a translucent activity // is letterboxed. return false; @@ -9487,7 +9507,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A public Rect getBounds() { // TODO(b/268458693): Refactor configuration inheritance in case of translucent activities final Rect superBounds = super.getBounds(); - return mLetterboxUiController.findOpaqueNotFinishingActivityBelow() + return mTransparentPolicy.findOpaqueNotFinishingActivityBelow() .map(ActivityRecord::getBounds) .orElseGet(() -> { if (mSizeCompatBounds != null) { @@ -9851,8 +9871,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A * Returns the min aspect ratio of this activity. */ float getMinAspectRatio() { - if (mLetterboxUiController.hasInheritedLetterboxBehavior()) { - return mLetterboxUiController.getInheritedMinAspectRatio(); + if (mTransparentPolicy.isRunning()) { + return mTransparentPolicy.getInheritedMinAspectRatio(); } if (info.applicationInfo == null) { return info.getMinAspectRatio(); @@ -9902,8 +9922,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } float getMaxAspectRatio() { - if (mLetterboxUiController.hasInheritedLetterboxBehavior()) { - return mLetterboxUiController.getInheritedMaxAspectRatio(); + if (mTransparentPolicy.isRunning()) { + return mTransparentPolicy.getInheritedMaxAspectRatio(); } return info.getMaxAspectRatio(); } diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java index 72b854be74bd..e6d81324efa1 100644 --- a/services/core/java/com/android/server/wm/ActivityStarter.java +++ b/services/core/java/com/android/server/wm/ActivityStarter.java @@ -139,6 +139,7 @@ import com.android.server.wm.BackgroundActivityStartController.BalCode; import com.android.server.wm.BackgroundActivityStartController.BalVerdict; import com.android.server.wm.LaunchParamsController.LaunchParams; import com.android.server.wm.TaskFragment.EmbeddingCheckResult; +import com.android.wm.shell.Flags; import java.io.PrintWriter; import java.lang.annotation.Retention; @@ -936,13 +937,8 @@ class ActivityStarter { } mLastStartReason = request.reason; mLastStartActivityTimeMs = System.currentTimeMillis(); - // Reset the ActivityRecord#mCurrentLaunchCanTurnScreenOn state of last start activity in - // case the state is not yet consumed during rapid activity launch. - if (mLastStartActivityRecord != null) { - mLastStartActivityRecord.setCurrentLaunchCanTurnScreenOn(false); - } - mLastStartActivityRecord = null; + final ActivityRecord previousStart = mLastStartActivityRecord; final IApplicationThread caller = request.caller; Intent intent = request.intent; NeededUriGrants intentGrants = request.intentGrants; @@ -1728,7 +1724,14 @@ class ActivityStarter { // Get top task at beginning because the order may be changed when reusing existing task. final Task prevTopRootTask = mPreferredTaskDisplayArea.getFocusedRootTask(); final Task prevTopTask = prevTopRootTask != null ? prevTopRootTask.getTopLeafTask() : null; - final Task reusedTask = resolveReusableTask(); + final boolean sourceActivityLaunchedFromBubble = + sourceRecord != null && sourceRecord.getLaunchedFromBubble(); + // if the flag is enabled, allow reusing bubbled tasks only if the source activity is + // bubbled. + final boolean includeLaunchedFromBubble = + Flags.onlyReuseBubbledTaskWhenLaunchedFromBubble() + ? sourceActivityLaunchedFromBubble : true; + final Task reusedTask = resolveReusableTask(includeLaunchedFromBubble); // If requested, freeze the task list if (mOptions != null && mOptions.freezeRecentTasksReordering() @@ -2727,8 +2730,11 @@ class ActivityStarter { /** * Decide whether the new activity should be inserted into an existing task. Returns null * if not or an ActivityRecord with the task into which the new activity should be added. + * + * @param includeLaunchedFromBubble whether a task whose top activity was launched from a bubble + * should be allowed to be reused for the new activity. */ - private Task resolveReusableTask() { + private Task resolveReusableTask(boolean includeLaunchedFromBubble) { // If a target task is specified, try to reuse that one if (mOptions != null && mOptions.getLaunchTaskId() != INVALID_TASK_ID) { Task launchTask = mRootWindowContainer.anyTaskForId(mOptions.getLaunchTaskId()); @@ -2772,7 +2778,8 @@ class ActivityStarter { } else { // Otherwise find the best task to put the activity in. intentActivity = - mRootWindowContainer.findTask(mStartActivity, mPreferredTaskDisplayArea); + mRootWindowContainer.findTask(mStartActivity, mPreferredTaskDisplayArea, + includeLaunchedFromBubble); } } diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java index 3aa63af014c8..cfd5300417b4 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java @@ -1439,6 +1439,8 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { resultTo.removeResultsLocked(r, resultWho, requestCode); } + final int origCallingUid = Binder.getCallingUid(); + final int origCallingPid = Binder.getCallingPid(); final long origId = Binder.clearCallingIdentity(); // TODO(b/64750076): Check if calling pid should really be -1. try { @@ -1446,13 +1448,14 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { options = new SafeActivityOptions(ActivityOptions.makeBasic()); } - // Fixes b/230492947 + // Fixes b/230492947 b/337726734 // Prevents background activity launch through #startNextMatchingActivity - // An activity going into the background could still go back to the foreground - // if the intent used matches both: - // - the activity in the background - // - a second activity. - options.getOptions(r).setAvoidMoveToFront(); + // launchedFromUid of the calling activity represents the app that launches it. + // It may have BAL privileges (i.e. the Launcher App). Using its identity to + // launch to launch next matching activity causes BAL. + // Change the realCallingUid to the calling activity's uid. + // In ActivityStarter, when caller is set, the callingUid and callingPid are + // ignored. So now both callingUid and realCallingUid is set to the caller app. final int res = getActivityStartController() .obtainStarter(intent, "startNextMatchingActivity") .setCaller(r.app.getThread()) @@ -1465,8 +1468,8 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { .setCallingUid(r.launchedFromUid) .setCallingPackage(r.launchedFromPackage) .setCallingFeatureId(r.launchedFromFeatureId) - .setRealCallingPid(-1) - .setRealCallingUid(r.launchedFromUid) + .setRealCallingPid(origCallingPid) + .setRealCallingUid(origCallingUid) .setActivityOptions(options) .setUserId(userId) .execute(); @@ -1507,7 +1510,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { a.persistableMode = ActivityInfo.PERSIST_NEVER; a.screenOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; a.colorMode = ActivityInfo.COLOR_MODE_DEFAULT; - a.flags |= ActivityInfo.FLAG_EXCLUDE_FROM_RECENTS; + a.flags |= ActivityInfo.FLAG_EXCLUDE_FROM_RECENTS | ActivityInfo.FLAG_SHOW_WHEN_LOCKED; a.configChanges = 0xffffffff; if (homePanelDream()) { diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java index a10f7e7b00d0..b6e6991656f2 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java +++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java @@ -155,7 +155,7 @@ import com.android.server.LocalServices; import com.android.server.am.ActivityManagerService; import com.android.server.am.HostingRecord; import com.android.server.am.UserState; -import com.android.server.pm.PackageManagerServiceUtils; +import com.android.server.pm.SaferIntentUtils; import com.android.server.utils.Slogf; import com.android.server.wm.ActivityMetricsLogger.LaunchingState; @@ -769,7 +769,7 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks { // (e.g. AMS.startActivityAsUser). final long token = Binder.clearCallingIdentity(); try { - return mService.getPackageManagerInternalLocked().resolveIntentExported( + return mService.getPackageManagerInternalLocked().resolveIntent( intent, resolvedType, modifiedFlags, privateResolveFlags, userId, true, filterCallingUid, callingPid); } finally { @@ -2064,21 +2064,6 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks { } } - boolean reportResumedActivityLocked(ActivityRecord r) { - // A resumed activity cannot be stopping. remove from list - mStoppingActivities.remove(r); - - final Task rootTask = r.getRootTask(); - if (rootTask.getDisplayArea().allResumedActivitiesComplete()) { - mRootWindowContainer.ensureActivitiesVisible(); - // Make sure activity & window visibility should be identical - // for all displays in this stage. - mRootWindowContainer.executeAppTransitionForAllDisplay(); - return true; - } - return false; - } - // Called when WindowManager has finished animating the launchingBehind activity to the back. private void handleLaunchTaskBehindCompleteLocked(ActivityRecord r) { final Task task = r.getTask(); @@ -2856,14 +2841,14 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks { // We need to temporarily disable the explicit intent filter matching enforcement // because Task does not store the resolved type of the intent data, causing filter // mismatch in certain cases. (b/240373119) - PackageManagerServiceUtils.DISABLE_ENFORCE_INTENTS_TO_MATCH_INTENT_FILTERS.set(true); + SaferIntentUtils.DISABLE_ENFORCE_INTENTS_TO_MATCH_INTENT_FILTERS.set(true); return mService.getActivityStartController().startActivityInPackage(taskCallingUid, callingPid, callingUid, callingPackage, callingFeatureId, intent, null, null, null, 0, 0, options, userId, task, "startActivityFromRecents", false /* validateIncomingUser */, null /* originatingPendingIntent */, BackgroundStartPrivileges.NONE); } finally { - PackageManagerServiceUtils.DISABLE_ENFORCE_INTENTS_TO_MATCH_INTENT_FILTERS.set(false); + SaferIntentUtils.DISABLE_ENFORCE_INTENTS_TO_MATCH_INTENT_FILTERS.set(false); synchronized (mService.mGlobalLock) { mService.continueWindowLayout(); } diff --git a/services/core/java/com/android/server/wm/BackNavigationController.java b/services/core/java/com/android/server/wm/BackNavigationController.java index f91ef1d41a0c..0febec9169c0 100644 --- a/services/core/java/com/android/server/wm/BackNavigationController.java +++ b/services/core/java/com/android/server/wm/BackNavigationController.java @@ -200,6 +200,7 @@ class BackNavigationController { } infoBuilder.setOnBackInvokedCallback(callbackInfo.getCallback()); infoBuilder.setAnimationCallback(callbackInfo.isAnimationCallback()); + infoBuilder.setTouchableRegion(window.getFrame()); mNavigationMonitor.startMonitor(window, navigationObserver); ProtoLog.d(WM_DEBUG_BACK_PREVIEW, "startBackNavigation currentTask=%s, " diff --git a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java index 19d7a3c8d86c..a4fb95964a5c 100644 --- a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java +++ b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java @@ -427,19 +427,6 @@ public class BackgroundActivityStartController { return name + "[debugOnly]"; } - /** @return valid targetSdk or <code>-1</code> */ - private int getTargetSdk(String packageName) { - if (packageName == null) { - return -1; - } - try { - PackageManager pm = mService.mContext.getPackageManager(); - return pm.getTargetSdkVersion(packageName); - } catch (Exception e) { - return -1; - } - } - private boolean hasRealCaller() { return mRealCallingUid != NO_PROCESS_UID; } @@ -1730,7 +1717,9 @@ public class BackgroundActivityStartController { state.mResultForRealCaller == null ? BAL_BLOCK : state.mResultForRealCaller.getRawCode(), state.mBalAllowedByPiSender.allowsBackgroundActivityStarts(), - state.realCallerExplicitOptInOrOut() + state.realCallerExplicitOptInOrOut(), + getTargetSdk(state.mCallingPackage), + getTargetSdk(state.mRealCallingPackage) ); } @@ -1811,6 +1800,19 @@ public class BackgroundActivityStartController { + ", taskFragment=" + ar.getTaskFragment(); } + /** @return valid targetSdk or <code>-1</code> */ + private int getTargetSdk(String packageName) { + if (packageName == null) { + return -1; + } + try { + PackageManager pm = mService.mContext.getPackageManager(); + return pm.getTargetSdkVersion(packageName); + } catch (Exception e) { + return -1; + } + } + private class FinishedActivityEntry { int mUid; int mTaskId; diff --git a/services/core/java/com/android/server/wm/DeferredDisplayUpdater.java b/services/core/java/com/android/server/wm/DeferredDisplayUpdater.java index 125eb2a3a810..be44629a1fcf 100644 --- a/services/core/java/com/android/server/wm/DeferredDisplayUpdater.java +++ b/services/core/java/com/android/server/wm/DeferredDisplayUpdater.java @@ -194,6 +194,16 @@ public class DeferredDisplayUpdater implements DisplayUpdater { final Rect startBounds = new Rect(0, 0, mDisplayContent.mInitialDisplayWidth, mDisplayContent.mInitialDisplayHeight); final int fromRotation = mDisplayContent.getRotation(); + if (Flags.blastSyncNotificationShadeOnDisplaySwitch() && physicalDisplayUpdated) { + final WindowState notificationShade = + mDisplayContent.getDisplayPolicy().getNotificationShade(); + if (notificationShade != null && notificationShade.isVisible() + && mDisplayContent.mAtmService.mKeyguardController.isKeyguardOrAodShowing( + mDisplayContent.mDisplayId)) { + Slog.i(TAG, notificationShade + " uses blast for display switch"); + notificationShade.mSyncMethodOverride = BLASTSyncEngine.METHOD_BLAST; + } + } mDisplayContent.mAtmService.deferWindowLayout(); try { diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index e2b09326af07..87ee5d8f7f13 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -4160,6 +4160,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp && mImeLayeringTarget != null && mImeLayeringTarget.mActivityRecord != null && mImeLayeringTarget.getWindowingMode() == WINDOWING_MODE_FULLSCREEN + && mImeLayeringTarget.getBounds().equals(mImeWindowsContainer.getBounds()) // IME is attached to app windows that fill display area. This excludes // letterboxed windows. && mImeLayeringTarget.matchesDisplayAreaBounds(); @@ -4191,7 +4192,12 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp && target.getDisplayContent().getImePolicy() == DISPLAY_IME_POLICY_LOCAL) { return target; } - return getImeFallback(); + if (android.view.inputmethod.Flags.refactorInsetsController()) { + final DisplayContent defaultDc = mWmService.getDefaultDisplayContentLocked(); + return defaultDc.mRemoteInsetsControlTarget; + } else { + return getImeFallback(); + } } InsetsControlTarget getImeFallback() { @@ -4624,6 +4630,10 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp && !mInputMethodSurfaceParent.isSameSurface( mImeWindowsContainer.getParent().mSurfaceControl)); updateImeControlTarget(forceUpdateImeParent); + + if (android.view.inputmethod.Flags.refactorInsetsController()) { + mInsetsStateController.getImeSourceProvider().onInputTargetChanged(target); + } } } @@ -4727,6 +4737,14 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp // in case seeing unexpected IME surface visibility change when delivering the IME leash // to the remote insets target during the IME restarting, but the focus window is not in // multi-windowing mode, return null target until the next input target updated. + if (android.view.inputmethod.Flags.refactorInsetsController()) { + // The control target could be the RemoteInsetsControlTarget (if the focussed + // view is on a virtual display that can not show the IME (and therefore it will + // be shown on the default display) + if (isDefaultDisplay && mRemoteInsetsControlTarget != null) { + return mRemoteInsetsControlTarget; + } + } return null; } @@ -5701,15 +5719,21 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp * @see Display#FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS */ boolean supportsSystemDecorations() { + boolean forceDesktopModeOnDisplay = forceDesktopMode(); + + if (com.android.window.flags.Flags.rearDisplayDisableForceDesktopSystemDecorations()) { + // System decorations should not be forced on a rear display due to security policies. + forceDesktopModeOnDisplay = + forceDesktopModeOnDisplay && ((mDisplay.getFlags() & Display.FLAG_REAR) == 0); + } + return (mWmService.mDisplayWindowSettings.shouldShowSystemDecorsLocked(this) || (mDisplay.getFlags() & FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS) != 0 - || forceDesktopMode()) + || forceDesktopModeOnDisplay) // VR virtual display will be used to run and render 2D app within a VR experience. && mDisplayId != mWmService.mVr2dDisplayId // Do not show system decorations on untrusted virtual display. - && isTrusted() - // No system decoration on rear display. - && (mDisplay.getFlags() & Display.FLAG_REAR) == 0; + && isTrusted(); } /** @@ -7062,9 +7086,13 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp @Override public boolean isRequestedVisible(@InsetsType int types) { - return ((types & ime()) != 0 - && getInsetsStateController().getImeSourceProvider().isImeShowing()) - || (mRequestedVisibleTypes & types) != 0; + if (android.view.inputmethod.Flags.refactorInsetsController()) { + return (mRequestedVisibleTypes & types) != 0; + } else { + return ((types & ime()) != 0 + && getInsetsStateController().getImeSourceProvider().isImeShowing()) + || (mRequestedVisibleTypes & types) != 0; + } } @Override @@ -7072,6 +7100,18 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp return mRequestedVisibleTypes; } + @Override + public void setImeInputTargetRequestedVisibility(boolean visible) { + if (android.view.inputmethod.Flags.refactorInsetsController()) { + try { + // TODO stats token + mRemoteInsetsController.setImeInputTargetRequestedVisibility(visible); + } catch (RemoteException e) { + Slog.w(TAG, "Failed to deliver setImeInputTargetRequestedVisibility", e); + } + } + } + /** * @see #getRequestedVisibleTypes() */ diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java index d0086aa24337..2f2395502e4b 100644 --- a/services/core/java/com/android/server/wm/DisplayPolicy.java +++ b/services/core/java/com/android/server/wm/DisplayPolicy.java @@ -2011,9 +2011,14 @@ public class DisplayPolicy { public String toString() { final StringBuilder tmpSb = new StringBuilder(32); return "{nonDecorInsets=" + mNonDecorInsets.toShortString(tmpSb) + + ", overrideNonDecorInsets=" + mOverrideNonDecorInsets.toShortString(tmpSb) + ", configInsets=" + mConfigInsets.toShortString(tmpSb) + + ", overrideConfigInsets=" + mOverrideConfigInsets.toShortString(tmpSb) + ", nonDecorFrame=" + mNonDecorFrame.toShortString(tmpSb) - + ", configFrame=" + mConfigFrame.toShortString(tmpSb) + '}'; + + ", overrideNonDecorFrame=" + mOverrideNonDecorFrame.toShortString(tmpSb) + + ", configFrame=" + mConfigFrame.toShortString(tmpSb) + + ", overrideConfigFrame=" + mOverrideConfigFrame.toShortString(tmpSb) + + '}'; } } diff --git a/services/core/java/com/android/server/wm/EmbeddedWindowController.java b/services/core/java/com/android/server/wm/EmbeddedWindowController.java index 0978cb49e863..a21ba2603ec6 100644 --- a/services/core/java/com/android/server/wm/EmbeddedWindowController.java +++ b/services/core/java/com/android/server/wm/EmbeddedWindowController.java @@ -77,12 +77,14 @@ class EmbeddedWindowController { mWindows.put(inputToken, window); final InputTransferToken inputTransferToken = window.getInputTransferToken(); mWindowsByInputTransferToken.put(inputTransferToken, window); - mWindowsByWindowToken.put(window.getWindowToken(), window); + final IBinder windowToken = window.getWindowToken(); + mWindowsByWindowToken.put(windowToken, window); updateProcessController(window); window.mClient.linkToDeath(()-> { synchronized (mGlobalLock) { mWindows.remove(inputToken); mWindowsByInputTransferToken.remove(inputTransferToken); + mWindowsByWindowToken.remove(windowToken); } }, 0); } catch (RemoteException e) { diff --git a/services/core/java/com/android/server/wm/EventLogTags.logtags b/services/core/java/com/android/server/wm/EventLogTags.logtags index d957591ab7f9..cc2249de010c 100644 --- a/services/core/java/com/android/server/wm/EventLogTags.logtags +++ b/services/core/java/com/android/server/wm/EventLogTags.logtags @@ -59,6 +59,10 @@ option java_package com.android.server.wm 31002 wm_task_moved (TaskId|1|5),(Root Task ID|1|5),(Display Id|1|5),(ToTop|1),(Index|1) # Task removed with source explanation. 31003 wm_task_removed (TaskId|1|5),(Root Task ID|1|5),(Display Id|1|5),(Reason|3) +# Embedded TaskFragment created +31004 wm_tf_created (Token|1|5),(TaskId|1|5) +# Embedded TaskFragment removed +31005 wm_tf_removed (Token|1|5),(TaskId|1|5) # Set the requested orientation of an activity. 31006 wm_set_requested_orientation (Orientation|1|5),(Component Name|3) diff --git a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java index e03ff6881bd8..156e9f98b7a7 100644 --- a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java +++ b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java @@ -36,6 +36,7 @@ import android.view.InsetsSource; import android.view.InsetsSourceConsumer; import android.view.InsetsSourceControl; import android.view.WindowInsets; +import android.view.inputmethod.Flags; import android.view.inputmethod.ImeTracker; import com.android.internal.annotations.VisibleForTesting; @@ -73,12 +74,51 @@ final class ImeInsetsSourceProvider extends InsetsSourceProvider { */ private boolean mServerVisible; + /** + * When the IME is not ready, it has givenInsetsPending. However, this could happen again, + * after it became serverVisible. This flag indicates is used to determine if it is + * readyForDispatching + */ + private boolean mGivenInsetsReady = false; + ImeInsetsSourceProvider(@NonNull InsetsSource source, @NonNull InsetsStateController stateController, @NonNull DisplayContent displayContent) { super(source, stateController, displayContent); } + @Override + void onPostLayout() { + super.onPostLayout(); + + if (android.view.inputmethod.Flags.refactorInsetsController()) { + final WindowState ws = + mWindowContainer != null ? mWindowContainer.asWindowState() : null; + final boolean givenInsetsPending = ws != null && ws.mGivenInsetsPending; + + // 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) { + mGivenInsetsReady = true; + mStateController.notifyControlChanged(mControlTarget); + } + } + } + + @Override + protected boolean isLeashReadyForDispatching() { + if (android.view.inputmethod.Flags.refactorInsetsController()) { + final WindowState ws = + mWindowContainer != null ? mWindowContainer.asWindowState() : null; + final boolean isDrawn = ws != null && ws.isDrawn(); + return super.isLeashReadyForDispatching() && mServerVisible && isDrawn + && mGivenInsetsReady; + } else { + return super.isLeashReadyForDispatching(); + } + } + @Nullable @Override InsetsSourceControl getControl(InsetsControlTarget target) { @@ -122,7 +162,16 @@ final class ImeInsetsSourceProvider extends InsetsSourceProvider { @Override void setServerVisible(boolean serverVisible) { - mServerVisible = serverVisible; + if (mServerVisible != serverVisible) { + mServerVisible = serverVisible; + // reset the leash if the server visibility becomes hidden + if (android.view.inputmethod.Flags.refactorInsetsController()) { + if (!serverVisible && !mFrozen) { + mGivenInsetsReady = false; + updateControlForTarget(mControlTarget, true /* force */); + } + } + } if (!mFrozen) { super.setServerVisible(serverVisible); } @@ -154,7 +203,13 @@ final class ImeInsetsSourceProvider extends InsetsSourceProvider { @Override protected void updateVisibility() { + boolean oldVisibility = mSource.isVisible(); super.updateVisibility(); + if (Flags.refactorInsetsController()) { + if (mSource.isVisible() && !oldVisibility && mImeRequester != null) { + reportImeDrawnForOrganizerIfNeeded(mImeRequester); + } + } onSourceChanged(); } @@ -166,21 +221,92 @@ final class ImeInsetsSourceProvider extends InsetsSourceProvider { target = target.getWindow().getImeControlTarget(); } super.updateControlForTarget(target, force); + if (Flags.refactorInsetsController()) { + if (target != null) { + invokeOnImeRequestedChangedListener(target.getWindow()); + } + } } @Override protected boolean updateClientVisibility(InsetsControlTarget caller) { - if (caller != getControlTarget()) { + InsetsControlTarget controlTarget = getControlTarget(); + if (caller != controlTarget) { + if (Flags.refactorInsetsController()) { + if (isImeInputTarget(caller)) { + // In case of the multi window mode, update the requestedVisibleTypes from + // the controlTarget (=RemoteInsetsControlTarget) via DisplayImeController. + // Then, trigger onRequestedVisibleTypesChanged for the controlTarget with + // its new requested visibility for the IME + boolean imeVisible = caller.isRequestedVisible(WindowInsets.Type.ime()); + if (controlTarget != null) { + controlTarget.setImeInputTargetRequestedVisibility(imeVisible); + } else { + // In case of a virtual display that cannot show the IME, the + // controlTarget will be null here, as no controlTarget was set yet. In + // that case, proceed similar to the multi window mode (fallback = + // RemoteInsetsControlTarget of the default display) + controlTarget = mDisplayContent.getImeHostOrFallback(caller.getWindow()); + + if (controlTarget != caller) { + controlTarget.setImeInputTargetRequestedVisibility(imeVisible); + } + } + + WindowState windowState = caller.getWindow(); + invokeOnImeRequestedChangedListener(windowState); + } + } return false; } boolean changed = super.updateClientVisibility(caller); - if (changed && caller.isRequestedVisible(mSource.getType())) { - reportImeDrawnForOrganizerIfNeeded(caller); + if (!Flags.refactorInsetsController()) { + if (changed && caller.isRequestedVisible(mSource.getType())) { + reportImeDrawnForOrganizerIfNeeded(caller); + } } changed |= mDisplayContent.onImeInsetsClientVisibilityUpdate(); + if (Flags.refactorInsetsController()) { + if (changed) { + // RemoteInsetsControlTarget does not have a window. In this case, we use the + // windowState from the imeInputTarget + WindowState windowState = caller.getWindow() != null ? caller.getWindow() + : ((mDisplayContent.getImeInputTarget() != null) + ? mDisplayContent.getImeInputTarget().getWindowState() : null); + invokeOnImeRequestedChangedListener(windowState); + } + } return changed; } + void onInputTargetChanged(InputTarget target) { + if (Flags.refactorInsetsController() && target != null) { + WindowState targetWin = target.getWindowState(); + InsetsControlTarget imeControlTarget = getControlTarget(); + if (target != imeControlTarget && targetWin != null) { + // If the targetWin is not the imeControlTarget (=RemoteInsetsControlTarget) let it + // know about the new requestedVisibleTypes for the IME. + if (imeControlTarget != null) { + imeControlTarget.setImeInputTargetRequestedVisibility( + (targetWin.getRequestedVisibleTypes() + & WindowInsets.Type.ime()) != 0); + } + } + } + } + + private void invokeOnImeRequestedChangedListener(WindowState windowState) { + final var imeListener = mDisplayContent.mWmService.mOnImeRequestedChangedListener; + if (imeListener != null) { + if (windowState != null) { + mDisplayContent.mWmService.mH.post(() -> { + imeListener.onImeRequestedChanged(windowState.mClient.asBinder(), + windowState.isRequestedVisible(WindowInsets.Type.ime())); + }); + } + } + } + private void reportImeDrawnForOrganizerIfNeeded(@NonNull InsetsControlTarget caller) { final WindowState callerWindow = caller.getWindow(); if (callerWindow == null) { @@ -273,6 +399,19 @@ final class ImeInsetsSourceProvider extends InsetsSourceProvider { // This can later become ready, so we don't want to cancel the pending request here. return; } + if (android.view.inputmethod.Flags.refactorInsetsController()) { + // Clear token here so we don't report an error in abortShowImePostLayout(). + abortShowImePostLayout(); + // The IME is drawn, so call into {@link WindowState#notifyInsetsControlChanged} + // if we have a leash + if (mControl != null && mControl.getLeash() != null + && mControlTarget.getWindow() != null + && !mControlTarget.getWindow().mGivenInsetsPending) { + int displayId = mDisplayContent.getDisplayId(); + mControlTarget.notifyInsetsControlChanged(displayId); + } + return; + } ImeTracker.forLogging().onProgress(mStatsToken, ImeTracker.PHASE_WM_SHOW_IME_RUNNER); ProtoLog.d(WM_DEBUG_IME, "Run showImeRunner"); diff --git a/services/core/java/com/android/server/wm/ImmersiveModeConfirmation.java b/services/core/java/com/android/server/wm/ImmersiveModeConfirmation.java index b2ba9d1cc8fe..bf99ccdf1b0a 100644 --- a/services/core/java/com/android/server/wm/ImmersiveModeConfirmation.java +++ b/services/core/java/com/android/server/wm/ImmersiveModeConfirmation.java @@ -20,6 +20,7 @@ import static android.app.ActivityManager.LOCK_TASK_MODE_LOCKED; import static android.app.ActivityManager.LOCK_TASK_MODE_NONE; import static android.view.Display.DEFAULT_DISPLAY; import static android.view.ViewRootImpl.CLIENT_TRANSIENT; +import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; import static android.window.DisplayAreaOrganizer.FEATURE_UNDEFINED; import static android.window.DisplayAreaOrganizer.KEY_ROOT_DISPLAY_AREA_ID; @@ -35,6 +36,7 @@ import android.content.Intent; import android.content.IntentFilter; import android.graphics.Insets; import android.graphics.PixelFormat; +import android.graphics.Rect; import android.graphics.drawable.ColorDrawable; import android.os.Binder; import android.os.Bundle; @@ -60,6 +62,7 @@ import android.view.animation.AnimationUtils; import android.view.animation.Interpolator; import android.widget.Button; import android.widget.FrameLayout; +import android.widget.RelativeLayout; import com.android.internal.R; @@ -233,6 +236,7 @@ public class ImmersiveModeConfirmation { | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL, PixelFormat.TRANSLUCENT); lp.setFitInsetsTypes(lp.getFitInsetsTypes() & ~Type.statusBars()); + lp.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; // Trusted overlay so touches outside the touchable area are allowed to pass through lp.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS | WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY @@ -245,13 +249,20 @@ public class ImmersiveModeConfirmation { private FrameLayout.LayoutParams getBubbleLayoutParams() { return new FrameLayout.LayoutParams( - mContext.getResources().getDimensionPixelSize( - R.dimen.immersive_mode_cling_width), + getClingWindowWidth(), ViewGroup.LayoutParams.WRAP_CONTENT, Gravity.CENTER_HORIZONTAL | Gravity.TOP); } /** + * Returns the width of the cling window. + */ + private int getClingWindowWidth() { + return mContext.getResources().getDimensionPixelSize( + R.dimen.immersive_mode_cling_width); + } + + /** * @return the window token that's used by all ImmersiveModeConfirmation windows. */ IBinder getWindowToken() { @@ -387,6 +398,24 @@ public class ImmersiveModeConfirmation { @Override public WindowInsets onApplyWindowInsets(WindowInsets insets) { + // If the top display cutout overlaps with the full-width (windowWidth=-1)/centered + // dialog, then adjust the dialog contents by the cutout + final int width = getWidth(); + final int windowWidth = getClingWindowWidth(); + final Rect topDisplayCutout = insets.getDisplayCutout() != null + ? insets.getDisplayCutout().getBoundingRectTop() + : new Rect(); + final boolean intersectsTopCutout = topDisplayCutout.intersects( + width - (windowWidth / 2), 0, + width + (windowWidth / 2), topDisplayCutout.bottom); + if (mClingWindow != null && + (windowWidth < 0 || (width > 0 && intersectsTopCutout))) { + final View iconView = mClingWindow.findViewById(R.id.immersive_cling_icon); + RelativeLayout.LayoutParams lp = (RelativeLayout.LayoutParams) + iconView.getLayoutParams(); + lp.topMargin = topDisplayCutout.bottom; + iconView.setLayoutParams(lp); + } // we will be hiding the nav bar, so layout as if it's already hidden return new WindowInsets.Builder(insets).setInsets( Type.systemBars(), Insets.NONE).build(); diff --git a/services/core/java/com/android/server/wm/InsetsControlTarget.java b/services/core/java/com/android/server/wm/InsetsControlTarget.java index cc3de7a3462c..07e249a2004f 100644 --- a/services/core/java/com/android/server/wm/InsetsControlTarget.java +++ b/services/core/java/com/android/server/wm/InsetsControlTarget.java @@ -85,6 +85,13 @@ interface InsetsControlTarget { return false; } + /** + * @param visible the requested visibility for the IME, used for + * {@link com.android.server.wm.DisplayContent.RemoteInsetsControlTarget} + */ + default void setImeInputTargetRequestedVisibility(boolean visible) { + } + /** Returns {@code target.getWindow()}, or null if {@code target} is {@code null}. */ static WindowState asWindowOrNull(InsetsControlTarget target) { return target != null ? target.getWindow() : null; diff --git a/services/core/java/com/android/server/wm/InsetsPolicy.java b/services/core/java/com/android/server/wm/InsetsPolicy.java index 3c556bf7b126..6288a429af81 100644 --- a/services/core/java/com/android/server/wm/InsetsPolicy.java +++ b/services/core/java/com/android/server/wm/InsetsPolicy.java @@ -424,7 +424,9 @@ class InsetsPolicy { // Use task bounds to calculating rounded corners if the task is not floating. final InsetsState state = copyState ? new InsetsState(originalState) : originalState; - state.setRoundedCornerFrame(task.getBounds()); + state.setRoundedCornerFrame(token.isFixedRotationTransforming() + ? token.getFixedRotationTransformDisplayBounds() + : task.getBounds()); return state; } } diff --git a/services/core/java/com/android/server/wm/InsetsSourceProvider.java b/services/core/java/com/android/server/wm/InsetsSourceProvider.java index 2288fe998b58..f68b67f626f9 100644 --- a/services/core/java/com/android/server/wm/InsetsSourceProvider.java +++ b/services/core/java/com/android/server/wm/InsetsSourceProvider.java @@ -69,12 +69,12 @@ class InsetsSourceProvider { protected final @NonNull InsetsStateController mStateController; protected @Nullable WindowContainer mWindowContainer; protected @Nullable InsetsSourceControl mControl; + protected @Nullable InsetsControlTarget mControlTarget; protected boolean mIsLeashReadyForDispatching; private final Rect mTmpRect = new Rect(); private final InsetsSourceControl mFakeControl; private final Consumer<Transaction> mSetLeashPositionConsumer; - private @Nullable InsetsControlTarget mControlTarget; private @Nullable InsetsControlTarget mPendingControlTarget; private @Nullable InsetsControlTarget mFakeControlTarget; @@ -350,11 +350,27 @@ class InsetsSourceProvider { boolean isServerVisible = windowState != null ? windowState.wouldBeVisibleIfPolicyIgnored() && windowState.isVisibleByPolicy() : mWindowContainer.isVisibleRequested(); + + if (android.view.inputmethod.Flags.refactorInsetsController()) { + if (mControl != null && mControl.getType() == WindowInsets.Type.ime() && !mServerVisible + && isServerVisible && windowState != null) { + // in case the IME becomes visible, we need to check if it is already drawn and + // does not have given insets pending. If it's not yet drawn, we do not set + // server visibility + isServerVisible = windowState.isDrawn() && !windowState.mGivenInsetsPending; + } + } + final boolean serverVisibleChanged = mServerVisible != isServerVisible; setServerVisible(isServerVisible); - updateInsetsControlPosition(windowState); + updateInsetsControlPosition(windowState, serverVisibleChanged); } void updateInsetsControlPosition(WindowState windowState) { + updateInsetsControlPosition(windowState, false); + } + + private void updateInsetsControlPosition(WindowState windowState, + boolean serverVisibleChanged) { if (mControl == null) { return; } @@ -390,6 +406,9 @@ class InsetsSourceProvider { mControl.setInsetsHint(insetsHint); changed = true; } + if (android.view.inputmethod.Flags.refactorInsetsController() && serverVisibleChanged) { + changed = true; + } if (changed) { mStateController.notifyControlChanged(mControlTarget); } @@ -590,6 +609,10 @@ class InsetsSourceProvider { mServerVisible, mClientVisible); } + protected boolean isLeashReadyForDispatching() { + return mIsLeashReadyForDispatching; + } + /** * Gets the source control for the given control target. If this is the provider's control * target, but the leash is not ready for dispatching, a new source control object with the @@ -600,7 +623,7 @@ class InsetsSourceProvider { @Nullable InsetsSourceControl getControl(InsetsControlTarget target) { if (target == mControlTarget) { - if (!mIsLeashReadyForDispatching && mControl != null) { + if (!isLeashReadyForDispatching() && mControl != null) { // The surface transaction of preparing leash is not applied yet. We don't send it // to the client in case that the client applies its transaction sooner than ours // that we could unexpectedly overwrite the surface state. @@ -750,9 +773,11 @@ class InsetsSourceProvider { @AnimationType int type, @NonNull OnAnimationFinishedCallback finishCallback) { // TODO(b/166736352): Check if we still need to control the IME visibility here. if (mSource.getType() == WindowInsets.Type.ime()) { - // TODO: use 0 alpha and remove t.hide() once b/138459974 is fixed. - t.setAlpha(animationLeash, 1 /* alpha */); - t.hide(animationLeash); + if (!android.view.inputmethod.Flags.refactorInsetsController() || !mClientVisible) { + // TODO: use 0 alpha and remove t.hide() once b/138459974 is fixed. + t.setAlpha(animationLeash, 1 /* alpha */); + t.hide(animationLeash); + } } ProtoLog.i(WM_DEBUG_WINDOW_INSETS, "ControlAdapter startAnimation mSource: %s controlTarget: %s", mSource, diff --git a/services/core/java/com/android/server/wm/InsetsStateController.java b/services/core/java/com/android/server/wm/InsetsStateController.java index 7a1f57bea680..a967f7af3bb9 100644 --- a/services/core/java/com/android/server/wm/InsetsStateController.java +++ b/services/core/java/com/android/server/wm/InsetsStateController.java @@ -221,10 +221,13 @@ class InsetsStateController { for (int i = mProviders.size() - 1; i >= 0; i--) { changed |= mProviders.valueAt(i).updateClientVisibility(caller); } - if (changed) { - notifyInsetsChanged(); - mDisplayContent.updateSystemGestureExclusion(); - mDisplayContent.getDisplayPolicy().updateSystemBarAttributes(); + if (!android.view.inputmethod.Flags.refactorInsetsController()) { + if (changed) { + notifyInsetsChanged(); + mDisplayContent.updateSystemGestureExclusion(); + + mDisplayContent.getDisplayPolicy().updateSystemBarAttributes(); + } } } @@ -357,6 +360,13 @@ class InsetsStateController { void notifyControlChanged(InsetsControlTarget target) { mPendingControlChanged.add(target); notifyPendingInsetsControlChanged(); + + if (android.view.inputmethod.Flags.refactorInsetsController()) { + notifyInsetsChanged(); + mDisplayContent.updateSystemGestureExclusion(); + mDisplayContent.updateKeepClearAreas(); + mDisplayContent.getDisplayPolicy().updateSystemBarAttributes(); + } } private void notifyPendingInsetsControlChanged() { diff --git a/services/core/java/com/android/server/wm/KeyguardController.java b/services/core/java/com/android/server/wm/KeyguardController.java index c683d4daa0a1..f70d2a58e781 100644 --- a/services/core/java/com/android/server/wm/KeyguardController.java +++ b/services/core/java/com/android/server/wm/KeyguardController.java @@ -538,13 +538,6 @@ class KeyguardController { || !mWindowManager.isKeyguardSecure(mService.getCurrentUserId()); } - /** - * @return Whether the dream activity is on top of default display. - */ - boolean isShowingDream() { - return getDisplayState(DEFAULT_DISPLAY).mShowingDream; - } - private void updateKeyguardSleepToken() { for (int displayNdx = mRootWindowContainer.getChildCount() - 1; displayNdx >= 0; displayNdx--) { @@ -631,7 +624,6 @@ class KeyguardController { * Note that this can be true even if the keyguard is disabled or not showing. */ private boolean mOccluded; - private boolean mShowingDream; private ActivityRecord mTopOccludesActivity; private ActivityRecord mDismissingKeyguardActivity; @@ -669,7 +661,6 @@ class KeyguardController { mRequestDismissKeyguard = false; mOccluded = false; - mShowingDream = false; mTopOccludesActivity = null; mDismissingKeyguardActivity = null; @@ -697,21 +688,18 @@ class KeyguardController { // Only the top activity may control occluded, as we can't occlude the Keyguard // if the top app doesn't want to occlude it. - occludedByActivity = mTopOccludesActivity != null + mOccluded = mTopOccludesActivity != null || (mDismissingKeyguardActivity != null && task.topRunningActivity() == mDismissingKeyguardActivity && controller.canShowWhileOccluded( true /* dismissKeyguard */, false /* showWhenLocked */)); // FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD only apply for secondary display. if (mDisplayId != DEFAULT_DISPLAY) { - occludedByActivity |= display.canShowWithInsecureKeyguard() + mOccluded |= display.canShowWithInsecureKeyguard() && controller.canDismissKeyguard(); } } - mShowingDream = display.getDisplayPolicy().isShowingDreamLw() && (top != null - && top.getActivityType() == ACTIVITY_TYPE_DREAM); - mOccluded = mShowingDream || occludedByActivity; mRequestDismissKeyguard = lastDismissKeyguardActivity != mDismissingKeyguardActivity && !mOccluded && !mKeyguardGoingAway && mDismissingKeyguardActivity != null; diff --git a/services/core/java/com/android/server/wm/LetterboxUiController.java b/services/core/java/com/android/server/wm/LetterboxUiController.java index b8d0694047c7..5e93e8930bab 100644 --- a/services/core/java/com/android/server/wm/LetterboxUiController.java +++ b/services/core/java/com/android/server/wm/LetterboxUiController.java @@ -40,7 +40,6 @@ import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LOCKED; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_NOSENSOR; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE; -import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_USER; import static android.content.pm.ActivityInfo.isFixedOrientation; import static android.content.pm.ActivityInfo.isFixedOrientationLandscape; @@ -54,10 +53,6 @@ import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_FULLSCREEN import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_SPLIT_SCREEN; import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_UNSET; import static android.content.res.Configuration.ORIENTATION_LANDSCAPE; -import static android.content.res.Configuration.ORIENTATION_UNDEFINED; -import static android.content.res.Configuration.SCREEN_HEIGHT_DP_UNDEFINED; -import static android.content.res.Configuration.SCREEN_WIDTH_DP_UNDEFINED; -import static android.content.res.Configuration.SMALLEST_SCREEN_WIDTH_DP_UNDEFINED; import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING; import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; @@ -80,7 +75,6 @@ import static com.android.internal.util.FrameworkStatsLog.APP_COMPAT_STATE_CHANG import static com.android.internal.util.FrameworkStatsLog.APP_COMPAT_STATE_CHANGED__LETTERBOX_POSITION__RIGHT; import static com.android.internal.util.FrameworkStatsLog.APP_COMPAT_STATE_CHANGED__LETTERBOX_POSITION__TOP; import static com.android.internal.util.FrameworkStatsLog.APP_COMPAT_STATE_CHANGED__LETTERBOX_POSITION__UNKNOWN_POSITION; -import static com.android.internal.util.FrameworkStatsLog.APP_COMPAT_STATE_CHANGED__STATE__UNKNOWN; import static com.android.internal.util.FrameworkStatsLog.LETTERBOX_POSITION_CHANGED__POSITION_CHANGE__BOTTOM_TO_CENTER; import static com.android.internal.util.FrameworkStatsLog.LETTERBOX_POSITION_CHANGED__POSITION_CHANGE__CENTER_TO_BOTTOM; import static com.android.internal.util.FrameworkStatsLog.LETTERBOX_POSITION_CHANGED__POSITION_CHANGE__CENTER_TO_LEFT; @@ -135,12 +129,7 @@ import com.android.server.wm.utils.OptPropFactory.OptProp; import com.android.window.flags.Flags; import java.io.PrintWriter; -import java.util.ArrayList; -import java.util.List; -import java.util.Optional; import java.util.function.BooleanSupplier; -import java.util.function.Consumer; -import java.util.function.Predicate; /** Controls behaviour of the letterbox UI for {@link mActivityRecord}. */ // TODO(b/185262487): Improve test coverage of this class. Parts of it are tested in @@ -150,13 +139,8 @@ import java.util.function.Predicate; // TODO(b/263021211): Consider renaming to more generic CompatUIController. final class LetterboxUiController { - private static final Predicate<ActivityRecord> FIRST_OPAQUE_NOT_FINISHING_ACTIVITY_PREDICATE = - ActivityRecord::occludesParent; - private static final String TAG = TAG_WITH_CLASS_NAME ? "LetterboxUiController" : TAG_ATM; - private static final float UNDEFINED_ASPECT_RATIO = 0f; - // Minimum value of mSetOrientationRequestCounter before qualifying as orientation request loop @VisibleForTesting static final int MIN_COUNT_TO_IGNORE_REQUEST_IN_LOOP = 2; @@ -188,10 +172,6 @@ final class LetterboxUiController { // Corresponds to OVERRIDE_RESPECT_REQUESTED_ORIENTATION private final boolean mIsOverrideRespectRequestedOrientationEnabled; - // The list of observers for the destroy event of candidate opaque activities - // when dealing with translucent activities. - private final List<LetterboxUiController> mDestroyListeners = new ArrayList<>(); - @NonNull private final OptProp mAllowOrientationOverrideOptProp; @NonNull @@ -206,34 +186,11 @@ final class LetterboxUiController { @NonNull private final OptProp mAllowUserAspectRatioFullscreenOverrideOptProp; - /* - * WindowContainerListener responsible to make translucent activities inherit - * constraints from the first opaque activity beneath them. It's null for not - * translucent activities. - */ - @Nullable - private WindowContainerListener mLetterboxConfigListener; - - @Nullable - @VisibleForTesting - ActivityRecord mFirstOpaqueActivityBeneath; - private boolean mShowWallpaperForLetterboxBackground; - // In case of transparent activities we might need to access the aspectRatio of the - // first opaque activity beneath. - private float mInheritedMinAspectRatio = UNDEFINED_ASPECT_RATIO; - private float mInheritedMaxAspectRatio = UNDEFINED_ASPECT_RATIO; - // Updated when ActivityRecord#setRequestedOrientation is called private long mTimeMsLastSetOrientationRequest = 0; - @Configuration.Orientation - private int mInheritedOrientation = ORIENTATION_UNDEFINED; - - // The app compat state for the opaque activity if any - private int mInheritedAppCompatState = APP_COMPAT_STATE_CHANGED__STATE__UNKNOWN; - // Counter for ActivityRecord#setRequestedOrientation private int mSetOrientationRequestCounter = 0; @@ -242,9 +199,6 @@ final class LetterboxUiController { @PackageManager.UserMinAspectRatio private int mUserAspectRatio = USER_MIN_ASPECT_RATIO_UNSET; - // The CompatDisplayInsets of the opaque activity beneath the translucent one. - private ActivityRecord.CompatDisplayInsets mInheritedCompatDisplayInsets; - @Nullable private Letterbox mLetterbox; @@ -361,14 +315,7 @@ final class LetterboxUiController { mLetterbox.destroy(); mLetterbox = null; } - for (int i = mDestroyListeners.size() - 1; i >= 0; i--) { - mDestroyListeners.get(i).updateInheritedLetterbox(); - } - mDestroyListeners.clear(); - if (mLetterboxConfigListener != null) { - mLetterboxConfigListener.onRemoved(); - mLetterboxConfigListener = null; - } + mActivityRecord.mTransparentPolicy.stop(); } void onMovedToDisplay(int displayId) { @@ -877,7 +824,7 @@ final class LetterboxUiController { // For this reason we use ActivityRecord#getBounds() that the translucent activity // inherits from the first opaque activity beneath and also takes care of the scaling // in case of activities in size compat mode. - final Rect innerFrame = hasInheritedLetterboxBehavior() + final Rect innerFrame = mActivityRecord.mTransparentPolicy.isRunning() ? mActivityRecord.getBounds() : w.getFrame(); mLetterbox.layout(spaceToFill, innerFrame, mTmpPoint); if (mDoubleTapEvent) { @@ -1341,19 +1288,20 @@ final class LetterboxUiController { if (!allowHorizontalReachabilityForThinLetterbox()) { return false; } + final Rect parentAppBoundsOverride = mActivityRecord.getParentAppBoundsOverride(); + final Rect parentAppBounds = parentAppBoundsOverride != null + ? parentAppBoundsOverride : parentConfiguration.windowConfiguration.getAppBounds(); // Use screen resolved bounds which uses resolved bounds or size compat bounds // as activity bounds can sometimes be empty - final Rect opaqueActivityBounds = hasInheritedLetterboxBehavior() - ? mFirstOpaqueActivityBeneath.getScreenResolvedBounds() - : mActivityRecord.getScreenResolvedBounds(); + final Rect opaqueActivityBounds = mActivityRecord.mTransparentPolicy + .getFirstOpaqueActivity().map(ActivityRecord::getScreenResolvedBounds) + .orElse(mActivityRecord.getScreenResolvedBounds()); return mLetterboxConfiguration.getIsHorizontalReachabilityEnabled() && parentConfiguration.windowConfiguration.getWindowingMode() == WINDOWING_MODE_FULLSCREEN // Check whether the activity fills the parent vertically. - && parentConfiguration.windowConfiguration.getAppBounds().height() - <= opaqueActivityBounds.height() - && parentConfiguration.windowConfiguration.getAppBounds().width() - > opaqueActivityBounds.width(); + && parentAppBounds.height() <= opaqueActivityBounds.height() + && parentAppBounds.width() > opaqueActivityBounds.width(); } @VisibleForTesting @@ -1379,19 +1327,20 @@ final class LetterboxUiController { if (!allowVerticalReachabilityForThinLetterbox()) { return false; } + final Rect parentAppBoundsOverride = mActivityRecord.getParentAppBoundsOverride(); + final Rect parentAppBounds = parentAppBoundsOverride != null + ? parentAppBoundsOverride : parentConfiguration.windowConfiguration.getAppBounds(); // Use screen resolved bounds which uses resolved bounds or size compat bounds - // as activity bounds can sometimes be empty - final Rect opaqueActivityBounds = hasInheritedLetterboxBehavior() - ? mFirstOpaqueActivityBeneath.getScreenResolvedBounds() - : mActivityRecord.getScreenResolvedBounds(); + // as activity bounds can sometimes be empty. + final Rect opaqueActivityBounds = mActivityRecord.mTransparentPolicy + .getFirstOpaqueActivity().map(ActivityRecord::getScreenResolvedBounds) + .orElse(mActivityRecord.getScreenResolvedBounds()); return mLetterboxConfiguration.getIsVerticalReachabilityEnabled() && parentConfiguration.windowConfiguration.getWindowingMode() == WINDOWING_MODE_FULLSCREEN // Check whether the activity fills the parent horizontally. - && parentConfiguration.windowConfiguration.getAppBounds().width() - <= opaqueActivityBounds.width() - && parentConfiguration.windowConfiguration.getAppBounds().height() - > opaqueActivityBounds.height(); + && parentAppBounds.width() <= opaqueActivityBounds.width() + && parentAppBounds.height() > opaqueActivityBounds.height(); } @VisibleForTesting @@ -1490,7 +1439,8 @@ final class LetterboxUiController { // corners because we assume the specific layout would. This is the case when the layout // of the translucent activity uses only a part of all the bounds because of the use of // LayoutParams.WRAP_CONTENT. - if (hasInheritedLetterboxBehavior() && (cropBounds.width() != mainWindow.mRequestedWidth + if (mActivityRecord.mTransparentPolicy.isRunning() + && (cropBounds.width() != mainWindow.mRequestedWidth || cropBounds.height() != mainWindow.mRequestedHeight)) { return null; } @@ -1794,173 +1744,6 @@ final class LetterboxUiController { ); } - /** - * Handles translucent activities letterboxing inheriting constraints from the - * first opaque activity beneath. - * @param parent The parent container. - */ - void updateInheritedLetterbox() { - final WindowContainer<?> parent = mActivityRecord.getParent(); - if (parent == null) { - return; - } - if (!mLetterboxConfiguration.isTranslucentLetterboxingEnabled()) { - return; - } - if (mLetterboxConfigListener != null) { - mLetterboxConfigListener.onRemoved(); - clearInheritedConfig(); - } - // In case mActivityRecord.hasCompatDisplayInsetsWithoutOverride() we don't apply the - // opaque activity constraints because we're expecting the activity is already letterboxed. - mFirstOpaqueActivityBeneath = mActivityRecord.getTask().getActivity( - FIRST_OPAQUE_NOT_FINISHING_ACTIVITY_PREDICATE /* callback */, - mActivityRecord /* boundary */, false /* includeBoundary */, - true /* traverseTopToBottom */); - if (mFirstOpaqueActivityBeneath == null || mFirstOpaqueActivityBeneath.isEmbedded()) { - // We skip letterboxing if the translucent activity doesn't have any opaque - // activities beneath or the activity below is embedded which never has letterbox. - mActivityRecord.recomputeConfiguration(); - return; - } - if (mActivityRecord.getTask() == null || mActivityRecord.fillsParent() - || mActivityRecord.hasCompatDisplayInsetsWithoutInheritance()) { - return; - } - mFirstOpaqueActivityBeneath.mLetterboxUiController.mDestroyListeners.add(this); - inheritConfiguration(mFirstOpaqueActivityBeneath); - mLetterboxConfigListener = WindowContainer.overrideConfigurationPropagation( - mActivityRecord, mFirstOpaqueActivityBeneath, - (opaqueConfig, transparentOverrideConfig) -> { - resetTranslucentOverrideConfig(transparentOverrideConfig); - final Rect parentBounds = parent.getWindowConfiguration().getBounds(); - final Rect bounds = transparentOverrideConfig.windowConfiguration.getBounds(); - final Rect letterboxBounds = opaqueConfig.windowConfiguration.getBounds(); - // We cannot use letterboxBounds directly here because the position relies on - // letterboxing. Using letterboxBounds directly, would produce a double offset. - bounds.set(parentBounds.left, parentBounds.top, - parentBounds.left + letterboxBounds.width(), - parentBounds.top + letterboxBounds.height()); - // We need to initialize appBounds to avoid NPE. The actual value will - // be set ahead when resolving the Configuration for the activity. - transparentOverrideConfig.windowConfiguration.setAppBounds(new Rect()); - inheritConfiguration(mFirstOpaqueActivityBeneath); - return transparentOverrideConfig; - }); - } - - /** - * @return {@code true} if the current activity is translucent with an opaque activity - * beneath. In this case it will inherit bounds, orientation and aspect ratios from - * the first opaque activity beneath. - */ - boolean hasInheritedLetterboxBehavior() { - return mLetterboxConfigListener != null; - } - - /** - * @return {@code true} if the current activity is translucent with an opaque activity - * beneath and needs to inherit its orientation. - */ - boolean hasInheritedOrientation() { - // To force a different orientation, the transparent one needs to have an explicit one - // otherwise the existing one is fine and the actual orientation will depend on the - // bounds. - // To avoid wrong behaviour, we're not forcing orientation for activities with not - // fixed orientation (e.g. permission dialogs). - return hasInheritedLetterboxBehavior() - && mActivityRecord.getOverrideOrientation() - != SCREEN_ORIENTATION_UNSPECIFIED; - } - - float getInheritedMinAspectRatio() { - return mInheritedMinAspectRatio; - } - - float getInheritedMaxAspectRatio() { - return mInheritedMaxAspectRatio; - } - - int getInheritedAppCompatState() { - return mInheritedAppCompatState; - } - - @Configuration.Orientation - int getInheritedOrientation() { - return mInheritedOrientation; - } - - ActivityRecord.CompatDisplayInsets getInheritedCompatDisplayInsets() { - return mInheritedCompatDisplayInsets; - } - - void clearInheritedCompatDisplayInsets() { - mInheritedCompatDisplayInsets = null; - } - - /** - * In case of translucent activities, it consumes the {@link ActivityRecord} of the first opaque - * activity beneath using the given consumer and returns {@code true}. - */ - boolean applyOnOpaqueActivityBelow(@NonNull Consumer<ActivityRecord> consumer) { - return findOpaqueNotFinishingActivityBelow() - .map(activityRecord -> { - consumer.accept(activityRecord); - return true; - }).orElse(false); - } - - /** - * @return The first not finishing opaque activity beneath the current translucent activity - * if it exists and the strategy is enabled. - */ - Optional<ActivityRecord> findOpaqueNotFinishingActivityBelow() { - if (!hasInheritedLetterboxBehavior() || mActivityRecord.getTask() == null) { - return Optional.empty(); - } - return Optional.ofNullable(mFirstOpaqueActivityBeneath); - } - - /** Resets the screen size related fields so they can be resolved by requested bounds later. */ - private static void resetTranslucentOverrideConfig(Configuration config) { - // The values for the following properties will be defined during the configuration - // resolution in {@link ActivityRecord#resolveOverrideConfiguration} using the - // properties inherited from the first not finishing opaque activity beneath. - config.orientation = ORIENTATION_UNDEFINED; - config.screenWidthDp = config.compatScreenWidthDp = SCREEN_WIDTH_DP_UNDEFINED; - config.screenHeightDp = config.compatScreenHeightDp = SCREEN_HEIGHT_DP_UNDEFINED; - config.smallestScreenWidthDp = config.compatSmallestScreenWidthDp = - SMALLEST_SCREEN_WIDTH_DP_UNDEFINED; - } - - private void inheritConfiguration(ActivityRecord firstOpaque) { - // To avoid wrong behaviour, we're not forcing a specific aspect ratio to activities - // which are not already providing one (e.g. permission dialogs) and presumably also - // not resizable. - if (mActivityRecord.getMinAspectRatio() != UNDEFINED_ASPECT_RATIO) { - mInheritedMinAspectRatio = firstOpaque.getMinAspectRatio(); - } - if (mActivityRecord.getMaxAspectRatio() != UNDEFINED_ASPECT_RATIO) { - mInheritedMaxAspectRatio = firstOpaque.getMaxAspectRatio(); - } - mInheritedOrientation = firstOpaque.getRequestedConfigurationOrientation(); - mInheritedAppCompatState = firstOpaque.getAppCompatState(); - mInheritedCompatDisplayInsets = firstOpaque.getCompatDisplayInsets(); - } - - private void clearInheritedConfig() { - if (mFirstOpaqueActivityBeneath != null) { - mFirstOpaqueActivityBeneath.mLetterboxUiController.mDestroyListeners.remove(this); - } - mFirstOpaqueActivityBeneath = null; - mLetterboxConfigListener = null; - mInheritedMinAspectRatio = UNDEFINED_ASPECT_RATIO; - mInheritedMaxAspectRatio = UNDEFINED_ASPECT_RATIO; - mInheritedOrientation = ORIENTATION_UNDEFINED; - mInheritedAppCompatState = APP_COMPAT_STATE_CHANGED__STATE__UNKNOWN; - mInheritedCompatDisplayInsets = null; - } - @NonNull private static BooleanSupplier asLazy(@NonNull BooleanSupplier supplier) { return new BooleanSupplier() { diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java index be8c2aea7ad1..54ba47eeb441 100644 --- a/services/core/java/com/android/server/wm/RootWindowContainer.java +++ b/services/core/java/com/android/server/wm/RootWindowContainer.java @@ -314,13 +314,19 @@ class RootWindowContainer extends WindowContainer<DisplayContent> private boolean isDocument; private Uri documentData; - void init(int activityType, String taskAffinity, Intent intent, ActivityInfo info) { + // determines whether to include bubbled tasks. defaults to true to preserve previous + // behavior. + private boolean mIncludeLaunchedFromBubble = true; + + void init(int activityType, String taskAffinity, Intent intent, ActivityInfo info, + boolean includeLaunchedFromBubble) { mActivityType = activityType; mTaskAffinity = taskAffinity; mIntent = intent; mInfo = info; mIdealRecord = null; mCandidateRecord = null; + mIncludeLaunchedFromBubble = includeLaunchedFromBubble; } /** @@ -362,7 +368,8 @@ class RootWindowContainer extends WindowContainer<DisplayContent> } // Overlays should not be considered as the task's logical top activity. - final ActivityRecord r = task.getTopNonFinishingActivity(false /* includeOverlays */); + final ActivityRecord r = task.getTopNonFinishingActivity( + false /* includeOverlays */, mIncludeLaunchedFromBubble); if (r == null || r.finishing || r.mUserId != userId || r.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE) { @@ -1693,7 +1700,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent> } /** - * Check if home activity start should be allowed on a display. + * Check if home activity start should be allowed on a {@link TaskDisplayArea}. * * @param homeInfo {@code ActivityInfo} of the home activity that is going to be * launched. @@ -1717,6 +1724,10 @@ class RootWindowContainer extends WindowContainer<DisplayContent> return false; } + if (taskDisplayArea != null && !taskDisplayArea.canHostHomeTask()) { + return false; + } + final int displayId = taskDisplayArea != null ? taskDisplayArea.getDisplayId() : INVALID_DISPLAY; if (shouldPlacePrimaryHomeOnDisplay(displayId)) { @@ -1894,6 +1905,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent> // Don't do recursive work. return; } + Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "RWC_ensureActivitiesVisible"); mTaskSupervisor.beginActivityVisibilityUpdate(); try { // First the front root tasks. In case any are not fullscreen and are in front of home. @@ -1903,6 +1915,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent> } } finally { mTaskSupervisor.endActivityVisibilityUpdate(); + Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); } } @@ -2366,18 +2379,20 @@ class RootWindowContainer extends WindowContainer<DisplayContent> } @Nullable - ActivityRecord findTask(ActivityRecord r, TaskDisplayArea preferredTaskDisplayArea) { + ActivityRecord findTask(ActivityRecord r, TaskDisplayArea preferredTaskDisplayArea, + boolean includeLaunchedFromBubble) { return findTask(r.getActivityType(), r.taskAffinity, r.intent, r.info, - preferredTaskDisplayArea); + preferredTaskDisplayArea, includeLaunchedFromBubble); } @Nullable ActivityRecord findTask(int activityType, String taskAffinity, Intent intent, ActivityInfo info, - TaskDisplayArea preferredTaskDisplayArea) { + TaskDisplayArea preferredTaskDisplayArea, boolean includeLaunchedFromBubble) { ProtoLog.d(WM_DEBUG_TASKS, "Looking for task of type=%s, taskAffinity=%s, intent=%s" - + ", info=%s, preferredTDA=%s", activityType, taskAffinity, intent, info, - preferredTaskDisplayArea); - mTmpFindTaskResult.init(activityType, taskAffinity, intent, info); + + ", info=%s, preferredTDA=%s, includeLaunchedFromBubble=%b", activityType, + taskAffinity, intent, info, preferredTaskDisplayArea, includeLaunchedFromBubble); + mTmpFindTaskResult.init(activityType, taskAffinity, intent, info, + includeLaunchedFromBubble); // Looking up task on preferred display area first ActivityRecord candidateActivity = null; diff --git a/services/core/java/com/android/server/wm/RunningTasks.java b/services/core/java/com/android/server/wm/RunningTasks.java index 1cc1a57756e9..7510180f54a6 100644 --- a/services/core/java/com/android/server/wm/RunningTasks.java +++ b/services/core/java/com/android/server/wm/RunningTasks.java @@ -157,7 +157,7 @@ class RunningTasks implements Consumer<Task> { // home & recent tasks return; } - if (task.isVisible()) { + if (task.isVisibleRequested()) { mTmpVisibleTasks.add(task); } else { mTmpInvisibleTasks.add(task); diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index 69a8aed04d96..22f718ddbd22 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -445,6 +445,7 @@ class Task extends TaskFragment { int mPrevDisplayId = INVALID_DISPLAY; int mMultiWindowRestoreWindowingMode = INVALID_WINDOWING_MODE; + WindowContainerToken mMultiWindowRestoreParent; /** * Last requested orientation reported to DisplayContent. This is different from {@link @@ -3739,7 +3740,13 @@ class Task extends TaskFragment { return !isOwnActivity && !isTrustedTaskFragment; } - void setDecorSurfaceBoosted( + /** + * Sets the requested boosted state for the decor surface. + * + * The caller must call {@link #commitDecorSurfaceBoostedState()} to ensure that the change is + * applied. + */ + void requestDecorSurfaceBoosted( @NonNull TaskFragment ownerTaskFragment, boolean isBoosted, @Nullable SurfaceControl.Transaction clientTransaction) { @@ -3747,9 +3754,17 @@ class Task extends TaskFragment { || mDecorSurfaceContainer.mOwnerTaskFragment != ownerTaskFragment) { return; } - mDecorSurfaceContainer.setBoosted(isBoosted, clientTransaction); - // scheduleAnimation() is called inside assignChildLayers(), which ensures that child - // surface visibility is updated with prepareSurfaces() + mDecorSurfaceContainer.requestBoosted(isBoosted, clientTransaction); + } + + void commitDecorSurfaceBoostedState() { + if (mDecorSurfaceContainer == null) { + return; + } + mDecorSurfaceContainer.commitBoostedState(); + + // assignChildLayers() calls scheduleAnimation(), which calls prepareSurfaces() + // to ensure child surface visibility. assignChildLayers(); } @@ -4620,6 +4635,25 @@ class Task extends TaskFragment { return TASK; } + /** + * Restores to the windowing mode saved when task requested to enter fullscreen using + * {@link Activity#requestFullscreenMode} API if it is valid. The task is also reparented to + * the previous parent if parent has changed. + */ + void restoreWindowingMode() { + if (mMultiWindowRestoreWindowingMode == INVALID_WINDOWING_MODE) { + return; + } + if (!getParent().mRemoteToken.toWindowContainerToken() + .equals(mMultiWindowRestoreParent)) { + // Restore previous parent if parent has changed. + final Task parent = fromWindowContainerToken(mMultiWindowRestoreParent); + reparent(parent, MAX_VALUE); + } + + setWindowingMode(mMultiWindowRestoreWindowingMode); + } + @Override public void setWindowingMode(int windowingMode) { // Calling Task#setWindowingMode() for leaf task since this is a specialization of @@ -4752,6 +4786,12 @@ class Task extends TaskFragment { if (com.android.window.flags.Flags.removePrepareSurfaceInPlacement() && lastParentBeforePip.mSyncState == SYNC_STATE_NONE) { lastParentBeforePip.prepareSurfaces(); + // If the moveToFront is a part of finishing transition, then make sure + // the z-order of tasks are up-to-date. + if (topActivity.mTransitionController.inFinishingTransition(topActivity)) { + Transition.assignLayers(taskDisplayArea, + taskDisplayArea.getPendingTransaction()); + } } } if (isPip2ExperimentEnabled) { @@ -6790,11 +6830,11 @@ class Task extends TaskFragment { * Associates the decor surface with the given TF, or create one if there * isn't one in the Task yet. The surface will be removed with the TF, * and become invisible if the TF is invisible. */ - void moveOrCreateDecorSurfaceFor(TaskFragment taskFragment) { + void moveOrCreateDecorSurfaceFor(TaskFragment taskFragment, boolean visible) { if (mDecorSurfaceContainer != null) { mDecorSurfaceContainer.mOwnerTaskFragment = taskFragment; } else { - mDecorSurfaceContainer = new DecorSurfaceContainer(taskFragment); + mDecorSurfaceContainer = new DecorSurfaceContainer(taskFragment, visible); assignChildLayers(); sendTaskFragmentParentInfoChangedIfNeeded(); } @@ -6813,6 +6853,13 @@ class Task extends TaskFragment { return mDecorSurfaceContainer != null ? mDecorSurfaceContainer.mDecorSurface : null; } + void setDecorSurfaceVisible(@NonNull SurfaceControl.Transaction t) { + if (mDecorSurfaceContainer == null) { + return; + } + t.show(mDecorSurfaceContainer.mDecorSurface); + } + /** * A class managing the decor surface. * @@ -6852,12 +6899,13 @@ class Task extends TaskFragment { @NonNull TaskFragment mOwnerTaskFragment; private boolean mIsBoosted; + private boolean mIsBoostedRequested; // The surface transactions that will be applied when the layer is reassigned. @NonNull private final List<SurfaceControl.Transaction> mPendingClientTransactions = new ArrayList<>(); - private DecorSurfaceContainer(@NonNull TaskFragment initialOwner) { + private DecorSurfaceContainer(@NonNull TaskFragment initialOwner, boolean visible) { mOwnerTaskFragment = initialOwner; mContainerSurface = makeSurface().setContainerLayer() .setParent(mSurfaceControl) @@ -6870,23 +6918,36 @@ class Task extends TaskFragment { mDecorSurface = makeSurface() .setParent(mContainerSurface) .setName(mSurfaceControl + " - decor surface") - .setHidden(false) + .setHidden(!visible) .setCallsite("Task.DecorSurfaceContainer") .build(); } - private void setBoosted( + /** + * Sets the requested boosted state. The state is not applied until + * {@link commitBoostedState} is called. + */ + private void requestBoosted( boolean isBoosted, @Nullable SurfaceControl.Transaction clientTransaction) { - mIsBoosted = isBoosted; - // The client transaction will be applied together with the next assignLayer. + mIsBoostedRequested = isBoosted; + // The client transaction will be applied together with the next commitBoostedState. if (clientTransaction != null) { mPendingClientTransactions.add(clientTransaction); } } + /** Applies the last requested boosted state. */ + private void commitBoostedState() { + mIsBoosted = mIsBoostedRequested; + applyPendingClientTransactions(getSyncTransaction()); + } + private void assignLayer(@NonNull SurfaceControl.Transaction t, int layer) { t.setLayer(mContainerSurface, layer); t.setVisibility(mContainerSurface, mOwnerTaskFragment.isVisible() || mIsBoosted); + } + + private void applyPendingClientTransactions(@NonNull SurfaceControl.Transaction t) { for (int i = 0; i < mPendingClientTransactions.size(); i++) { t.merge(mPendingClientTransactions.get(i)); } diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java index 61022cc971e2..ab72e3c42d85 100644 --- a/services/core/java/com/android/server/wm/TaskFragment.java +++ b/services/core/java/com/android/server/wm/TaskFragment.java @@ -40,6 +40,8 @@ import static android.os.Process.INVALID_UID; import static android.os.Process.SYSTEM_UID; import static android.os.UserHandle.USER_NULL; import static android.view.Display.INVALID_DISPLAY; +import static android.view.Surface.ROTATION_270; +import static android.view.Surface.ROTATION_90; import static android.view.WindowManager.LayoutParams.FLAG_DIM_BEHIND; import static android.view.WindowManager.TRANSIT_CLOSE; import static android.view.WindowManager.TRANSIT_FLAG_OPEN_BEHIND; @@ -87,6 +89,7 @@ import android.content.PermissionChecker; import android.content.pm.ActivityInfo; import android.content.pm.PackageManager; import android.content.res.Configuration; +import android.graphics.Insets; import android.graphics.Point; import android.graphics.Rect; import android.os.IBinder; @@ -1101,21 +1104,34 @@ class TaskFragment extends WindowContainer<WindowContainer> { } ActivityRecord getTopNonFinishingActivity() { - return getTopNonFinishingActivity(true /* includeOverlays */); + return getTopNonFinishingActivity( + true /* includeOverlays */, true /* includeLaunchedFromBubble */); } /** * Returns the top-most non-finishing activity, even if the activity is NOT ok to show to * the current user. * @param includeOverlays whether the task overlay activity should be included. + * @param includeLaunchedFromBubble whether activities that were launched from a bubble should + * be included. * @see #topRunningActivity(boolean) */ - ActivityRecord getTopNonFinishingActivity(boolean includeOverlays) { - // Split into 2 to avoid object creation due to variable capture. + ActivityRecord getTopNonFinishingActivity(boolean includeOverlays, + boolean includeLaunchedFromBubble) { + // Split to avoid object creation due to variable capture. if (includeOverlays) { - return getActivity((r) -> !r.finishing); + if (includeLaunchedFromBubble) { + return getActivity(r -> !r.finishing); + } else { + return getActivity(r -> !r.finishing && !r.getLaunchedFromBubble()); + } + } + if (includeLaunchedFromBubble) { + return getActivity(r -> !r.finishing && !r.isTaskOverlay()); + } else { + return getActivity( + r -> !r.finishing && !r.isTaskOverlay() && !r.getLaunchedFromBubble()); } - return getActivity((r) -> !r.finishing && !r.isTaskOverlay()); } ActivityRecord topRunningActivity() { @@ -2225,7 +2241,43 @@ class TaskFragment extends WindowContainer<WindowContainer> { static class ConfigOverrideHint { @Nullable DisplayInfo mTmpOverrideDisplayInfo; @Nullable ActivityRecord.CompatDisplayInsets mTmpCompatInsets; + @Nullable Rect mTmpParentAppBoundsOverride; + int mTmpOverrideConfigOrientation; boolean mUseOverrideInsetsForConfig; + + void resolveTmpOverrides(DisplayContent dc, Configuration parentConfig, + boolean isFixedRotationTransforming) { + mTmpParentAppBoundsOverride = new Rect(parentConfig.windowConfiguration.getAppBounds()); + final Insets insets; + if (mUseOverrideInsetsForConfig && dc != null) { + // Insets are decoupled from configuration by default from V+, use legacy + // compatibility behaviour for apps targeting SDK earlier than 35 + // (see applySizeOverrideIfNeeded). + int rotation = parentConfig.windowConfiguration.getRotation(); + if (rotation == ROTATION_UNDEFINED && !isFixedRotationTransforming) { + rotation = dc.getRotation(); + } + final boolean rotated = (rotation == ROTATION_90 || rotation == ROTATION_270); + final int dw = rotated ? dc.mBaseDisplayHeight : dc.mBaseDisplayWidth; + final int dh = rotated ? dc.mBaseDisplayWidth : dc.mBaseDisplayHeight; + DisplayPolicy.DecorInsets.Info decorInsets = dc.getDisplayPolicy() + .getDecorInsetsInfo(rotation, dw, dh); + final Rect stableBounds = decorInsets.mOverrideConfigFrame; + mTmpOverrideConfigOrientation = stableBounds.width() > stableBounds.height() + ? ORIENTATION_LANDSCAPE : ORIENTATION_PORTRAIT; + insets = Insets.of(decorInsets.mOverrideNonDecorInsets); + } else { + insets = Insets.NONE; + } + mTmpParentAppBoundsOverride.inset(insets); + } + + void resetTmpOverrides() { + mTmpOverrideDisplayInfo = null; + mTmpCompatInsets = null; + mTmpParentAppBoundsOverride = null; + mTmpOverrideConfigOrientation = ORIENTATION_UNDEFINED; + } } void computeConfigResourceOverrides(@NonNull Configuration inOutConfig, @@ -2311,7 +2363,9 @@ class TaskFragment extends WindowContainer<WindowContainer> { if (!customContainerPolicy && windowingMode != WINDOWING_MODE_FREEFORM) { final Rect containingAppBounds; if (insideParentBounds) { - containingAppBounds = parentConfig.windowConfiguration.getAppBounds(); + containingAppBounds = useOverrideInsetsForConfig + ? overrideHint.mTmpParentAppBoundsOverride + : parentConfig.windowConfiguration.getAppBounds(); } else { // Restrict appBounds to display non-decor rather than parent because the // override bounds are beyond the parent. Otherwise, it won't match the @@ -2999,6 +3053,9 @@ class TaskFragment extends WindowContainer<WindowContainer> { @Override void removeImmediately() { + if (asTask() == null) { + EventLogTags.writeWmTfRemoved(System.identityHashCode(this), getTaskId()); + } mIsRemovalRequested = false; resetAdjacentTaskFragment(); cleanUpEmbeddedTaskFragment(); diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java index 218334e9ecae..63ca46991c73 100644 --- a/services/core/java/com/android/server/wm/Transition.java +++ b/services/core/java/com/android/server/wm/Transition.java @@ -52,6 +52,7 @@ import static android.window.TransitionInfo.FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY; import static android.window.TransitionInfo.FLAG_IS_BEHIND_STARTING_WINDOW; import static android.window.TransitionInfo.FLAG_IS_DISPLAY; import static android.window.TransitionInfo.FLAG_IS_INPUT_METHOD; +import static android.window.TransitionInfo.FLAG_IS_OCCLUDED; import static android.window.TransitionInfo.FLAG_IS_VOICE_INTERACTION; import static android.window.TransitionInfo.FLAG_IS_WALLPAPER; import static android.window.TransitionInfo.FLAG_MOVED_TO_TOP; @@ -95,6 +96,7 @@ import android.view.Display; import android.view.SurfaceControl; import android.view.WindowManager; import android.window.ScreenCapture; +import android.window.TaskFragmentAnimationParams; import android.window.TransitionInfo; import android.window.WindowContainerTransaction; @@ -1046,7 +1048,7 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener { // the animation played. This puts the layers back into the correct order. for (int i = displays.size() - 1; i >= 0; --i) { if (displays.valueAt(i) == null) continue; - updateDisplayLayers(displays.valueAt(i), t); + assignLayers(displays.valueAt(i), t); } for (int i = 0; i < info.getRootCount(); ++i) { @@ -1054,12 +1056,13 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener { } } - private static void updateDisplayLayers(DisplayContent dc, SurfaceControl.Transaction t) { - dc.mTransitionController.mBuildingFinishLayers = true; + /** Assigns the layers for the start or end state of transition. */ + static void assignLayers(WindowContainer<?> wc, SurfaceControl.Transaction t) { + wc.mTransitionController.mBuildingFinishLayers = true; try { - dc.assignChildLayers(t); + wc.assignChildLayers(t); } finally { - dc.mTransitionController.mBuildingFinishLayers = false; + wc.mTransitionController.mBuildingFinishLayers = false; } } @@ -1372,13 +1375,22 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener { // processed all the participants first (in particular, we want to trigger pip-enter first) for (int i = 0; i < mParticipants.size(); ++i) { final ActivityRecord ar = mParticipants.valueAt(i).asActivityRecord(); + if (ar == null) continue; + // If the activity was just inserted to an invisible task, it will keep INITIALIZING // state. Then no need to notify the callback to avoid clearing some states // unexpectedly, e.g. launch-task-behind. - if (ar != null && (ar.isVisibleRequested() - || !ar.isState(ActivityRecord.State.INITIALIZING))) { + if (ar.isVisibleRequested() || !ar.isState(ActivityRecord.State.INITIALIZING)) { mController.dispatchLegacyAppTransitionFinished(ar); } + + // Reset the ActivityRecord#mCurrentLaunchCanTurnScreenOn state if it is not the top + // running activity. Doing so in case the state is not yet consumed during rapid + // activity launch. + if (ar.currentLaunchCanTurnScreenOn() && ar.getDisplayContent() != null + && ar.getDisplayContent().topRunningActivity() != ar) { + ar.setCurrentLaunchCanTurnScreenOn(false); + } } // Update the input-sink (touch-blocking) state now that the animation is finished. @@ -1893,6 +1905,8 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener { for (int i = changes.size() - 1; i >= 0; --i) { if (mTargets.get(i).mContainer.asActivityRecord() != null) { changes.get(i).setAnimationOptions(mOverrideOptions); + // TODO(b/295805497): Extract mBackgroundColor from AnimationOptions. + changes.get(i).setBackgroundColor(mOverrideOptions.getBackgroundColor()); } } } @@ -2704,7 +2718,7 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener { rootLeash.setUnreleasedWarningCallSite("Transition.calculateTransitionRoots"); // Update layers to start transaction because we prevent assignment during collect, so // the layer of transition root can be correct. - updateDisplayLayers(dc, startT); + assignLayers(dc, startT); startT.setLayer(rootLeash, leashReference.getLastLayer()); outInfo.addRootLeash(endDisplayId, rootLeash, ancestor.getBounds().left, ancestor.getBounds().top); @@ -2728,12 +2742,13 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener { return out; } - final AnimationOptions animOptions = calculateAnimationOptionsForActivityTransition(type, - sortedTargets); - if (!Flags.moveAnimationOptionsToChange() && animOptions != null) { - out.setAnimationOptions(animOptions); + final AnimationOptions animOptionsForActivityTransition = + calculateAnimationOptionsForActivityTransition(type, sortedTargets); + if (!Flags.moveAnimationOptionsToChange() && animOptionsForActivityTransition != null) { + out.setAnimationOptions(animOptionsForActivityTransition); } + final ArraySet<WindowContainer> occludedAtEndContainers = new ArraySet<>(); // Convert all the resolved ChangeInfos into TransactionInfo.Change objects in order. final int count = sortedTargets.size(); for (int i = 0; i < count; ++i) { @@ -2757,8 +2772,26 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener { info.mReadyFlags = change.getFlags(); change.setDisplayId(info.mDisplayId, getDisplayId(target)); + // Add FLAGS_IS_OCCLUDED to preventing from visible-translucent change which belows + // the non-translucent change playing unexpected open animation. + if (change.getMode() == TRANSIT_TO_FRONT || change.getMode() == TRANSIT_OPEN) { + for (int occIndex = occludedAtEndContainers.size() - 1; occIndex >= 0; --occIndex) { + if (target.isDescendantOf(occludedAtEndContainers.valueAt(occIndex))) { + change.setFlags(change.getFlags() | FLAG_IS_OCCLUDED); + break; + } + } + } + if (!change.hasFlags(FLAG_TRANSLUCENT) && (change.getMode() == TRANSIT_OPEN + || change.getMode() == TRANSIT_TO_FRONT + || change.getMode() == TRANSIT_CHANGE)) { + occludedAtEndContainers.add(target.getParent()); + } + final Task task = target.asTask(); final TaskFragment taskFragment = target.asTaskFragment(); + final boolean isEmbeddedTaskFragment = taskFragment != null + && taskFragment.isEmbedded(); final ActivityRecord activityRecord = target.asActivityRecord(); if (task != null) { @@ -2798,7 +2831,7 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener { change.setEndAbsBounds(bounds); } - if (activityRecord != null || (taskFragment != null && taskFragment.isEmbedded())) { + if (activityRecord != null || isEmbeddedTaskFragment) { final int backgroundColor; final TaskFragment organizedTf = activityRecord != null ? activityRecord.getOrganizedTaskFragment() @@ -2823,9 +2856,27 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener { change.setBackgroundColor(ColorUtils.setAlphaComponent(backgroundColor, 255)); } - if (Flags.moveAnimationOptionsToChange() && activityRecord != null - && animOptions != null) { - change.setAnimationOptions(animOptions); + AnimationOptions animOptions = null; + if (Flags.moveAnimationOptionsToChange()) { + if (activityRecord != null && animOptionsForActivityTransition != null) { + animOptions = animOptionsForActivityTransition; + } else if (Flags.activityEmbeddingOverlayPresentationFlag() + && isEmbeddedTaskFragment) { + final TaskFragmentAnimationParams params = taskFragment.getAnimationParams(); + if (params.hasOverrideAnimation()) { + // Only set AnimationOptions if there's any animation override. + // We use separated field for backgroundColor, and + // AnimationOptions#backgroundColor will be removed in long term. + animOptions = AnimationOptions.makeCustomAnimOptions( + taskFragment.getTask().getBasePackageName(), + params.getOpenAnimationResId(), params.getChangeAnimationResId(), + params.getCloseAnimationResId(), 0 /* backgroundColor */, + false /* overrideTaskTransition */); + } + } + if (animOptions != null) { + change.setAnimationOptions(animOptions); + } } if (activityRecord != null) { diff --git a/services/core/java/com/android/server/wm/TransparentPolicy.java b/services/core/java/com/android/server/wm/TransparentPolicy.java new file mode 100644 index 000000000000..b408397d1861 --- /dev/null +++ b/services/core/java/com/android/server/wm/TransparentPolicy.java @@ -0,0 +1,353 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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.wm; + +import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; +import static android.content.res.Configuration.ORIENTATION_UNDEFINED; +import static android.content.res.Configuration.SCREEN_HEIGHT_DP_UNDEFINED; +import static android.content.res.Configuration.SCREEN_WIDTH_DP_UNDEFINED; +import static android.content.res.Configuration.SMALLEST_SCREEN_WIDTH_DP_UNDEFINED; + +import static com.android.internal.util.FrameworkStatsLog.APP_COMPAT_STATE_CHANGED__STATE__UNKNOWN; +import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM; +import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.content.res.Configuration; +import android.graphics.Rect; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.function.BooleanSupplier; +import java.util.function.Consumer; +import java.util.function.Predicate; + +/** + * Encapsulate logic about translucent activities. + * <p/> + * An activity is defined as translucent if {@link ActivityRecord#fillsParent()} returns + * {@code false}. When the policy is running for a letterboxed activity, a transparent activity + * will inherit constraints about bounds, aspect ratios and orientation from the first not finishing + * activity below. + */ +class TransparentPolicy { + + private static final String TAG = TAG_WITH_CLASS_NAME ? "TransparentPolicy" : TAG_ATM; + + // The predicate used to find the first opaque not finishing activity below the potential + // transparent activity. + private static final Predicate<ActivityRecord> FIRST_OPAQUE_NOT_FINISHING_ACTIVITY_PREDICATE = + ActivityRecord::occludesParent; + + // The ActivityRecord this policy relates to. + @NonNull + private final ActivityRecord mActivityRecord; + + // If transparent activity policy is enabled. + @NonNull + private final BooleanSupplier mIsTranslucentLetterboxingEnabledSupplier; + + // The list of observers for the destroy event of candidate opaque activities + // when dealing with translucent activities. + @NonNull + private final List<TransparentPolicy> mDestroyListeners = new ArrayList<>(); + + // The current state for the possible transparent activity + @NonNull + private final TransparentPolicyState mTransparentPolicyState; + + TransparentPolicy(@NonNull ActivityRecord activityRecord, + @NonNull LetterboxConfiguration letterboxConfiguration) { + mActivityRecord = activityRecord; + mIsTranslucentLetterboxingEnabledSupplier = + letterboxConfiguration::isTranslucentLetterboxingEnabled; + mTransparentPolicyState = new TransparentPolicyState(activityRecord); + } + + /** + * Handles translucent activities letterboxing inheriting constraints from the + * first opaque activity beneath. + */ + void start() { + if (!mIsTranslucentLetterboxingEnabledSupplier.getAsBoolean()) { + return; + } + final WindowContainer<?> parent = mActivityRecord.getParent(); + if (parent == null) { + return; + } + mTransparentPolicyState.reset(); + // In case mActivityRecord.hasCompatDisplayInsetsWithoutOverride() we don't apply the + // opaque activity constraints because we're expecting the activity is already letterboxed. + final ActivityRecord firstOpaqueActivity = mActivityRecord.getTask().getActivity( + FIRST_OPAQUE_NOT_FINISHING_ACTIVITY_PREDICATE /* callback */, + mActivityRecord /* boundary */, false /* includeBoundary */, + true /* traverseTopToBottom */); + // We check if we need for some reason to skip the policy gievn the specific first + // opaque activity + if (shouldSkipTransparentPolicy(firstOpaqueActivity)) { + return; + } + mTransparentPolicyState.start(firstOpaqueActivity); + } + + void stop() { + for (int i = mDestroyListeners.size() - 1; i >= 0; i--) { + mDestroyListeners.get(i).start(); + } + mDestroyListeners.clear(); + mTransparentPolicyState.reset(); + } + + /** + * @return {@code true} if the current activity is translucent with an opaque activity + * beneath and the related policy is running. In this case it will inherit bounds, orientation + * and aspect ratios from the first opaque activity beneath. + */ + boolean isRunning() { + return mTransparentPolicyState.isRunning(); + } + + /** + * @return {@code true} if the current activity is translucent with an opaque activity + * beneath and needs to inherit its orientation. + */ + boolean hasInheritedOrientation() { + // To avoid wrong behaviour (e.g. permission dialogs not centered or with wrong size), + // transparent activities inherit orientation from the first opaque activity below only if + // they explicitly define an orientation different from SCREEN_ORIENTATION_UNSPECIFIED. + return isRunning() + && mActivityRecord.getOverrideOrientation() + != SCREEN_ORIENTATION_UNSPECIFIED; + } + + float getInheritedMinAspectRatio() { + return mTransparentPolicyState.mInheritedMinAspectRatio; + } + + float getInheritedMaxAspectRatio() { + return mTransparentPolicyState.mInheritedMaxAspectRatio; + } + + int getInheritedAppCompatState() { + return mTransparentPolicyState.mInheritedAppCompatState; + } + + @Configuration.Orientation + int getInheritedOrientation() { + return mTransparentPolicyState.mInheritedOrientation; + } + + ActivityRecord.CompatDisplayInsets getInheritedCompatDisplayInsets() { + return mTransparentPolicyState.mInheritedCompatDisplayInsets; + } + + void clearInheritedCompatDisplayInsets() { + mTransparentPolicyState.clearInheritedCompatDisplayInsets(); + } + + TransparentPolicyState getTransparentPolicyState() { + return mTransparentPolicyState; + } + + /** + * In case of translucent activities, it consumes the {@link ActivityRecord} of the first opaque + * activity beneath using the given consumer and returns {@code true}. + */ + boolean applyOnOpaqueActivityBelow(@NonNull Consumer<ActivityRecord> consumer) { + return mTransparentPolicyState.applyOnOpaqueActivityBelow(consumer); + } + + @NonNull + Optional<ActivityRecord> getFirstOpaqueActivity() { + return isRunning() ? Optional.of(mTransparentPolicyState.mFirstOpaqueActivity) + : Optional.empty(); + } + + /** + * @return The first not finishing opaque activity beneath the current translucent activity + * if it exists and the strategy is enabled. + */ + Optional<ActivityRecord> findOpaqueNotFinishingActivityBelow() { + return mTransparentPolicyState.findOpaqueNotFinishingActivityBelow(); + } + + // We evaluate the case when the policy should not be applied. + private boolean shouldSkipTransparentPolicy(@Nullable ActivityRecord opaqueActivity) { + if (opaqueActivity == null || opaqueActivity.isEmbedded()) { + // We skip letterboxing if the translucent activity doesn't have any + // opaque activities beneath or the activity below is embedded which + // never has letterbox. + mActivityRecord.recomputeConfiguration(); + return true; + } + if (mActivityRecord.getTask() == null || mActivityRecord.fillsParent() + || mActivityRecord.hasCompatDisplayInsetsWithoutInheritance()) { + return true; + } + return false; + } + + /** Resets the screen size related fields so they can be resolved by requested bounds later. */ + private static void resetTranslucentOverrideConfig(Configuration config) { + // The values for the following properties will be defined during the configuration + // resolution in {@link ActivityRecord#resolveOverrideConfiguration} using the + // properties inherited from the first not finishing opaque activity beneath. + config.orientation = ORIENTATION_UNDEFINED; + config.screenWidthDp = config.compatScreenWidthDp = SCREEN_WIDTH_DP_UNDEFINED; + config.screenHeightDp = config.compatScreenHeightDp = SCREEN_HEIGHT_DP_UNDEFINED; + config.smallestScreenWidthDp = config.compatSmallestScreenWidthDp = + SMALLEST_SCREEN_WIDTH_DP_UNDEFINED; + } + + private void inheritConfiguration(ActivityRecord firstOpaque) { + mTransparentPolicyState.inheritFromOpaque(firstOpaque); + } + + /** + * Encapsulate the state for the current translucent activity when the transparent policy + * has started. + */ + static class TransparentPolicyState { + // Aspect ratio value to consider as undefined. + private static final float UNDEFINED_ASPECT_RATIO = 0f; + + @NonNull + private final ActivityRecord mActivityRecord; + + @Configuration.Orientation + private int mInheritedOrientation = ORIENTATION_UNDEFINED; + private float mInheritedMinAspectRatio = UNDEFINED_ASPECT_RATIO; + private float mInheritedMaxAspectRatio = UNDEFINED_ASPECT_RATIO; + + // The app compat state for the opaque activity if any + private int mInheritedAppCompatState = APP_COMPAT_STATE_CHANGED__STATE__UNKNOWN; + + // The CompatDisplayInsets of the opaque activity beneath the translucent one. + @Nullable + private ActivityRecord.CompatDisplayInsets mInheritedCompatDisplayInsets; + + @Nullable + private ActivityRecord mFirstOpaqueActivity; + + /* + * WindowContainerListener responsible to make translucent activities inherit + * constraints from the first opaque activity beneath them. It's null for not + * translucent activities. + */ + @Nullable + private WindowContainerListener mLetterboxConfigListener; + + TransparentPolicyState(@NonNull ActivityRecord activityRecord) { + mActivityRecord = activityRecord; + } + + private void start(@NonNull ActivityRecord firstOpaqueActivity) { + mFirstOpaqueActivity = firstOpaqueActivity; + mFirstOpaqueActivity.mTransparentPolicy + .mDestroyListeners.add(mActivityRecord.mTransparentPolicy); + inheritFromOpaque(firstOpaqueActivity); + final WindowContainer<?> parent = mActivityRecord.getParent(); + mLetterboxConfigListener = WindowContainer.overrideConfigurationPropagation( + mActivityRecord, mFirstOpaqueActivity, + (opaqueConfig, transparentOverrideConfig) -> { + resetTranslucentOverrideConfig(transparentOverrideConfig); + final Rect parentBounds = parent.getWindowConfiguration().getBounds(); + final Rect bounds = transparentOverrideConfig + .windowConfiguration.getBounds(); + final Rect letterboxBounds = opaqueConfig.windowConfiguration.getBounds(); + // We cannot use letterboxBounds directly here because the position relies + // on letterboxing. Using letterboxBounds directly, would produce a + // double offset. + bounds.set(parentBounds.left, parentBounds.top, + parentBounds.left + letterboxBounds.width(), + parentBounds.top + letterboxBounds.height()); + // We need to initialize appBounds to avoid NPE. The actual value will + // be set ahead when resolving the Configuration for the activity. + transparentOverrideConfig.windowConfiguration.setAppBounds(new Rect()); + inheritFromOpaque(mFirstOpaqueActivity); + return transparentOverrideConfig; + }); + } + + private void inheritFromOpaque(@NonNull ActivityRecord opaqueActivity) { + // To avoid wrong behaviour, we're not forcing a specific aspect ratio to activities + // which are not already providing one (e.g. permission dialogs) and presumably also + // not resizable. + if (mActivityRecord.getMinAspectRatio() != UNDEFINED_ASPECT_RATIO) { + mInheritedMinAspectRatio = opaqueActivity.getMinAspectRatio(); + } + if (mActivityRecord.getMaxAspectRatio() != UNDEFINED_ASPECT_RATIO) { + mInheritedMaxAspectRatio = opaqueActivity.getMaxAspectRatio(); + } + mInheritedOrientation = opaqueActivity.getRequestedConfigurationOrientation(); + mInheritedAppCompatState = opaqueActivity.getAppCompatState(); + mInheritedCompatDisplayInsets = opaqueActivity.getCompatDisplayInsets(); + } + + private void reset() { + if (mLetterboxConfigListener != null) { + mLetterboxConfigListener.onRemoved(); + } + mLetterboxConfigListener = null; + mInheritedOrientation = ORIENTATION_UNDEFINED; + mInheritedMinAspectRatio = UNDEFINED_ASPECT_RATIO; + mInheritedMaxAspectRatio = UNDEFINED_ASPECT_RATIO; + mInheritedAppCompatState = APP_COMPAT_STATE_CHANGED__STATE__UNKNOWN; + mInheritedCompatDisplayInsets = null; + if (mFirstOpaqueActivity != null) { + mFirstOpaqueActivity.mTransparentPolicy + .mDestroyListeners.remove(mActivityRecord.mTransparentPolicy); + } + mFirstOpaqueActivity = null; + } + + private boolean isRunning() { + return mLetterboxConfigListener != null; + } + + private void clearInheritedCompatDisplayInsets() { + mInheritedCompatDisplayInsets = null; + } + + /** + * @return The first not finishing opaque activity beneath the current translucent activity + * if it exists and the strategy is enabled. + */ + private Optional<ActivityRecord> findOpaqueNotFinishingActivityBelow() { + if (!isRunning() || mActivityRecord.getTask() == null) { + return Optional.empty(); + } + return Optional.ofNullable(mFirstOpaqueActivity); + } + + /** + * In case of translucent activities, it consumes the {@link ActivityRecord} of the first + * opaque activity beneath using the given consumer and returns {@code true}. + */ + private boolean applyOnOpaqueActivityBelow(@NonNull Consumer<ActivityRecord> consumer) { + return findOpaqueNotFinishingActivityBelow() + .map(activityRecord -> { + consumer.accept(activityRecord); + return true; + }).orElse(false); + } + } + +} diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java index edbba9244738..70143bae9fb0 100644 --- a/services/core/java/com/android/server/wm/WindowContainer.java +++ b/services/core/java/com/android/server/wm/WindowContainer.java @@ -1077,9 +1077,6 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< if (dc != null && dc != this) { dc.getPendingTransaction().merge(mPendingTransaction); } - if (dc != this && mLocalInsetsSources != null) { - mLocalInsetsSources.clear(); - } for (int i = mChildren.size() - 1; i >= 0; --i) { final WindowContainer child = mChildren.get(i); child.onDisplayChanged(dc); diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java index 19053f70e7e6..a42cb09f5500 100644 --- a/services/core/java/com/android/server/wm/WindowManagerInternal.java +++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java @@ -340,6 +340,19 @@ public abstract class WindowManagerInternal { } /** + * An interface to be notified about requested changes in the IME visibility. + */ + public interface OnImeRequestedChangedListener { + /** + * Called when the requested IME visibility is changed. + * + * @param windowToken The window token + * @param imeVisible {@code true} if the IME should be shown, {@code false} to hide + */ + void onImeRequestedChanged(IBinder windowToken, boolean imeVisible); + } + + /** * An interface to customize drag and drop behaviors. */ public interface IDragDropCallback { @@ -682,6 +695,14 @@ public abstract class WindowManagerInternal { public abstract void setOnHardKeyboardStatusChangeListener( OnHardKeyboardStatusChangeListener listener); + + /** + * Sets the callback listener for requested IME visibility changes in ImeInsetsSourceProvider + * + * @param listener The listener to set + */ + public abstract void setOnImeRequestedChangedListener(OnImeRequestedChangedListener listener); + /** * Requests the window manager to resend the windows for accessibility on specified display. * diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 753f481c1649..b814ccd2babe 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -781,7 +781,7 @@ public class WindowManagerService extends IWindowManager.Stub boolean mHardKeyboardAvailable; WindowManagerInternal.OnHardKeyboardStatusChangeListener mHardKeyboardStatusChangeListener; - + WindowManagerInternal.OnImeRequestedChangedListener mOnImeRequestedChangedListener; @Nullable ImeTargetChangeListener mImeTargetChangeListener; SettingsObserver mSettingsObserver; @@ -3502,10 +3502,11 @@ public class WindowManagerService extends IWindowManager.Stub if (!checkCallingPermission(permission.CONTROL_KEYGUARD, "dismissKeyguard")) { throw new SecurityException("Requires CONTROL_KEYGUARD permission"); } - if (!dreamHandlesConfirmKeys() && mAtmService.mKeyguardController.isShowingDream()) { - mAtmService.mTaskSupervisor.wakeUp("leaveDream"); - } synchronized (mGlobalLock) { + if (!dreamHandlesConfirmKeys() + && getDefaultDisplayContentLocked().getDisplayPolicy().isShowingDreamLw()) { + mAtmService.mTaskSupervisor.wakeUp("leaveDream"); + } mPolicy.dismissKeyguardLw(callback, message); } } @@ -8128,6 +8129,27 @@ public class WindowManagerService extends IWindowManager.Stub getInputTargetFromWindowTokenLocked(imeTargetWindowToken); if (imeTarget != null) { imeTarget.getDisplayContent().updateImeInputAndControlTarget(imeTarget); + + if (android.view.inputmethod.Flags.refactorInsetsController()) { + // In case of a virtual display that may not show the IME, reset the + // inputTarget of all other displays + WindowState imeWindowState = imeTarget.getWindowState(); + if (imeWindowState != null) { + InsetsControlTarget fallback = + imeTarget.getDisplayContent().getImeHostOrFallback( + imeWindowState); + if (imeWindowState != fallback) { + // fallback should be the RemoteInsetsControlTarget of the + // default display + int currentDisplayId = imeTarget.getDisplayContent().getDisplayId(); + mRoot.forAllDisplays(display -> { + if (display.getDisplayId() != currentDisplayId) { + display.setImeInputTarget(null); + } + }); + } + } + } } } } @@ -8148,6 +8170,14 @@ public class WindowManagerService extends IWindowManager.Stub } @Override + public void setOnImeRequestedChangedListener( + OnImeRequestedChangedListener listener) { + synchronized (mGlobalLock) { + mOnImeRequestedChangedListener = listener; + } + } + + @Override public void computeWindowsForAccessibility(int displayId) { mAccessibilityController.performComputeChangedWindowsNot(displayId, true); } diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java index 7eda53f2e28e..72109d34ec8a 100644 --- a/services/core/java/com/android/server/wm/WindowOrganizerController.java +++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java @@ -23,8 +23,8 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; import static android.app.WindowConfiguration.WINDOW_CONFIG_BOUNDS; import static android.view.Display.DEFAULT_DISPLAY; import static android.window.TaskFragmentOperation.OP_TYPE_CLEAR_ADJACENT_TASK_FRAGMENTS; -import static android.window.TaskFragmentOperation.OP_TYPE_CREATE_TASK_FRAGMENT; import static android.window.TaskFragmentOperation.OP_TYPE_CREATE_OR_MOVE_TASK_FRAGMENT_DECOR_SURFACE; +import static android.window.TaskFragmentOperation.OP_TYPE_CREATE_TASK_FRAGMENT; import static android.window.TaskFragmentOperation.OP_TYPE_DELETE_TASK_FRAGMENT; import static android.window.TaskFragmentOperation.OP_TYPE_REMOVE_TASK_FRAGMENT_DECOR_SURFACE; import static android.window.TaskFragmentOperation.OP_TYPE_REORDER_TO_BOTTOM_OF_TASK; @@ -1595,11 +1595,31 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub break; } case OP_TYPE_CREATE_OR_MOVE_TASK_FRAGMENT_DECOR_SURFACE: { - taskFragment.getTask().moveOrCreateDecorSurfaceFor(taskFragment); + final Task task = taskFragment.getTask(); + if (task == null) { + break; + } + // If any TaskFragment in the Task is collected by the transition, we make the decor + // surface visible in sync with the TaskFragment transition. Otherwise, we make the + // decor surface visible immediately. + final TaskFragment syncTaskFragment = transition != null + ? task.getTaskFragment(transition.mParticipants::contains) + : null; + + if (syncTaskFragment != null) { + task.moveOrCreateDecorSurfaceFor(taskFragment, false /* visible */); + task.setDecorSurfaceVisible(syncTaskFragment.getSyncTransaction()); + } else { + task.moveOrCreateDecorSurfaceFor(taskFragment, true /* visible */); + } break; } case OP_TYPE_REMOVE_TASK_FRAGMENT_DECOR_SURFACE: { - taskFragment.getTask().removeDecorSurface(); + final Task task = taskFragment.getTask(); + if (task == null) { + break; + } + task.removeDecorSurface(); break; } case OP_TYPE_SET_DIM_ON_TASK: { @@ -1627,21 +1647,15 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub clientTransaction.sanitize(caller.mPid, caller.mUid); } - if (transition != null) { - // The decor surface boost/unboost must happen after the transition is - // completed. Otherwise, the decor surface could be moved before Shell - // completes the transition, causing flicker. - transition.addTransitionEndedListener(() -> - task.setDecorSurfaceBoosted( - taskFragment, - operation.getBooleanValue() /* isBoosted */, - clientTransaction)); - } else { - task.setDecorSurfaceBoosted( - taskFragment, - operation.getBooleanValue() /* isBoosted */, - clientTransaction); - } + task.requestDecorSurfaceBoosted( + taskFragment, + operation.getBooleanValue() /* isBoosted */, + clientTransaction); + + // The decor surface boost/unboost must be applied after the transition is + // completed. Otherwise, the decor surface could be moved before Shell completes + // the transition, causing flicker. + runAfterTransition(transition, task::commitDecorSurfaceBoostedState); } break; } @@ -1654,6 +1668,19 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub return effects; } + /** + * Executes the provided {@code runnable} after the {@code transition}. If the + * {@code transition} is {@code null}, the {@code runnable} is executed immediately. + */ + private static void runAfterTransition( + @Nullable Transition transition, @NonNull Runnable runnable) { + if (transition == null) { + runnable.run(); + } else { + transition.addTransitionEndedListener(runnable); + } + } + private boolean validateTaskFragmentOperation( @NonNull WindowContainerTransaction.HierarchyOp hop, @Nullable IBinder errorCallbackToken, @Nullable ITaskFragmentOrganizer organizer) { @@ -1835,7 +1862,7 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub task.getParent().positionChildAt( hop.getToTop() ? POSITION_TOP : POSITION_BOTTOM, - task, false /* includingParents */); + task, hop.includingParents()); } return TRANSACT_EFFECTS_LIFECYCLE; } @@ -2361,6 +2388,7 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub position = POSITION_TOP; } ownerTask.addChild(taskFragment, position); + EventLogTags.writeWmTfCreated(System.identityHashCode(taskFragment), ownerTask.mTaskId); taskFragment.setWindowingMode(creationParams.getWindowingMode()); if (!creationParams.getInitialRelativeBounds().isEmpty()) { // The surface operations for the task fragment should sync with the transition. diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index a7b3f4fad399..dcd4bd68c3fc 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -1814,9 +1814,11 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP if (mInsetsSourceProviders == null) { return false; } + final @InsetsType int decorInsetsTypes = + mWmService.mConfigTypes | mWmService.mOverrideConfigTypes; for (int i = mInsetsSourceProviders.size() - 1; i >= 0; i--) { final InsetsSource source = mInsetsSourceProviders.valueAt(i).getSource(); - if ((source.getType() & mWmService.mConfigTypes) != 0) { + if ((source.getType() & decorInsetsTypes) != 0) { return true; } } diff --git a/services/core/jni/com_android_server_vibrator_VibratorController.cpp b/services/core/jni/com_android_server_vibrator_VibratorController.cpp index f47a59d6cec9..4be21d872383 100644 --- a/services/core/jni/com_android_server_vibrator_VibratorController.cpp +++ b/services/core/jni/com_android_server_vibrator_VibratorController.cpp @@ -131,17 +131,28 @@ public: } std::function<void()> createCallback(jlong vibrationId) { - return [vibrationId, this]() { + auto callbackId = ++mCallbackId; + return [vibrationId, callbackId, this]() { + auto currentCallbackId = mCallbackId.load(); + if (currentCallbackId != callbackId) { + // This callback is from an older HAL call that is no longer relevant to the service + return; + } auto jniEnv = GetOrAttachJNIEnvironment(sJvm); jniEnv->CallVoidMethod(mCallbackListener, sMethodIdOnComplete, mVibratorId, vibrationId); }; } + void disableOldCallbacks() { + mCallbackId++; + } + private: const std::shared_ptr<vibrator::HalController> mHal; const int32_t mVibratorId; const jobject mCallbackListener; + std::atomic<int64_t> mCallbackId; }; static aidl::BrakingPwle brakingPwle(aidl::Braking braking, int32_t duration) { @@ -236,6 +247,7 @@ static void vibratorOff(JNIEnv* env, jclass /* clazz */, jlong ptr) { } auto offFn = [](vibrator::HalWrapper* hal) { return hal->off(); }; wrapper->halCall<void>(offFn, "off"); + wrapper->disableOldCallbacks(); } static void vibratorSetAmplitude(JNIEnv* env, jclass /* clazz */, jlong ptr, jfloat amplitude) { diff --git a/services/core/xsd/display-device-config/display-device-config.xsd b/services/core/xsd/display-device-config/display-device-config.xsd index 610b502f2a07..eeb8b9b0b469 100644 --- a/services/core/xsd/display-device-config/display-device-config.xsd +++ b/services/core/xsd/display-device-config/display-device-config.xsd @@ -779,6 +779,21 @@ </xs:complexType> <xs:complexType name="blockingZoneConfig"> + <!-- list of supported modes for blocking zone . Each point corresponds to one mode. + Supported only for lowerBlockingZoneConfigs + Mode format is : first = refreshRate, second = vsyncRate. E.g. : + <supportedModes> + <point> + <first>60</first> // refreshRate + <second>60</second> //vsyncRate + </point> + .... + </supportedModes> + --> + <xs:element type="nonNegativeFloatToFloatMap" name="supportedModes" minOccurs="0"> + <xs:annotation name="nullable"/> + <xs:annotation name="final"/> + </xs:element> <xs:element name="defaultRefreshRate" type="xs:nonNegativeInteger" minOccurs="1" maxOccurs="1"> <xs:annotation name="final"/> diff --git a/services/core/xsd/display-device-config/schema/current.txt b/services/core/xsd/display-device-config/schema/current.txt index 203a6d99dba1..757b23a2df7e 100644 --- a/services/core/xsd/display-device-config/schema/current.txt +++ b/services/core/xsd/display-device-config/schema/current.txt @@ -35,9 +35,11 @@ package com.android.server.display.config { method public final com.android.server.display.config.BlockingZoneThreshold getBlockingZoneThreshold(); method public final java.math.BigInteger getDefaultRefreshRate(); method @Nullable public final String getRefreshRateThermalThrottlingId(); + method @Nullable public final com.android.server.display.config.NonNegativeFloatToFloatMap getSupportedModes(); method public final void setBlockingZoneThreshold(com.android.server.display.config.BlockingZoneThreshold); method public final void setDefaultRefreshRate(java.math.BigInteger); method public final void setRefreshRateThermalThrottlingId(@Nullable String); + method public final void setSupportedModes(@Nullable com.android.server.display.config.NonNegativeFloatToFloatMap); } public class BlockingZoneThreshold { diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyDefinition.java b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyDefinition.java index a0ea4e9b94f4..901cafa70d53 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyDefinition.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyDefinition.java @@ -240,7 +240,7 @@ final class PolicyDefinition<V> { POLICY_FLAG_LOCAL_ONLY_POLICY | POLICY_FLAG_INHERITABLE | POLICY_FLAG_NON_COEXISTABLE_POLICY | POLICY_FLAG_SKIP_ENFORCEMENT_IF_UNCHANGED, - PolicyEnforcerCallbacks::noOp, + PolicyEnforcerCallbacks::setApplicationRestrictions, new BundlePolicySerializer()); /** diff --git a/services/incremental/Android.bp b/services/incremental/Android.bp index 957d7c352bdf..ce05c820cbbc 100644 --- a/services/incremental/Android.bp +++ b/services/incremental/Android.bp @@ -66,7 +66,6 @@ cc_defaults { "libprotobuf-cpp-lite", "service.incremental.proto", "libvold_binder", - "libc++fs", "libziparchive_for_incfs", ], shared_libs: [ diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index cfe4e17eb1be..107c2947c550 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -1196,11 +1196,12 @@ public final class SystemServer implements Dumpable { mSystemServiceManager.startService(RecoverySystemService.Lifecycle.class); t.traceEnd(); + // Initialize RescueParty. + RescueParty.registerHealthObserver(mSystemContext); if (!Flags.recoverabilityDetection()) { // Now that we have the bare essentials of the OS up and running, take // note that we just booted, which might send out a rescue party if // we're stuck in a runtime restart loop. - RescueParty.registerHealthObserver(mSystemContext); PackageWatchdog.getInstance(mSystemContext).noteBoot(); } @@ -2917,10 +2918,10 @@ public final class SystemServer implements Dumpable { t.traceEnd(); if (Flags.recoverabilityDetection()) { - // Now that we have the essential services needed for rescue party, initialize - // RescuParty. note that we just booted, which might send out a rescue party if - // we're stuck in a runtime restart loop. - RescueParty.registerHealthObserver(mSystemContext); + // Now that we have the essential services needed for mitigations, register the boot + // with package watchdog. + // Note that we just booted, which might send out a rescue party if we're stuck in a + // runtime restart loop. PackageWatchdog.getInstance(mSystemContext).noteBoot(); } diff --git a/services/permission/java/com/android/server/permission/access/appop/AppOpService.kt b/services/permission/java/com/android/server/permission/access/appop/AppOpService.kt index 3bdcd9b0fdcd..161a8168d993 100644 --- a/services/permission/java/com/android/server/permission/access/appop/AppOpService.kt +++ b/services/permission/java/com/android/server/permission/access/appop/AppOpService.kt @@ -246,24 +246,32 @@ class AppOpService(private val service: AccessCheckingService) : AppOpsCheckingS } override fun setUidMode(uid: Int, deviceId: String, code: Int, mode: Int): Boolean { + val appId = UserHandle.getAppId(uid) + val userId = UserHandle.getUserId(uid) + val appOpName = AppOpsManager.opToPublicName(code) + if ( Flags.runtimePermissionAppopsMappingEnabled() && code in runtimeAppOpToPermissionNames ) { - Slog.w( - LOG_TAG, - "Cannot set UID mode for runtime permission app op, " + - " callingUid = ${Binder.getCallingUid()}, " + + val oldMode = + service.getState { with(appIdPolicy) { getAppOpMode(appId, userId, appOpName) } } + val wouldHaveChanged = oldMode != mode + val logMessage = + (if (wouldHaveChanged) "Blocked" else "Ignored") + + " setUidMode call for runtime permission app op:" + " uid = $uid," + " code = ${AppOpsManager.opToName(code)}," + - " mode = ${AppOpsManager.modeToName(mode)}", - RuntimeException() - ) + " mode = ${AppOpsManager.modeToName(mode)}," + + " callingUid = ${Binder.getCallingUid()}," + + " oldMode = ${AppOpsManager.modeToName(oldMode)}" + if (wouldHaveChanged) { + Slog.e(LOG_TAG, logMessage, RuntimeException()) + } else { + Slog.w(LOG_TAG, logMessage) + } return false } - val appId = UserHandle.getAppId(uid) - val userId = UserHandle.getUserId(uid) - val appOpName = AppOpsManager.opToPublicName(code) var wasChanged: Boolean service.mutateState { wasChanged = with(appIdPolicy) { setAppOpMode(appId, userId, appOpName, mode) } diff --git a/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionPolicy.kt b/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionPolicy.kt index d3072000a56e..bb0838db97b5 100644 --- a/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionPolicy.kt +++ b/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionPolicy.kt @@ -1277,10 +1277,11 @@ class AppIdPermissionPolicy : SchemePolicy() { permissionName ) else -> - permissionAllowlist.getProductSignatureAppAllowlistState( - packageName, - permissionName - ) + permissionAllowlist.getApexSignatureAppAllowlistState(packageName, permissionName) + ?: permissionAllowlist.getProductSignatureAppAllowlistState( + packageName, + permissionName + ) ?: permissionAllowlist.getVendorSignatureAppAllowlistState( packageName, permissionName diff --git a/services/permission/java/com/android/server/permission/access/permission/PermissionService.kt b/services/permission/java/com/android/server/permission/access/permission/PermissionService.kt index 649955614851..78dbc60dbae0 100644 --- a/services/permission/java/com/android/server/permission/access/permission/PermissionService.kt +++ b/services/permission/java/com/android/server/permission/access/permission/PermissionService.kt @@ -533,8 +533,7 @@ class PermissionService(private val service: AccessCheckingService) : val packageState = packageManagerLocal.withFilteredSnapshot(Binder.getCallingUid(), userId).use { it.getPackageState(packageName) - } - ?: return PackageManager.PERMISSION_DENIED + } ?: return PackageManager.PERMISSION_DENIED val isPermissionGranted = service.getState { isPermissionGranted(packageState, userId, permissionName, deviceId) } @@ -1164,8 +1163,7 @@ class PermissionService(private val service: AccessCheckingService) : val packageState = packageManagerLocal.withFilteredSnapshot(Binder.getCallingUid(), userId).use { it.getPackageState(packageName) - } - ?: return false + } ?: return false service.getState { if (isPermissionGranted(packageState, userId, permissionName, deviceId)) { @@ -1216,8 +1214,7 @@ class PermissionService(private val service: AccessCheckingService) : val packageState = packageManagerLocal.withFilteredSnapshot(callingUid, userId).use { it.getPackageState(packageName) - } - ?: return false + } ?: return false val appId = packageState.appId if (UserHandle.getAppId(callingUid) != appId) { return false @@ -1546,8 +1543,7 @@ class PermissionService(private val service: AccessCheckingService) : val packageState = packageManagerLocal.withFilteredSnapshot(callingUid, userId).use { it.getPackageState(packageName) - } - ?: return null + } ?: return null val androidPackage = packageState.androidPackage ?: return null val isCallerPrivileged = @@ -1710,8 +1706,7 @@ class PermissionService(private val service: AccessCheckingService) : PackageManager.FLAG_PERMISSION_WHITELIST_INSTALLER, userId ) - ?.let { ArraySet(permissionNames).apply { this += it }.toList() } - ?: permissionNames + ?.let { ArraySet(permissionNames).apply { this += it }.toList() } ?: permissionNames setAllowlistedRestrictedPermissionsUnchecked( androidPackage, @@ -2753,27 +2748,25 @@ class PermissionService(private val service: AccessCheckingService) : ) { return false } - return try { - val contentResolver = context.contentResolver - val userId = UserHandle.getUserId(uid) - val isInSetup = - Settings.Secure.getIntForUser( - contentResolver, - Settings.Secure.USER_SETUP_COMPLETE, - userId - ) == 0 - val isInDeferredSetup = - Settings.Secure.getIntForUser( - contentResolver, - Settings.Secure.USER_SETUP_PERSONALIZATION_STATE, - userId - ) == Settings.Secure.USER_SETUP_PERSONALIZATION_STARTED - isInSetup || isInDeferredSetup + + val userId = UserHandle.getUserId(uid) + + val isInSetup = getSecureInt(Settings.Secure.USER_SETUP_COMPLETE, userId) == 0 + if (isInSetup) return true + + val isInDeferredSetup = + getSecureInt(Settings.Secure.USER_SETUP_PERSONALIZATION_STATE, userId) == + Settings.Secure.USER_SETUP_PERSONALIZATION_STARTED + return isInDeferredSetup + } + + private fun getSecureInt(settingName: String, userId: Int): Int? = + try { + Settings.Secure.getIntForUser(context.contentResolver, settingName, userId) } catch (e: Settings.SettingNotFoundException) { - Slog.w(LOG_TAG, "Failed to check if the user is in restore: $e") - false + Slog.i(LOG_TAG, "Setting $settingName not found", e) + null } - } } private class OnPermissionsChangeListeners(looper: Looper) : Handler(looper) { diff --git a/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java b/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java index 9f9764853bef..cd70ed23a824 100644 --- a/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java +++ b/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java @@ -25,6 +25,7 @@ import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.hardware.camera2.CameraManager; import android.os.Handler; import android.os.IBinder.DeathRecipient; import android.os.Looper; @@ -58,7 +59,6 @@ import java.util.concurrent.TimeUnit; public final class ProfcollectForwardingService extends SystemService { public static final String LOG_TAG = "ProfcollectForwardingService"; - private static final boolean DEBUG = Log.isLoggable(LOG_TAG, Log.DEBUG); private static final String INTENT_UPLOAD_PROFILES = "com.android.server.profcollect.UPLOAD_PROFILES"; private static final long BG_PROCESS_INTERVAL = TimeUnit.HOURS.toMillis(4); // every 4 hours. @@ -120,9 +120,6 @@ public final class ProfcollectForwardingService extends SystemService { @Override public void onStart() { - if (DEBUG) { - Log.d(LOG_TAG, "Profcollect forwarding service start"); - } connectNativeService(); } @@ -245,9 +242,6 @@ public final class ProfcollectForwardingService extends SystemService { @Override public boolean onStartJob(JobParameters params) { - if (DEBUG) { - Log.d(LOG_TAG, "Starting background process job"); - } createAndUploadReport(sSelfService); jobFinished(params, false); return true; @@ -265,6 +259,7 @@ public final class ProfcollectForwardingService extends SystemService { BackgroundThread.get().getThreadHandler().post( () -> { registerAppLaunchObserver(); + registerCameraOpenObserver(); registerDex2oatObserver(); registerOTAObserver(); }); @@ -289,9 +284,6 @@ public final class ProfcollectForwardingService extends SystemService { "applaunch_trace_freq", 2); int randomNum = ThreadLocalRandom.current().nextInt(100); if (randomNum < traceFrequency) { - if (DEBUG) { - Log.d(LOG_TAG, "Tracing on app launch event: " + packageName); - } BackgroundThread.get().getThreadHandler().post(() -> { try { mIProfcollect.trace_once("applaunch"); @@ -331,19 +323,14 @@ public final class ProfcollectForwardingService extends SystemService { "dex2oat_trace_freq", 25); int randomNum = ThreadLocalRandom.current().nextInt(100); if (randomNum < traceFrequency) { - if (DEBUG) { - Log.d(LOG_TAG, "Tracing on dex2oat event"); - } - BackgroundThread.get().getThreadHandler().post(() -> { + // Dex2oat could take a while before it starts. Add a short delay before start tracing. + BackgroundThread.get().getThreadHandler().postDelayed(() -> { try { - // Dex2oat could take a while before it starts. Add a short delay before start - // tracing. - Thread.sleep(1000); mIProfcollect.trace_once("dex2oat"); - } catch (RemoteException | InterruptedException e) { + } catch (RemoteException e) { Log.e(LOG_TAG, "Failed to initiate trace: " + e.getMessage()); } - }); + }, 1000); } } @@ -352,11 +339,6 @@ public final class ProfcollectForwardingService extends SystemService { updateEngine.bind(new UpdateEngineCallback() { @Override public void onStatusUpdate(int status, float percent) { - if (DEBUG) { - Log.d(LOG_TAG, "Received OTA status update, status: " + status + ", percent: " - + percent); - } - if (status == UpdateEngine.UpdateStatusConstants.UPDATED_NEED_REBOOT) { createAndUploadReport(sSelfService); } @@ -388,8 +370,37 @@ public final class ProfcollectForwardingService extends SystemService { .putExtra("filename", reportName); pfs.getContext().sendBroadcast(intent); }); - if (DEBUG) { - Log.d(LOG_TAG, "Sent report for upload."); - } + } + + private void registerCameraOpenObserver() { + CameraManager cm = getContext().getSystemService(CameraManager.class); + cm.registerAvailabilityCallback(new CameraManager.AvailabilityCallback() { + @Override + public void onCameraOpened(String cameraId, String packageId) { + Log.d(LOG_TAG, "Received camera open event from: " + packageId); + // Skip face auth and Android System Intelligence, since they trigger way too + // often. + if (packageId.startsWith("client.pid") + || packageId.equals("com.google.android.as")) { + return; + } + // Sample for a fraction of camera events. + final int traceFrequency = + DeviceConfig.getInt(DeviceConfig.NAMESPACE_PROFCOLLECT_NATIVE_BOOT, + "camera_trace_freq", 10); + int randomNum = ThreadLocalRandom.current().nextInt(100); + if (randomNum >= traceFrequency) { + return; + } + // Wait for 1s before starting tracing. + BackgroundThread.get().getThreadHandler().postDelayed(() -> { + try { + mIProfcollect.trace_once("camera"); + } catch (RemoteException e) { + Log.e(LOG_TAG, "Failed to initiate trace: " + e.getMessage()); + } + }, 1000); + } + }, null); } } diff --git a/services/tests/InputMethodSystemServerTests/src/com/android/inputmethodservice/AndroidTest.xml b/services/tests/InputMethodSystemServerTests/src/com/android/inputmethodservice/AndroidTest.xml index 820628c98dee..8e6954b474cb 100644 --- a/services/tests/InputMethodSystemServerTests/src/com/android/inputmethodservice/AndroidTest.xml +++ b/services/tests/InputMethodSystemServerTests/src/com/android/inputmethodservice/AndroidTest.xml @@ -25,6 +25,12 @@ <option name="test-file-name" value="FrameworksImeTests.apk" /> </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" /> + <option name="run-command" value="settings put secure immersive_mode_confirmations confirmed" /> + </target_preparer> + <option name="test-tag" value="FrameworksImeTests" /> <test class="com.android.tradefed.testtype.AndroidJUnitTest" > diff --git a/services/tests/InputMethodSystemServerTests/src/com/android/inputmethodservice/InputMethodServiceTest.java b/services/tests/InputMethodSystemServerTests/src/com/android/inputmethodservice/InputMethodServiceTest.java index 1535298f74e3..2029b71034eb 100644 --- a/services/tests/InputMethodSystemServerTests/src/com/android/inputmethodservice/InputMethodServiceTest.java +++ b/services/tests/InputMethodSystemServerTests/src/com/android/inputmethodservice/InputMethodServiceTest.java @@ -48,6 +48,7 @@ import androidx.test.platform.app.InstrumentationRegistry; import com.android.apps.inputmethod.simpleime.ims.InputMethodServiceWrapper; import com.android.apps.inputmethod.simpleime.testing.TestActivity; +import com.android.compatibility.common.util.SystemUtil; import com.android.internal.inputmethod.InputMethodNavButtonFlags; import org.junit.After; @@ -834,8 +835,7 @@ public class InputMethodServiceTest { private String executeShellCommand(String cmd) throws IOException { Log.i(TAG, "Run command: " + cmd); - return UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()) - .executeShellCommand(cmd); + return SystemUtil.runShellCommandOrThrow(cmd); } private void clickOnEditorText() { diff --git a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/DefaultImeVisibilityApplierTest.java b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/DefaultImeVisibilityApplierTest.java index 221a99102daa..a4ca317ce914 100644 --- a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/DefaultImeVisibilityApplierTest.java +++ b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/DefaultImeVisibilityApplierTest.java @@ -68,12 +68,15 @@ import org.junit.runner.RunWith; public class DefaultImeVisibilityApplierTest extends InputMethodManagerServiceTestBase { private DefaultImeVisibilityApplier mVisibilityApplier; + private int mUserId = 0; + @Before public void setUp() throws RemoteException { super.setUp(); mVisibilityApplier = (DefaultImeVisibilityApplier) mInputMethodManagerService.getVisibilityApplier(); synchronized (ImfLock.class) { + mUserId = mInputMethodManagerService.getCurrentImeUserIdLocked(); mInputMethodManagerService.setAttachedClientForTesting(requireNonNull( mInputMethodManagerService.getClientStateLocked(mMockInputMethodClient))); } @@ -103,7 +106,7 @@ public class DefaultImeVisibilityApplierTest extends InputMethodManagerServiceTe assertThrows(IllegalArgumentException.class, () -> { synchronized (ImfLock.class) { mVisibilityApplier.applyImeVisibility(mWindowToken, ImeTracker.Token.empty(), - STATE_INVALID); + STATE_INVALID, mUserId); } }); } @@ -112,7 +115,8 @@ public class DefaultImeVisibilityApplierTest extends InputMethodManagerServiceTe public void testApplyImeVisibility_showIme() { final var statsToken = ImeTracker.Token.empty(); synchronized (ImfLock.class) { - mVisibilityApplier.applyImeVisibility(mWindowToken, statsToken, STATE_SHOW_IME); + mVisibilityApplier.applyImeVisibility(mWindowToken, statsToken, STATE_SHOW_IME, + mUserId); } verify(mMockWindowManagerInternal).showImePostLayout(eq(mWindowToken), eq(statsToken)); } @@ -121,7 +125,8 @@ public class DefaultImeVisibilityApplierTest extends InputMethodManagerServiceTe public void testApplyImeVisibility_hideIme() { final var statsToken = ImeTracker.Token.empty(); synchronized (ImfLock.class) { - mVisibilityApplier.applyImeVisibility(mWindowToken, statsToken, STATE_HIDE_IME); + mVisibilityApplier.applyImeVisibility(mWindowToken, statsToken, STATE_HIDE_IME, + mUserId); } verify(mMockWindowManagerInternal).hideIme(eq(mWindowToken), anyInt() /* displayId */, eq(statsToken)); @@ -132,7 +137,7 @@ public class DefaultImeVisibilityApplierTest extends InputMethodManagerServiceTe mInputMethodManagerService.mImeWindowVis = IME_ACTIVE; synchronized (ImfLock.class) { mVisibilityApplier.applyImeVisibility(mWindowToken, ImeTracker.Token.empty(), - STATE_HIDE_IME_EXPLICIT); + STATE_HIDE_IME_EXPLICIT, mUserId); } verifyHideSoftInput(true, true); } @@ -142,7 +147,7 @@ public class DefaultImeVisibilityApplierTest extends InputMethodManagerServiceTe mInputMethodManagerService.mImeWindowVis = IME_ACTIVE; synchronized (ImfLock.class) { mVisibilityApplier.applyImeVisibility(mWindowToken, ImeTracker.Token.empty(), - STATE_HIDE_IME_NOT_ALWAYS); + STATE_HIDE_IME_NOT_ALWAYS, mUserId); } verifyHideSoftInput(true, true); } @@ -151,7 +156,7 @@ public class DefaultImeVisibilityApplierTest extends InputMethodManagerServiceTe public void testApplyImeVisibility_showImeImplicit() throws Exception { synchronized (ImfLock.class) { mVisibilityApplier.applyImeVisibility(mWindowToken, ImeTracker.Token.empty(), - STATE_SHOW_IME_IMPLICIT); + STATE_SHOW_IME_IMPLICIT, mUserId); } verifyShowSoftInput(true, true, 0 /* showFlags */); } @@ -166,10 +171,13 @@ public class DefaultImeVisibilityApplierTest extends InputMethodManagerServiceTe final var statsToken = ImeTracker.Token.empty(); synchronized (ImfLock.class) { - final int displayIdToShowIme = mInputMethodManagerService.getDisplayIdToShowImeLocked(); + final var bindingController = + mInputMethodManagerService.getInputMethodBindingController(mUserId); + final int displayIdToShowIme = bindingController.getDisplayIdToShowIme(); // Verify hideIme will apply the expected displayId when the default IME // visibility applier app STATE_HIDE_IME. - mVisibilityApplier.applyImeVisibility(mWindowToken, statsToken, STATE_HIDE_IME); + mVisibilityApplier.applyImeVisibility(mWindowToken, statsToken, STATE_HIDE_IME, + mUserId); verify(mInputMethodManagerService.mWindowManagerInternal).hideIme( eq(mWindowToken), eq(displayIdToShowIme), eq(statsToken)); } @@ -204,7 +212,9 @@ public class DefaultImeVisibilityApplierTest extends InputMethodManagerServiceTe // Simulate the system hides the IME when switching IME services in different users. // (e.g. unbinding the IME from the current user to the profile user) final var statsToken = ImeTracker.Token.empty(); - final int displayIdToShowIme = mInputMethodManagerService.getDisplayIdToShowImeLocked(); + final var bindingController = + mInputMethodManagerService.getInputMethodBindingController(mUserId); + final int displayIdToShowIme = bindingController.getDisplayIdToShowIme(); mInputMethodManagerService.hideCurrentInputLocked(mWindowToken, statsToken, 0 /* flags */, null /* resultReceiver */, HIDE_SWITCH_USER); @@ -214,7 +224,7 @@ public class DefaultImeVisibilityApplierTest extends InputMethodManagerServiceTe // the IME hidden state. // The unbind will cancel the previous stats token, and create a new one internally. verify(mVisibilityApplier).applyImeVisibility( - eq(mWindowToken), any(), eq(STATE_HIDE_IME)); + eq(mWindowToken), any(), eq(STATE_HIDE_IME), eq(mUserId) /* userId */); verify(mInputMethodManagerService.mWindowManagerInternal).hideIme( eq(mWindowToken), eq(displayIdToShowIme), and(not(eq(statsToken)), notNull())); } diff --git a/services/tests/displayservicetests/src/com/android/server/display/AutomaticBrightnessControllerTest.java b/services/tests/displayservicetests/src/com/android/server/display/AutomaticBrightnessControllerTest.java index d3efcb6e50b3..2a458c42b841 100644 --- a/services/tests/displayservicetests/src/com/android/server/display/AutomaticBrightnessControllerTest.java +++ b/services/tests/displayservicetests/src/com/android/server/display/AutomaticBrightnessControllerTest.java @@ -52,7 +52,6 @@ import androidx.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; -import com.android.server.display.brightness.clamper.BrightnessClamperController; import com.android.server.display.config.HysteresisLevels; import com.android.server.display.feature.DisplayManagerFlags; import com.android.server.testutils.OffsettableClock; @@ -102,8 +101,6 @@ public class AutomaticBrightnessControllerTest { @Mock Handler mNoOpHandler; @Mock BrightnessRangeController mBrightnessRangeController; @Mock - BrightnessClamperController mBrightnessClamperController; - @Mock DisplayManagerFlags mDisplayManagerFlags; @Mock BrightnessThrottler mBrightnessThrottler; @@ -181,7 +178,7 @@ public class AutomaticBrightnessControllerTest { mContext, mBrightnessRangeController, mBrightnessThrottler, useHorizon ? AMBIENT_LIGHT_HORIZON_SHORT : 1, useHorizon ? AMBIENT_LIGHT_HORIZON_LONG : 10000, userLux, userNits, - mBrightnessClamperController, mDisplayManagerFlags + mDisplayManagerFlags ); when(mBrightnessRangeController.getCurrentBrightnessMax()).thenReturn( diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java index 46d08b0ce018..9a25b1acfaae 100644 --- a/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java +++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java @@ -948,6 +948,22 @@ public final class DisplayDeviceConfigTest { assertThat(supportedModeData.vsyncRate).isEqualTo(120); } + @Test + public void testLowLightBlockingZoneSupportedModesFromConfigFile() throws IOException { + setupDisplayDeviceConfigFromDisplayConfigFile(); + + RefreshRateData refreshRateData = mDisplayDeviceConfig.getRefreshRateData(); + assertNotNull(refreshRateData); + assertThat(refreshRateData.lowLightBlockingZoneSupportedModes).hasSize(2); + SupportedModeData supportedModeData = + refreshRateData.lowLightBlockingZoneSupportedModes.get(0); + assertThat(supportedModeData.refreshRate).isEqualTo(60); + assertThat(supportedModeData.vsyncRate).isEqualTo(60); + supportedModeData = refreshRateData.lowLightBlockingZoneSupportedModes.get(1); + assertThat(supportedModeData.refreshRate).isEqualTo(240); + assertThat(supportedModeData.vsyncRate).isEqualTo(240); + } + private String getValidLuxThrottling() { return "<luxThrottling>\n" + " <brightnessLimitMap>\n" @@ -1117,6 +1133,19 @@ public final class DisplayDeviceConfigTest { + "</lowPowerSupportedModes>\n"; } + private String getLowLightVrrSupportedModesConfig() { + return "<supportedModes>\n" + + " <point>\n" + + " <first>60</first>\n" + + " <second>60</second>\n" + + " </point>\n" + + " <point>\n" + + " <first>240</first>\n" + + " <second>240</second>\n" + + " </point>\n" + + "</supportedModes>\n"; + } + private String getHdrBrightnessConfig() { return "<hdrBrightnessConfig>\n" + " <brightnessMap>\n" @@ -1624,6 +1653,7 @@ public final class DisplayDeviceConfigTest { + "<nits>-1</nits>\n" + "</displayBrightnessPoint>\n" + "</blockingZoneThreshold>\n" + + getLowLightVrrSupportedModesConfig() + "</lowerBlockingZoneConfigs>\n" + "<higherBlockingZoneConfigs>\n" + "<defaultRefreshRate>90</defaultRefreshRate>\n" diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceInfoTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceInfoTest.java new file mode 100644 index 000000000000..bacbf890affb --- /dev/null +++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceInfoTest.java @@ -0,0 +1,148 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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 static com.android.server.display.DisplayDeviceInfo.DIFF_COLOR_MODE; +import static com.android.server.display.DisplayDeviceInfo.DIFF_COMMITTED_STATE; +import static com.android.server.display.DisplayDeviceInfo.DIFF_HDR_SDR_RATIO; +import static com.android.server.display.DisplayDeviceInfo.DIFF_MODE_ID; +import static com.android.server.display.DisplayDeviceInfo.DIFF_RENDER_TIMINGS; +import static com.android.server.display.DisplayDeviceInfo.DIFF_ROTATION; +import static com.android.server.display.DisplayDeviceInfo.DIFF_STATE; + +import static com.google.common.truth.Truth.assertThat; + +import android.view.Display; +import android.view.Surface; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +@SmallTest +@RunWith(AndroidJUnit4.class) +public class DisplayDeviceInfoTest { + + @Test + public void testDiff_noChange() { + var oldDdi = createInfo(); + var newDdi = createInfo(); + + assertThat(oldDdi.diff(newDdi)).isEqualTo(0); + } + + @Test + public void testDiff_state() { + var oldDdi = createInfo(); + var newDdi = createInfo(); + + newDdi.state = Display.STATE_VR; + assertThat(oldDdi.diff(newDdi)).isEqualTo(DIFF_STATE); + } + + @Test + public void testDiff_committedState() { + var oldDdi = createInfo(); + var newDdi = createInfo(); + + newDdi.committedState = Display.STATE_UNKNOWN; + assertThat(oldDdi.diff(newDdi)).isEqualTo(DIFF_COMMITTED_STATE); + } + + @Test + public void testDiff_colorMode() { + var oldDdi = createInfo(); + var newDdi = createInfo(); + + newDdi.colorMode = Display.COLOR_MODE_DISPLAY_P3; + assertThat(oldDdi.diff(newDdi)).isEqualTo(DIFF_COLOR_MODE); + } + + @Test + public void testDiff_hdrSdrRatio() { + var oldDdi = createInfo(); + var newDdi = createInfo(); + + /* First change new ratio to non-NaN */ + newDdi.hdrSdrRatio = 2.3f; + assertThat(oldDdi.diff(newDdi)).isEqualTo(DIFF_HDR_SDR_RATIO); + + /* Then change old to be non-NaN and also distinct */ + oldDdi.hdrSdrRatio = 1.1f; + assertThat(oldDdi.diff(newDdi)).isEqualTo(DIFF_HDR_SDR_RATIO); + + /* Now make the new one NaN and the old one non-NaN */ + newDdi.hdrSdrRatio = Float.NaN; + assertThat(oldDdi.diff(newDdi)).isEqualTo(DIFF_HDR_SDR_RATIO); + } + + @Test + public void testDiff_rotation() { + var oldDdi = createInfo(); + var newDdi = createInfo(); + + newDdi.rotation = Surface.ROTATION_270; + assertThat(oldDdi.diff(newDdi)).isEqualTo(DIFF_ROTATION); + } + + @Test + public void testDiff_frameRate() { + var oldDdi = createInfo(); + var newDdi = createInfo(); + + newDdi.renderFrameRate = 123.4f; + assertThat(oldDdi.diff(newDdi)).isEqualTo(DIFF_RENDER_TIMINGS); + newDdi.renderFrameRate = oldDdi.renderFrameRate; + + newDdi.appVsyncOffsetNanos = 31222221; + assertThat(oldDdi.diff(newDdi)).isEqualTo(DIFF_RENDER_TIMINGS); + newDdi.appVsyncOffsetNanos = oldDdi.appVsyncOffsetNanos; + + newDdi.presentationDeadlineNanos = 23000000; + assertThat(oldDdi.diff(newDdi)).isEqualTo(DIFF_RENDER_TIMINGS); + } + + @Test + public void testDiff_modeId() { + var oldDdi = createInfo(); + var newDdi = createInfo(); + + newDdi.modeId = 9; + assertThat(oldDdi.diff(newDdi)).isEqualTo(DIFF_MODE_ID); + } + + private static DisplayDeviceInfo createInfo() { + var ddi = new DisplayDeviceInfo(); + ddi.name = "TestDisplayDeviceInfo"; + ddi.uniqueId = "test:51651561321"; + ddi.width = 671; + ddi.height = 483; + ddi.modeId = 2; + ddi.renderFrameRate = 68.9f; + ddi.supportedModes = new Display.Mode[] { + new Display.Mode.Builder().setRefreshRate(68.9f).setResolution(671, 483).build(), + }; + ddi.appVsyncOffsetNanos = 6233332; + ddi.presentationDeadlineNanos = 11500000; + ddi.rotation = Surface.ROTATION_90; + ddi.state = Display.STATE_ON; + ddi.committedState = Display.STATE_DOZE_SUSPEND; + return ddi; + } +} 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 d0eb83aa986b..211ab03813b3 100644 --- a/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java +++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java @@ -2569,6 +2569,63 @@ public class DisplayManagerServiceTest { } @Test + public void testPowerOnAndOffInternalDisplay() { + manageDisplaysPermission(/* granted= */ true); + DisplayManagerService displayManager = new DisplayManagerService(mContext, mBasicInjector); + DisplayManagerService.BinderService bs = displayManager.new BinderService(); + LogicalDisplayMapper logicalDisplayMapper = displayManager.getLogicalDisplayMapper(); + FakeDisplayManagerCallback callback = new FakeDisplayManagerCallback(); + bs.registerCallbackWithEventMask(callback, STANDARD_AND_CONNECTION_DISPLAY_EVENTS); + + callback.expectsEvent(EVENT_DISPLAY_ADDED); + FakeDisplayDevice displayDevice = + createFakeDisplayDevice(displayManager, new float[]{60f}, Display.TYPE_INTERNAL); + callback.waitForExpectedEvent(); + + LogicalDisplay display = + logicalDisplayMapper.getDisplayLocked(displayDevice, /* includeDisabled= */ true); + + assertThat(displayDevice.getDisplayDeviceInfoLocked().committedState) + .isEqualTo(Display.STATE_ON); + + assertThat(displayManager.requestDisplayPower(display.getDisplayIdLocked(), false)) + .isTrue(); + + assertThat(displayDevice.getDisplayDeviceInfoLocked().committedState) + .isEqualTo(Display.STATE_OFF); + + assertThat(displayManager.requestDisplayPower(display.getDisplayIdLocked(), true)) + .isTrue(); + + assertThat(displayDevice.getDisplayDeviceInfoLocked().committedState) + .isEqualTo(Display.STATE_ON); + } + + @Test + public void testPowerOnAndOffInternalDisplay_withoutPermission_shouldThrowException() { + DisplayManagerService displayManager = new DisplayManagerService(mContext, mBasicInjector); + DisplayManagerService.BinderService bs = displayManager.new BinderService(); + LogicalDisplayMapper logicalDisplayMapper = displayManager.getLogicalDisplayMapper(); + FakeDisplayManagerCallback callback = new FakeDisplayManagerCallback(); + bs.registerCallbackWithEventMask(callback, STANDARD_AND_CONNECTION_DISPLAY_EVENTS); + + callback.expectsEvent(EVENT_DISPLAY_ADDED); + FakeDisplayDevice displayDevice = + createFakeDisplayDevice(displayManager, new float[]{60f}, Display.TYPE_INTERNAL); + callback.waitForExpectedEvent(); + + LogicalDisplay display = + logicalDisplayMapper.getDisplayLocked(displayDevice, /* includeDisabled= */ true); + var displayId = display.getDisplayIdLocked(); + + assertThat(displayDevice.getDisplayDeviceInfoLocked().committedState) + .isEqualTo(Display.STATE_ON); + + assertThrows(SecurityException.class, () -> bs.requestDisplayPower(displayId, true)); + assertThrows(SecurityException.class, () -> bs.requestDisplayPower(displayId, false)); + } + + @Test public void testEnableExternalDisplay_withDisplayManagement_shouldSignalDisplayAdded() { when(mMockFlags.isConnectedDisplayManagementEnabled()).thenReturn(true); manageDisplaysPermission(/* granted= */ true); @@ -3529,6 +3586,7 @@ public class DisplayManagerServiceTest { public void setDisplayDeviceInfo(DisplayDeviceInfo displayDeviceInfo) { mDisplayDeviceInfo = displayDeviceInfo; + mDisplayDeviceInfo.committedState = Display.STATE_ON; } @Override @@ -3558,5 +3616,14 @@ public class DisplayManagerServiceTest { public Display.Mode getUserPreferredDisplayModeLocked() { return mPreferredMode; } + + @Override + public Runnable requestDisplayStateLocked( + final int state, + final float brightnessState, + final float sdrBrightnessState, + @Nullable DisplayOffloadSessionImpl displayOffloadSession) { + return () -> mDisplayDeviceInfo.committedState = state; + } } } 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 8fd1e6baf522..95f0b65ec35c 100644 --- a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java +++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java @@ -39,6 +39,7 @@ import static org.mockito.Mockito.never; import static org.mockito.Mockito.reset; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; import android.app.ActivityManager; @@ -1284,14 +1285,37 @@ public final class DisplayPowerControllerTest { @Test public void testDwbcCallsHappenOnHandler() { + when(mDisplayManagerFlagsMock.isAdaptiveTone1Enabled()).thenReturn(true); mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID); + // Send a display power request + DisplayPowerRequest dpr = new DisplayPowerRequest(); + dpr.policy = DisplayPowerRequest.POLICY_BRIGHT; + dpr.useProximitySensor = true; + mHolder.dpc.requestPowerState(dpr, false /* waitForNegativeProximity */); + + // Run updatePowerState + advanceTime(1); + + setUpDisplay(DISPLAY_ID, "new_unique_id", mHolder.display, mock(DisplayDevice.class), + mHolder.config, /* isEnabled= */ true); + + // dispatch handler looper + advanceTime(1); + clearInvocations(mDisplayWhiteBalanceControllerMock, mHolder.automaticBrightnessController, + mHolder.animator); mHolder.dpc.setAutomaticScreenBrightnessMode(AUTO_BRIGHTNESS_MODE_IDLE); - verify(mDisplayWhiteBalanceControllerMock, never()).setStrongModeEnabled(true); + verifyNoMoreInteractions(mDisplayWhiteBalanceControllerMock, + mHolder.automaticBrightnessController, + mHolder.animator); // dispatch handler looper advanceTime(1); - verify(mDisplayWhiteBalanceControllerMock, times(1)).setStrongModeEnabled(true); + verify(mHolder.automaticBrightnessController).switchMode(AUTO_BRIGHTNESS_MODE_IDLE, + /* sendUpdate= */ true); + verify(mHolder.animator).setAnimationTimeLimits(BRIGHTNESS_RAMP_INCREASE_MAX_IDLE, + BRIGHTNESS_RAMP_DECREASE_MAX_IDLE); + verify(mDisplayWhiteBalanceControllerMock).setStrongModeEnabled(true); } @Test @@ -1391,7 +1415,7 @@ 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())).thenAnswer( + when(mHolder.clamperController.clamp(any(), anyFloat(), anyBoolean(), anyInt())).thenAnswer( invocation -> DisplayBrightnessState.builder() .setIsSlowChange(invocation.getArgument(2)) .setBrightness(invocation.getArgument(1)) @@ -1415,7 +1439,7 @@ 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())).thenAnswer( + when(mHolder.clamperController.clamp(any(), anyFloat(), anyBoolean(), anyInt())).thenAnswer( invocation -> DisplayBrightnessState.builder() .setIsSlowChange(invocation.getArgument(2)) .setBrightness(invocation.getArgument(1)) @@ -1506,6 +1530,9 @@ public final class DisplayPowerControllerTest { // switch to idle mode mHolder.dpc.setAutomaticScreenBrightnessMode(AUTO_BRIGHTNESS_MODE_IDLE); + // Process the MSG_SWITCH_AUTOBRIGHTNESS_MODE event + advanceTime(1); + // A second time when switching to idle mode. verify(mHolder.animator, times(2)).setAnimationTimeLimits(BRIGHTNESS_RAMP_INCREASE_MAX, BRIGHTNESS_RAMP_DECREASE_MAX); @@ -1532,6 +1559,8 @@ public final class DisplayPowerControllerTest { // switch to idle mode mHolder.dpc.setAutomaticScreenBrightnessMode(AUTO_BRIGHTNESS_MODE_IDLE); + // Process the MSG_SWITCH_AUTOBRIGHTNESS_MODE event + advanceTime(1); verify(mHolder.animator).setAnimationTimeLimits(BRIGHTNESS_RAMP_INCREASE_MAX_IDLE, BRIGHTNESS_RAMP_DECREASE_MAX_IDLE); } @@ -2065,7 +2094,7 @@ public final class DisplayPowerControllerTest { BrightnessClamperController clamperController = mock(BrightnessClamperController.class); when(hbmController.getCurrentBrightnessMax()).thenReturn(PowerManager.BRIGHTNESS_MAX); - when(clamperController.clamp(any(), anyFloat(), anyBoolean())).thenAnswer( + when(clamperController.clamp(any(), anyFloat(), anyBoolean(), anyInt())).thenAnswer( invocation -> DisplayBrightnessState.builder() .setIsSlowChange(invocation.getArgument(2)) .setBrightness(invocation.getArgument(1)) @@ -2299,7 +2328,7 @@ public final class DisplayPowerControllerTest { BrightnessClamperController getBrightnessClamperController(Handler handler, BrightnessClamperController.ClamperChangeListener clamperChangeListener, BrightnessClamperController.DisplayDeviceData data, Context context, - DisplayManagerFlags flags) { + DisplayManagerFlags flags, SensorManager sensorManager) { return mClamperController; } diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessControllerTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessControllerTest.java index 323ef6a28e14..88d3238861f5 100644 --- a/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessControllerTest.java +++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessControllerTest.java @@ -33,6 +33,7 @@ import static org.mockito.Mockito.when; import android.content.Context; import android.content.res.Resources; import android.hardware.SensorManager; +import android.hardware.display.DisplayManagerInternal; import android.hardware.display.DisplayManagerInternal.DisplayPowerRequest; import android.os.Handler; import android.os.HandlerExecutor; @@ -119,7 +120,8 @@ public final class DisplayBrightnessControllerTest { int targetDisplayState = Display.STATE_DOZE; when(mDisplayBrightnessStrategySelector.selectStrategy( any(StrategySelectionRequest.class))).thenReturn(displayBrightnessStrategy); - mDisplayBrightnessController.updateBrightness(displayPowerRequest, targetDisplayState); + mDisplayBrightnessController.updateBrightness(displayPowerRequest, targetDisplayState, mock( + DisplayManagerInternal.DisplayOffloadSession.class)); verify(displayBrightnessStrategy).updateBrightness( eq(new StrategyExecutionRequest(displayPowerRequest, DEFAULT_BRIGHTNESS, /* userSetBrightnessChanged= */ false))); @@ -128,6 +130,12 @@ public final class DisplayBrightnessControllerTest { } @Test + public void isAllowAutoBrightnessWhileDozingDelegatesToDozeBrightnessStrategy() { + mDisplayBrightnessController.isAllowAutoBrightnessWhileDozing(); + verify(mDisplayBrightnessStrategySelector).isAllowAutoBrightnessWhileDozing(); + } + + @Test public void isAllowAutoBrightnessWhileDozingConfigDelegatesToDozeBrightnessStrategy() { mDisplayBrightnessController.isAllowAutoBrightnessWhileDozingConfig(); verify(mDisplayBrightnessStrategySelector).isAllowAutoBrightnessWhileDozingConfig(); diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessStrategySelectorTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessStrategySelectorTest.java index df9671235071..639d06d21510 100644 --- a/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessStrategySelectorTest.java +++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessStrategySelectorTest.java @@ -16,13 +16,15 @@ package com.android.server.display.brightness; +import static junit.framework.Assert.assertFalse; + import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyZeroInteractions; import static org.mockito.Mockito.when; import android.content.ContentResolver; @@ -97,6 +99,9 @@ public final class DisplayBrightnessStrategySelectorTest { @Mock private DisplayManagerFlags mDisplayManagerFlags; + @Mock + private DisplayManagerInternal.DisplayOffloadSession mDisplayOffloadSession; + private DisplayBrightnessStrategySelector mDisplayBrightnessStrategySelector; private Context mContext; private DisplayBrightnessStrategySelector.Injector mInjector = @@ -191,7 +196,7 @@ public final class DisplayBrightnessStrategySelectorTest { DISALLOW_AUTO_BRIGHTNESS_WHILE_DOZING); assertEquals(mDisplayBrightnessStrategySelector.selectStrategy( new StrategySelectionRequest(displayPowerRequest, Display.STATE_DOZE, - 0.1f, false)), + 0.1f, false, mDisplayOffloadSession)), mDozeBrightnessModeStrategy); } @@ -205,7 +210,30 @@ public final class DisplayBrightnessStrategySelectorTest { DISALLOW_AUTO_BRIGHTNESS_WHILE_DOZING); assertNotEquals(mDisplayBrightnessStrategySelector.selectStrategy( new StrategySelectionRequest(displayPowerRequest, Display.STATE_DOZE, - 0.1f, false)), + 0.1f, false, mDisplayOffloadSession)), + mDozeBrightnessModeStrategy); + } + + @Test + public void selectStrategyDoesNotSelectDozeStrategyWhenOffloadSessionAutoBrightnessIsEnabled() { + when(mDisplayManagerFlags.offloadControlsDozeAutoBrightness()).thenReturn(true); + when(mDisplayManagerFlags.isDisplayOffloadEnabled()).thenReturn(true); + when(mDisplayOffloadSession.allowAutoBrightnessInDoze()).thenReturn(true); + when(mResources.getBoolean(R.bool.config_allowAutoBrightnessWhileDozing)).thenReturn( + true); + when(mResources.getBoolean(R.bool.config_allowAutoBrightnessWhileDozing)).thenReturn( + true); + + mDisplayBrightnessStrategySelector = new DisplayBrightnessStrategySelector(mContext, + mInjector, DISPLAY_ID, mDisplayManagerFlags); + DisplayManagerInternal.DisplayPowerRequest displayPowerRequest = mock( + DisplayManagerInternal.DisplayPowerRequest.class); + displayPowerRequest.policy = DisplayManagerInternal.DisplayPowerRequest.POLICY_DOZE; + displayPowerRequest.dozeScreenBrightness = 0.2f; + + assertNotEquals(mDisplayBrightnessStrategySelector.selectStrategy( + new StrategySelectionRequest(displayPowerRequest, Display.STATE_DOZE, + 0.1f, false, mDisplayOffloadSession)), mDozeBrightnessModeStrategy); } @@ -215,7 +243,7 @@ public final class DisplayBrightnessStrategySelectorTest { DisplayManagerInternal.DisplayPowerRequest.class); assertEquals(mDisplayBrightnessStrategySelector.selectStrategy( new StrategySelectionRequest(displayPowerRequest, Display.STATE_OFF, - 0.1f, false)), + 0.1f, false, mDisplayOffloadSession)), mScreenOffBrightnessModeStrategy); } @@ -227,7 +255,7 @@ public final class DisplayBrightnessStrategySelectorTest { when(mFollowerBrightnessStrategy.getBrightnessToFollow()).thenReturn(Float.NaN); assertEquals(mDisplayBrightnessStrategySelector.selectStrategy( new StrategySelectionRequest(displayPowerRequest, Display.STATE_ON, - 0.1f, false)), + 0.1f, false, mDisplayOffloadSession)), mOverrideBrightnessStrategy); } @@ -240,7 +268,7 @@ public final class DisplayBrightnessStrategySelectorTest { when(mTemporaryBrightnessStrategy.getTemporaryScreenBrightness()).thenReturn(0.3f); assertEquals(mDisplayBrightnessStrategySelector.selectStrategy( new StrategySelectionRequest(displayPowerRequest, Display.STATE_ON, - 0.1f, false)), + 0.1f, false, mDisplayOffloadSession)), mTemporaryBrightnessStrategy); } @@ -254,7 +282,7 @@ public final class DisplayBrightnessStrategySelectorTest { when(mTemporaryBrightnessStrategy.getTemporaryScreenBrightness()).thenReturn(Float.NaN); assertEquals(mDisplayBrightnessStrategySelector.selectStrategy( new StrategySelectionRequest(displayPowerRequest, Display.STATE_ON, - 0.1f, false)), + 0.1f, false, mDisplayOffloadSession)), mBoostBrightnessStrategy); } @@ -268,7 +296,7 @@ public final class DisplayBrightnessStrategySelectorTest { when(mOffloadBrightnessStrategy.getOffloadScreenBrightness()).thenReturn(Float.NaN); assertEquals(mDisplayBrightnessStrategySelector.selectStrategy( new StrategySelectionRequest(displayPowerRequest, Display.STATE_ON, - 0.1f, false)), + 0.1f, false, mDisplayOffloadSession)), mInvalidBrightnessStrategy); } @@ -279,7 +307,7 @@ public final class DisplayBrightnessStrategySelectorTest { when(mFollowerBrightnessStrategy.getBrightnessToFollow()).thenReturn(0.3f); assertEquals(mDisplayBrightnessStrategySelector.selectStrategy( new StrategySelectionRequest(displayPowerRequest, Display.STATE_ON, - 0.1f, false)), + 0.1f, false, mDisplayOffloadSession)), mFollowerBrightnessStrategy); } @@ -297,13 +325,18 @@ public final class DisplayBrightnessStrategySelectorTest { when(mOffloadBrightnessStrategy.getOffloadScreenBrightness()).thenReturn(0.3f); assertEquals(mDisplayBrightnessStrategySelector.selectStrategy( new StrategySelectionRequest(displayPowerRequest, Display.STATE_ON, - 0.1f, false)), + 0.1f, false, mDisplayOffloadSession)), mOffloadBrightnessStrategy); } @Test public void selectStrategy_selectsAutomaticStrategyWhenValid() { when(mDisplayManagerFlags.isRefactorDisplayPowerControllerEnabled()).thenReturn(true); + when(mDisplayManagerFlags.offloadControlsDozeAutoBrightness()).thenReturn(true); + when(mDisplayManagerFlags.isDisplayOffloadEnabled()).thenReturn(true); + when(mDisplayOffloadSession.allowAutoBrightnessInDoze()).thenReturn(true); + when(mResources.getBoolean(R.bool.config_allowAutoBrightnessWhileDozing)).thenReturn( + true); mDisplayBrightnessStrategySelector = new DisplayBrightnessStrategySelector(mContext, mInjector, DISPLAY_ID, mDisplayManagerFlags); DisplayManagerInternal.DisplayPowerRequest displayPowerRequest = mock( @@ -316,13 +349,11 @@ public final class DisplayBrightnessStrategySelectorTest { when(mAutomaticBrightnessStrategy.isAutoBrightnessValid()).thenReturn(true); assertEquals(mDisplayBrightnessStrategySelector.selectStrategy( new StrategySelectionRequest(displayPowerRequest, Display.STATE_ON, - 0.1f, false)), + 0.1f, false, mDisplayOffloadSession)), mAutomaticBrightnessStrategy); - verifyZeroInteractions(mOffloadBrightnessStrategy); verify(mAutomaticBrightnessStrategy).setAutoBrightnessState(Display.STATE_ON, - false, BrightnessReason.REASON_UNKNOWN, + true, BrightnessReason.REASON_UNKNOWN, DisplayManagerInternal.DisplayPowerRequest.POLICY_BRIGHT, 0.1f, false); - } @Test @@ -341,7 +372,7 @@ public final class DisplayBrightnessStrategySelectorTest { when(mAutoBrightnessFallbackStrategy.isValid()).thenReturn(true); assertEquals(mDisplayBrightnessStrategySelector.selectStrategy( new StrategySelectionRequest(displayPowerRequest, Display.STATE_ON, - 0.1f, false)), + 0.1f, false, mDisplayOffloadSession)), mAutoBrightnessFallbackStrategy); } @@ -359,7 +390,7 @@ public final class DisplayBrightnessStrategySelectorTest { assertNotEquals(mOffloadBrightnessStrategy, mDisplayBrightnessStrategySelector.selectStrategy( new StrategySelectionRequest(displayPowerRequest, Display.STATE_ON, - 0.1f, false))); + 0.1f, false, mDisplayOffloadSession))); } @Test @@ -377,7 +408,7 @@ public final class DisplayBrightnessStrategySelectorTest { when(mAutomaticBrightnessStrategy.isAutoBrightnessValid()).thenReturn(false); assertEquals(mDisplayBrightnessStrategySelector.selectStrategy( new StrategySelectionRequest(displayPowerRequest, Display.STATE_ON, - 0.1f, false)), + 0.1f, false, mDisplayOffloadSession)), mFallbackBrightnessStrategy); } @@ -392,7 +423,7 @@ public final class DisplayBrightnessStrategySelectorTest { mDisplayBrightnessStrategySelector.selectStrategy( new StrategySelectionRequest(displayPowerRequest, Display.STATE_ON, - 0.1f, false)); + 0.1f, false, mDisplayOffloadSession)); StrategySelectionNotifyRequest strategySelectionNotifyRequest = new StrategySelectionNotifyRequest(displayPowerRequest, Display.STATE_ON, @@ -422,4 +453,78 @@ public final class DisplayBrightnessStrategySelectorTest { assertEquals(mAutomaticBrightnessStrategy, mDisplayBrightnessStrategySelector.getAutomaticBrightnessStrategy()); } + + @Test + public void setAllowAutoBrightnessWhileDozing_enabledWhenConfigAndOffloadSessionAreEnabled() { + when(mDisplayManagerFlags.offloadControlsDozeAutoBrightness()).thenReturn(true); + when(mDisplayManagerFlags.isDisplayOffloadEnabled()).thenReturn(true); + when(mDisplayOffloadSession.allowAutoBrightnessInDoze()).thenReturn(true); + when(mResources.getBoolean(R.bool.config_allowAutoBrightnessWhileDozing)).thenReturn( + true); + mDisplayBrightnessStrategySelector = new DisplayBrightnessStrategySelector(mContext, + mInjector, DISPLAY_ID, mDisplayManagerFlags); + mDisplayBrightnessStrategySelector + .setAllowAutoBrightnessWhileDozing(mDisplayOffloadSession); + assertTrue(mDisplayBrightnessStrategySelector.isAllowAutoBrightnessWhileDozing()); + } + + @Test + public void setAllowAutoBrightnessWhileDozing_disabledWhenOffloadSessionFlagIsDisabled() { + when(mDisplayManagerFlags.offloadControlsDozeAutoBrightness()).thenReturn(true); + when(mDisplayManagerFlags.isDisplayOffloadEnabled()).thenReturn(true); + when(mDisplayOffloadSession.allowAutoBrightnessInDoze()).thenReturn(false); + when(mResources.getBoolean(R.bool.config_allowAutoBrightnessWhileDozing)).thenReturn( + true); + mDisplayBrightnessStrategySelector = new DisplayBrightnessStrategySelector(mContext, + mInjector, DISPLAY_ID, mDisplayManagerFlags); + mDisplayBrightnessStrategySelector + .setAllowAutoBrightnessWhileDozing(mDisplayOffloadSession); + assertFalse(mDisplayBrightnessStrategySelector.isAllowAutoBrightnessWhileDozing()); + } + + @Test + public void setAllowAutoBrightnessWhileDozing_disabledWhenABWhileDozingConfigIsDisabled() { + when(mDisplayManagerFlags.offloadControlsDozeAutoBrightness()).thenReturn(true); + when(mDisplayManagerFlags.isDisplayOffloadEnabled()).thenReturn(true); + when(mDisplayOffloadSession.allowAutoBrightnessInDoze()).thenReturn(true); + when(mResources.getBoolean(R.bool.config_allowAutoBrightnessWhileDozing)).thenReturn( + false); + mDisplayBrightnessStrategySelector = new DisplayBrightnessStrategySelector(mContext, + mInjector, DISPLAY_ID, mDisplayManagerFlags); + mDisplayBrightnessStrategySelector + .setAllowAutoBrightnessWhileDozing(mDisplayOffloadSession); + assertFalse(mDisplayBrightnessStrategySelector.isAllowAutoBrightnessWhileDozing()); + } + + @Test + public void setAllowAutoBrightnessWhileDozing_EnabledWhenOffloadSessionIsNotSet() { + when(mResources.getBoolean(R.bool.config_allowAutoBrightnessWhileDozing)).thenReturn( + true); + mDisplayBrightnessStrategySelector = new DisplayBrightnessStrategySelector(mContext, + mInjector, DISPLAY_ID, mDisplayManagerFlags); + mDisplayBrightnessStrategySelector.setAllowAutoBrightnessWhileDozing(null); + assertTrue(mDisplayBrightnessStrategySelector.isAllowAutoBrightnessWhileDozing()); + } + + @Test + public void setAllowAutoBrightnessWhileDozing_EnabledWhenFlagsAreDisabled() { + when(mDisplayManagerFlags.offloadControlsDozeAutoBrightness()).thenReturn(true); + when(mResources.getBoolean(R.bool.config_allowAutoBrightnessWhileDozing)).thenReturn( + true); + mDisplayBrightnessStrategySelector = new DisplayBrightnessStrategySelector(mContext, + mInjector, DISPLAY_ID, mDisplayManagerFlags); + + // Same as the config_allowAutoBrightnessWhileDozing when either of the concerned flags + // are disabled + when(mDisplayManagerFlags.isDisplayOffloadEnabled()).thenReturn(false); + mDisplayBrightnessStrategySelector + .setAllowAutoBrightnessWhileDozing(mDisplayOffloadSession); + assertTrue(mDisplayBrightnessStrategySelector.isAllowAutoBrightnessWhileDozing()); + + when(mDisplayManagerFlags.isDisplayOffloadEnabled()).thenReturn(true); + when(mDisplayManagerFlags.offloadControlsDozeAutoBrightness()).thenReturn(false); + mDisplayBrightnessStrategySelector + .setAllowAutoBrightnessWhileDozing(mDisplayOffloadSession); + assertTrue(mDisplayBrightnessStrategySelector.isAllowAutoBrightnessWhileDozing()); + } } 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 5487bc53ffce..69043f5704de 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 @@ -16,14 +16,20 @@ package com.android.server.display.brightness.clamper; +import static android.view.Display.STATE_ON; + import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; 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.Sensor; +import android.hardware.SensorEventListener; +import android.hardware.SensorManager; import android.hardware.display.BrightnessInfo; import android.hardware.display.DisplayManagerInternal; import android.os.Handler; @@ -34,11 +40,15 @@ import android.testing.TestableContext; import androidx.test.filters.SmallTest; import androidx.test.platform.app.InstrumentationRegistry; +import com.android.internal.util.test.FakeSettingsProvider; +import com.android.internal.util.test.FakeSettingsProviderRule; import com.android.server.display.DisplayBrightnessState; import com.android.server.display.DisplayDeviceConfig; +import com.android.server.display.TestUtils; import com.android.server.display.brightness.BrightnessReason; import com.android.server.display.feature.DeviceConfigParameterProvider; import com.android.server.display.feature.DisplayManagerFlags; +import com.android.server.testutils.OffsettableClock; import com.android.server.testutils.TestHandler; import org.junit.Before; @@ -54,13 +64,16 @@ import java.util.List; public class BrightnessClamperControllerTest { private static final float FLOAT_TOLERANCE = 0.001f; - private final TestHandler mTestHandler = new TestHandler(null); + private final OffsettableClock mClock = new OffsettableClock(); + private final TestHandler mTestHandler = new TestHandler(null, mClock); @Rule public final TestableContext mMockContext = new TestableContext( InstrumentationRegistry.getInstrumentation().getContext()); @Mock private BrightnessClamperController.ClamperChangeListener mMockExternalListener; + @Mock + private SensorManager mSensorManager; @Mock private BrightnessClamperController.DisplayDeviceData mMockDisplayDeviceData; @@ -74,15 +87,25 @@ public class BrightnessClamperControllerTest { private BrightnessModifier mMockModifier; @Mock private DisplayManagerInternal.DisplayPowerRequest mMockRequest; + + Sensor mLightSensor; + @Mock private DeviceConfig.Properties mMockProperties; private BrightnessClamperController mClamperController; private TestInjector mTestInjector; + @Rule + public FakeSettingsProviderRule mSettingsProviderRule = FakeSettingsProvider.rule(); + @Before - public void setUp() { + public void setUp() throws Exception { MockitoAnnotations.initMocks(this); + mLightSensor = TestUtils.createSensor(Sensor.TYPE_LIGHT, "Light Sensor"); mTestInjector = new TestInjector(List.of(mMockClamper), List.of(mMockModifier)); + when(mSensorManager.getDefaultSensor(anyInt())).thenReturn(mLightSensor); + when(mMockModifier.shouldListenToLightSensor()).thenReturn(true); + mClamperController = createBrightnessClamperController(); } @@ -132,7 +155,7 @@ public class BrightnessClamperControllerTest { public void testClamp_AppliesModifier() { float initialBrightness = 0.2f; boolean initialSlowChange = true; - mClamperController.clamp(mMockRequest, initialBrightness, initialSlowChange); + mClamperController.clamp(mMockRequest, initialBrightness, initialSlowChange, STATE_ON); verify(mMockModifier).apply(eq(mMockRequest), any()); } @@ -151,7 +174,7 @@ public class BrightnessClamperControllerTest { mTestHandler.flush(); DisplayBrightnessState state = mClamperController.clamp(mMockRequest, initialBrightness, - initialSlowChange); + initialSlowChange, STATE_ON); assertEquals(initialBrightness, state.getBrightness(), FLOAT_TOLERANCE); assertEquals(PowerManager.BRIGHTNESS_MAX, state.getMaxBrightness(), FLOAT_TOLERANCE); @@ -175,7 +198,7 @@ public class BrightnessClamperControllerTest { mTestHandler.flush(); DisplayBrightnessState state = mClamperController.clamp(mMockRequest, initialBrightness, - initialSlowChange); + initialSlowChange, STATE_ON); assertEquals(clampedBrightness, state.getBrightness(), FLOAT_TOLERANCE); assertEquals(clampedBrightness, state.getMaxBrightness(), FLOAT_TOLERANCE); @@ -199,7 +222,7 @@ public class BrightnessClamperControllerTest { mTestHandler.flush(); DisplayBrightnessState state = mClamperController.clamp(mMockRequest, initialBrightness, - initialSlowChange); + initialSlowChange, STATE_ON); assertEquals(initialBrightness, state.getBrightness(), FLOAT_TOLERANCE); assertEquals(clampedBrightness, state.getMaxBrightness(), FLOAT_TOLERANCE); @@ -223,10 +246,10 @@ public class BrightnessClamperControllerTest { mTestHandler.flush(); // first call of clamp method mClamperController.clamp(mMockRequest, initialBrightness, - initialSlowChange); + initialSlowChange, STATE_ON); // immediately second call of clamp method DisplayBrightnessState state = mClamperController.clamp(mMockRequest, initialBrightness, - initialSlowChange); + initialSlowChange, STATE_ON); assertEquals(clampedBrightness, state.getBrightness(), FLOAT_TOLERANCE); assertEquals(clampedBrightness, state.getMaxBrightness(), FLOAT_TOLERANCE); @@ -237,6 +260,31 @@ public class BrightnessClamperControllerTest { } @Test + public void testAmbientLuxChanges() throws Exception { + ArgumentCaptor<SensorEventListener> listenerCaptor = ArgumentCaptor.forClass( + SensorEventListener.class); + + verify(mSensorManager).registerListener(listenerCaptor.capture(), eq(mLightSensor), + anyInt(), any(Handler.class)); + SensorEventListener listener = listenerCaptor.getValue(); + + when(mSensorManager.getSensorList(eq(Sensor.TYPE_ALL))).thenReturn(List.of(mLightSensor)); + + float initialBrightness = 0.8f; + boolean initialSlowChange = true; + + DisplayBrightnessState state = mClamperController.clamp(mMockRequest, initialBrightness, + initialSlowChange, STATE_ON); + assertEquals(initialBrightness, state.getBrightness(), FLOAT_TOLERANCE); + + listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 50, mClock.now())); + verify(mMockModifier).setAmbientLux(50); + + listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 300, mClock.now())); + verify(mMockModifier).setAmbientLux(300); + } + + @Test public void testStop() { mClamperController.stop(); verify(mMockModifier).stop(); @@ -245,7 +293,7 @@ public class BrightnessClamperControllerTest { private BrightnessClamperController createBrightnessClamperController() { return new BrightnessClamperController(mTestInjector, mTestHandler, mMockExternalListener, - mMockDisplayDeviceData, mMockContext, mFlags); + mMockDisplayDeviceData, mMockContext, mFlags, mSensorManager); } private class TestInjector extends BrightnessClamperController.Injector { @@ -282,7 +330,7 @@ public class BrightnessClamperControllerTest { @Override List<BrightnessStateModifier> getModifiers(DisplayManagerFlags flags, Context context, Handler handler, BrightnessClamperController.ClamperChangeListener listener, - DisplayDeviceConfig displayDeviceConfig) { + DisplayDeviceConfig displayDeviceConfig, SensorManager sensorManager) { return mModifiers; } } diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessLowLuxModifierTest.kt b/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessLowLuxModifierTest.kt index 21e066dd0922..d672435096b9 100644 --- a/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessLowLuxModifierTest.kt +++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessLowLuxModifierTest.kt @@ -20,8 +20,6 @@ import android.platform.test.annotations.RequiresFlagsEnabled import android.provider.Settings import android.testing.TestableContext import androidx.test.platform.app.InstrumentationRegistry -import com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_DISABLED -import com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_ENABLED import com.android.server.display.DisplayDeviceConfig import com.android.server.display.brightness.BrightnessReason import com.android.server.display.feature.flags.Flags @@ -54,10 +52,12 @@ class BrightnessLowLuxModifierTest { @Before fun setUp() { modifier = - BrightnessLowLuxModifier(testHandler, + BrightnessLowLuxModifier( + testHandler, mockClamperChangeListener, context, - mockDisplayDeviceConfig) + mockDisplayDeviceConfig + ) // values below transition point (even dimmer range) // nits: 0.1 -> backlight 0.02 -> brightness -> 0.1 @@ -66,7 +66,7 @@ class BrightnessLowLuxModifierTest { whenever(mockDisplayDeviceConfig.getBrightnessFromBacklight(/* backlight = */ 0.02f)) .thenReturn(LOW_LUX_BRIGHTNESS) - // values above transition point (noraml range) + // values above transition point (normal range) // nits: 10 -> backlight 0.2 -> brightness -> 0.3 whenever(mockDisplayDeviceConfig.getBacklightFromNits(/* nits= */ 2f)) .thenReturn(0.15f) @@ -95,12 +95,12 @@ class BrightnessLowLuxModifierTest { // test transition point ensures brightness doesn't drop when setting is off. Settings.Secure.putIntForUser(context.contentResolver, Settings.Secure.EVEN_DIMMER_ACTIVATED, 0, USER_ID) - modifier.setAutoBrightnessState(AUTO_BRIGHTNESS_ENABLED) modifier.recalculateLowerBound() testHandler.flush() assertThat(modifier.brightnessLowerBound).isEqualTo(TRANSITION_POINT) assertThat(modifier.brightnessReason).isEqualTo(0) // no reason - ie off - modifier.onAmbientLuxChange(3000.0f) + modifier.setAmbientLux(3000f) + testHandler.flush() assertThat(modifier.isActive).isFalse() assertThat(modifier.brightnessLowerBound).isEqualTo(TRANSITION_POINT) @@ -115,8 +115,8 @@ class BrightnessLowLuxModifierTest { Settings.Secure.EVEN_DIMMER_ACTIVATED, 1, USER_ID) Settings.Secure.putFloatForUser(context.contentResolver, Settings.Secure.EVEN_DIMMER_MIN_NITS, 0.1f, USER_ID) - modifier.setAutoBrightnessState(AUTO_BRIGHTNESS_ENABLED) - modifier.onAmbientLuxChange(400.0f) + modifier.setAmbientLux(400f) + testHandler.flush() assertThat(modifier.isActive).isTrue() @@ -133,7 +133,6 @@ class BrightnessLowLuxModifierTest { Settings.Secure.EVEN_DIMMER_ACTIVATED, 1, USER_ID) Settings.Secure.putFloatForUser(context.contentResolver, Settings.Secure.EVEN_DIMMER_MIN_NITS, 10.0f, USER_ID) - modifier.setAutoBrightnessState(AUTO_BRIGHTNESS_ENABLED) modifier.recalculateLowerBound() testHandler.flush() @@ -152,8 +151,8 @@ class BrightnessLowLuxModifierTest { Settings.Secure.EVEN_DIMMER_ACTIVATED, 1, USER_ID) // on Settings.Secure.putFloatForUser(context.contentResolver, Settings.Secure.EVEN_DIMMER_MIN_NITS, 1.0f, USER_ID) - modifier.setAutoBrightnessState(AUTO_BRIGHTNESS_ENABLED) - modifier.onAmbientLuxChange(400.0f) + modifier.setAmbientLux(400f) + testHandler.flush() assertThat(modifier.isActive).isTrue() @@ -180,8 +179,8 @@ class BrightnessLowLuxModifierTest { Settings.Secure.EVEN_DIMMER_ACTIVATED, 0, USER_ID) // off Settings.Secure.putFloatForUser(context.contentResolver, Settings.Secure.EVEN_DIMMER_MIN_NITS, 1.0f, USER_ID) - modifier.setAutoBrightnessState(AUTO_BRIGHTNESS_ENABLED) - modifier.onAmbientLuxChange(400.0f) + modifier.setAmbientLux(400f) + testHandler.flush() assertThat(modifier.isActive).isFalse() @@ -203,15 +202,14 @@ class BrightnessLowLuxModifierTest { @Test @RequiresFlagsEnabled(Flags.FLAG_EVEN_DIMMER) - fun testDisabledWhenAutobrightnessIsOff() { + fun testEnabledEvenWhenAutobrightnessIsOff() { // test that high lux prevents low brightness range. Settings.Secure.putIntForUser(context.contentResolver, Settings.Secure.EVEN_DIMMER_ACTIVATED, 1, USER_ID) // on Settings.Secure.putFloatForUser(context.contentResolver, Settings.Secure.EVEN_DIMMER_MIN_NITS, 1.0f, USER_ID) - modifier.setAutoBrightnessState(AUTO_BRIGHTNESS_ENABLED) - modifier.onAmbientLuxChange(400.0f) + modifier.setAmbientLux(400f) testHandler.flush() assertThat(modifier.isActive).isTrue() @@ -219,15 +217,13 @@ class BrightnessLowLuxModifierTest { assertThat(modifier.brightnessReason).isEqualTo(BrightnessReason.MODIFIER_MIN_LUX) assertThat(modifier.brightnessLowerBound).isEqualTo(LOW_LUX_BRIGHTNESS) - - modifier.setAutoBrightnessState(AUTO_BRIGHTNESS_DISABLED) - modifier.onAmbientLuxChange(400.0f) + modifier.setAmbientLux(400f) testHandler.flush() - assertThat(modifier.isActive).isFalse() + assertThat(modifier.isActive).isTrue() // Test restriction from lux setting - assertThat(modifier.brightnessReason).isEqualTo(0) - assertThat(modifier.brightnessLowerBound).isEqualTo(TRANSITION_POINT) + assertThat(modifier.brightnessReason).isEqualTo(BrightnessReason.MODIFIER_MIN_LUX) + assertThat(modifier.brightnessLowerBound).isEqualTo(LOW_LUX_BRIGHTNESS) } } diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategyTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategyTest.java index 8a33f341df56..1d04baaa6acd 100644 --- a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategyTest.java +++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategyTest.java @@ -570,6 +570,86 @@ public class AutomaticBrightnessStrategyTest { assertEquals(expectedDisplayBrightnessState, actualDisplayBrightnessState); } + @Test + public void + updateBrightness_constructsDisplayBrightnessState_withNoAdjustmentFlag_isSlowChange() { + BrightnessEvent brightnessEvent = new BrightnessEvent(DISPLAY_ID); + mAutomaticBrightnessStrategy = new AutomaticBrightnessStrategy( + mContext, DISPLAY_ID, displayId -> brightnessEvent, mDisplayManagerFlags); + mAutomaticBrightnessStrategy.setAutomaticBrightnessController( + mAutomaticBrightnessController); + float brightness = 0.4f; + BrightnessReason brightnessReason = new BrightnessReason(); + brightnessReason.setReason(BrightnessReason.REASON_AUTOMATIC); + when(mAutomaticBrightnessController.getAutomaticScreenBrightness(brightnessEvent)) + .thenReturn(brightness); + + // Set the state such that auto-brightness was already applied + mAutomaticBrightnessStrategy.setAutoBrightnessApplied(true); + + // Update the auto-brightess validity state to change the isSlowChange flag + mAutomaticBrightnessStrategy.isAutoBrightnessValid(); + + DisplayManagerInternal.DisplayPowerRequest displayPowerRequest = + mock(DisplayManagerInternal.DisplayPowerRequest.class); + + DisplayBrightnessState expectedDisplayBrightnessState = new DisplayBrightnessState.Builder() + .setBrightness(brightness) + .setSdrBrightness(brightness) + .setBrightnessReason(brightnessReason) + .setDisplayBrightnessStrategyName(mAutomaticBrightnessStrategy.getName()) + .setIsSlowChange(true) + .setBrightnessEvent(brightnessEvent) + .setBrightnessAdjustmentFlag(0) + .setShouldUpdateScreenBrightnessSetting(true) + .setIsUserInitiatedChange(true) + .build(); + DisplayBrightnessState actualDisplayBrightnessState = mAutomaticBrightnessStrategy + .updateBrightness(new StrategyExecutionRequest(displayPowerRequest, 0.6f, + /* userSetBrightnessChanged= */ true)); + assertEquals(expectedDisplayBrightnessState, actualDisplayBrightnessState); + } + + + @Test + public void updateBrightness_autoBrightnessNotApplied_noAdjustments_isNotSlowChange() { + BrightnessEvent brightnessEvent = new BrightnessEvent(DISPLAY_ID); + mAutomaticBrightnessStrategy = new AutomaticBrightnessStrategy( + mContext, DISPLAY_ID, displayId -> brightnessEvent, mDisplayManagerFlags); + mAutomaticBrightnessStrategy.setAutomaticBrightnessController( + mAutomaticBrightnessController); + float brightness = 0.4f; + BrightnessReason brightnessReason = new BrightnessReason(); + brightnessReason.setReason(BrightnessReason.REASON_AUTOMATIC); + when(mAutomaticBrightnessController.getAutomaticScreenBrightness(brightnessEvent)) + .thenReturn(brightness); + + // Set the state such that auto-brightness was not already applied + mAutomaticBrightnessStrategy.setAutoBrightnessApplied(false); + + // Update the auto-brightess validity state to change the isSlowChange flag + mAutomaticBrightnessStrategy.isAutoBrightnessValid(); + + DisplayManagerInternal.DisplayPowerRequest displayPowerRequest = + mock(DisplayManagerInternal.DisplayPowerRequest.class); + + DisplayBrightnessState expectedDisplayBrightnessState = new DisplayBrightnessState.Builder() + .setBrightness(brightness) + .setSdrBrightness(brightness) + .setBrightnessReason(brightnessReason) + .setDisplayBrightnessStrategyName(mAutomaticBrightnessStrategy.getName()) + .setIsSlowChange(false) + .setBrightnessEvent(brightnessEvent) + .setBrightnessAdjustmentFlag(0) + .setShouldUpdateScreenBrightnessSetting(true) + .setIsUserInitiatedChange(true) + .build(); + DisplayBrightnessState actualDisplayBrightnessState = mAutomaticBrightnessStrategy + .updateBrightness(new StrategyExecutionRequest(displayPowerRequest, 0.6f, + /* userSetBrightnessChanged= */ true)); + assertEquals(expectedDisplayBrightnessState, actualDisplayBrightnessState); + } + private void setPendingAutoBrightnessAdjustment(float pendingAutoBrightnessAdjustment) { Settings.System.putFloat(mContext.getContentResolver(), Settings.System.SCREEN_AUTO_BRIGHTNESS_ADJ, pendingAutoBrightnessAdjustment); 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 88c0daaffcd2..95702aa1bce1 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 @@ -19,11 +19,12 @@ package com.android.server.display.mode import android.content.Context import android.content.ContextWrapper import android.hardware.display.BrightnessInfo -import android.util.SparseArray import android.view.Display import androidx.test.core.app.ApplicationProvider import androidx.test.filters.SmallTest import com.android.server.display.DisplayDeviceConfig +import com.android.server.display.config.RefreshRateData +import com.android.server.display.config.SupportedModeData import com.android.server.display.feature.DisplayManagerFlags import com.android.server.display.mode.DisplayModeDirector.DisplayDeviceConfigProvider import com.android.server.testutils.TestHandler @@ -39,6 +40,13 @@ import org.mockito.junit.MockitoJUnit import org.mockito.kotlin.mock import org.mockito.kotlin.whenever +private val LOW_LIGHT_REFRESH_RATE_DATA = createRefreshRateData( + lowLightBlockingZoneSupportedModes = listOf( + SupportedModeData(60f, 60f), SupportedModeData(240f, 240f))) +private val EXPECTED_SUPPORTED_MODES_VOTE = SupportedRefreshRatesVote( + listOf(SupportedRefreshRatesVote.RefreshRates(60f, 60f), + SupportedRefreshRatesVote.RefreshRates(240f, 240f))) + @SmallTest @RunWith(TestParameterInjector::class) class BrightnessObserverTest { @@ -65,21 +73,25 @@ class BrightnessObserverTest { whenever(mockFlags.isVsyncLowLightVoteEnabled).thenReturn(testCase.vsyncLowLightVoteEnabled) val displayModeDirector = DisplayModeDirector( spyContext, testHandler, mockInjector, mockFlags, mockDisplayDeviceConfigProvider) - val ddcByDisplay = SparseArray<DisplayDeviceConfig>() whenever(mockDeviceConfig.isVrrSupportEnabled).thenReturn(testCase.vrrSupported) - ddcByDisplay.put(Display.DEFAULT_DISPLAY, mockDeviceConfig) - displayModeDirector.injectDisplayDeviceConfigByDisplay(ddcByDisplay) + whenever(mockDeviceConfig.refreshRateData).thenReturn(testCase.refreshRateData) + whenever(mockDeviceConfig.defaultLowBlockingZoneRefreshRate).thenReturn(-1) + + displayModeDirector.defaultDisplayDeviceUpdated(mockDeviceConfig) + val brightnessObserver = displayModeDirector.BrightnessObserver( spyContext, testHandler, mockInjector, mockFlags) - + // set mRefreshRateChangeable to true brightnessObserver.onRefreshRateSettingChangedLocked(0.0f, 120.0f) brightnessObserver.updateBlockingZoneThresholds(mockDeviceConfig, false) - brightnessObserver.onDeviceConfigRefreshRateInLowZoneChanged(60) + brightnessObserver.onDeviceConfigRefreshRateInLowZoneChanged(testCase.refreshRateInLowZone) brightnessObserver.onDisplayChanged(Display.DEFAULT_DISPLAY) assertThat(displayModeDirector.getVote(VotesStorage.GLOBAL_ID, - Vote.PRIORITY_FLICKER_REFRESH_RATE_SWITCH)).isEqualTo(testCase.expectedVote) + Vote.PRIORITY_FLICKER_REFRESH_RATE)).isEqualTo(testCase.expectedRefreshRateVote) + assertThat(displayModeDirector.getVote(VotesStorage.GLOBAL_ID, + Vote.PRIORITY_FLICKER_REFRESH_RATE_SWITCH)).isEqualTo(testCase.expectedSwitchVote) } private fun setUpLowBrightnessZone() { @@ -98,14 +110,20 @@ class BrightnessObserverTest { enum class LowLightTestCase( val vrrSupported: Boolean, val vsyncLowLightVoteEnabled: Boolean, - internal val expectedVote: Vote + val refreshRateData: RefreshRateData, + val refreshRateInLowZone: Int, + internal val expectedRefreshRateVote: Vote, + internal val expectedSwitchVote: Vote?, ) { - ALL_ENABLED(true, true, CombinedVote( - listOf(DisableRefreshRateSwitchingVote(true), - SupportedRefreshRatesVote( - listOf(SupportedRefreshRatesVote.RefreshRates(60f, 60f), - SupportedRefreshRatesVote.RefreshRates(120f, 120f)))))), - VRR_NOT_SUPPORTED(false, true, DisableRefreshRateSwitchingVote(true)), - VSYNC_VOTE_DISABLED(true, false, DisableRefreshRateSwitchingVote(true)) + ALL_ENABLED(true, true, LOW_LIGHT_REFRESH_RATE_DATA, 60, + EXPECTED_SUPPORTED_MODES_VOTE, null), + ALL_ENABLED_NO_RR_IN_LOW_ZONE(true, true, LOW_LIGHT_REFRESH_RATE_DATA, 0, + EXPECTED_SUPPORTED_MODES_VOTE, null), + VRR_NOT_SUPPORTED(false, true, LOW_LIGHT_REFRESH_RATE_DATA, 60, + Vote.forPhysicalRefreshRates(60f, 60f), DisableRefreshRateSwitchingVote(true)), + VSYNC_VOTE_DISABLED(true, false, LOW_LIGHT_REFRESH_RATE_DATA, 50, + Vote.forPhysicalRefreshRates(50f, 50f), DisableRefreshRateSwitchingVote(true)), + NO_LOW_LIGHT_CONFIG(true, true, createRefreshRateData(), 40, + Vote.forPhysicalRefreshRates(40f, 40f), DisableRefreshRateSwitchingVote(true)), } }
\ No newline at end of file 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 714b423fae70..242d5593c3c8 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 @@ -132,7 +132,8 @@ public class DisplayModeDirectorTest { /* defaultPeakRefreshRate= */ 0, /* defaultRefreshRateInHbmHdr= */ 0, /* defaultRefreshRateInHbmSunlight= */ 0, - /* lowPowerSupportedModes =*/ List.of()); + /* lowPowerSupportedModes= */ List.of(), + /* lowLightBlockingZoneSupportedModes= */ List.of()); public static Collection<Object[]> getAppRequestedSizeTestCases() { var appRequestedSizeTestCases = Arrays.asList(new Object[][] { @@ -3170,7 +3171,8 @@ public class DisplayModeDirectorTest { /* defaultPeakRefreshRate= */ 65, /* defaultRefreshRateInHbmHdr= */ 65, /* defaultRefreshRateInHbmSunlight= */ 75, - /* lowPowerSupportedModes= */ List.of()); + /* lowPowerSupportedModes= */ List.of(), + /* lowLightBlockingZoneSupportedModes= */ List.of()); when(displayDeviceConfig.getRefreshRateData()).thenReturn(refreshRateData); when(displayDeviceConfig.getDefaultLowBlockingZoneRefreshRate()).thenReturn(50); when(displayDeviceConfig.getDefaultHighBlockingZoneRefreshRate()).thenReturn(55); diff --git a/services/tests/displayservicetests/src/com/android/server/display/mode/TestUtils.kt b/services/tests/displayservicetests/src/com/android/server/display/mode/TestUtils.kt index 1206e30b9e88..5b07166d63e4 100644 --- a/services/tests/displayservicetests/src/com/android/server/display/mode/TestUtils.kt +++ b/services/tests/displayservicetests/src/com/android/server/display/mode/TestUtils.kt @@ -34,9 +34,10 @@ fun createRefreshRateData( defaultPeakRefreshRate: Int = 60, defaultRefreshRateInHbmHdr: Int = 60, defaultRefreshRateInHbmSunlight: Int = 60, - lowPowerSupportedModes: List<SupportedModeData> = emptyList() + lowPowerSupportedModes: List<SupportedModeData> = emptyList(), + lowLightBlockingZoneSupportedModes: List<SupportedModeData> = emptyList() ): RefreshRateData { return RefreshRateData(defaultRefreshRate, defaultPeakRefreshRate, defaultRefreshRateInHbmHdr, defaultRefreshRateInHbmSunlight, - lowPowerSupportedModes) + lowPowerSupportedModes, lowLightBlockingZoneSupportedModes) } diff --git a/services/tests/displayservicetests/src/com/android/server/display/mode/VotesStorageTest.java b/services/tests/displayservicetests/src/com/android/server/display/mode/VotesStorageTest.java index a248d6de118f..0125ddb7b7d9 100644 --- a/services/tests/displayservicetests/src/com/android/server/display/mode/VotesStorageTest.java +++ b/services/tests/displayservicetests/src/com/android/server/display/mode/VotesStorageTest.java @@ -38,10 +38,10 @@ import org.mockito.MockitoAnnotations; @RunWith(AndroidJUnit4.class) public class VotesStorageTest { private static final int DISPLAY_ID = 100; - private static final int PRIORITY = Vote.PRIORITY_APP_REQUEST_SIZE; + private static final @Vote.Priority int PRIORITY = Vote.PRIORITY_APP_REQUEST_SIZE; private static final Vote VOTE = Vote.forDisableRefreshRateSwitching(); private static final int DISPLAY_ID_OTHER = 101; - private static final int PRIORITY_OTHER = Vote.PRIORITY_FLICKER_REFRESH_RATE; + private static final @Vote.Priority int PRIORITY_OTHER = Vote.PRIORITY_FLICKER_REFRESH_RATE; private static final Vote VOTE_OTHER = Vote.forBaseModeRefreshRate(10f); @Mock diff --git a/services/tests/mockingservicestests/src/com/android/server/am/ApplicationStartInfoTest.java b/services/tests/mockingservicestests/src/com/android/server/am/ApplicationStartInfoTest.java index e88e28b37551..ee96c2abbf46 100644 --- a/services/tests/mockingservicestests/src/com/android/server/am/ApplicationStartInfoTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/am/ApplicationStartInfoTest.java @@ -74,6 +74,15 @@ public class ApplicationStartInfoTest { private static final String TAG = ApplicationStartInfoTest.class.getSimpleName(); private static final ComponentName COMPONENT = new ComponentName("com.android.test", ".Foo"); + private static final int APP_1_UID = 10123; + private static final int APP_1_PID_1 = 12345; + private static final int APP_1_PID_2 = 12346; + private static final int APP_1_DEFINING_UID = 23456; + private static final int APP_1_UID_USER_2 = 1010123; + private static final int APP_1_PID_USER_2 = 12347; + private static final String APP_1_PROCESS_NAME = "com.android.test.stub1:process"; + private static final String APP_1_PACKAGE_NAME = "com.android.test.stub1"; + @Rule public ServiceThreadRule mServiceThreadRule = new ServiceThreadRule(); @Mock private AppOpsService mAppOpsService; @Mock private PackageManagerInternal mPackageManagerInt; @@ -111,6 +120,12 @@ public class ApplicationStartInfoTest { // Remove stale instance of PackageManagerInternal if there is any LocalServices.removeServiceForTest(PackageManagerInternal.class); LocalServices.addService(PackageManagerInternal.class, mPackageManagerInt); + + mAppStartInfoTracker.clearProcessStartInfo(true); + mAppStartInfoTracker.mAppStartInfoLoaded.set(true); + mAppStartInfoTracker.mAppStartInfoHistoryListSize = + mAppStartInfoTracker.APP_START_INFO_HISTORY_LIST_SIZE; + doNothing().when(mAppStartInfoTracker).schedulePersistProcessStartInfo(anyBoolean()); } @After @@ -120,26 +135,12 @@ public class ApplicationStartInfoTest { @Test public void testApplicationStartInfo() throws Exception { - mAppStartInfoTracker.clearProcessStartInfo(true); - mAppStartInfoTracker.mAppStartInfoLoaded.set(true); - mAppStartInfoTracker.mAppStartInfoHistoryListSize = - mAppStartInfoTracker.APP_START_INFO_HISTORY_LIST_SIZE; mAppStartInfoTracker.mProcStartStoreDir = new File(mContext.getFilesDir(), AppStartInfoTracker.APP_START_STORE_DIR); assertTrue(FileUtils.createDir(mAppStartInfoTracker.mProcStartStoreDir)); mAppStartInfoTracker.mProcStartInfoFile = new File(mAppStartInfoTracker.mProcStartStoreDir, AppStartInfoTracker.APP_START_INFO_FILE); - doNothing().when(mAppStartInfoTracker).schedulePersistProcessStartInfo(anyBoolean()); - - final int app1Uid = 10123; - final int app1Pid1 = 12345; - final int app1Pid2 = 12346; - final int app1DefiningUid = 23456; - final int app1UidUser2 = 1010123; - final int app1PidUser2 = 12347; - final String app1ProcessName = "com.android.test.stub1:process"; - final String app1PackageName = "com.android.test.stub1"; final long appStartTimestampIntentStarted = 1000000; final long appStartTimestampActivityLaunchFinished = 2000000; final long appStartTimestampFirstFrameDrawn = 2500000; @@ -149,23 +150,23 @@ public class ApplicationStartInfoTest { final long appStartTimestampRContentProvider = 6000000; ProcessRecord app = makeProcessRecord( - app1Pid1, // pid - app1Uid, // uid - app1Uid, // packageUid + APP_1_PID_1, // pid + APP_1_UID, // uid + APP_1_UID, // packageUid null, // definingUid - app1ProcessName, // processName - app1PackageName); // packageName + APP_1_PROCESS_NAME, // processName + APP_1_PACKAGE_NAME); // packageName ArrayList<ApplicationStartInfo> list = new ArrayList<ApplicationStartInfo>(); // Case 1: Activity start intent failed mAppStartInfoTracker.onIntentStarted(buildIntent(COMPONENT), appStartTimestampIntentStarted); - mAppStartInfoTracker.getStartInfo(app1PackageName, app1Uid, app1Pid1, 0, list); + mAppStartInfoTracker.getStartInfo(APP_1_PACKAGE_NAME, APP_1_UID, APP_1_PID_1, 0, list); verifyInProgressRecordsSize(1); assertEquals(list.size(), 0); - verifyInProgApplicationStartInfo( + verifyInProgressApplicationStartInfo( 0, // index 0, // pid 0, // uid @@ -179,7 +180,7 @@ public class ApplicationStartInfoTest { mAppStartInfoTracker.onIntentFailed(appStartTimestampIntentStarted); list.clear(); - mAppStartInfoTracker.getStartInfo(app1PackageName, app1Uid, app1Pid1, 0, list); + mAppStartInfoTracker.getStartInfo(APP_1_PACKAGE_NAME, APP_1_UID, APP_1_PID_1, 0, list); verifyInProgressRecordsSize(0); assertEquals(list.size(), 0); @@ -189,24 +190,24 @@ public class ApplicationStartInfoTest { mAppStartInfoTracker.onIntentStarted(buildIntent(COMPONENT), appStartTimestampIntentStarted); list.clear(); - mAppStartInfoTracker.getStartInfo(app1PackageName, app1Uid, app1Pid1, 0, list); + mAppStartInfoTracker.getStartInfo(APP_1_PACKAGE_NAME, APP_1_UID, APP_1_PID_1, 0, list); verifyInProgressRecordsSize(1); assertEquals(list.size(), 0); mAppStartInfoTracker.onActivityLaunched(appStartTimestampIntentStarted, COMPONENT, ApplicationStartInfo.START_TYPE_COLD, app); list.clear(); - mAppStartInfoTracker.getStartInfo(app1PackageName, app1Uid, app1Pid1, 0, list); + mAppStartInfoTracker.getStartInfo(APP_1_PACKAGE_NAME, APP_1_UID, APP_1_PID_1, 0, list); verifyInProgressRecordsSize(1); assertEquals(list.size(), 1); - verifyInProgApplicationStartInfo( + verifyInProgressApplicationStartInfo( 0, // index - app1Pid1, // pid - app1Uid, // uid - app1Uid, // packageUid + APP_1_PID_1, // pid + APP_1_UID, // uid + APP_1_UID, // packageUid null, // definingUid - app1ProcessName, // processName + APP_1_PROCESS_NAME, // processName ApplicationStartInfo.START_REASON_START_ACTIVITY, // reason ApplicationStartInfo.STARTUP_STATE_STARTED, // startup state ApplicationStartInfo.START_TYPE_COLD, // state type @@ -214,17 +215,17 @@ public class ApplicationStartInfoTest { mAppStartInfoTracker.onActivityLaunchCancelled(appStartTimestampIntentStarted); list.clear(); - mAppStartInfoTracker.getStartInfo(app1PackageName, app1Uid, app1Pid1, 0, list); + mAppStartInfoTracker.getStartInfo(APP_1_PACKAGE_NAME, APP_1_UID, APP_1_PID_1, 0, list); verifyInProgressRecordsSize(0); assertEquals(list.size(), 1); verifyApplicationStartInfo( list.get(0), // info - app1Pid1, // pid - app1Uid, // uid - app1Uid, // packageUid + APP_1_PID_1, // pid + APP_1_UID, // uid + APP_1_UID, // packageUid null, // definingUid - app1ProcessName, // processName + APP_1_PROCESS_NAME, // processName ApplicationStartInfo.START_REASON_START_ACTIVITY, // reason ApplicationStartInfo.STARTUP_STATE_ERROR, // startup state ApplicationStartInfo.START_TYPE_COLD, // state type @@ -236,24 +237,24 @@ public class ApplicationStartInfoTest { mAppStartInfoTracker.onIntentStarted(buildIntent(COMPONENT), appStartTimestampIntentStarted); list.clear(); - mAppStartInfoTracker.getStartInfo(app1PackageName, app1Uid, app1Pid1, 0, list); + mAppStartInfoTracker.getStartInfo(APP_1_PACKAGE_NAME, APP_1_UID, APP_1_PID_1, 0, list); verifyInProgressRecordsSize(1); assertEquals(list.size(), 0); mAppStartInfoTracker.onActivityLaunched(appStartTimestampIntentStarted, COMPONENT, ApplicationStartInfo.START_TYPE_COLD, app); list.clear(); - mAppStartInfoTracker.getStartInfo(app1PackageName, app1Uid, app1Pid1, 0, list); + mAppStartInfoTracker.getStartInfo(APP_1_PACKAGE_NAME, APP_1_UID, APP_1_PID_1, 0, list); verifyInProgressRecordsSize(1); assertEquals(list.size(), 1); - verifyInProgApplicationStartInfo( + verifyInProgressApplicationStartInfo( 0, // index - app1Pid1, // pid - app1Uid, // uid - app1Uid, // packageUid + APP_1_PID_1, // pid + APP_1_UID, // uid + APP_1_UID, // packageUid null, // definingUid - app1ProcessName, // processName + APP_1_PROCESS_NAME, // processName ApplicationStartInfo.START_REASON_START_ACTIVITY, // reason ApplicationStartInfo.STARTUP_STATE_STARTED, // startup state ApplicationStartInfo.START_TYPE_COLD, // state type @@ -261,11 +262,11 @@ public class ApplicationStartInfoTest { verifyApplicationStartInfo( list.get(0), // info - app1Pid1, // pid - app1Uid, // uid - app1Uid, // packageUid + APP_1_PID_1, // pid + APP_1_UID, // uid + APP_1_UID, // packageUid null, // definingUid - app1ProcessName, // processName + APP_1_PROCESS_NAME, // processName ApplicationStartInfo.START_REASON_START_ACTIVITY, // reason ApplicationStartInfo.STARTUP_STATE_STARTED, // startup state ApplicationStartInfo.START_TYPE_COLD, // state type @@ -273,20 +274,20 @@ public class ApplicationStartInfoTest { mAppStartInfoTracker.onActivityLaunchFinished(appStartTimestampIntentStarted, COMPONENT, appStartTimestampActivityLaunchFinished, ApplicationStartInfo.LAUNCH_MODE_STANDARD); - mAppStartInfoTracker.addTimestampToStart(app1PackageName, app1Uid, + mAppStartInfoTracker.addTimestampToStart(APP_1_PACKAGE_NAME, APP_1_UID, appStartTimestampFirstFrameDrawn, ApplicationStartInfo.START_TIMESTAMP_FIRST_FRAME); list.clear(); - mAppStartInfoTracker.getStartInfo(app1PackageName, app1Uid, app1Pid1, 0, list); + mAppStartInfoTracker.getStartInfo(APP_1_PACKAGE_NAME, APP_1_UID, APP_1_PID_1, 0, list); verifyInProgressRecordsSize(1); assertEquals(list.size(), 1); - verifyInProgApplicationStartInfo( + verifyInProgressApplicationStartInfo( 0, // index - app1Pid1, // pid - app1Uid, // uid - app1Uid, // packageUid + APP_1_PID_1, // pid + APP_1_UID, // uid + APP_1_UID, // packageUid null, // definingUid - app1ProcessName, // processName + APP_1_PROCESS_NAME, // processName ApplicationStartInfo.START_REASON_START_ACTIVITY, // reason ApplicationStartInfo.STARTUP_STATE_FIRST_FRAME_DRAWN, // startup state ApplicationStartInfo.START_TYPE_COLD, // state type @@ -295,17 +296,17 @@ public class ApplicationStartInfoTest { mAppStartInfoTracker.onReportFullyDrawn(appStartTimestampIntentStarted, appStartTimestampReportFullyDrawn); list.clear(); - mAppStartInfoTracker.getStartInfo(app1PackageName, app1Uid, app1Pid1, 0, list); + mAppStartInfoTracker.getStartInfo(APP_1_PACKAGE_NAME, APP_1_UID, APP_1_PID_1, 0, list); verifyInProgressRecordsSize(0); assertEquals(list.size(), 1); verifyApplicationStartInfo( list.get(0), // info - app1Pid1, // pid - app1Uid, // uid - app1Uid, // packageUid + APP_1_PID_1, // pid + APP_1_UID, // uid + APP_1_UID, // packageUid null, // definingUid - app1ProcessName, // processName + APP_1_PROCESS_NAME, // processName ApplicationStartInfo.START_REASON_START_ACTIVITY, // reason ApplicationStartInfo.STARTUP_STATE_FIRST_FRAME_DRAWN, // startup state ApplicationStartInfo.START_TYPE_COLD, // state type @@ -316,26 +317,26 @@ public class ApplicationStartInfoTest { // Case 4: Create an other app1 record with different pid started for a service sleep(1); app = makeProcessRecord( - app1Pid2, // pid - app1Uid, // uid - app1Uid, // packageUid - app1DefiningUid, // definingUid - app1ProcessName, // processName - app1PackageName); // packageName + APP_1_PID_2, // pid + APP_1_UID, // uid + APP_1_UID, // packageUid + APP_1_DEFINING_UID, // definingUid + APP_1_PROCESS_NAME, // processName + APP_1_PACKAGE_NAME); // packageName ServiceRecord service = ServiceRecord.newEmptyInstanceForTest(mAms); mAppStartInfoTracker.handleProcessServiceStart(appStartTimestampService, app, service); list.clear(); - mAppStartInfoTracker.getStartInfo(app1PackageName, app1Uid, 0, 0, list); + mAppStartInfoTracker.getStartInfo(APP_1_PACKAGE_NAME, APP_1_UID, 0, 0, list); assertEquals(list.size(), 2); verifyApplicationStartInfo( list.get(0), // info - app1Pid2, // pid - app1Uid, // uid - app1Uid, // packageUid - app1DefiningUid, // definingUid - app1ProcessName, // processName + APP_1_PID_2, // pid + APP_1_UID, // uid + APP_1_UID, // packageUid + APP_1_DEFINING_UID, // definingUid + APP_1_PROCESS_NAME, // processName ApplicationStartInfo.START_REASON_SERVICE, // reason ApplicationStartInfo.STARTUP_STATE_STARTED, // startup state ApplicationStartInfo.START_TYPE_COLD, // state type @@ -344,39 +345,41 @@ public class ApplicationStartInfoTest { // Case 5: Create an instance of app1 with a different user started for a broadcast sleep(1); app = makeProcessRecord( - app1PidUser2, // pid - app1UidUser2, // uid - app1UidUser2, // packageUid + APP_1_PID_USER_2, // pid + APP_1_UID_USER_2, // uid + APP_1_UID_USER_2, // packageUid null, // definingUid - app1ProcessName, // processName - app1PackageName); // packageName + APP_1_PROCESS_NAME, // processName + APP_1_PACKAGE_NAME); // packageName mAppStartInfoTracker.handleProcessBroadcastStart(appStartTimestampBroadcast, app, buildIntent(COMPONENT), false /* isAlarm */); list.clear(); - mAppStartInfoTracker.getStartInfo(app1PackageName, app1UidUser2, app1PidUser2, 0, list); + mAppStartInfoTracker.getStartInfo(APP_1_PACKAGE_NAME, APP_1_UID_USER_2, APP_1_PID_USER_2, 0, + list); assertEquals(list.size(), 1); verifyApplicationStartInfo( list.get(0), // info - app1PidUser2, // pid - app1UidUser2, // uid - app1UidUser2, // packageUid + APP_1_PID_USER_2, // pid + APP_1_UID_USER_2, // uid + APP_1_UID_USER_2, // packageUid null, // definingUid - app1ProcessName, // processName + APP_1_PROCESS_NAME, // processName ApplicationStartInfo.START_REASON_BROADCAST, // reason ApplicationStartInfo.STARTUP_STATE_STARTED, // startup state ApplicationStartInfo.START_TYPE_COLD, // state type ApplicationStartInfo.LAUNCH_MODE_STANDARD); // launch mode // Case 6: User 2 gets removed - mAppStartInfoTracker.onPackageRemoved(app1PackageName, app1UidUser2, false); + mAppStartInfoTracker.onPackageRemoved(APP_1_PACKAGE_NAME, APP_1_UID_USER_2, false); list.clear(); - mAppStartInfoTracker.getStartInfo(app1PackageName, app1UidUser2, app1PidUser2, 0, list); + mAppStartInfoTracker.getStartInfo(APP_1_PACKAGE_NAME, APP_1_UID_USER_2, APP_1_PID_USER_2, 0, + list); assertEquals(list.size(), 0); list.clear(); - mAppStartInfoTracker.getStartInfo(app1PackageName, app1Uid, app1PidUser2, 0, list); + mAppStartInfoTracker.getStartInfo(APP_1_PACKAGE_NAME, APP_1_UID, APP_1_PID_USER_2, 0, list); assertEquals(list.size(), 2); @@ -416,7 +419,7 @@ public class ApplicationStartInfoTest { // Case 8: Save and load again ArrayList<ApplicationStartInfo> original = new ArrayList<ApplicationStartInfo>(); - mAppStartInfoTracker.getStartInfo(null, app1Uid, 0, 0, original); + mAppStartInfoTracker.getStartInfo(null, APP_1_UID, 0, 0, original); assertTrue(original.size() > 0); mAppStartInfoTracker.persistProcessStartInfo(); @@ -424,12 +427,12 @@ public class ApplicationStartInfoTest { mAppStartInfoTracker.clearProcessStartInfo(false); list.clear(); - mAppStartInfoTracker.getStartInfo(null, app1Uid, 0, 0, list); + mAppStartInfoTracker.getStartInfo(null, APP_1_UID, 0, 0, list); assertEquals(0, list.size()); mAppStartInfoTracker.loadExistingProcessStartInfo(); list.clear(); - mAppStartInfoTracker.getStartInfo(null, app1Uid, 0, 0, list); + mAppStartInfoTracker.getStartInfo(null, APP_1_UID, 0, 0, list); assertEquals(original.size(), list.size()); for (int i = list.size() - 1; i >= 0; i--) { @@ -437,6 +440,48 @@ public class ApplicationStartInfoTest { } } + /** + * Test to make sure that in progress records stay within their size limits and discard the + * correct records. + */ + @SuppressWarnings("GuardedBy") + @Test + public void testInProgressRecordsLimit() throws Exception { + ProcessRecord app = makeProcessRecord( + APP_1_PID_1, // pid + APP_1_UID, // uid + APP_1_UID, // packageUid + null, // definingUid + APP_1_PROCESS_NAME, // processName + APP_1_PACKAGE_NAME); // packageName + + // Mock performing 2 x MAX_IN_PROGRESS_RECORDS successful starts and ensure that the list + // never exceeds the expected size of MAX_IN_PROGRESS_RECORDS. + for (int i = 0; i < AppStartInfoTracker.MAX_IN_PROGRESS_RECORDS * 2; i++) { + Long startTime = Long.valueOf(i); + mAppStartInfoTracker.onIntentStarted(buildIntent(COMPONENT), startTime); + verifyInProgressRecordsSize( + Math.min(i + 1, AppStartInfoTracker.MAX_IN_PROGRESS_RECORDS)); + + mAppStartInfoTracker.onActivityLaunched(startTime, COMPONENT, + ApplicationStartInfo.START_TYPE_COLD, app); + verifyInProgressRecordsSize( + Math.min(i + 1, AppStartInfoTracker.MAX_IN_PROGRESS_RECORDS)); + + mAppStartInfoTracker.onActivityLaunchFinished(startTime, COMPONENT, + startTime + 100, ApplicationStartInfo.LAUNCH_MODE_STANDARD); + verifyInProgressRecordsSize( + Math.min(i + 1, AppStartInfoTracker.MAX_IN_PROGRESS_RECORDS)); + + // Make sure that the record added in this iteration is still present. + assertTrue(mAppStartInfoTracker.mInProgressRecords.containsKey(startTime)); + } + + // Confirm that after 2 x MAX_IN_PROGRESS_RECORDS starts only MAX_IN_PROGRESS_RECORDS are + // present. + verifyInProgressRecordsSize(AppStartInfoTracker.MAX_IN_PROGRESS_RECORDS); + } + private static <T> void setFieldValue(Class clazz, Object obj, String fieldName, T val) { try { Field field = clazz.getDeclaredField(fieldName); @@ -484,16 +529,16 @@ public class ApplicationStartInfoTest { private void verifyInProgressRecordsSize(int expectedSize) { synchronized (mAppStartInfoTracker.mLock) { - assertEquals(mAppStartInfoTracker.mInProgRecords.size(), expectedSize); + assertEquals(mAppStartInfoTracker.mInProgressRecords.size(), expectedSize); } } - private void verifyInProgApplicationStartInfo(int index, + private void verifyInProgressApplicationStartInfo(int index, Integer pid, Integer uid, Integer packageUid, Integer definingUid, String processName, Integer reason, Integer startupState, Integer startType, Integer launchMode) { synchronized (mAppStartInfoTracker.mLock) { - verifyApplicationStartInfo(mAppStartInfoTracker.mInProgRecords.valueAt(index), + verifyApplicationStartInfo(mAppStartInfoTracker.mInProgressRecords.valueAt(index), pid, uid, packageUid, definingUid, processName, reason, startupState, startType, launchMode); } 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 28c7fb2396dd..488ce66ca99b 100644 --- a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java +++ b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java @@ -44,6 +44,7 @@ import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_SHORT_SERVI import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; +import static com.android.server.am.ActivityManagerService.FOLLOW_UP_OOMADJUSTER_UPDATE_MSG; import static com.android.server.am.ProcessList.BACKUP_APP_ADJ; import static com.android.server.am.ProcessList.CACHED_APP_MAX_ADJ; import static com.android.server.am.ProcessList.CACHED_APP_MIN_ADJ; @@ -77,6 +78,7 @@ import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.any; import static org.mockito.Mockito.anyBoolean; import static org.mockito.Mockito.anyInt; +import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doCallRealMethod; import static org.mockito.Mockito.doNothing; @@ -113,11 +115,10 @@ import com.android.server.wm.ActivityTaskManagerService; import com.android.server.wm.WindowProcessController; import org.junit.After; -import org.junit.AfterClass; import org.junit.Before; -import org.junit.BeforeClass; import org.junit.Rule; import org.junit.Test; +import org.mockito.ArgumentCaptor; import java.io.File; import java.lang.reflect.Field; @@ -164,92 +165,86 @@ public class MockingOomAdjusterTests { private static int sFirstCachedAdj = ProcessList.CACHED_APP_MIN_ADJ + ProcessList.CACHED_APP_IMPORTANCE_LEVELS; - private static Context sContext; - private static PackageManagerInternal sPackageManagerInternal; - private static ActivityManagerService sService; + private Context mContext; + private PackageManagerInternal mPackageManagerInternal; + private ActivityManagerService mService; + private OomAdjusterInjector mInjector = new OomAdjusterInjector(); @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); @SuppressWarnings("GuardedBy") - @BeforeClass - public static void setUpOnce() { - sContext = getInstrumentation().getTargetContext(); + @Before + public void setUp() { + mContext = getInstrumentation().getTargetContext(); System.setProperty("dexmaker.share_classloader", "true"); - sPackageManagerInternal = mock(PackageManagerInternal.class); - doReturn(new ComponentName("", "")).when(sPackageManagerInternal) + mPackageManagerInternal = mock(PackageManagerInternal.class); + doReturn(new ComponentName("", "")).when(mPackageManagerInternal) .getSystemUiServiceComponent(); // Remove stale instance of PackageManagerInternal if there is any LocalServices.removeServiceForTest(PackageManagerInternal.class); - LocalServices.addService(PackageManagerInternal.class, sPackageManagerInternal); - - sService = mock(ActivityManagerService.class); - sService.mActivityTaskManager = new ActivityTaskManagerService(sContext); - sService.mActivityTaskManager.initialize(null, null, sContext.getMainLooper()); - sService.mPackageManagerInt = sPackageManagerInternal; - sService.mAtmInternal = spy(sService.mActivityTaskManager.getAtmInternal()); - - sService.mConstants = new ActivityManagerConstants(sContext, sService, - sContext.getMainThreadHandler()); - setFieldValue(ActivityManagerService.class, sService, "mContext", - sContext); + LocalServices.addService(PackageManagerInternal.class, mPackageManagerInternal); + + mService = mock(ActivityManagerService.class); + mService.mActivityTaskManager = new ActivityTaskManagerService(mContext); + mService.mActivityTaskManager.initialize(null, null, mContext.getMainLooper()); + mService.mPackageManagerInt = mPackageManagerInternal; + mService.mAtmInternal = spy(mService.mActivityTaskManager.getAtmInternal()); + + mService.mConstants = new ActivityManagerConstants(mContext, mService, + mContext.getMainThreadHandler()); + setFieldValue(ActivityManagerService.class, mService, "mContext", + mContext); ProcessList pr = spy(new ProcessList()); - pr.mService = sService; + pr.mService = mService; AppProfiler profiler = mock(AppProfiler.class); - setFieldValue(ActivityManagerService.class, sService, "mProcessList", + setFieldValue(ActivityManagerService.class, mService, "mProcessList", pr); - setFieldValue(ActivityManagerService.class, sService, "mHandler", + setFieldValue(ActivityManagerService.class, mService, "mHandler", mock(ActivityManagerService.MainHandler.class)); - setFieldValue(ActivityManagerService.class, sService, "mProcessStats", - new ProcessStatsService(sService, new File(sContext.getFilesDir(), "procstats"))); - setFieldValue(ActivityManagerService.class, sService, "mBackupTargets", + setFieldValue(ActivityManagerService.class, mService, "mProcessStats", + new ProcessStatsService(mService, new File(mContext.getFilesDir(), "procstats"))); + setFieldValue(ActivityManagerService.class, mService, "mBackupTargets", mock(SparseArray.class)); - setFieldValue(ActivityManagerService.class, sService, "mUserController", + setFieldValue(ActivityManagerService.class, mService, "mUserController", mock(UserController.class)); - setFieldValue(ActivityManagerService.class, sService, "mAppProfiler", profiler); - setFieldValue(ActivityManagerService.class, sService, "mProcLock", + setFieldValue(ActivityManagerService.class, mService, "mAppProfiler", profiler); + setFieldValue(ActivityManagerService.class, mService, "mProcLock", new ActivityManagerProcLock()); - setFieldValue(ActivityManagerService.class, sService, "mServices", - spy(new ActiveServices(sService))); - setFieldValue(ActivityManagerService.class, sService, "mInternal", + setFieldValue(ActivityManagerService.class, mService, "mServices", + spy(new ActiveServices(mService))); + setFieldValue(ActivityManagerService.class, mService, "mInternal", mock(ActivityManagerService.LocalService.class)); - setFieldValue(ActivityManagerService.class, sService, "mBatteryStatsService", + setFieldValue(ActivityManagerService.class, mService, "mBatteryStatsService", mock(BatteryStatsService.class)); - setFieldValue(ActivityManagerService.class, sService, "mInjector", - new ActivityManagerService.Injector(sContext)); - doReturn(mock(AppOpsManager.class)).when(sService).getAppOpsManager(); - doCallRealMethod().when(sService).enqueueOomAdjTargetLocked(any(ProcessRecord.class)); - doCallRealMethod().when(sService).updateOomAdjPendingTargetsLocked(OOM_ADJ_REASON_ACTIVITY); + setFieldValue(ActivityManagerService.class, mService, "mInjector", + new ActivityManagerService.Injector(mContext)); + doReturn(mock(AppOpsManager.class)).when(mService).getAppOpsManager(); + doCallRealMethod().when(mService).enqueueOomAdjTargetLocked(any(ProcessRecord.class)); + doCallRealMethod().when(mService).updateOomAdjPendingTargetsLocked(OOM_ADJ_REASON_ACTIVITY); setFieldValue(AppProfiler.class, profiler, "mProfilerLock", new Object()); doReturn(new ActivityManagerService.ProcessChangeItem()).when(pr) .enqueueProcessChangeItemLocked(anyInt(), anyInt()); - sService.mOomAdjuster = sService.mConstants.ENABLE_NEW_OOMADJ - ? new OomAdjusterModernImpl(sService, sService.mProcessList, - new ActiveUids(sService, false)) - : new OomAdjuster(sService, sService.mProcessList, new ActiveUids(sService, false)); - sService.mOomAdjuster.mAdjSeq = 10000; - sService.mWakefulness = new AtomicInteger(PowerManagerInternal.WAKEFULNESS_AWAKE); - if (sService.mConstants.USE_TIERED_CACHED_ADJ) { + mService.mOomAdjuster = mService.mConstants.ENABLE_NEW_OOMADJ + ? new OomAdjusterModernImpl(mService, mService.mProcessList, + new ActiveUids(mService, false), mInjector) + : new OomAdjuster(mService, mService.mProcessList, new ActiveUids(mService, false), + mInjector); + mService.mOomAdjuster.mAdjSeq = 10000; + mService.mWakefulness = new AtomicInteger(PowerManagerInternal.WAKEFULNESS_AWAKE); + if (mService.mConstants.USE_TIERED_CACHED_ADJ) { sFirstCachedAdj = ProcessList.CACHED_APP_MIN_ADJ + 10; } - } - - @Before - public void setUp() { mSetFlagsRule.enableFlags(Flags.FLAG_NEW_FGS_RESTRICTION_LOGIC); } - @AfterClass - public static void tearDownOnce() { - LocalServices.removeServiceForTest(PackageManagerInternal.class); - } - @SuppressWarnings("GuardedBy") @After public void tearDown() { - sService.mOomAdjuster.resetInternal(); - sService.mOomAdjuster.mActiveUids.clear(); + mService.mOomAdjuster.resetInternal(); + mService.mOomAdjuster.mActiveUids.clear(); + LocalServices.removeServiceForTest(PackageManagerInternal.class); } private static <T> void setFieldValue(Class clazz, Object obj, String fieldName, T val) { @@ -278,7 +273,7 @@ public class MockingOomAdjusterTests { */ @SuppressWarnings("GuardedBy") private void setProcessesToLru(ProcessRecord... apps) { - ArrayList<ProcessRecord> lru = sService.mProcessList.getLruProcessesLOSP(); + ArrayList<ProcessRecord> lru = mService.mProcessList.getLruProcessesLOSP(); lru.clear(); Collections.addAll(lru, apps); } @@ -292,20 +287,20 @@ public class MockingOomAdjusterTests { @SuppressWarnings("GuardedBy") private void updateOomAdj(ProcessRecord... apps) { if (apps.length == 0) { - updateProcessRecordNodes(sService.mProcessList.getLruProcessesLOSP()); - sService.mOomAdjuster.updateOomAdjLocked(OOM_ADJ_REASON_NONE); + updateProcessRecordNodes(mService.mProcessList.getLruProcessesLOSP()); + mService.mOomAdjuster.updateOomAdjLocked(OOM_ADJ_REASON_NONE); } else { updateProcessRecordNodes(Arrays.asList(apps)); if (apps.length == 1) { final ProcessRecord app = apps[0]; - if (!sService.mProcessList.getLruProcessesLOSP().contains(app)) { - sService.mProcessList.getLruProcessesLOSP().add(app); + if (!mService.mProcessList.getLruProcessesLOSP().contains(app)) { + mService.mProcessList.getLruProcessesLOSP().add(app); } - sService.mOomAdjuster.updateOomAdjLocked(apps[0], OOM_ADJ_REASON_NONE); + mService.mOomAdjuster.updateOomAdjLocked(apps[0], OOM_ADJ_REASON_NONE); } else { setProcessesToLru(apps); - sService.mOomAdjuster.updateOomAdjLocked(OOM_ADJ_REASON_NONE); - sService.mProcessList.getLruProcessesLOSP().clear(); + mService.mOomAdjuster.updateOomAdjLocked(OOM_ADJ_REASON_NONE); + mService.mProcessList.getLruProcessesLOSP().clear(); } } } @@ -318,10 +313,10 @@ public class MockingOomAdjusterTests { private void updateOomAdjPending(ProcessRecord... apps) { setProcessesToLru(apps); for (ProcessRecord app : apps) { - sService.mOomAdjuster.enqueueOomAdjTargetLocked(app); + mService.mOomAdjuster.enqueueOomAdjTargetLocked(app); } - sService.mOomAdjuster.updateOomAdjPendingTargetsLocked(OOM_ADJ_REASON_NONE); - sService.mProcessList.getLruProcessesLOSP().clear(); + mService.mOomAdjuster.updateOomAdjPendingTargetsLocked(OOM_ADJ_REASON_NONE); + mService.mProcessList.getLruProcessesLOSP().clear(); } /** @@ -343,9 +338,9 @@ public class MockingOomAdjusterTests { MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true)); app.mState.setMaxAdj(PERSISTENT_PROC_ADJ); app.mState.setHasTopUi(true); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_ASLEEP); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_ASLEEP); updateOomAdj(app); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); assertProcStates(app, PROCESS_STATE_BOUND_FOREGROUND_SERVICE, PERSISTENT_PROC_ADJ, SCHED_GROUP_RESTRICTED); @@ -359,7 +354,7 @@ public class MockingOomAdjusterTests { MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true)); app.mState.setMaxAdj(PERSISTENT_PROC_ADJ); app.mState.setHasTopUi(true); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app); assertProcStates(app, PROCESS_STATE_PERSISTENT_UI, PERSISTENT_PROC_ADJ, @@ -372,10 +367,10 @@ public class MockingOomAdjusterTests { ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID, MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true)); app.mState.setMaxAdj(PERSISTENT_PROC_ADJ); - doReturn(app).when(sService).getTopApp(); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + doReturn(app).when(mService).getTopApp(); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app); - doReturn(null).when(sService).getTopApp(); + doReturn(null).when(mService).getTopApp(); assertProcStates(app, PROCESS_STATE_PERSISTENT_UI, PERSISTENT_PROC_ADJ, SCHED_GROUP_TOP_APP); @@ -386,11 +381,11 @@ public class MockingOomAdjusterTests { public void testUpdateOomAdj_DoOne_TopApp_Awake() { ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID, MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true)); - doReturn(PROCESS_STATE_TOP).when(sService.mAtmInternal).getTopProcessState(); - doReturn(app).when(sService).getTopApp(); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + doReturn(PROCESS_STATE_TOP).when(mService.mAtmInternal).getTopProcessState(); + doReturn(app).when(mService).getTopApp(); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app); - doReturn(null).when(sService).getTopApp(); + doReturn(null).when(mService).getTopApp(); assertProcStates(app, PROCESS_STATE_TOP, FOREGROUND_APP_ADJ, SCHED_GROUP_TOP_APP); } @@ -400,11 +395,11 @@ public class MockingOomAdjusterTests { public void testUpdateOomAdj_DoOne_RunningAnimations() { ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID, MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true)); - doReturn(PROCESS_STATE_TOP_SLEEPING).when(sService.mAtmInternal).getTopProcessState(); + doReturn(PROCESS_STATE_TOP_SLEEPING).when(mService.mAtmInternal).getTopProcessState(); app.mState.setRunningRemoteAnimation(true); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app); - doReturn(PROCESS_STATE_TOP).when(sService.mAtmInternal).getTopProcessState(); + doReturn(PROCESS_STATE_TOP).when(mService.mAtmInternal).getTopProcessState(); assertProcStates(app, PROCESS_STATE_TOP_SLEEPING, VISIBLE_APP_ADJ, SCHED_GROUP_TOP_APP); } @@ -415,7 +410,7 @@ public class MockingOomAdjusterTests { ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID, MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true)); doReturn(mock(ActiveInstrumentation.class)).when(app).getActiveInstrumentation(); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app); doCallRealMethod().when(app).getActiveInstrumentation(); @@ -429,11 +424,11 @@ public class MockingOomAdjusterTests { public void testUpdateOomAdj_DoOne_ReceivingBroadcast() { ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID, MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true)); - doReturn(true).when(sService).isReceivingBroadcastLocked(any(ProcessRecord.class), + doReturn(true).when(mService).isReceivingBroadcastLocked(any(ProcessRecord.class), any(int[].class)); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app); - doReturn(false).when(sService).isReceivingBroadcastLocked(any(ProcessRecord.class), + doReturn(false).when(mService).isReceivingBroadcastLocked(any(ProcessRecord.class), any(int[].class)); assertProcStates(app, PROCESS_STATE_RECEIVER, FOREGROUND_APP_ADJ, SCHED_GROUP_BACKGROUND); @@ -445,7 +440,7 @@ public class MockingOomAdjusterTests { ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID, MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true)); app.mServices.startExecutingService(mock(ServiceRecord.class)); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app); assertProcStates(app, PROCESS_STATE_SERVICE, FOREGROUND_APP_ADJ, SCHED_GROUP_BACKGROUND); @@ -456,13 +451,13 @@ public class MockingOomAdjusterTests { public void testUpdateOomAdj_DoOne_TopApp_Sleeping() { ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID, MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true)); - doReturn(PROCESS_STATE_TOP_SLEEPING).when(sService.mAtmInternal).getTopProcessState(); - doReturn(app).when(sService).getTopApp(); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_ASLEEP); + doReturn(PROCESS_STATE_TOP_SLEEPING).when(mService.mAtmInternal).getTopProcessState(); + doReturn(app).when(mService).getTopApp(); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_ASLEEP); updateOomAdj(app); - doReturn(null).when(sService).getTopApp(); - doReturn(PROCESS_STATE_TOP).when(sService.mAtmInternal).getTopProcessState(); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + doReturn(null).when(mService).getTopApp(); + doReturn(PROCESS_STATE_TOP).when(mService.mAtmInternal).getTopProcessState(); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); assertProcStates(app, PROCESS_STATE_TOP_SLEEPING, FOREGROUND_APP_ADJ, SCHED_GROUP_BACKGROUND); @@ -475,8 +470,8 @@ public class MockingOomAdjusterTests { MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true)); app.mState.setCurRawAdj(CACHED_APP_MIN_ADJ); app.mState.setCurAdj(CACHED_APP_MIN_ADJ); - doReturn(null).when(sService).getTopApp(); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + doReturn(null).when(mService).getTopApp(); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app); final int expectedAdj = sFirstCachedAdj; @@ -505,7 +500,7 @@ public class MockingOomAdjusterTests { return 0; })).when(wpc).computeOomAdjFromActivities( any(WindowProcessController.ComputeOomAdjCallback.class)); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app); assertProcStates(app, PROCESS_STATE_TOP, VISIBLE_APP_ADJ, SCHED_GROUP_TOP_APP); @@ -522,7 +517,7 @@ public class MockingOomAdjusterTests { WindowProcessController wpc = app.getWindowProcessController(); doReturn(true).when(wpc).hasRecentTasks(); app.mState.setLastTopTime(SystemClock.uptimeMillis()); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app); doCallRealMethod().when(wpc).hasRecentTasks(); @@ -536,7 +531,7 @@ public class MockingOomAdjusterTests { MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true)); app.mServices.setHasForegroundServices(true, ServiceInfo.FOREGROUND_SERVICE_TYPE_LOCATION, /* hasNoneType=*/false); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app); assertProcStates(app, PROCESS_STATE_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ, @@ -550,7 +545,7 @@ public class MockingOomAdjusterTests { ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID, MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true)); app.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app); assertProcStates(app, PROCESS_STATE_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ, @@ -561,10 +556,10 @@ public class MockingOomAdjusterTests { @SuppressWarnings("GuardedBy") @Test public void testUpdateOomAdj_DoOne_FgService_ShortFgs() { - sService.mConstants.TOP_TO_FGS_GRACE_DURATION = 100_000; - sService.mConstants.mShortFgsProcStateExtraWaitDuration = 200_000; + mService.mConstants.TOP_TO_FGS_GRACE_DURATION = 100_000; + mService.mConstants.mShortFgsProcStateExtraWaitDuration = 200_000; - ServiceRecord s = ServiceRecord.newEmptyInstanceForTest(sService); + ServiceRecord s = ServiceRecord.newEmptyInstanceForTest(mService); s.appInfo = new ApplicationInfo(); s.startRequested = true; s.isForeground = true; @@ -579,7 +574,7 @@ public class MockingOomAdjusterTests { app.mServices.setHasForegroundServices(true, FOREGROUND_SERVICE_TYPE_SHORT_SERVICE, /* hasNoneType=*/false); app.mState.setLastTopTime(SystemClock.uptimeMillis()); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app); @@ -596,8 +591,8 @@ public class MockingOomAdjusterTests { FOREGROUND_SERVICE_TYPE_SHORT_SERVICE, /* hasNoneType=*/false); app.mServices.startService(s); app.mState.setLastTopTime(SystemClock.uptimeMillis() - - sService.mConstants.TOP_TO_FGS_GRACE_DURATION); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + - mService.mConstants.TOP_TO_FGS_GRACE_DURATION); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app); @@ -607,14 +602,14 @@ public class MockingOomAdjusterTests { } // SHORT_SERVICE, timed out already. - s = ServiceRecord.newEmptyInstanceForTest(sService); + s = ServiceRecord.newEmptyInstanceForTest(mService); s.appInfo = new ApplicationInfo(); s.startRequested = true; s.isForeground = true; s.foregroundServiceType = FOREGROUND_SERVICE_TYPE_SHORT_SERVICE; s.setShortFgsInfo(SystemClock.uptimeMillis() - - sService.mConstants.mShortFgsTimeoutDuration - - sService.mConstants.mShortFgsProcStateExtraWaitDuration); + - mService.mConstants.mShortFgsTimeoutDuration + - mService.mConstants.mShortFgsProcStateExtraWaitDuration); { ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID, MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true)); @@ -622,8 +617,8 @@ public class MockingOomAdjusterTests { FOREGROUND_SERVICE_TYPE_SHORT_SERVICE, /* hasNoneType=*/false); app.mServices.startService(s); app.mState.setLastTopTime(SystemClock.uptimeMillis() - - sService.mConstants.TOP_TO_FGS_GRACE_DURATION); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + - mService.mConstants.TOP_TO_FGS_GRACE_DURATION); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app); @@ -639,7 +634,7 @@ public class MockingOomAdjusterTests { ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID, MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true)); app.mState.setHasOverlayUi(true); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app); assertProcStates(app, PROCESS_STATE_IMPORTANT_FOREGROUND, PERCEPTIBLE_APP_ADJ, @@ -653,12 +648,26 @@ public class MockingOomAdjusterTests { MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true)); app.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true); app.mState.setLastTopTime(SystemClock.uptimeMillis()); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app); assertProcStates(app, PROCESS_STATE_FOREGROUND_SERVICE, - PERCEPTIBLE_RECENT_FOREGROUND_APP_ADJ, SCHED_GROUP_DEFAULT); + PERCEPTIBLE_RECENT_FOREGROUND_APP_ADJ, SCHED_GROUP_DEFAULT, "fg-service-act"); assertBfsl(app); + + if (!Flags.followUpOomadjUpdates()) return; + + final ArgumentCaptor<Long> followUpTimeCaptor = ArgumentCaptor.forClass(Long.class); + verify(mService.mHandler).sendEmptyMessageAtTime( + eq(FOLLOW_UP_OOMADJUSTER_UPDATE_MSG), followUpTimeCaptor.capture()); + mInjector.jumpUptimeAheadTo(followUpTimeCaptor.getValue()); + mService.mOomAdjuster.updateOomAdjFollowUpTargetsLocked(); + + assertProcStates(app, PROCESS_STATE_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ, + SCHED_GROUP_DEFAULT, "fg-service"); + // Follow up should not have been called again. + verify(mService.mHandler).sendEmptyMessageAtTime(eq(FOLLOW_UP_OOMADJUSTER_UPDATE_MSG), + followUpTimeCaptor.capture()); } @SuppressWarnings("GuardedBy") @@ -678,12 +687,24 @@ public class MockingOomAdjusterTests { s.lastTopAlmostPerceptibleBindRequestUptimeMs = nowUptime; s.getConnections().clear(); app.mServices.updateHasTopStartedAlmostPerceptibleServices(); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app); assertEquals(PERCEPTIBLE_RECENT_FOREGROUND_APP_ADJ + 2, app.mState.getSetAdj()); - sService.mOomAdjuster.resetInternal(); + if (!Flags.followUpOomadjUpdates()) return; + + final ArgumentCaptor<Long> followUpTimeCaptor = ArgumentCaptor.forClass(Long.class); + verify(mService.mHandler).sendEmptyMessageAtTime( + eq(FOLLOW_UP_OOMADJUSTER_UPDATE_MSG), followUpTimeCaptor.capture()); + mInjector.jumpUptimeAheadTo(followUpTimeCaptor.getValue()); + mService.mOomAdjuster.updateOomAdjFollowUpTargetsLocked(); + + assertEquals(sFirstCachedAdj, app.mState.getSetAdj()); + // Follow up should not have been called again. + verify(mService.mHandler).sendEmptyMessageAtTime(eq(FOLLOW_UP_OOMADJUSTER_UPDATE_MSG), + followUpTimeCaptor.capture()); + } // Out of grace period but valid binding allows the adjustment. @@ -698,14 +719,14 @@ public class MockingOomAdjusterTests { ServiceRecord s = bindService(app, system, null, null, Context.BIND_ALMOST_PERCEPTIBLE + 2, mock(IBinder.class)); s.lastTopAlmostPerceptibleBindRequestUptimeMs = - nowUptime - 2 * sService.mConstants.mServiceBindAlmostPerceptibleTimeoutMs; + nowUptime - 2 * mService.mConstants.mServiceBindAlmostPerceptibleTimeoutMs; app.mServices.updateHasTopStartedAlmostPerceptibleServices(); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app); assertEquals(PERCEPTIBLE_RECENT_FOREGROUND_APP_ADJ + 2, app.mState.getSetAdj()); - sService.mOomAdjuster.resetInternal(); + mService.mOomAdjuster.resetInternal(); } // Out of grace period and no valid binding so no adjustment. @@ -720,15 +741,15 @@ public class MockingOomAdjusterTests { ServiceRecord s = bindService(app, system, null, null, Context.BIND_ALMOST_PERCEPTIBLE, mock(IBinder.class)); s.lastTopAlmostPerceptibleBindRequestUptimeMs = - nowUptime - 2 * sService.mConstants.mServiceBindAlmostPerceptibleTimeoutMs; + nowUptime - 2 * mService.mConstants.mServiceBindAlmostPerceptibleTimeoutMs; s.getConnections().clear(); app.mServices.updateHasTopStartedAlmostPerceptibleServices(); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app); assertNotEquals(PERCEPTIBLE_RECENT_FOREGROUND_APP_ADJ + 2, app.mState.getSetAdj()); - sService.mOomAdjuster.resetInternal(); + mService.mOomAdjuster.resetInternal(); } } @@ -744,7 +765,7 @@ public class MockingOomAdjusterTests { // Simulate the system starting and binding to a service in the app. ServiceRecord s = bindService(app, system, null, null, Context.BIND_ALMOST_PERCEPTIBLE, mock(IBinder.class)); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(system, app); assertProcStates(app, PROCESS_STATE_IMPORTANT_FOREGROUND, @@ -757,7 +778,7 @@ public class MockingOomAdjusterTests { ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID, MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true)); app.mState.setForcingToImportant(new Object()); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app); assertProcStates(app, PROCESS_STATE_TRANSIENT_BACKGROUND, PERCEPTIBLE_APP_ADJ, @@ -771,7 +792,7 @@ public class MockingOomAdjusterTests { MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true)); WindowProcessController wpc = app.getWindowProcessController(); doReturn(true).when(wpc).isHeavyWeightProcess(); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app); doReturn(false).when(wpc).isHeavyWeightProcess(); @@ -786,7 +807,7 @@ public class MockingOomAdjusterTests { MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true)); WindowProcessController wpc = app.getWindowProcessController(); doReturn(true).when(wpc).isHomeProcess(); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app); assertProcStates(app, PROCESS_STATE_HOME, HOME_APP_ADJ, SCHED_GROUP_BACKGROUND); @@ -800,11 +821,25 @@ public class MockingOomAdjusterTests { WindowProcessController wpc = app.getWindowProcessController(); doReturn(true).when(wpc).isPreviousProcess(); doReturn(true).when(wpc).hasActivities(); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app); assertProcStates(app, PROCESS_STATE_LAST_ACTIVITY, PREVIOUS_APP_ADJ, - SCHED_GROUP_BACKGROUND); + SCHED_GROUP_BACKGROUND, "previous"); + + if (!Flags.followUpOomadjUpdates()) return; + + final ArgumentCaptor<Long> followUpTimeCaptor = ArgumentCaptor.forClass(Long.class); + verify(mService.mHandler).sendEmptyMessageAtTime(eq(FOLLOW_UP_OOMADJUSTER_UPDATE_MSG), + followUpTimeCaptor.capture()); + mInjector.jumpUptimeAheadTo(followUpTimeCaptor.getValue()); + mService.mOomAdjuster.updateOomAdjFollowUpTargetsLocked(); + + assertProcStates(app, PROCESS_STATE_LAST_ACTIVITY, CACHED_APP_MIN_ADJ, + SCHED_GROUP_BACKGROUND, "previous-expired"); + // Follow up should not have been called again. + verify(mService.mHandler).sendEmptyMessageAtTime(eq(FOLLOW_UP_OOMADJUSTER_UPDATE_MSG), + followUpTimeCaptor.capture()); } @SuppressWarnings("GuardedBy") @@ -814,10 +849,10 @@ public class MockingOomAdjusterTests { MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true)); BackupRecord backupTarget = new BackupRecord(null, 0, 0, 0); backupTarget.app = app; - doReturn(backupTarget).when(sService.mBackupTargets).get(anyInt()); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + doReturn(backupTarget).when(mService.mBackupTargets).get(anyInt()); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app); - doReturn(null).when(sService.mBackupTargets).get(anyInt()); + doReturn(null).when(mService.mBackupTargets).get(anyInt()); assertProcStates(app, PROCESS_STATE_TRANSIENT_BACKGROUND, BACKUP_APP_ADJ, SCHED_GROUP_BACKGROUND); @@ -829,7 +864,7 @@ public class MockingOomAdjusterTests { ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID, MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true)); app.mServices.setHasClientActivities(true); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app); assertEquals(PROCESS_STATE_CACHED_ACTIVITY_CLIENT, app.mState.getSetProcState()); @@ -841,7 +876,7 @@ public class MockingOomAdjusterTests { ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID, MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true)); app.mServices.setTreatLikeActivity(true); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app); assertEquals(PROCESS_STATE_CACHED_ACTIVITY, app.mState.getSetProcState()); @@ -858,7 +893,7 @@ public class MockingOomAdjusterTests { s.startRequested = true; s.lastActivity = SystemClock.uptimeMillis(); app.mServices.startService(s); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app); assertProcStates(app, PROCESS_STATE_SERVICE, SERVICE_B_ADJ, SCHED_GROUP_BACKGROUND); @@ -870,7 +905,7 @@ public class MockingOomAdjusterTests { ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID, MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false)); app.mState.setMaxAdj(PERCEPTIBLE_LOW_APP_ADJ); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app); assertProcStates(app, PROCESS_STATE_CACHED_EMPTY, PERCEPTIBLE_LOW_APP_ADJ, @@ -884,8 +919,8 @@ public class MockingOomAdjusterTests { MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false)); app.mState.setCurRawAdj(SERVICE_ADJ); app.mState.setCurAdj(SERVICE_ADJ); - doReturn(null).when(sService).getTopApp(); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + doReturn(null).when(mService).getTopApp(); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app); assertTrue(ProcessList.CACHED_APP_MIN_ADJ <= app.mState.getSetAdj()); @@ -902,7 +937,7 @@ public class MockingOomAdjusterTests { s.startRequested = true; s.lastActivity = SystemClock.uptimeMillis(); app.mServices.startService(s); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app); assertProcStates(app, PROCESS_STATE_SERVICE, SERVICE_ADJ, SCHED_GROUP_BACKGROUND); @@ -918,11 +953,11 @@ public class MockingOomAdjusterTests { ServiceRecord s = bindService(app, client, null, null, Context.BIND_WAIVE_PRIORITY, mock(IBinder.class)); s.startRequested = true; - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); - doReturn(PROCESS_STATE_TOP).when(sService.mAtmInternal).getTopProcessState(); - doReturn(client).when(sService).getTopApp(); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + doReturn(PROCESS_STATE_TOP).when(mService.mAtmInternal).getTopProcessState(); + doReturn(client).when(mService).getTopApp(); updateOomAdj(client, app); - doReturn(null).when(sService).getTopApp(); + doReturn(null).when(mService).getTopApp(); assertProcStates(app, PROCESS_STATE_SERVICE, sFirstCachedAdj, SCHED_GROUP_BACKGROUND); } @@ -937,7 +972,7 @@ public class MockingOomAdjusterTests { client.mServices.setTreatLikeActivity(true); bindService(app, client, null, null, Context.BIND_WAIVE_PRIORITY | Context.BIND_TREAT_LIKE_ACTIVITY, mock(IBinder.class)); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(client, app); assertEquals(PROCESS_STATE_CACHED_ACTIVITY, app.mState.getSetProcState()); @@ -956,9 +991,9 @@ public class MockingOomAdjusterTests { ConnectionRecord cr = s.getConnections().get(binder).get(0); setFieldValue(ConnectionRecord.class, cr, "activity", mock(ActivityServiceConnectionsHolder.class)); - doReturn(client).when(sService).getTopApp(); + doReturn(client).when(mService).getTopApp(); doReturn(true).when(cr.activity).isActivityVisible(); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(client, app); assertEquals(FOREGROUND_APP_ADJ, app.mState.getSetAdj()); @@ -971,7 +1006,7 @@ public class MockingOomAdjusterTests { ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID, MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false)); bindService(app, app, null, null, 0, mock(IBinder.class)); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app); assertProcStates(app, PROCESS_STATE_CACHED_EMPTY, sFirstCachedAdj, SCHED_GROUP_BACKGROUND); @@ -986,7 +1021,7 @@ public class MockingOomAdjusterTests { MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false)); client.mServices.setTreatLikeActivity(true); bindService(app, client, null, null, 0, mock(IBinder.class)); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(client, app); assertEquals(PROCESS_STATE_CACHED_EMPTY, app.mState.getSetProcState()); @@ -1005,11 +1040,11 @@ public class MockingOomAdjusterTests { MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false)); bindService(app, client, null, null, Context.BIND_ALLOW_OOM_MANAGEMENT, mock(IBinder.class)); - doReturn(PROCESS_STATE_TOP).when(sService.mAtmInternal).getTopProcessState(); - doReturn(client).when(sService).getTopApp(); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + doReturn(PROCESS_STATE_TOP).when(mService.mAtmInternal).getTopProcessState(); + doReturn(client).when(mService).getTopApp(); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(client, app); - doReturn(null).when(sService).getTopApp(); + doReturn(null).when(mService).getTopApp(); assertEquals(PREVIOUS_APP_ADJ, app.mState.getSetAdj()); } @@ -1024,7 +1059,7 @@ public class MockingOomAdjusterTests { bindService(app, client, null, null, Context.BIND_FOREGROUND_SERVICE, mock(IBinder.class)); client.mState.setMaxAdj(PERSISTENT_PROC_ADJ); client.mState.setHasTopUi(true); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(client, app); assertProcStates(app, PROCESS_STATE_BOUND_FOREGROUND_SERVICE, VISIBLE_APP_ADJ, @@ -1041,7 +1076,7 @@ public class MockingOomAdjusterTests { MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false)); bindService(app, client, null, null, Context.BIND_IMPORTANT, mock(IBinder.class)); client.mServices.startExecutingService(mock(ServiceRecord.class)); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(client, app); assertEquals(FOREGROUND_APP_ADJ, app.mState.getSetAdj()); @@ -1056,11 +1091,11 @@ public class MockingOomAdjusterTests { ProcessRecord client = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID, MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false)); bindService(app, client, null, null, 0, mock(IBinder.class)); - doReturn(PROCESS_STATE_TOP).when(sService.mAtmInternal).getTopProcessState(); - doReturn(client).when(sService).getTopApp(); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + doReturn(PROCESS_STATE_TOP).when(mService.mAtmInternal).getTopProcessState(); + doReturn(client).when(mService).getTopApp(); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(client, app); - doReturn(null).when(sService).getTopApp(); + doReturn(null).when(mService).getTopApp(); assertProcStates(app, PROCESS_STATE_BOUND_TOP, VISIBLE_APP_ADJ, SCHED_GROUP_DEFAULT); } @@ -1074,7 +1109,7 @@ public class MockingOomAdjusterTests { MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false)); bindService(app, client, null, null, Context.BIND_FOREGROUND_SERVICE, mock(IBinder.class)); client.mState.setMaxAdj(PERSISTENT_PROC_ADJ); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(client, app); assertEquals(PROCESS_STATE_BOUND_FOREGROUND_SERVICE, app.mState.getSetProcState()); @@ -1092,7 +1127,7 @@ public class MockingOomAdjusterTests { MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false)); bindService(app, client, null, null, Context.BIND_NOT_FOREGROUND, mock(IBinder.class)); client.mState.setMaxAdj(PERSISTENT_PROC_ADJ); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(client, app); assertEquals(PROCESS_STATE_TRANSIENT_BACKGROUND, app.mState.getSetProcState()); @@ -1108,7 +1143,7 @@ public class MockingOomAdjusterTests { MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false)); bindService(app, client, null, null, 0, mock(IBinder.class)); client.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(client, app); assertEquals(PROCESS_STATE_FOREGROUND_SERVICE, client.mState.getSetProcState()); @@ -1128,7 +1163,7 @@ public class MockingOomAdjusterTests { bindService(app, client, null, null, 0, mock(IBinder.class)); // In order to trick OomAdjuster to think it has a short-service, we need this logic. - ServiceRecord s = ServiceRecord.newEmptyInstanceForTest(sService); + ServiceRecord s = ServiceRecord.newEmptyInstanceForTest(mService); s.appInfo = new ApplicationInfo(); s.startRequested = true; s.isForeground = true; @@ -1139,7 +1174,7 @@ public class MockingOomAdjusterTests { client.mServices.setHasForegroundServices(true, FOREGROUND_SERVICE_TYPE_SHORT_SERVICE, /* hasNoneType=*/false); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(client, app); // Client only has a SHORT_FGS, so it doesn't have BFSL, and that's propagated. @@ -1159,7 +1194,7 @@ public class MockingOomAdjusterTests { MOCKAPP3_PROCESSNAME, MOCKAPP3_PACKAGENAME, false)); // In order to trick OomAdjuster to think it has a short-service, we need this logic. - ServiceRecord s = ServiceRecord.newEmptyInstanceForTest(sService); + ServiceRecord s = ServiceRecord.newEmptyInstanceForTest(mService); s.appInfo = new ApplicationInfo(); s.startRequested = true; s.isForeground = true; @@ -1170,7 +1205,7 @@ public class MockingOomAdjusterTests { app2.mServices.setHasForegroundServices(true, FOREGROUND_SERVICE_TYPE_SHORT_SERVICE, /* hasNoneType=*/false); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app2); // Client only has a SHORT_FGS, so it doesn't have BFSL, and that's propagated. @@ -1211,11 +1246,11 @@ public class MockingOomAdjusterTests { bindService(app, client, null, null, Context.BIND_ABOVE_CLIENT, mock(IBinder.class)); BackupRecord backupTarget = new BackupRecord(null, 0, 0, 0); backupTarget.app = client; - doReturn(backupTarget).when(sService.mBackupTargets).get(anyInt()); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + doReturn(backupTarget).when(mService.mBackupTargets).get(anyInt()); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(client, app); - doReturn(null).when(sService.mBackupTargets).get(anyInt()); + doReturn(null).when(mService.mBackupTargets).get(anyInt()); assertEquals(BACKUP_APP_ADJ, app.mState.getSetAdj()); assertNoBfsl(app); @@ -1236,7 +1271,7 @@ public class MockingOomAdjusterTests { MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false)); bindService(app, client, null, null, Context.BIND_NOT_PERCEPTIBLE, mock(IBinder.class)); client.mState.setRunningRemoteAnimation(true); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(client, app); assertEquals(PERCEPTIBLE_LOW_APP_ADJ, app.mState.getSetAdj()); @@ -1251,7 +1286,7 @@ public class MockingOomAdjusterTests { MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false)); bindService(app, client, null, null, Context.BIND_NOT_VISIBLE, mock(IBinder.class)); client.mState.setRunningRemoteAnimation(true); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(client, app); assertEquals(PERCEPTIBLE_APP_ADJ, app.mState.getSetAdj()); @@ -1266,7 +1301,7 @@ public class MockingOomAdjusterTests { MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false)); bindService(app, client, null, null, 0, mock(IBinder.class)); client.mState.setHasOverlayUi(true); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(client, app); assertEquals(PERCEPTIBLE_APP_ADJ, app.mState.getSetAdj()); @@ -1284,12 +1319,12 @@ public class MockingOomAdjusterTests { Context.BIND_ALMOST_PERCEPTIBLE | Context.BIND_NOT_FOREGROUND, mock(IBinder.class)); client.mState.setMaxAdj(PERSISTENT_PROC_ADJ); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(client, app); assertEquals(PERCEPTIBLE_MEDIUM_APP_ADJ + 2, app.mState.getSetAdj()); - sService.mOomAdjuster.resetInternal(); + mService.mOomAdjuster.resetInternal(); } { @@ -1303,13 +1338,13 @@ public class MockingOomAdjusterTests { Context.BIND_ALMOST_PERCEPTIBLE | Context.BIND_NOT_FOREGROUND, mock(IBinder.class)); client.mState.setMaxAdj(PERSISTENT_PROC_ADJ); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(client, app); doReturn(false).when(wpc).isHeavyWeightProcess(); assertEquals(PERCEPTIBLE_MEDIUM_APP_ADJ + 2, app.mState.getSetAdj()); - sService.mOomAdjuster.resetInternal(); + mService.mOomAdjuster.resetInternal(); } { @@ -1321,12 +1356,12 @@ public class MockingOomAdjusterTests { Context.BIND_ALMOST_PERCEPTIBLE, mock(IBinder.class)); client.mState.setMaxAdj(PERSISTENT_PROC_ADJ); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(client, app); assertEquals(PERCEPTIBLE_APP_ADJ + 1, app.mState.getSetAdj()); - sService.mOomAdjuster.resetInternal(); + mService.mOomAdjuster.resetInternal(); } { @@ -1340,13 +1375,13 @@ public class MockingOomAdjusterTests { Context.BIND_ALMOST_PERCEPTIBLE, mock(IBinder.class)); client.mState.setMaxAdj(PERSISTENT_PROC_ADJ); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(client, app); doReturn(false).when(wpc).isHeavyWeightProcess(); assertEquals(PERCEPTIBLE_APP_ADJ + 1, app.mState.getSetAdj()); - sService.mOomAdjuster.resetInternal(); + mService.mOomAdjuster.resetInternal(); } } @@ -1359,7 +1394,7 @@ public class MockingOomAdjusterTests { MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false)); bindService(app, client, null, null, 0, mock(IBinder.class)); client.mState.setRunningRemoteAnimation(true); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(client, app); assertEquals(VISIBLE_APP_ADJ, app.mState.getSetAdj()); @@ -1375,7 +1410,7 @@ public class MockingOomAdjusterTests { bindService(app, client, null, null, Context.BIND_IMPORTANT_BACKGROUND, mock(IBinder.class)); client.mState.setHasOverlayUi(true); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(client, app); assertEquals(PROCESS_STATE_IMPORTANT_BACKGROUND, app.mState.getSetProcState()); @@ -1402,7 +1437,7 @@ public class MockingOomAdjusterTests { MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false)); bindProvider(app, client, null, null, false); client.mServices.setTreatLikeActivity(true); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app, client); assertProcStates(app, PROCESS_STATE_CACHED_EMPTY, sFirstCachedAdj, SCHED_GROUP_BACKGROUND); @@ -1416,11 +1451,11 @@ public class MockingOomAdjusterTests { ProcessRecord client = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID, MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false)); bindProvider(app, client, null, null, false); - doReturn(PROCESS_STATE_TOP).when(sService.mAtmInternal).getTopProcessState(); - doReturn(client).when(sService).getTopApp(); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + doReturn(PROCESS_STATE_TOP).when(mService.mAtmInternal).getTopProcessState(); + doReturn(client).when(mService).getTopApp(); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(client, app); - doReturn(null).when(sService).getTopApp(); + doReturn(null).when(mService).getTopApp(); assertProcStates(app, PROCESS_STATE_BOUND_TOP, FOREGROUND_APP_ADJ, SCHED_GROUP_DEFAULT); } @@ -1434,7 +1469,7 @@ public class MockingOomAdjusterTests { MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false)); client.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true); bindProvider(app, client, null, null, false); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(client, app); assertProcStates(app, PROCESS_STATE_BOUND_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ, @@ -1452,7 +1487,7 @@ public class MockingOomAdjusterTests { MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false)); // In order to trick OomAdjuster to think it has a short-service, we need this logic. - ServiceRecord s = ServiceRecord.newEmptyInstanceForTest(sService); + ServiceRecord s = ServiceRecord.newEmptyInstanceForTest(mService); s.appInfo = new ApplicationInfo(); s.startRequested = true; s.isForeground = true; @@ -1464,7 +1499,7 @@ public class MockingOomAdjusterTests { client.mServices.setHasForegroundServices(true, FOREGROUND_SERVICE_TYPE_SHORT_SERVICE, /* hasNoneType=*/false); bindProvider(app, client, null, null, false); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(client, app); // Client only has a SHORT_FGS, so it doesn't have BFSL, and that's propagated. @@ -1486,7 +1521,7 @@ public class MockingOomAdjusterTests { ProcessRecord client = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID, MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false)); bindProvider(app, client, null, null, true); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(client, app); assertProcStates(app, PROCESS_STATE_IMPORTANT_FOREGROUND, FOREGROUND_APP_ADJ, @@ -1499,11 +1534,25 @@ public class MockingOomAdjusterTests { ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID, MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false)); app.mProviders.setLastProviderTime(SystemClock.uptimeMillis()); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app); assertProcStates(app, PROCESS_STATE_LAST_ACTIVITY, PREVIOUS_APP_ADJ, - SCHED_GROUP_BACKGROUND); + SCHED_GROUP_BACKGROUND, "recent-provider"); + + if (!Flags.followUpOomadjUpdates()) return; + + final ArgumentCaptor<Long> followUpTimeCaptor = ArgumentCaptor.forClass(Long.class); + verify(mService.mHandler).sendEmptyMessageAtTime(eq(FOLLOW_UP_OOMADJUSTER_UPDATE_MSG), + followUpTimeCaptor.capture()); + mInjector.jumpUptimeAheadTo(followUpTimeCaptor.getValue()); + mService.mOomAdjuster.updateOomAdjFollowUpTargetsLocked(); + + assertProcStates(app, PROCESS_STATE_CACHED_EMPTY, sFirstCachedAdj, SCHED_GROUP_BACKGROUND, + "cch-empty"); + // Follow up should not have been called again. + verify(mService.mHandler).sendEmptyMessageAtTime(eq(FOLLOW_UP_OOMADJUSTER_UPDATE_MSG), + followUpTimeCaptor.capture()); } @SuppressWarnings("GuardedBy") @@ -1517,11 +1566,11 @@ public class MockingOomAdjusterTests { ProcessRecord client2 = spy(makeDefaultProcessRecord(MOCKAPP3_PID, MOCKAPP3_UID, MOCKAPP3_PROCESSNAME, MOCKAPP3_PACKAGENAME, false)); bindService(client, client2, null, null, 0, mock(IBinder.class)); - doReturn(PROCESS_STATE_TOP).when(sService.mAtmInternal).getTopProcessState(); - doReturn(client2).when(sService).getTopApp(); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + doReturn(PROCESS_STATE_TOP).when(mService.mAtmInternal).getTopProcessState(); + doReturn(client2).when(mService).getTopApp(); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(client, client2, app); - doReturn(null).when(sService).getTopApp(); + doReturn(null).when(mService).getTopApp(); assertProcStates(app, PROCESS_STATE_BOUND_TOP, VISIBLE_APP_ADJ, SCHED_GROUP_DEFAULT); @@ -1539,7 +1588,7 @@ public class MockingOomAdjusterTests { MOCKAPP3_PROCESSNAME, MOCKAPP3_PACKAGENAME, false)); bindService(app, client2, null, null, 0, mock(IBinder.class)); client2.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(client, client2, app); assertProcStates(app, PROCESS_STATE_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ, @@ -1559,7 +1608,7 @@ public class MockingOomAdjusterTests { MOCKAPP3_PROCESSNAME, MOCKAPP3_PACKAGENAME, false)); bindService(client, client2, null, null, 0, mock(IBinder.class)); client2.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(client, client2, app); assertProcStates(app, PROCESS_STATE_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ, @@ -1584,7 +1633,7 @@ public class MockingOomAdjusterTests { // Note: We add processes to LRU but still call updateOomAdjLocked() with a specific // processes. setProcessesToLru(app, client, client2); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app); assertProcStates(app, PROCESS_STATE_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ, @@ -1598,7 +1647,7 @@ public class MockingOomAdjusterTests { assertBfsl(client2); client2.mServices.setHasForegroundServices(false, 0, /* hasNoneType=*/false); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(client2); assertEquals(PROCESS_STATE_CACHED_EMPTY, client2.mState.getSetProcState()); @@ -1622,7 +1671,7 @@ public class MockingOomAdjusterTests { MOCKAPP3_PROCESSNAME, MOCKAPP3_PACKAGENAME, false)); bindService(client2, client, null, null, 0, mock(IBinder.class)); client.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app, client, client2); assertProcStates(app, PROCESS_STATE_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ, @@ -1649,7 +1698,7 @@ public class MockingOomAdjusterTests { bindService(client, client2, null, null, 0, mock(IBinder.class)); bindService(client2, client, null, null, 0, mock(IBinder.class)); client.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app, client, client2); assertProcStates(app, PROCESS_STATE_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ, @@ -1683,7 +1732,7 @@ public class MockingOomAdjusterTests { bindService(client3, client4, null, null, 0, mock(IBinder.class)); bindService(client4, client3, null, null, 0, mock(IBinder.class)); client.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app, client, client2, client3, client4); assertProcStates(app, PROCESS_STATE_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ, @@ -1720,7 +1769,7 @@ public class MockingOomAdjusterTests { MOCKAPP4_PROCESSNAME, MOCKAPP4_PACKAGENAME, false)); client3.mState.setForcingToImportant(new Object()); bindService(app, client3, null, null, 0, mock(IBinder.class)); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app, client, client2, client3); assertProcStates(app, PROCESS_STATE_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ, @@ -1746,7 +1795,7 @@ public class MockingOomAdjusterTests { MOCKAPP4_PROCESSNAME, MOCKAPP4_PACKAGENAME, false)); client3.mState.setForcingToImportant(new Object()); bindService(app, client3, null, null, 0, mock(IBinder.class)); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app, client, client2, client3); assertProcStates(app, PROCESS_STATE_TRANSIENT_BACKGROUND, PERCEPTIBLE_APP_ADJ, @@ -1773,7 +1822,7 @@ public class MockingOomAdjusterTests { MOCKAPP5_PROCESSNAME, MOCKAPP5_PACKAGENAME, false)); client4.mState.setForcingToImportant(new Object()); bindService(app, client4, null, null, 0, mock(IBinder.class)); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app, client, client2, client3, client4); assertProcStates(app, PROCESS_STATE_TRANSIENT_BACKGROUND, PERCEPTIBLE_APP_ADJ, @@ -1802,7 +1851,7 @@ public class MockingOomAdjusterTests { MOCKAPP5_PROCESSNAME, MOCKAPP5_PACKAGENAME, false)); client4.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true); bindService(app, client4, null, null, 0, mock(IBinder.class)); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app, client, client2, client3, client4); assertProcStates(app, PROCESS_STATE_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ, @@ -1828,7 +1877,7 @@ public class MockingOomAdjusterTests { MOCKAPP4_PROCESSNAME, MOCKAPP4_PACKAGENAME, false)); client3.mState.setForcingToImportant(new Object()); bindService(app, client3, null, null, 0, mock(IBinder.class)); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(client, client2, client3, app); assertProcStates(app, PROCESS_STATE_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ, @@ -1848,7 +1897,7 @@ public class MockingOomAdjusterTests { MOCKAPP3_PROCESSNAME, MOCKAPP3_PACKAGENAME, false)); bindProvider(client, client2, null, null, false); client2.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(client, client2, app); assertProcStates(app, PROCESS_STATE_BOUND_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ, @@ -1869,7 +1918,7 @@ public class MockingOomAdjusterTests { bindProvider(client, client2, null, null, false); client2.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true); bindService(client2, app, null, null, 0, mock(IBinder.class)); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app, client, client2); assertProcStates(app, PROCESS_STATE_BOUND_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ, @@ -1889,7 +1938,7 @@ public class MockingOomAdjusterTests { MOCKAPP3_PROCESSNAME, MOCKAPP3_PACKAGENAME, false)); bindProvider(client, client2, null, null, false); client2.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(client, client2, app); assertProcStates(app, PROCESS_STATE_BOUND_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ, @@ -1910,7 +1959,7 @@ public class MockingOomAdjusterTests { bindProvider(client, client2, null, null, false); client2.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true); bindProvider(client2, app, null, null, false); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app, client, client2); assertProcStates(app, PROCESS_STATE_BOUND_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ, @@ -1936,7 +1985,7 @@ public class MockingOomAdjusterTests { client1.mState.setMaxAdj(PERSISTENT_PROC_ADJ); client2.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(client1, client2, app1, app2); assertProcStates(app1, PROCESS_STATE_BOUND_FOREGROUND_SERVICE, VISIBLE_APP_ADJ, @@ -1957,7 +2006,7 @@ public class MockingOomAdjusterTests { assertProcStates(app2, PROCESS_STATE_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ, SCHED_GROUP_DEFAULT); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_ASLEEP); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_ASLEEP); updateOomAdj(client1, client2, app1, app2); assertProcStates(app1, PROCESS_STATE_IMPORTANT_FOREGROUND, VISIBLE_APP_ADJ, SCHED_GROUP_TOP_APP); @@ -2028,9 +2077,9 @@ public class MockingOomAdjusterTests { SCHED_GROUP_DEFAULT); client2.mState.setHasOverlayUi(false); - doReturn(PROCESS_STATE_TOP).when(sService.mAtmInternal).getTopProcessState(); - doReturn(client2).when(sService).getTopApp(); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + doReturn(PROCESS_STATE_TOP).when(mService.mAtmInternal).getTopProcessState(); + doReturn(client2).when(mService).getTopApp(); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(client2, app2); assertProcStates(app2, PROCESS_STATE_BOUND_TOP, VISIBLE_APP_ADJ, @@ -2047,7 +2096,7 @@ public class MockingOomAdjusterTests { client1.mState.setMaxAdj(PERSISTENT_PROC_ADJ); app1.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); bindService(app1, client1, null, null, Context.BIND_NOT_PERCEPTIBLE, mock(IBinder.class)); @@ -2068,7 +2117,7 @@ public class MockingOomAdjusterTests { client1.mState.setMaxAdj(PERSISTENT_PROC_ADJ); app1.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); bindService(app1, client1, null, null, Context.BIND_ALMOST_PERCEPTIBLE, mock(IBinder.class)); @@ -2088,7 +2137,7 @@ public class MockingOomAdjusterTests { app.setPendingFinishAttach(true); app.mState.setHasForegroundActivities(false); - sService.mOomAdjuster.setAttachingProcessStatesLSP(app); + mService.mOomAdjuster.setAttachingProcessStatesLSP(app); updateOomAdj(app); assertProcStates(app, PROCESS_STATE_CACHED_EMPTY, FOREGROUND_APP_ADJ, @@ -2102,9 +2151,9 @@ public class MockingOomAdjusterTests { MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false)); app.setPendingFinishAttach(true); app.mState.setHasForegroundActivities(true); - doReturn(app).when(sService).getTopApp(); + doReturn(app).when(mService).getTopApp(); - sService.mOomAdjuster.setAttachingProcessStatesLSP(app); + mService.mOomAdjuster.setAttachingProcessStatesLSP(app); updateOomAdj(app); assertProcStates(app, PROCESS_STATE_TOP, FOREGROUND_APP_ADJ, @@ -2124,10 +2173,10 @@ public class MockingOomAdjusterTests { MOCKAPP4_PROCESSNAME, MOCKAPP3_PACKAGENAME, false)); final ProcessRecord app3 = spy(makeDefaultProcessRecord(MOCKAPP5_PID, MOCKAPP5_UID, MOCKAPP5_PROCESSNAME, MOCKAPP5_PACKAGENAME, false)); - final UidRecord app1UidRecord = new UidRecord(MOCKAPP_UID, sService); - final UidRecord app2UidRecord = new UidRecord(MOCKAPP2_UID, sService); - final UidRecord app3UidRecord = new UidRecord(MOCKAPP5_UID, sService); - final UidRecord clientUidRecord = new UidRecord(MOCKAPP3_UID, sService); + final UidRecord app1UidRecord = new UidRecord(MOCKAPP_UID, mService); + final UidRecord app2UidRecord = new UidRecord(MOCKAPP2_UID, mService); + final UidRecord app3UidRecord = new UidRecord(MOCKAPP5_UID, mService); + final UidRecord clientUidRecord = new UidRecord(MOCKAPP3_UID, mService); app1.setUidRecord(app1UidRecord); app2.setUidRecord(app2UidRecord); app3.setUidRecord(app3UidRecord); @@ -2137,7 +2186,7 @@ public class MockingOomAdjusterTests { client1.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true); client2.mState.setForcingToImportant(new Object()); setProcessesToLru(app1, app2, app3, client1, client2); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); final ComponentName cn1 = ComponentName.unflattenFromString( MOCKAPP_PACKAGENAME + "/.TestService"); @@ -2164,10 +2213,10 @@ public class MockingOomAdjusterTests { c2s.startRequested = true; try { - sService.mOomAdjuster.mActiveUids.put(MOCKAPP_UID, app1UidRecord); - sService.mOomAdjuster.mActiveUids.put(MOCKAPP2_UID, app2UidRecord); - sService.mOomAdjuster.mActiveUids.put(MOCKAPP5_UID, app3UidRecord); - sService.mOomAdjuster.mActiveUids.put(MOCKAPP3_UID, clientUidRecord); + mService.mOomAdjuster.mActiveUids.put(MOCKAPP_UID, app1UidRecord); + mService.mOomAdjuster.mActiveUids.put(MOCKAPP2_UID, app2UidRecord); + mService.mOomAdjuster.mActiveUids.put(MOCKAPP5_UID, app3UidRecord); + mService.mOomAdjuster.mActiveUids.put(MOCKAPP3_UID, clientUidRecord); setServiceMap(s1, MOCKAPP_UID, cn1); setServiceMap(s2, MOCKAPP2_UID, cn2); @@ -2195,10 +2244,10 @@ public class MockingOomAdjusterTests { app2UidRecord.setIdle(true); app3UidRecord.setIdle(true); clientUidRecord.setIdle(true); - doReturn(ActivityManager.APP_START_MODE_DELAYED).when(sService) + doReturn(ActivityManager.APP_START_MODE_DELAYED).when(mService) .getAppStartModeLOSP(anyInt(), any(String.class), anyInt(), anyInt(), anyBoolean(), anyBoolean(), anyBoolean()); - doNothing().when(sService.mServices) + doNothing().when(mService.mServices) .scheduleServiceTimeoutLocked(any(ProcessRecord.class)); updateOomAdj(client1, client2, app1, app2, app3); @@ -2206,11 +2255,11 @@ public class MockingOomAdjusterTests { assertEquals(PROCESS_STATE_SERVICE, app1.mState.getSetProcState()); assertEquals(PROCESS_STATE_SERVICE, client2.mState.getSetProcState()); } finally { - doCallRealMethod().when(sService) + doCallRealMethod().when(mService) .getAppStartModeLOSP(anyInt(), any(String.class), anyInt(), anyInt(), anyBoolean(), anyBoolean(), anyBoolean()); - sService.mServices.mServiceMap.clear(); - sService.mOomAdjuster.mActiveUids.clear(); + mService.mServices.mServiceMap.clear(); + mService.mOomAdjuster.mActiveUids.clear(); } } @@ -2224,7 +2273,7 @@ public class MockingOomAdjusterTests { MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false)); app2.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app, app2); assertProcStates(app, PROCESS_STATE_TRANSIENT_BACKGROUND, PERCEPTIBLE_APP_ADJ, @@ -2244,7 +2293,7 @@ public class MockingOomAdjusterTests { MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false)); app2.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true); bindService(app, app2, null, null, 0, mock(IBinder.class)); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app, app2); assertProcStates(app, PROCESS_STATE_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ, @@ -2268,7 +2317,7 @@ public class MockingOomAdjusterTests { bindService(app2, app3, null, null, 0, mock(IBinder.class)); app3.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true); bindService(app3, app, null, null, 0, mock(IBinder.class)); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app, app2, app3); assertProcStates(app, PROCESS_STATE_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ, @@ -2313,7 +2362,7 @@ public class MockingOomAdjusterTests { MOCKAPP5_PROCESSNAME, MOCKAPP5_PACKAGENAME, false)); app5.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true); bindService(app, app5, null, s, 0, mock(IBinder.class)); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app, app2, app3, app4, app5); assertProcStates(app, PROCESS_STATE_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ, @@ -2355,7 +2404,7 @@ public class MockingOomAdjusterTests { MOCKAPP5_PROCESSNAME, MOCKAPP5_PACKAGENAME, false)); app5.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true); bindService(app, app5, null, s, 0, mock(IBinder.class)); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app5, app4, app3, app2, app); assertProcStates(app, PROCESS_STATE_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ, @@ -2397,7 +2446,7 @@ public class MockingOomAdjusterTests { MOCKAPP5_PROCESSNAME, MOCKAPP5_PACKAGENAME, false)); app5.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true); bindService(app, app5, null, s, 0, mock(IBinder.class)); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app3, app4, app2, app, app5); assertProcStates(app, PROCESS_STATE_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ, @@ -2437,7 +2486,7 @@ public class MockingOomAdjusterTests { client3.mState.setMaxAdj(PERSISTENT_PROC_ADJ); bindService(app, client3, null, null, Context.BIND_INCLUDE_CAPABILITIES, mock(IBinder.class)); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app, client, client2, client3); final int expected = PROCESS_CAPABILITY_ALL & ~PROCESS_CAPABILITY_BFSL; @@ -2468,7 +2517,7 @@ public class MockingOomAdjusterTests { MOCKAPP5_PROCESSNAME, MOCKAPP5_PACKAGENAME, false)); app5.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true); bindProvider(app, app5, cr, null, false); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app, app2, app3, app4, app5); assertProcStates(app, PROCESS_STATE_BOUND_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ, @@ -2512,8 +2561,8 @@ public class MockingOomAdjusterTests { doCallRealMethod().when(s).getConnections(); s.startRequested = true; s.lastActivity = now; - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); - sService.mOomAdjuster.mNumServiceProcs = 3; + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mOomAdjuster.mNumServiceProcs = 3; updateOomAdj(app3, app2, app); assertEquals(SERVICE_B_ADJ, app3.mState.getSetAdj()); @@ -2530,15 +2579,15 @@ public class MockingOomAdjusterTests { MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false)); final int userOwner = 0; final int userOther = 1; - final int cachedAdj1 = sService.mConstants.USE_TIERED_CACHED_ADJ + final int cachedAdj1 = mService.mConstants.USE_TIERED_CACHED_ADJ ? CACHED_APP_MIN_ADJ + 10 : CACHED_APP_MIN_ADJ + ProcessList.CACHED_APP_IMPORTANCE_LEVELS; - final int cachedAdj2 = sService.mConstants.USE_TIERED_CACHED_ADJ + final int cachedAdj2 = mService.mConstants.USE_TIERED_CACHED_ADJ ? CACHED_APP_MIN_ADJ + 10 : cachedAdj1 + ProcessList.CACHED_APP_IMPORTANCE_LEVELS * 2; - doReturn(userOwner).when(sService.mUserController).getCurrentUserId(); + doReturn(userOwner).when(mService.mUserController).getCurrentUserId(); - final ArrayList<ProcessRecord> lru = sService.mProcessList.getLruProcessesLOSP(); + final ArrayList<ProcessRecord> lru = mService.mProcessList.getLruProcessesLOSP(); lru.clear(); lru.add(app2); lru.add(app); @@ -2549,10 +2598,10 @@ public class MockingOomAdjusterTests { MOCKAPP2_PACKAGENAME + "/.TestService"); final long now = SystemClock.uptimeMillis(); - sService.mConstants.KEEP_WARMING_SERVICES.clear(); + mService.mConstants.KEEP_WARMING_SERVICES.clear(); final ServiceInfo si = mock(ServiceInfo.class); si.applicationInfo = mock(ApplicationInfo.class); - ServiceRecord s = spy(new ServiceRecord(sService, cn, cn, null, 0, null, + ServiceRecord s = spy(new ServiceRecord(mService, cn, cn, null, 0, null, si, false, null)); doReturn(new ArrayMap<IBinder, ArrayList<ConnectionRecord>>()).when(s).getConnections(); s.startRequested = true; @@ -2564,16 +2613,16 @@ public class MockingOomAdjusterTests { final ServiceInfo si2 = mock(ServiceInfo.class); si2.applicationInfo = mock(ApplicationInfo.class); si2.applicationInfo.uid = MOCKAPP2_UID_OTHER; - ServiceRecord s2 = spy(new ServiceRecord(sService, cn2, cn2, null, 0, null, + ServiceRecord s2 = spy(new ServiceRecord(mService, cn2, cn2, null, 0, null, si2, false, null)); doReturn(new ArrayMap<IBinder, ArrayList<ConnectionRecord>>()).when(s2).getConnections(); s2.startRequested = true; - s2.lastActivity = now - sService.mConstants.MAX_SERVICE_INACTIVITY - 1; + s2.lastActivity = now - mService.mConstants.MAX_SERVICE_INACTIVITY - 1; app2.mServices.startService(s2); app2.mState.setHasShownUi(false); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(); assertProcStates(app, true, PROCESS_STATE_SERVICE, cachedAdj1, "cch-started-ui-services"); @@ -2590,7 +2639,7 @@ public class MockingOomAdjusterTests { app.mState.setSetProcState(PROCESS_STATE_NONEXISTENT); app.mState.setAdjType(null); app.mState.setSetAdj(UNKNOWN_ADJ); - s.lastActivity = now - sService.mConstants.MAX_SERVICE_INACTIVITY - 1; + s.lastActivity = now - mService.mConstants.MAX_SERVICE_INACTIVITY - 1; updateOomAdj(); assertProcStates(app, true, PROCESS_STATE_SERVICE, cachedAdj1, "cch-started-services"); @@ -2600,9 +2649,9 @@ public class MockingOomAdjusterTests { app.mState.setAdjType(null); app.mState.setSetAdj(UNKNOWN_ADJ); app.mState.setHasShownUi(true); - sService.mConstants.KEEP_WARMING_SERVICES.add(cn); - sService.mConstants.KEEP_WARMING_SERVICES.add(cn2); - s = spy(new ServiceRecord(sService, cn, cn, null, 0, null, + mService.mConstants.KEEP_WARMING_SERVICES.add(cn); + mService.mConstants.KEEP_WARMING_SERVICES.add(cn2); + s = spy(new ServiceRecord(mService, cn, cn, null, 0, null, si, false, null)); doReturn(new ArrayMap<IBinder, ArrayList<ConnectionRecord>>()).when(s).getConnections(); s.startRequested = true; @@ -2618,14 +2667,14 @@ public class MockingOomAdjusterTests { app.mState.setAdjType(null); app.mState.setSetAdj(UNKNOWN_ADJ); app.mState.setHasShownUi(false); - s.lastActivity = now - sService.mConstants.MAX_SERVICE_INACTIVITY - 1; + s.lastActivity = now - mService.mConstants.MAX_SERVICE_INACTIVITY - 1; updateOomAdj(); assertProcStates(app, false, PROCESS_STATE_SERVICE, SERVICE_ADJ, "started-services"); assertProcStates(app2, true, PROCESS_STATE_SERVICE, cachedAdj1, "cch-started-services"); - doReturn(userOther).when(sService.mUserController).getCurrentUserId(); - sService.mOomAdjuster.handleUserSwitchedLocked(); + doReturn(userOther).when(mService.mUserController).getCurrentUserId(); + mService.mOomAdjuster.handleUserSwitchedLocked(); updateOomAdj(); assertProcStates(app, true, PROCESS_STATE_SERVICE, cachedAdj1, "cch-started-services"); @@ -2637,9 +2686,9 @@ public class MockingOomAdjusterTests { public void testUpdateOomAdj_DoOne_AboveClient_SameProcess() { ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID, MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true)); - doReturn(PROCESS_STATE_TOP).when(sService.mAtmInternal).getTopProcessState(); - doReturn(app).when(sService).getTopApp(); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + doReturn(PROCESS_STATE_TOP).when(mService.mAtmInternal).getTopProcessState(); + doReturn(app).when(mService).getTopApp(); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app); assertEquals(FOREGROUND_APP_ADJ, app.mState.getSetAdj()); @@ -2672,8 +2721,8 @@ public class MockingOomAdjusterTests { s = bindService(app3, app2, null, null, 0, mock(IBinder.class)); s.lastActivity = now; - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); - sService.mOomAdjuster.mNumServiceProcs = 3; + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mOomAdjuster.mNumServiceProcs = 3; updateOomAdj(app, app2, app3); assertEquals(SERVICE_ADJ, app.mState.getSetAdj()); @@ -2688,9 +2737,9 @@ public class MockingOomAdjusterTests { public void testUpdateOomAdj_DoOne_AboveClient_NotStarted() { ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID, MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true)); - doReturn(PROCESS_STATE_TOP).when(sService.mAtmInternal).getTopProcessState(); - doReturn(app).when(sService).getTopApp(); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + doReturn(PROCESS_STATE_TOP).when(mService.mAtmInternal).getTopProcessState(); + doReturn(app).when(mService).getTopApp(); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app); assertEquals(FOREGROUND_APP_ADJ, app.mState.getSetAdj()); @@ -2718,7 +2767,7 @@ public class MockingOomAdjusterTests { ServiceRecord s = makeServiceRecord(app); s.startRequested = true; s.lastActivity = SystemClock.uptimeMillis(); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(); assertProcStates(app, PROCESS_STATE_SERVICE, SERVICE_ADJ, SCHED_GROUP_BACKGROUND); @@ -2738,7 +2787,7 @@ public class MockingOomAdjusterTests { ServiceRecord s = makeServiceRecord(app); s.startRequested = true; s.lastActivity = SystemClock.uptimeMillis(); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdjPending(app); assertProcStates(app, PROCESS_STATE_SERVICE, SERVICE_ADJ, SCHED_GROUP_BACKGROUND); @@ -2760,7 +2809,7 @@ public class MockingOomAdjusterTests { ServiceRecord s = makeServiceRecord(app); s.startRequested = true; s.lastActivity = SystemClock.uptimeMillis(); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(); assertProcStates(app, PROCESS_STATE_SERVICE, SERVICE_ADJ, SCHED_GROUP_BACKGROUND); @@ -2781,7 +2830,7 @@ public class MockingOomAdjusterTests { ServiceRecord s = makeServiceRecord(app); s.startRequested = true; s.lastActivity = SystemClock.uptimeMillis(); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdjPending(app); assertProcStates(app, PROCESS_STATE_SERVICE, SERVICE_ADJ, SCHED_GROUP_BACKGROUND); @@ -2809,7 +2858,7 @@ public class MockingOomAdjusterTests { client.mState.setMaxAdj(PERSISTENT_PROC_ADJ); attributedClient.mServices.setHasForegroundServices(true, 0, true); bindService(sandboxService, client, attributedClient, null, 0, mock(IBinder.class)); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(); assertProcStates(client, PROCESS_STATE_PERSISTENT, PERSISTENT_PROC_ADJ, SCHED_GROUP_DEFAULT); @@ -2830,13 +2879,13 @@ public class MockingOomAdjusterTests { // App1 binds to app2 and gets temp allowlisted. bindService(app2, app, null, null, 0, mock(IBinder.class)); - sService.mOomAdjuster.setUidTempAllowlistStateLSP(MOCKAPP_UID, true); + mService.mOomAdjuster.setUidTempAllowlistStateLSP(MOCKAPP_UID, true); assertEquals(true, app.getUidRecord().isSetAllowListed()); assertEquals(true, app.mOptRecord.shouldNotFreeze()); assertEquals(true, app2.mOptRecord.shouldNotFreeze()); - sService.mOomAdjuster.setUidTempAllowlistStateLSP(MOCKAPP_UID, false); + mService.mOomAdjuster.setUidTempAllowlistStateLSP(MOCKAPP_UID, false); assertEquals(false, app.getUidRecord().isSetAllowListed()); assertEquals(false, app.mOptRecord.shouldNotFreeze()); assertEquals(false, app2.mOptRecord.shouldNotFreeze()); @@ -2856,8 +2905,8 @@ public class MockingOomAdjusterTests { // App1 and app2 both bind to app3 and get temp allowlisted. bindService(app3, app, null, null, 0, mock(IBinder.class)); bindService(app3, app2, null, null, 0, mock(IBinder.class)); - sService.mOomAdjuster.setUidTempAllowlistStateLSP(MOCKAPP_UID, true); - sService.mOomAdjuster.setUidTempAllowlistStateLSP(MOCKAPP2_UID, true); + mService.mOomAdjuster.setUidTempAllowlistStateLSP(MOCKAPP_UID, true); + mService.mOomAdjuster.setUidTempAllowlistStateLSP(MOCKAPP2_UID, true); assertEquals(true, app.getUidRecord().isSetAllowListed()); assertEquals(true, app2.getUidRecord().isSetAllowListed()); @@ -2866,7 +2915,7 @@ public class MockingOomAdjusterTests { assertEquals(true, app3.mOptRecord.shouldNotFreeze()); // Remove app1 from allowlist. - sService.mOomAdjuster.setUidTempAllowlistStateLSP(MOCKAPP_UID, false); + mService.mOomAdjuster.setUidTempAllowlistStateLSP(MOCKAPP_UID, false); assertEquals(false, app.getUidRecord().isSetAllowListed()); assertEquals(true, app2.getUidRecord().isSetAllowListed()); assertEquals(false, app.mOptRecord.shouldNotFreeze()); @@ -2874,7 +2923,7 @@ public class MockingOomAdjusterTests { assertEquals(true, app3.mOptRecord.shouldNotFreeze()); // Now remove app2 from allowlist. - sService.mOomAdjuster.setUidTempAllowlistStateLSP(MOCKAPP2_UID, false); + mService.mOomAdjuster.setUidTempAllowlistStateLSP(MOCKAPP2_UID, false); assertEquals(false, app.getUidRecord().isSetAllowListed()); assertEquals(false, app2.getUidRecord().isSetAllowListed()); assertEquals(false, app.mOptRecord.shouldNotFreeze()); @@ -2882,6 +2931,73 @@ public class MockingOomAdjusterTests { assertEquals(false, app3.mOptRecord.shouldNotFreeze()); } + @SuppressWarnings("GuardedBy") + @Test + public void testUpdateOomAdj_DoAll_ClientlessService() { + ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID, + MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false)); + + setProcessesToLru(app); + ServiceRecord s = makeServiceRecord(app); + s.startRequested = true; + s.lastActivity = SystemClock.uptimeMillis(); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + updateOomAdj(); + assertProcStates(app, PROCESS_STATE_SERVICE, SERVICE_ADJ, SCHED_GROUP_BACKGROUND, + "started-services"); + + if (!Flags.followUpOomadjUpdates()) return; + + final ArgumentCaptor<Long> followUpTimeCaptor = ArgumentCaptor.forClass(Long.class); + verify(mService.mHandler).sendEmptyMessageAtTime( + eq(FOLLOW_UP_OOMADJUSTER_UPDATE_MSG), followUpTimeCaptor.capture()); + mInjector.jumpUptimeAheadTo(followUpTimeCaptor.getValue()); + mService.mOomAdjuster.updateOomAdjFollowUpTargetsLocked(); + + assertProcStates(app, PROCESS_STATE_SERVICE, sFirstCachedAdj, SCHED_GROUP_BACKGROUND, + "cch-started-services"); + // Follow up should not have been called again. + verify(mService.mHandler).sendEmptyMessageAtTime(eq(FOLLOW_UP_OOMADJUSTER_UPDATE_MSG), + followUpTimeCaptor.capture()); + } + + @SuppressWarnings("GuardedBy") + @Test + public void testUpdateOomAdj_DoAll_Multiple_Provider_Retention() { + ProcessRecord app1 = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID, + MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false)); + ProcessRecord app2 = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID, + MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false)); + app1.mProviders.setLastProviderTime(SystemClock.uptimeMillis()); + app2.mProviders.setLastProviderTime(SystemClock.uptimeMillis() + 2000); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + setProcessesToLru(app1, app2); + mService.mOomAdjuster.updateOomAdjLocked(OOM_ADJ_REASON_NONE); + + assertProcStates(app1, PROCESS_STATE_LAST_ACTIVITY, PREVIOUS_APP_ADJ, + SCHED_GROUP_BACKGROUND, "recent-provider"); + assertProcStates(app2, PROCESS_STATE_LAST_ACTIVITY, PREVIOUS_APP_ADJ, + SCHED_GROUP_BACKGROUND, "recent-provider"); + + if (!Flags.followUpOomadjUpdates()) return; + + final ArgumentCaptor<Long> followUpTimeCaptor = ArgumentCaptor.forClass(Long.class); + verify(mService.mHandler, atLeastOnce()).sendEmptyMessageAtTime( + eq(FOLLOW_UP_OOMADJUSTER_UPDATE_MSG), followUpTimeCaptor.capture()); + mInjector.jumpUptimeAheadTo(followUpTimeCaptor.getValue()); + mService.mOomAdjuster.updateOomAdjFollowUpTargetsLocked(); + + assertProcStates(app1, PROCESS_STATE_CACHED_EMPTY, sFirstCachedAdj, SCHED_GROUP_BACKGROUND, + "cch-empty"); + + verify(mService.mHandler, atLeastOnce()).sendEmptyMessageAtTime( + eq(FOLLOW_UP_OOMADJUSTER_UPDATE_MSG), followUpTimeCaptor.capture()); + mInjector.jumpUptimeAheadTo(followUpTimeCaptor.getValue()); + mService.mOomAdjuster.updateOomAdjFollowUpTargetsLocked(); + assertProcStates(app2, PROCESS_STATE_CACHED_EMPTY, sFirstCachedAdj, SCHED_GROUP_BACKGROUND, + "cch-empty"); + } + private ProcessRecord makeDefaultProcessRecord(int pid, int uid, String processName, String packageName, boolean hasShownUi) { return new ProcessRecordBuilder(pid, uid, processName, packageName).setHasShownUi( @@ -2904,7 +3020,7 @@ public class MockingOomAdjusterTests { } private void setServiceMap(ServiceRecord s, int uid, ComponentName cn) { - ActiveServices.ServiceMap serviceMap = sService.mServices.mServiceMap.get( + ActiveServices.ServiceMap serviceMap = mService.mServices.mServiceMap.get( UserHandle.getUserId(uid)); if (serviceMap == null) { serviceMap = mock(ActiveServices.ServiceMap.class); @@ -2916,7 +3032,7 @@ public class MockingOomAdjusterTests { new ArrayMap<>()); setFieldValue(ActiveServices.ServiceMap.class, serviceMap, "mDelayedStartList", new ArrayList<>()); - sService.mServices.mServiceMap.put(UserHandle.getUserId(uid), serviceMap); + mService.mServices.mServiceMap.put(UserHandle.getUserId(uid), serviceMap); } serviceMap.mServicesByInstanceName.put(cn, s); } @@ -2957,6 +3073,7 @@ public class MockingOomAdjusterTests { return record; } + @SuppressWarnings("GuardedBy") private void assertProcStates(ProcessRecord app, int expectedProcState, int expectedAdj, int expectedSchedGroup) { final ProcessStateRecord state = app.mState; @@ -2974,6 +3091,7 @@ public class MockingOomAdjusterTests { } } + @SuppressWarnings("GuardedBy") private void assertProcStates(ProcessRecord app, boolean expectedCached, int expectedProcState, int expectedAdj, String expectedAdjType) { final ProcessStateRecord state = app.mState; @@ -2992,7 +3110,26 @@ public class MockingOomAdjusterTests { } } - private static class ProcessRecordBuilder { + @SuppressWarnings("GuardedBy") + private void assertProcStates(ProcessRecord app, int expectedProcState, int expectedAdj, + int expectedSchedGroup, String expectedAdjType) { + final ProcessStateRecord state = app.mState; + assertEquals(expectedAdjType, state.getAdjType()); + assertEquals(expectedProcState, state.getSetProcState()); + assertEquals(expectedAdj, state.getSetAdj()); + assertEquals(expectedSchedGroup, state.getSetSchedGroup()); + + // Below BFGS should never have BFSL. + if (expectedProcState > PROCESS_STATE_BOUND_FOREGROUND_SERVICE) { + assertNoBfsl(app); + } + // Above FGS should always have BFSL. + if (expectedProcState < PROCESS_STATE_FOREGROUND_SERVICE) { + assertBfsl(app); + } + } + + private class ProcessRecordBuilder { @SuppressWarnings("UnusedVariable") int mPid; int mUid; @@ -3069,17 +3206,17 @@ public class MockingOomAdjusterTests { ai.packageName = mPackageName; ai.longVersionCode = mVersionCode; ai.targetSdkVersion = mTargetSdkVersion; - doCallRealMethod().when(sService).getPackageManagerInternal(); - doReturn(null).when(sPackageManagerInternal).getApplicationInfo( + doCallRealMethod().when(mService).getPackageManagerInternal(); + doReturn(null).when(mPackageManagerInternal).getApplicationInfo( eq(mSdkSandboxClientAppPackage), anyLong(), anyInt(), anyInt()); - ProcessRecord app = new ProcessRecord(sService, ai, mProcessName, mUid, + ProcessRecord app = new ProcessRecord(mService, ai, mProcessName, mUid, mSdkSandboxClientAppPackage, -1, null); final ProcessStateRecord state = app.mState; final ProcessServiceRecord services = app.mServices; final ProcessReceiverRecord receivers = app.mReceivers; final ProcessProfileRecord profile = app.mProfile; final ProcessProviderRecord providers = app.mProviders; - app.makeActive(mock(IApplicationThread.class), sService.mProcessStats); + app.makeActive(mock(IApplicationThread.class), mService.mProcessStats); app.setLastActivityTime(mLastActivityTime); app.setKilledByAm(mKilledByAm); app.setIsolatedEntryPoint(mIsolatedEntryPoint); @@ -3124,14 +3261,35 @@ public class MockingOomAdjusterTests { } providers.setLastProviderTime(mLastProviderTime); - UidRecord uidRec = sService.mOomAdjuster.mActiveUids.get(mUid); + UidRecord uidRec = mService.mOomAdjuster.mActiveUids.get(mUid); if (uidRec == null) { - uidRec = new UidRecord(mUid, sService); - sService.mOomAdjuster.mActiveUids.put(mUid, uidRec); + uidRec = new UidRecord(mUid, mService); + mService.mOomAdjuster.mActiveUids.put(mUid, uidRec); } uidRec.addProcess(app); app.setUidRecord(uidRec); return app; } } + + static class OomAdjusterInjector extends OomAdjuster.Injector { + // Jump ahead in time by this offset amount. + long mTimeOffsetMillis = 0; + + void jumpUptimeAheadTo(long uptimeMillis) { + final long jumpMs = uptimeMillis - getUptimeMillis(); + if (jumpMs <= 0) return; + mTimeOffsetMillis += jumpMs; + } + + @Override + long getUptimeMillis() { + return SystemClock.uptimeMillis() + mTimeOffsetMillis; + } + + @Override + long getElapsedRealtimeMillis() { + return SystemClock.elapsedRealtime() + mTimeOffsetMillis; + } + } } diff --git a/services/tests/mockingservicestests/src/com/android/server/am/ServiceBindingOomAdjPolicyTest.java b/services/tests/mockingservicestests/src/com/android/server/am/ServiceBindingOomAdjPolicyTest.java index daa827eacf44..5f126774835d 100644 --- a/services/tests/mockingservicestests/src/com/android/server/am/ServiceBindingOomAdjPolicyTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/am/ServiceBindingOomAdjPolicyTest.java @@ -556,7 +556,7 @@ public final class ServiceBindingOomAdjPolicyTest { rInfo.serviceInfo = makeServiceInfo(compName.getClassName(), compName.getPackageName(), serviceUid); doReturn(rInfo).when(mPackageManagerInt).resolveService(any(Intent.class), any(), - anyLong(), anyInt(), anyInt()); + anyLong(), anyInt(), anyInt(), anyInt()); return serviceIntent; } diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt b/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt index 396edae2f672..9ab607de474d 100644 --- a/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt +++ b/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt @@ -140,6 +140,7 @@ class MockSystem(withSession: (StaticMockitoSessionBuilder) -> Unit = {}) { PropertyInvalidatedCache.disableForTestMode() val apply = ExtendedMockito.mockitoSession() .strictness(Strictness.LENIENT) + .mockStatic(SaferIntentUtils::class.java) .mockStatic(SystemProperties::class.java) .mockStatic(SystemConfig::class.java) .mockStatic(SELinuxMMAC::class.java, Mockito.CALLS_REAL_METHODS) diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/PackageManagerTracedLockTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/PackageManagerTracedLockTest.java new file mode 100644 index 000000000000..eebd921c5a94 --- /dev/null +++ b/services/tests/mockingservicestests/src/com/android/server/pm/PackageManagerTracedLockTest.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.pm; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertFalse; +import static org.testng.Assert.assertTrue; + +import android.os.Handler; +import android.os.HandlerThread; + +import androidx.test.runner.AndroidJUnit4; + +import org.junit.After; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +@RunWith(AndroidJUnit4.class) +public class PackageManagerTracedLockTest { + PackageManagerTracedLock mLock = new PackageManagerTracedLock(); + PackageManagerTracedLock.RawLock mRawLock = mLock.getRawLock(); + + @After + public void tearDown() { + if (mRawLock.isHeldByCurrentThread()) { + mRawLock.unlock(); + } + } + + @Test + public void testAcquireLock() { + assertFalse(mRawLock.isLocked()); + try (var autoClosableLock = mLock.acquireLock()) { + assertTrue(mRawLock.isHeldByCurrentThread()); + assertTrue(mRawLock.isLocked()); + assertEquals(autoClosableLock.getRawLock(), mRawLock); + assertEquals(1, mRawLock.getHoldCount()); + try (var autoClosableLock2 = mLock.acquireLock()) { + assertTrue(mRawLock.isHeldByCurrentThread()); + assertTrue(mRawLock.isLocked()); + assertEquals(autoClosableLock2.getRawLock(), mRawLock); + assertEquals(2, mRawLock.getHoldCount()); + } + assertTrue(mRawLock.isHeldByCurrentThread()); + assertTrue(mRawLock.isLocked()); + assertEquals(1, mRawLock.getHoldCount()); + } + assertFalse(mRawLock.isHeldByCurrentThread()); + assertFalse(mRawLock.isLocked()); + assertEquals(0, mRawLock.getHoldCount()); + } + + @Test + public void testUnlockInsideTry() { + assertFalse(mRawLock.isLocked()); + try (var autoClosableLock = mLock.acquireLock()) { + assertTrue(mRawLock.isHeldByCurrentThread()); + assertTrue(mRawLock.isLocked()); + assertEquals(autoClosableLock.getRawLock(), mRawLock); + assertEquals(1, mRawLock.getHoldCount()); + mRawLock.unlock(); + assertFalse(mRawLock.isHeldByCurrentThread()); + assertFalse(mRawLock.isLocked()); + assertEquals(0, mRawLock.getHoldCount()); + mRawLock.lock(); + } + assertFalse(mRawLock.isHeldByCurrentThread()); + assertFalse(mRawLock.isLocked()); + assertEquals(0, mRawLock.getHoldCount()); + } + + @Test + public void testRawLock() { + assertFalse(mRawLock.isLocked()); + mRawLock.lock(); + assertTrue(mRawLock.isLocked()); + assertTrue(mRawLock.isHeldByCurrentThread()); + assertEquals(1, mRawLock.getHoldCount()); + assertTrue(mRawLock.tryLock()); + assertTrue(mRawLock.isLocked()); + assertTrue(mRawLock.isHeldByCurrentThread()); + assertEquals(2, mRawLock.getHoldCount()); + mRawLock.unlock(); + assertTrue(mRawLock.isLocked()); + assertTrue(mRawLock.isHeldByCurrentThread()); + assertEquals(1, mRawLock.getHoldCount()); + mRawLock.unlock(); + assertFalse(mRawLock.isLocked()); + assertFalse(mRawLock.isHeldByCurrentThread()); + } + + @Test + public void testTrylock() throws InterruptedException { + assertFalse(mRawLock.isLocked()); + HandlerThread thread = new HandlerThread("PackageManagerTracedLockTestThread", + android.os.Process.THREAD_PRIORITY_BACKGROUND); + thread.start(); + Handler handler = new Handler(thread.getLooper()); + handler.post(() -> mRawLock.lock()); + waitForHandler(handler); + assertTrue(mRawLock.isLocked()); + assertFalse(mRawLock.isHeldByCurrentThread()); + assertEquals(0, mRawLock.getHoldCount()); + assertFalse(mRawLock.tryLock()); + handler.post(() -> mRawLock.unlock()); + waitForHandler(handler); + assertFalse(mRawLock.isLocked()); + assertFalse(mRawLock.isHeldByCurrentThread()); + assertEquals(0, mRawLock.getHoldCount()); + thread.interrupt(); + } + + private void waitForHandler(Handler handler) throws InterruptedException { + final CountDownLatch latch = new CountDownLatch(1); + handler.post(latch::countDown); + assertTrue(latch.await(1, TimeUnit.SECONDS)); + } +} diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/dex/DexManagerTests.java b/services/tests/mockingservicestests/src/com/android/server/pm/dex/DexManagerTests.java index 7dae23529fc6..a67844172ffa 100644 --- a/services/tests/mockingservicestests/src/com/android/server/pm/dex/DexManagerTests.java +++ b/services/tests/mockingservicestests/src/com/android/server/pm/dex/DexManagerTests.java @@ -83,8 +83,6 @@ public class DexManagerTests { @Mock BatteryManager mMockBatteryManager; @Mock PowerManager mMockPowerManager; - private final Object mInstallLock = new Object(); - private DynamicCodeLogger mDynamicCodeLogger; private DexManager mDexManager; @@ -160,8 +158,8 @@ public class DexManagerTests { .getSystemService(PowerManager.class); mDynamicCodeLogger = new DynamicCodeLogger(mInstaller); - mDexManager = new DexManager(mockContext, /*PackageDexOptimizer*/ null, mInstaller, - mInstallLock, mDynamicCodeLogger, mPM); + mDexManager = new DexManager(mockContext, /*PackageDexOptimizer*/ null, + mDynamicCodeLogger, mPM); // Foo and Bar are available to user0. // Only Bar is available to user1; diff --git a/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperCropperTest.java b/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperCropperTest.java index 1b0a8d2222b9..f1bf86f2f57c 100644 --- a/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperCropperTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperCropperTest.java @@ -211,9 +211,11 @@ public class WallpaperCropperTest { new Rect(100, 200, bitmapSize.x - 100, bitmapSize.y))) { for (int mode: ALL_MODES) { for (boolean parallax: List.of(true, false)) { - assertThat(WallpaperCropper.getAdjustedCrop( - crop, bitmapSize, displaySize, parallax, mode)) - .isEqualTo(crop); + for (boolean rtl: List.of(true, false)) { + assertThat(WallpaperCropper.getAdjustedCrop( + crop, bitmapSize, displaySize, parallax, rtl, mode)) + .isEqualTo(crop); + } } } } @@ -234,8 +236,11 @@ public class WallpaperCropperTest { Point expectedCropSize = new Point(expectedWidth, 1000); for (int mode: ALL_MODES) { assertThat(WallpaperCropper.getAdjustedCrop( - crop, bitmapSize, displaySize, true, mode)) - .isEqualTo(centerOf(crop, expectedCropSize)); + crop, bitmapSize, displaySize, true, false, mode)) + .isEqualTo(leftOf(crop, expectedCropSize)); + assertThat(WallpaperCropper.getAdjustedCrop( + crop, bitmapSize, displaySize, true, true, mode)) + .isEqualTo(rightOf(crop, expectedCropSize)); } } @@ -254,9 +259,11 @@ public class WallpaperCropperTest { Point bitmapSize = new Point(acceptableWidth, 1000); Rect crop = new Rect(0, 0, bitmapSize.x, bitmapSize.y); for (int mode : ALL_MODES) { - assertThat(WallpaperCropper.getAdjustedCrop( - crop, bitmapSize, displaySize, true, mode)) - .isEqualTo(crop); + for (boolean rtl : List.of(false, true)) { + assertThat(WallpaperCropper.getAdjustedCrop( + crop, bitmapSize, displaySize, true, rtl, mode)) + .isEqualTo(crop); + } } } } @@ -286,9 +293,11 @@ public class WallpaperCropperTest { for (int i = 0; i < crops.size(); i++) { Rect crop = crops.get(i); Rect expectedCrop = expectedAdjustedCrops.get(i); - assertThat(WallpaperCropper.getAdjustedCrop( - crop, bitmapSize, displaySize, false, WallpaperCropper.ADD)) - .isEqualTo(expectedCrop); + for (boolean rtl: List.of(false, true)) { + assertThat(WallpaperCropper.getAdjustedCrop( + crop, bitmapSize, displaySize, false, rtl, WallpaperCropper.ADD)) + .isEqualTo(expectedCrop); + } } } @@ -309,9 +318,11 @@ public class WallpaperCropperTest { Point expectedCropSize = new Point(1000, 1000); for (Rect crop: crops) { - assertThat(WallpaperCropper.getAdjustedCrop( - crop, bitmapSize, displaySize, false, WallpaperCropper.REMOVE)) - .isEqualTo(centerOf(crop, expectedCropSize)); + for (boolean rtl : List.of(false, true)) { + assertThat(WallpaperCropper.getAdjustedCrop( + crop, bitmapSize, displaySize, false, rtl, WallpaperCropper.REMOVE)) + .isEqualTo(centerOf(crop, expectedCropSize)); + } } } @@ -338,14 +349,14 @@ public class WallpaperCropperTest { Rect crop = crops.get(i); Rect expected = expectedAdjustedCrops.get(i); assertThat(WallpaperCropper.getAdjustedCrop( - crop, bitmapSize, displaySize, false, WallpaperCropper.BALANCE)) + crop, bitmapSize, displaySize, false, false, WallpaperCropper.BALANCE)) .isEqualTo(expected); Rect transposedCrop = new Rect(crop.top, crop.left, crop.bottom, crop.right); Rect expectedTransposed = new Rect( expected.top, expected.left, expected.bottom, expected.right); assertThat(WallpaperCropper.getAdjustedCrop(transposedCrop, bitmapSize, - transposedDisplaySize, false, WallpaperCropper.BALANCE)) + transposedDisplaySize, false, false, WallpaperCropper.BALANCE)) .isEqualTo(expectedTransposed); } } @@ -376,9 +387,11 @@ public class WallpaperCropperTest { Point displaySize = displaySizes.get(i); Point expectedCropSize = expectedCropSizes.get(i); for (boolean rtl : List.of(false, true)) { + Rect expectedCrop = rtl ? rightOf(bitmapRect, expectedCropSize) + : leftOf(bitmapRect, expectedCropSize); assertThat(mWallpaperCropper.getCrop( displaySize, bitmapSize, suggestedCrops, rtl)) - .isEqualTo(centerOf(bitmapRect, expectedCropSize)); + .isEqualTo(expectedCrop); } } } diff --git a/services/tests/ondeviceintelligencetests/OWNERS b/services/tests/ondeviceintelligencetests/OWNERS new file mode 100644 index 000000000000..09774f78d712 --- /dev/null +++ b/services/tests/ondeviceintelligencetests/OWNERS @@ -0,0 +1 @@ +file:/core/java/android/app/ondeviceintelligence/OWNERS 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 851cf4a535a2..a3f0770ec8ba 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 @@ -91,7 +91,7 @@ public class BatteryUsageStatsTest { final Parcel parcel = Parcel.obtain(); parcel.writeParcelable(outBatteryUsageStats, 0); - assertThat(parcel.dataSize()).isLessThan(8000); + assertThat(parcel.dataSize()).isLessThan(12000); parcel.setDataPosition(0); diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/CameraPowerStatsTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/CameraPowerStatsTest.java new file mode 100644 index 000000000000..36deb08de8de --- /dev/null +++ b/services/tests/powerstatstests/src/com/android/server/power/stats/CameraPowerStatsTest.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.server.power.stats; + +import static android.os.BatteryConsumer.PROCESS_STATE_BACKGROUND; +import static android.os.BatteryConsumer.PROCESS_STATE_CACHED; +import static android.os.BatteryConsumer.PROCESS_STATE_FOREGROUND; +import static android.os.BatteryConsumer.PROCESS_STATE_FOREGROUND_SERVICE; + +import static com.android.server.power.stats.AggregatedPowerStatsConfig.POWER_STATE_OTHER; +import static com.android.server.power.stats.AggregatedPowerStatsConfig.SCREEN_STATE_ON; +import static com.android.server.power.stats.AggregatedPowerStatsConfig.SCREEN_STATE_OTHER; +import static com.android.server.power.stats.AggregatedPowerStatsConfig.STATE_POWER; +import static com.android.server.power.stats.AggregatedPowerStatsConfig.STATE_PROCESS_STATE; +import static com.android.server.power.stats.AggregatedPowerStatsConfig.STATE_SCREEN; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.Mockito.when; + +import android.hardware.power.stats.EnergyConsumerType; +import android.os.BatteryConsumer; +import android.os.BatteryStats; +import android.os.Handler; +import android.os.Process; +import android.platform.test.ravenwood.RavenwoodRule; + +import com.android.internal.os.Clock; +import com.android.internal.os.MonotonicClock; +import com.android.internal.os.PowerProfile; +import com.android.internal.os.PowerStats; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.util.function.IntSupplier; + +public class CameraPowerStatsTest { + @Rule(order = 0) + public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder() + .setProvideMainThread(true) + .build(); + + @Rule(order = 1) + public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule() + .setAveragePower(PowerProfile.POWER_CAMERA, 100.0) + .initMeasuredEnergyStatsLocked(); + + private static final double PRECISION = 0.00001; + private static final int APP_UID1 = Process.FIRST_APPLICATION_UID + 42; + private static final int APP_UID2 = Process.FIRST_APPLICATION_UID + 101; + private static final int VOLTAGE_MV = 3500; + private static final int ENERGY_CONSUMER_ID = 777; + + private final PowerStatsUidResolver mUidResolver = new PowerStatsUidResolver(); + @Mock + private PowerStatsCollector.ConsumedEnergyRetriever mConsumedEnergyRetriever; + + EnergyConsumerPowerStatsCollector.Injector mInjector = + new EnergyConsumerPowerStatsCollector.Injector() { + @Override + public Handler getHandler() { + return mStatsRule.getHandler(); + } + + @Override + public Clock getClock() { + return mStatsRule.getMockClock(); + } + + @Override + public PowerStatsUidResolver getUidResolver() { + return mUidResolver; + } + + @Override + public long getPowerStatsCollectionThrottlePeriod(String powerComponentName) { + return 0; + } + + @Override + public PowerStatsCollector.ConsumedEnergyRetriever getConsumedEnergyRetriever() { + return mConsumedEnergyRetriever; + } + + @Override + public IntSupplier getVoltageSupplier() { + return () -> VOLTAGE_MV; + } + }; + + private MonotonicClock mMonotonicClock; + + @Before + public void setup() { + MockitoAnnotations.initMocks(this); + mMonotonicClock = new MonotonicClock(0, mStatsRule.getMockClock()); + } + + @Test + public void energyConsumerModel() { + when(mConsumedEnergyRetriever.getEnergyConsumerIds(EnergyConsumerType.CAMERA, null)) + .thenReturn(new int[]{ENERGY_CONSUMER_ID}); + CameraPowerStatsProcessor processor = new CameraPowerStatsProcessor( + mStatsRule.getPowerProfile(), mUidResolver); + + PowerComponentAggregatedPowerStats stats = createAggregatedPowerStats(processor); + + CameraPowerStatsCollector collector = new CameraPowerStatsCollector(mInjector); + collector.addConsumer( + powerStats -> { + processor.addPowerStats(stats, powerStats, mMonotonicClock.monotonicTime()); + }); + collector.setEnabled(true); + + // Establish a baseline + when(mConsumedEnergyRetriever.getConsumedEnergyUws(new int[]{ENERGY_CONSUMER_ID})) + .thenReturn(new long[]{uCtoUj(10000)}); + collector.collectAndDeliverStats(); + + processor.noteStateChange(stats, buildHistoryItem(0, true, APP_UID1)); + + // Turn the screen off after 2.5 seconds + stats.setState(STATE_SCREEN, SCREEN_STATE_OTHER, 2500); + stats.setUidState(APP_UID1, STATE_PROCESS_STATE, PROCESS_STATE_BACKGROUND, 2500); + stats.setUidState(APP_UID1, STATE_PROCESS_STATE, PROCESS_STATE_FOREGROUND_SERVICE, 5000); + + processor.noteStateChange(stats, buildHistoryItem(6000, false, APP_UID1)); + + when(mConsumedEnergyRetriever.getConsumedEnergyUws(new int[]{ENERGY_CONSUMER_ID})) + .thenReturn(new long[]{uCtoUj(2_170_000)}); + collector.collectAndDeliverStats(); + + processor.noteStateChange(stats, buildHistoryItem(7000, true, APP_UID2)); + + mStatsRule.setTime(11_000, 11_000); + when(mConsumedEnergyRetriever.getConsumedEnergyUws(new int[]{ENERGY_CONSUMER_ID})) + .thenReturn(new long[]{uCtoUj(3_610_000)}); + collector.collectAndDeliverStats(); + + processor.finish(stats, 11_000); + + PowerStats.Descriptor descriptor = stats.getPowerStatsDescriptor(); + BinaryStatePowerStatsLayout statsLayout = new BinaryStatePowerStatsLayout(); + statsLayout.fromExtras(descriptor.extras); + + // Total estimated power = 3,600,000 uC = 1.0 mAh + // of which 3,000,000 is distributed: + // Screen-on - 2500/6000 * 2160000 = 900000 uC = 0.25 mAh + // Screen-off - 3500/6000 * 2160000 = 1260000 uC = 0.35 mAh + // and 600,000 was fully with screen off: + // Screen-off - 1440000 uC = 0.4 mAh + long[] deviceStats = new long[descriptor.statsArrayLength]; + stats.getDeviceStats(deviceStats, states(POWER_STATE_OTHER, SCREEN_STATE_ON)); + assertThat(statsLayout.getDevicePowerEstimate(deviceStats)) + .isWithin(PRECISION).of(0.25); + + stats.getDeviceStats(deviceStats, states(POWER_STATE_OTHER, SCREEN_STATE_OTHER)); + assertThat(statsLayout.getDevicePowerEstimate(deviceStats)) + .isWithin(PRECISION).of(0.35 + 0.4); + + // UID1 = + // 2,160,000 uC = 0.6 mAh + // split between three different states + // fg screen-on: 2500/6000 + // bg screen-off: 2500/6000 + // fgs screen-off: 1000/6000 + double expectedPower1 = 0.6; + long[] uidStats = new long[descriptor.uidStatsArrayLength]; + stats.getUidStats(uidStats, APP_UID1, + states(POWER_STATE_OTHER, SCREEN_STATE_ON, PROCESS_STATE_FOREGROUND)); + assertThat(statsLayout.getUidPowerEstimate(uidStats)) + .isWithin(PRECISION).of(expectedPower1 * 2500 / 6000); + + + stats.getUidStats(uidStats, APP_UID1, + states(POWER_STATE_OTHER, SCREEN_STATE_OTHER, PROCESS_STATE_BACKGROUND)); + assertThat(statsLayout.getUidPowerEstimate(uidStats)) + .isWithin(PRECISION).of(expectedPower1 * 2500 / 6000); + + stats.getUidStats(uidStats, APP_UID1, + states(POWER_STATE_OTHER, SCREEN_STATE_OTHER, PROCESS_STATE_FOREGROUND_SERVICE)); + assertThat(statsLayout.getUidPowerEstimate(uidStats)) + .isWithin(PRECISION).of(expectedPower1 * 1000 / 6000); + + // UID2 = + // 1440000 mA-ms = 0.4 mAh + // all in the same state + double expectedPower2 = 0.4; + stats.getUidStats(uidStats, APP_UID2, + states(POWER_STATE_OTHER, SCREEN_STATE_OTHER, PROCESS_STATE_CACHED)); + assertThat(statsLayout.getUidPowerEstimate(uidStats)) + .isWithin(PRECISION).of(expectedPower2); + + stats.getUidStats(uidStats, APP_UID2, + states(POWER_STATE_OTHER, SCREEN_STATE_ON, PROCESS_STATE_CACHED)); + assertThat(statsLayout.getUidPowerEstimate(uidStats)) + .isWithin(PRECISION).of(0); + } + + private BatteryStats.HistoryItem buildHistoryItem(int timestamp, boolean stateOn, + int uid) { + mStatsRule.setTime(timestamp, timestamp); + BatteryStats.HistoryItem historyItem = new BatteryStats.HistoryItem(); + historyItem.time = mMonotonicClock.monotonicTime(); + historyItem.states2 = stateOn ? BatteryStats.HistoryItem.STATE2_CAMERA_FLAG : 0; + if (stateOn) { + historyItem.eventCode = BatteryStats.HistoryItem.EVENT_STATE_CHANGE + | BatteryStats.HistoryItem.EVENT_FLAG_START; + } else { + historyItem.eventCode = BatteryStats.HistoryItem.EVENT_STATE_CHANGE + | BatteryStats.HistoryItem.EVENT_FLAG_FINISH; + } + historyItem.eventTag = historyItem.localEventTag; + historyItem.eventTag.uid = uid; + historyItem.eventTag.string = "camera"; + return historyItem; + } + + private int[] states(int... states) { + return states; + } + + private static PowerComponentAggregatedPowerStats createAggregatedPowerStats( + BinaryStatePowerStatsProcessor processor) { + AggregatedPowerStatsConfig config = new AggregatedPowerStatsConfig(); + config.trackPowerComponent(BatteryConsumer.POWER_COMPONENT_CAMERA) + .trackDeviceStates( + AggregatedPowerStatsConfig.STATE_POWER, + AggregatedPowerStatsConfig.STATE_SCREEN) + .trackUidStates( + AggregatedPowerStatsConfig.STATE_POWER, + AggregatedPowerStatsConfig.STATE_SCREEN, + AggregatedPowerStatsConfig.STATE_PROCESS_STATE) + .setProcessor(processor); + + AggregatedPowerStats aggregatedPowerStats = new AggregatedPowerStats(config); + PowerComponentAggregatedPowerStats powerComponentStats = + aggregatedPowerStats.getPowerComponentStats(BatteryConsumer.POWER_COMPONENT_CAMERA); + processor.start(powerComponentStats, 0); + + powerComponentStats.setState(STATE_POWER, POWER_STATE_OTHER, 0); + powerComponentStats.setState(STATE_SCREEN, SCREEN_STATE_ON, 0); + powerComponentStats.setUidState(APP_UID1, STATE_PROCESS_STATE, PROCESS_STATE_FOREGROUND, 0); + powerComponentStats.setUidState(APP_UID2, STATE_PROCESS_STATE, PROCESS_STATE_CACHED, 0); + + return powerComponentStats; + } + + private static long uCtoUj(long uc) { + return (long) (uc * (double) VOLTAGE_MV / 1000); + } +} diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/GnssPowerStatsTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/GnssPowerStatsTest.java new file mode 100644 index 000000000000..8a391c6bb2ea --- /dev/null +++ b/services/tests/powerstatstests/src/com/android/server/power/stats/GnssPowerStatsTest.java @@ -0,0 +1,375 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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.stats; + +import static android.os.BatteryConsumer.PROCESS_STATE_BACKGROUND; +import static android.os.BatteryConsumer.PROCESS_STATE_CACHED; +import static android.os.BatteryConsumer.PROCESS_STATE_FOREGROUND; +import static android.os.BatteryConsumer.PROCESS_STATE_FOREGROUND_SERVICE; + +import static com.android.server.power.stats.AggregatedPowerStatsConfig.POWER_STATE_OTHER; +import static com.android.server.power.stats.AggregatedPowerStatsConfig.SCREEN_STATE_ON; +import static com.android.server.power.stats.AggregatedPowerStatsConfig.SCREEN_STATE_OTHER; +import static com.android.server.power.stats.AggregatedPowerStatsConfig.STATE_POWER; +import static com.android.server.power.stats.AggregatedPowerStatsConfig.STATE_PROCESS_STATE; +import static com.android.server.power.stats.AggregatedPowerStatsConfig.STATE_SCREEN; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.Mockito.when; + +import android.hardware.power.stats.EnergyConsumerType; +import android.location.GnssSignalQuality; +import android.os.BatteryConsumer; +import android.os.BatteryStats; +import android.os.Handler; +import android.os.Process; +import android.platform.test.ravenwood.RavenwoodRule; + +import com.android.internal.os.Clock; +import com.android.internal.os.MonotonicClock; +import com.android.internal.os.PowerProfile; +import com.android.internal.os.PowerStats; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.util.function.IntSupplier; + +public class GnssPowerStatsTest { + @Rule(order = 0) + public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder() + .setProvideMainThread(true) + .build(); + + @Rule(order = 1) + public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule() + .setAveragePower(PowerProfile.POWER_GPS_ON, 100.0) + .setAveragePower(PowerProfile.POWER_GPS_SIGNAL_QUALITY_BASED, new double[]{1000, 100}) + .initMeasuredEnergyStatsLocked(); + + private static final double PRECISION = 0.00001; + private static final int APP_UID1 = Process.FIRST_APPLICATION_UID + 42; + private static final int APP_UID2 = Process.FIRST_APPLICATION_UID + 101; + private static final int VOLTAGE_MV = 3500; + private static final int ENERGY_CONSUMER_ID = 777; + + private final PowerStatsUidResolver mUidResolver = new PowerStatsUidResolver(); + @Mock + private PowerStatsCollector.ConsumedEnergyRetriever mConsumedEnergyRetriever; + + EnergyConsumerPowerStatsCollector.Injector mInjector = + new EnergyConsumerPowerStatsCollector.Injector() { + @Override + public Handler getHandler() { + return mStatsRule.getHandler(); + } + + @Override + public Clock getClock() { + return mStatsRule.getMockClock(); + } + + @Override + public PowerStatsUidResolver getUidResolver() { + return mUidResolver; + } + + @Override + public long getPowerStatsCollectionThrottlePeriod(String powerComponentName) { + return 0; + } + + @Override + public PowerStatsCollector.ConsumedEnergyRetriever getConsumedEnergyRetriever() { + return mConsumedEnergyRetriever; + } + + @Override + public IntSupplier getVoltageSupplier() { + return () -> VOLTAGE_MV; + } + }; + + private MonotonicClock mMonotonicClock; + + @Before + public void setup() { + MockitoAnnotations.initMocks(this); + mMonotonicClock = new MonotonicClock(0, mStatsRule.getMockClock()); + } + + @Test + public void powerProfileModel() { + // ODPM unsupported + when(mConsumedEnergyRetriever.getEnergyConsumerIds(EnergyConsumerType.GNSS, null)) + .thenReturn(new int[0]); + GnssPowerStatsProcessor processor = new GnssPowerStatsProcessor( + mStatsRule.getPowerProfile(), mUidResolver); + + PowerComponentAggregatedPowerStats stats = createAggregatedPowerStats(processor); + + GnssPowerStatsCollector collector = new GnssPowerStatsCollector(mInjector); + collector.addConsumer( + powerStats -> { + processor.addPowerStats(stats, powerStats, mMonotonicClock.monotonicTime()); + }); + collector.setEnabled(true); + + // Establish a baseline + collector.collectAndDeliverStats(); + + processor.noteStateChange(stats, buildHistoryItem(0, true, APP_UID1)); + + // Turn the screen off after 2.5 seconds + stats.setState(STATE_SCREEN, SCREEN_STATE_OTHER, 2500); + stats.setUidState(APP_UID1, STATE_PROCESS_STATE, PROCESS_STATE_BACKGROUND, 2500); + stats.setUidState(APP_UID1, STATE_PROCESS_STATE, PROCESS_STATE_FOREGROUND_SERVICE, 5000); + + processor.noteStateChange(stats, buildHistoryItem(6000, false, APP_UID1)); + + collector.collectAndDeliverStats(); + + processor.noteStateChange(stats, buildHistoryItem(7000, true, APP_UID2)); + processor.noteStateChange(stats, buildHistoryItem(7000, + GnssSignalQuality.GNSS_SIGNAL_QUALITY_GOOD)); + processor.noteStateChange(stats, buildHistoryItem(8000, + GnssSignalQuality.GNSS_SIGNAL_QUALITY_POOR)); + mStatsRule.setTime(11_000, 11_000); + collector.collectAndDeliverStats(); + + processor.finish(stats, 11_000); + + PowerStats.Descriptor descriptor = stats.getPowerStatsDescriptor(); + BinaryStatePowerStatsLayout statsLayout = new BinaryStatePowerStatsLayout(); + statsLayout.fromExtras(descriptor.extras); + + // scr-on, GNSS-good: 2500 * 100 = 250000 mA-ms = 0.06944 mAh + // scr-off GNSS=good: 4500 * 100 = 0.12500 mAh + // scr-off GNSS=poor: 3000 * 1000 = 0.83333 mAh + // scr-off GNSS-on: 0.12500 + 0.83333 = 0.95833 mAh + long[] deviceStats = new long[descriptor.statsArrayLength]; + stats.getDeviceStats(deviceStats, states(POWER_STATE_OTHER, SCREEN_STATE_ON)); + assertThat(statsLayout.getDevicePowerEstimate(deviceStats)) + .isWithin(PRECISION).of(0.06944); + + stats.getDeviceStats(deviceStats, states(POWER_STATE_OTHER, SCREEN_STATE_OTHER)); + assertThat(statsLayout.getDevicePowerEstimate(deviceStats)) + .isWithin(PRECISION).of(0.12500 + 0.83333); + + // UID1 = + // scr-on FG: 2500 -> 0.06944 mAh + // scr-off BG: 2500/7500 * 0.95833 = 0.31944 mAh + // scr-off FGS: 1000/7500 * 0.95833 = 0.12777 mAh + long[] uidStats = new long[descriptor.uidStatsArrayLength]; + stats.getUidStats(uidStats, APP_UID1, + states(POWER_STATE_OTHER, SCREEN_STATE_ON, PROCESS_STATE_FOREGROUND)); + assertThat(statsLayout.getUidPowerEstimate(uidStats)) + .isWithin(PRECISION).of(0.06944); + + stats.getUidStats(uidStats, APP_UID1, + states(POWER_STATE_OTHER, SCREEN_STATE_OTHER, PROCESS_STATE_BACKGROUND)); + assertThat(statsLayout.getUidPowerEstimate(uidStats)) + .isWithin(PRECISION).of(0.31944); + + stats.getUidStats(uidStats, APP_UID1, + states(POWER_STATE_OTHER, SCREEN_STATE_OTHER, PROCESS_STATE_FOREGROUND_SERVICE)); + assertThat(statsLayout.getUidPowerEstimate(uidStats)) + .isWithin(PRECISION).of(0.12777); + + // UID2 = + // scr-off cached: 4000/7500 * 0.95833 = 0.51111 mAh + stats.getUidStats(uidStats, APP_UID2, + states(POWER_STATE_OTHER, SCREEN_STATE_OTHER, PROCESS_STATE_CACHED)); + assertThat(statsLayout.getUidPowerEstimate(uidStats)) + .isWithin(PRECISION).of(0.51111); + + stats.getUidStats(uidStats, APP_UID2, + states(POWER_STATE_OTHER, SCREEN_STATE_ON, PROCESS_STATE_CACHED)); + assertThat(statsLayout.getUidPowerEstimate(uidStats)) + .isWithin(PRECISION).of(0); + } + + @Test + public void energyConsumerModel() { + when(mConsumedEnergyRetriever.getEnergyConsumerIds(EnergyConsumerType.GNSS, null)) + .thenReturn(new int[]{ENERGY_CONSUMER_ID}); + GnssPowerStatsProcessor processor = new GnssPowerStatsProcessor( + mStatsRule.getPowerProfile(), mUidResolver); + + PowerComponentAggregatedPowerStats stats = createAggregatedPowerStats(processor); + + GnssPowerStatsCollector collector = new GnssPowerStatsCollector(mInjector); + collector.addConsumer( + powerStats -> { + processor.addPowerStats(stats, powerStats, mMonotonicClock.monotonicTime()); + }); + collector.setEnabled(true); + + // Establish a baseline + when(mConsumedEnergyRetriever.getConsumedEnergyUws(new int[]{ENERGY_CONSUMER_ID})) + .thenReturn(new long[]{uCtoUj(10000)}); + collector.collectAndDeliverStats(); + + processor.noteStateChange(stats, buildHistoryItem(0, true, APP_UID1)); + + // Turn the screen off after 2.5 seconds + stats.setState(STATE_SCREEN, SCREEN_STATE_OTHER, 2500); + stats.setUidState(APP_UID1, STATE_PROCESS_STATE, PROCESS_STATE_BACKGROUND, 2500); + stats.setUidState(APP_UID1, STATE_PROCESS_STATE, PROCESS_STATE_FOREGROUND_SERVICE, 5000); + + processor.noteStateChange(stats, buildHistoryItem(6000, false, APP_UID1)); + + when(mConsumedEnergyRetriever.getConsumedEnergyUws(new int[]{ENERGY_CONSUMER_ID})) + .thenReturn(new long[]{uCtoUj(2_170_000)}); + collector.collectAndDeliverStats(); + + processor.noteStateChange(stats, buildHistoryItem(7000, true, APP_UID2)); + processor.noteStateChange(stats, buildHistoryItem(7000, + GnssSignalQuality.GNSS_SIGNAL_QUALITY_GOOD)); + processor.noteStateChange(stats, buildHistoryItem(8000, + GnssSignalQuality.GNSS_SIGNAL_QUALITY_POOR)); + mStatsRule.setTime(11_000, 11_000); + when(mConsumedEnergyRetriever.getConsumedEnergyUws(new int[]{ENERGY_CONSUMER_ID})) + .thenReturn(new long[]{uCtoUj(3_610_000)}); + collector.collectAndDeliverStats(); + + processor.finish(stats, 11_000); + + PowerStats.Descriptor descriptor = stats.getPowerStatsDescriptor(); + BinaryStatePowerStatsLayout statsLayout = new BinaryStatePowerStatsLayout(); + statsLayout.fromExtras(descriptor.extras); + + // Total estimated power = 3,600,000 uC = 1.0 mAh + // of which 3,000,000 is distributed: + // Screen-on - 2500/6000 * 2160000 = 900000 uC = 0.25 mAh + // Screen-off - 3500/6000 * 2160000 = 1260000 uC = 0.35 mAh + // and 600,000 was fully with screen off: + // Screen-off - 1440000 uC = 0.4 mAh + long[] deviceStats = new long[descriptor.statsArrayLength]; + stats.getDeviceStats(deviceStats, states(POWER_STATE_OTHER, SCREEN_STATE_ON)); + assertThat(statsLayout.getDevicePowerEstimate(deviceStats)) + .isWithin(PRECISION).of(0.25); + + stats.getDeviceStats(deviceStats, states(POWER_STATE_OTHER, SCREEN_STATE_OTHER)); + assertThat(statsLayout.getDevicePowerEstimate(deviceStats)) + .isWithin(PRECISION).of(0.35 + 0.4); + + // UID1 = + // 2,160,000 uC = 0.6 mAh + // split between three different states + // fg screen-on: 2500/6000 + // bg screen-off: 2500/6000 + // fgs screen-off: 1000/6000 + double expectedPower1 = 0.6; + long[] uidStats = new long[descriptor.uidStatsArrayLength]; + stats.getUidStats(uidStats, APP_UID1, + states(POWER_STATE_OTHER, SCREEN_STATE_ON, PROCESS_STATE_FOREGROUND)); + assertThat(statsLayout.getUidPowerEstimate(uidStats)) + .isWithin(PRECISION).of(expectedPower1 * 2500 / 6000); + + stats.getUidStats(uidStats, APP_UID1, + states(POWER_STATE_OTHER, SCREEN_STATE_OTHER, PROCESS_STATE_BACKGROUND)); + assertThat(statsLayout.getUidPowerEstimate(uidStats)) + .isWithin(PRECISION).of(expectedPower1 * 2500 / 6000); + + stats.getUidStats(uidStats, APP_UID1, + states(POWER_STATE_OTHER, SCREEN_STATE_OTHER, PROCESS_STATE_FOREGROUND_SERVICE)); + assertThat(statsLayout.getUidPowerEstimate(uidStats)) + .isWithin(PRECISION).of(expectedPower1 * 1000 / 6000); + + // UID2 = + // 1440000 mA-ms = 0.4 mAh + // all in the same state + double expectedPower2 = 0.4; + stats.getUidStats(uidStats, APP_UID2, + states(POWER_STATE_OTHER, SCREEN_STATE_OTHER, PROCESS_STATE_CACHED)); + assertThat(statsLayout.getUidPowerEstimate(uidStats)) + .isWithin(PRECISION).of(expectedPower2); + + stats.getUidStats(uidStats, APP_UID2, + states(POWER_STATE_OTHER, SCREEN_STATE_ON, PROCESS_STATE_CACHED)); + assertThat(statsLayout.getUidPowerEstimate(uidStats)) + .isWithin(PRECISION).of(0); + } + + private BatteryStats.HistoryItem buildHistoryItem(int timestamp, boolean stateOn, + int uid) { + mStatsRule.setTime(timestamp, timestamp); + BatteryStats.HistoryItem historyItem = new BatteryStats.HistoryItem(); + historyItem.time = mMonotonicClock.monotonicTime(); + historyItem.states = stateOn ? BatteryStats.HistoryItem.STATE_GPS_ON_FLAG : 0; + if (stateOn) { + historyItem.eventCode = BatteryStats.HistoryItem.EVENT_STATE_CHANGE + | BatteryStats.HistoryItem.EVENT_FLAG_START; + } else { + historyItem.eventCode = BatteryStats.HistoryItem.EVENT_STATE_CHANGE + | BatteryStats.HistoryItem.EVENT_FLAG_FINISH; + } + historyItem.eventTag = historyItem.localEventTag; + historyItem.eventTag.uid = uid; + historyItem.eventTag.string = "gnss"; + return historyItem; + } + + private BatteryStats.HistoryItem buildHistoryItem(int timestamp, int signalLevel) { + mStatsRule.setTime(timestamp, timestamp); + BatteryStats.HistoryItem historyItem = new BatteryStats.HistoryItem(); + historyItem.time = mMonotonicClock.monotonicTime(); + historyItem.states = BatteryStats.HistoryItem.STATE_GPS_ON_FLAG; + historyItem.states2 = + signalLevel << BatteryStats.HistoryItem.STATE2_GPS_SIGNAL_QUALITY_SHIFT; + return historyItem; + } + + private int[] states(int... states) { + return states; + } + + private static PowerComponentAggregatedPowerStats createAggregatedPowerStats( + BinaryStatePowerStatsProcessor processor) { + AggregatedPowerStatsConfig config = new AggregatedPowerStatsConfig(); + config.trackPowerComponent(BatteryConsumer.POWER_COMPONENT_GNSS) + .trackDeviceStates( + AggregatedPowerStatsConfig.STATE_POWER, + AggregatedPowerStatsConfig.STATE_SCREEN) + .trackUidStates( + AggregatedPowerStatsConfig.STATE_POWER, + AggregatedPowerStatsConfig.STATE_SCREEN, + AggregatedPowerStatsConfig.STATE_PROCESS_STATE) + .setProcessor(processor); + + AggregatedPowerStats aggregatedPowerStats = new AggregatedPowerStats(config); + PowerComponentAggregatedPowerStats powerComponentStats = + aggregatedPowerStats.getPowerComponentStats(BatteryConsumer.POWER_COMPONENT_GNSS); + processor.start(powerComponentStats, 0); + + powerComponentStats.setState(STATE_POWER, POWER_STATE_OTHER, 0); + powerComponentStats.setState(STATE_SCREEN, SCREEN_STATE_ON, 0); + powerComponentStats.setUidState(APP_UID1, STATE_PROCESS_STATE, PROCESS_STATE_FOREGROUND, 0); + powerComponentStats.setUidState(APP_UID2, STATE_PROCESS_STATE, PROCESS_STATE_CACHED, 0); + + return powerComponentStats; + } + + private static long uCtoUj(long uc) { + return (long) (uc * (double) VOLTAGE_MV / 1000); + } +} diff --git a/services/tests/servicestests/src/com/android/server/accessibility/ProxyManagerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/ProxyManagerTest.java index d4f0d5aa7ef6..52b33db556e6 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/ProxyManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/ProxyManagerTest.java @@ -593,12 +593,6 @@ public class ProxyManagerTest { } @Override - public void onMagnificationSystemUIConnectionChanged(boolean connected) - throws RemoteException { - - } - - @Override public void onMagnificationChanged(int displayId, Region region, MagnificationConfig config) throws RemoteException { diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationConnectionManagerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationConnectionManagerTest.java index 0de5807067e6..87fe6cf8f283 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationConnectionManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationConnectionManagerTest.java @@ -59,7 +59,6 @@ import android.view.accessibility.MagnificationAnimationCallback; import androidx.test.core.app.ApplicationProvider; import androidx.test.filters.FlakyTest; -import com.android.compatibility.common.util.TestUtils; import com.android.internal.util.test.FakeSettingsProvider; import com.android.server.LocalServices; import com.android.server.accessibility.AccessibilityTraceManager; @@ -77,7 +76,6 @@ import org.mockito.invocation.InvocationOnMock; */ public class MagnificationConnectionManagerTest { - private static final int WAIT_CONNECTION_TIMEOUT_SECOND = 1; private static final int CURRENT_USER_ID = UserHandle.USER_SYSTEM; private static final int SERVICE_ID = 1; @@ -117,19 +115,17 @@ public class MagnificationConnectionManagerTest { private void stubSetConnection(boolean needDelay) { doAnswer((InvocationOnMock invocation) -> { final boolean connect = (Boolean) invocation.getArguments()[0]; - // Use post to simulate setConnection() called by another process. - final Context context = ApplicationProvider.getApplicationContext(); + // Simulates setConnection() called by another process. if (needDelay) { + final Context context = ApplicationProvider.getApplicationContext(); context.getMainThreadHandler().postDelayed( () -> { mMagnificationConnectionManager.setConnection( connect ? mMockConnection.getConnection() : null); }, 10); } else { - context.getMainThreadHandler().post(() -> { - mMagnificationConnectionManager.setConnection( - connect ? mMockConnection.getConnection() : null); - }); + mMagnificationConnectionManager.setConnection( + connect ? mMockConnection.getConnection() : null); } return true; }).when(mMockStatusBarManagerInternal).requestMagnificationConnection(anyBoolean()); @@ -633,10 +629,9 @@ public class MagnificationConnectionManagerTest { } @Test - public void isConnected_requestConnection_expectedValue() throws Exception { + public void isConnected_requestConnection_expectedValue() throws RemoteException { mMagnificationConnectionManager.requestConnection(true); - TestUtils.waitUntil("connection is not ready", WAIT_CONNECTION_TIMEOUT_SECOND, - () -> mMagnificationConnectionManager.isConnected()); + assertTrue(mMagnificationConnectionManager.isConnected()); mMagnificationConnectionManager.requestConnection(false); assertFalse(mMagnificationConnectionManager.isConnected()); diff --git a/services/tests/servicestests/src/com/android/server/am/OomAdjusterTests.java b/services/tests/servicestests/src/com/android/server/am/OomAdjusterTests.java index 8cbed2c7ffb8..31bf5f046cff 100644 --- a/services/tests/servicestests/src/com/android/server/am/OomAdjusterTests.java +++ b/services/tests/servicestests/src/com/android/server/am/OomAdjusterTests.java @@ -61,20 +61,6 @@ public class OomAdjusterTests { private static final long USAGE_STATS_INTERACTION = 10 * 60 * 1000L; private static final long SERVICE_USAGE_INTERACTION = 60 * 1000; - static class MyOomAdjuster extends OomAdjuster { - - MyOomAdjuster(ActivityManagerService service, ProcessList processList, - ActiveUids activeUids) { - super(service, processList, activeUids); - } - - @Override - protected boolean isChangeEnabled(int changeId, ApplicationInfo app, - boolean defaultValue) { - return true; - } - } - @BeforeClass public static void setUpOnce() { sContext = getInstrumentation().getTargetContext(); @@ -99,7 +85,15 @@ public class OomAdjusterTests { final AppProfiler profiler = mock(AppProfiler.class); setFieldValue(AppProfiler.class, profiler, "mProfilerLock", new Object()); setFieldValue(ActivityManagerService.class, sService, "mAppProfiler", profiler); - sService.mOomAdjuster = new MyOomAdjuster(sService, sService.mProcessList, null); + final OomAdjuster.Injector injector = new OomAdjuster.Injector(){ + @Override + boolean isChangeEnabled(int changeId, ApplicationInfo app, + boolean defaultValue) { + return true; + } + }; + sService.mOomAdjuster = new OomAdjuster(sService, sService.mProcessList, null, + injector); LocalServices.addService(UsageStatsManagerInternal.class, mock(UsageStatsManagerInternal.class)); sService.mUsageStatsService = LocalServices.getService(UsageStatsManagerInternal.class); diff --git a/services/tests/servicestests/src/com/android/server/audio/AudioServerPermissionProviderTest.java b/services/tests/servicestests/src/com/android/server/audio/AudioServerPermissionProviderTest.java index 8d772ad5c124..0f3b0aa72b72 100644 --- a/services/tests/servicestests/src/com/android/server/audio/AudioServerPermissionProviderTest.java +++ b/services/tests/servicestests/src/com/android/server/audio/AudioServerPermissionProviderTest.java @@ -15,15 +15,23 @@ */ package com.android.server.audio; +import static com.android.server.audio.AudioServerPermissionProvider.MONITORED_PERMS; + +import static org.mockito.AdditionalMatchers.aryEq; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyByte; import static org.mockito.ArgumentMatchers.argThat; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.any; +import static org.mockito.Mockito.clearInvocations; +import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import android.os.RemoteException; import android.platform.test.annotations.Presubmit; import androidx.test.ext.junit.runners.AndroidJUnit4; @@ -45,6 +53,8 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Objects; +import java.util.function.BiPredicate; +import java.util.function.Supplier; @RunWith(AndroidJUnit4.class) @Presubmit @@ -64,6 +74,9 @@ public final class AudioServerPermissionProviderTest { @Mock public PackageState mMockPackageStateFive_10001_four; @Mock public PackageState mMockPackageStateSix_10000_two; + @Mock public BiPredicate<Integer, String> mMockPermPred; + @Mock public Supplier<int[]> mMockUserIdSupplier; + public List<UidPackageState> mInitPackageListExpected; // Argument matcher which matches that the state is equal even if the package names are out of @@ -148,6 +161,13 @@ public final class AudioServerPermissionProviderTest { when(mMockPackageStateSix_10000_two.getAppId()).thenReturn(10000); when(mMockPackageStateSix_10000_two.getPackageName()).thenReturn("com.package.two"); + + when(mMockUserIdSupplier.get()).thenReturn(new int[] {0, 1}); + + when(mMockPermPred.test(eq(10000), eq(MONITORED_PERMS[0]))).thenReturn(true); + when(mMockPermPred.test(eq(110001), eq(MONITORED_PERMS[0]))).thenReturn(true); + when(mMockPermPred.test(eq(10001), eq(MONITORED_PERMS[1]))).thenReturn(true); + when(mMockPermPred.test(eq(110000), eq(MONITORED_PERMS[1]))).thenReturn(true); } @Test @@ -168,7 +188,9 @@ public final class AudioServerPermissionProviderTest { createUidPackageState( 10001, List.of("com.package.two", "com.package.four"))); - mPermissionProvider = new AudioServerPermissionProvider(initPackageListData); + mPermissionProvider = + new AudioServerPermissionProvider( + initPackageListData, mMockPermPred, mMockUserIdSupplier); mPermissionProvider.onServiceStart(mMockPc); verify(mMockPc) .populatePackagesForUids(argThat(new PackageStateListMatcher(expectedPackageList))); @@ -179,7 +201,9 @@ public final class AudioServerPermissionProviderTest { // 10000: one | 10001: two var initPackageListData = List.of(mMockPackageStateOne_10000_one, mMockPackageStateTwo_10001_two); - mPermissionProvider = new AudioServerPermissionProvider(initPackageListData); + mPermissionProvider = + new AudioServerPermissionProvider( + initPackageListData, mMockPermPred, mMockUserIdSupplier); mPermissionProvider.onServiceStart(mMockPc); // new uid, including user component @@ -196,7 +220,9 @@ public final class AudioServerPermissionProviderTest { // 10000: one | 10001: two var initPackageListData = List.of(mMockPackageStateOne_10000_one, mMockPackageStateTwo_10001_two); - mPermissionProvider = new AudioServerPermissionProvider(initPackageListData); + mPermissionProvider = + new AudioServerPermissionProvider( + initPackageListData, mMockPermPred, mMockUserIdSupplier); mPermissionProvider.onServiceStart(mMockPc); // Includes user-id @@ -211,7 +237,9 @@ public final class AudioServerPermissionProviderTest { // 10000: one | 10001: two var initPackageListData = List.of(mMockPackageStateOne_10000_one, mMockPackageStateTwo_10001_two); - mPermissionProvider = new AudioServerPermissionProvider(initPackageListData); + mPermissionProvider = + new AudioServerPermissionProvider( + initPackageListData, mMockPermPred, mMockUserIdSupplier); mPermissionProvider.onServiceStart(mMockPc); // Includes user-id @@ -233,7 +261,9 @@ public final class AudioServerPermissionProviderTest { mMockPackageStateOne_10000_one, mMockPackageStateTwo_10001_two, mMockPackageStateSix_10000_two); - mPermissionProvider = new AudioServerPermissionProvider(initPackageListData); + mPermissionProvider = + new AudioServerPermissionProvider( + initPackageListData, mMockPermPred, mMockUserIdSupplier); mPermissionProvider.onServiceStart(mMockPc); // Includes user-id @@ -255,7 +285,9 @@ public final class AudioServerPermissionProviderTest { mMockPackageStateOne_10000_one, mMockPackageStateTwo_10001_two, mMockPackageStateSix_10000_two); - mPermissionProvider = new AudioServerPermissionProvider(initPackageListData); + mPermissionProvider = + new AudioServerPermissionProvider( + initPackageListData, mMockPermPred, mMockUserIdSupplier); mPermissionProvider.onServiceStart(mMockPc); mPermissionProvider.onModifyPackageState(1_10000, "com.package.one", true /* isRemove */); verify(mMockPc) @@ -299,6 +331,72 @@ public final class AudioServerPermissionProviderTest { verify(newMockPc).updatePackagesForUid(any()); } + @Test + public void testPermissionsPopulated_onStart() throws Exception { + // expected state from setUp: + // PERM[0]: [10000, 110001] + // PERM[1]: [10001, 110000] + // PERM[...]: [] + var initPackageListData = + List.of(mMockPackageStateOne_10000_one, mMockPackageStateTwo_10001_two); + mPermissionProvider = + new AudioServerPermissionProvider( + initPackageListData, mMockPermPred, mMockUserIdSupplier); + + mPermissionProvider.onServiceStart(mMockPc); + verify(mMockPc).populatePermissionState(eq((byte) 0), aryEq(new int[] {10000, 110001})); + verify(mMockPc).populatePermissionState(eq((byte) 1), aryEq(new int[] {10001, 110000})); + for (int i = 2; i < MONITORED_PERMS.length; i++) { + verify(mMockPc).populatePermissionState(eq((byte) i), aryEq(new int[] {})); + } + verify(mMockPc, times(MONITORED_PERMS.length)).populatePermissionState(anyByte(), any()); + } + + @Test + public void testPermissionsPopulated_onChange() throws Exception { + var initPackageListData = + List.of(mMockPackageStateOne_10000_one, mMockPackageStateTwo_10001_two); + mPermissionProvider = + new AudioServerPermissionProvider( + initPackageListData, mMockPermPred, mMockUserIdSupplier); + + mPermissionProvider.onServiceStart(mMockPc); + clearInvocations(mMockPc); + // Ensure the provided permission state is changed + when(mMockPermPred.test(eq(110001), eq(MONITORED_PERMS[1]))).thenReturn(true); + + mPermissionProvider.onPermissionStateChanged(); + verify(mMockPc) + .populatePermissionState(eq((byte) 1), aryEq(new int[] {10001, 110000, 110001})); + verify(mMockPc).populatePermissionState(anyByte(), any()); // exactly once + } + + @Test + public void testPermissionPopulatedDeferred_onDeadService() throws Exception { + var initPackageListData = + List.of(mMockPackageStateOne_10000_one, mMockPackageStateTwo_10001_two); + mPermissionProvider = + new AudioServerPermissionProvider( + initPackageListData, mMockPermPred, mMockUserIdSupplier); + + // throw on the first call to mark the service as dead + doThrow(new RemoteException()) + .doNothing() + .when(mMockPc) + .populatePermissionState(anyByte(), any()); + mPermissionProvider.onServiceStart(mMockPc); + clearInvocations(mMockPc); + clearInvocations(mMockPermPred); + + mPermissionProvider.onPermissionStateChanged(); + verify(mMockPermPred, never()).test(any(), any()); + verify(mMockPc, never()).populatePermissionState(anyByte(), any()); + mPermissionProvider.onServiceStart(mMockPc); + for (int i = 0; i < MONITORED_PERMS.length; i++) { + verify(mMockPc).populatePermissionState(eq((byte) i), any()); + } + } + private static UidPackageState createUidPackageState(int uid, List<String> packages) { var res = new UidPackageState(); res.uid = uid; diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClientTest.java index 40de1b253dea..182d60328440 100644 --- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClientTest.java +++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClientTest.java @@ -320,7 +320,7 @@ public class FingerprintAuthenticationClientTest { } @Test - public void luxProbeNotEnabledOnStartWhenNotWake() throws RemoteException { + public void luxProbeDisabledOnStartWhenNotWake() throws RemoteException { luxProbeEnabledOnStart(false /* isAwake */); } @@ -337,6 +337,7 @@ public class FingerprintAuthenticationClientTest { .getValue().toAidlContext()); verify(mLuxProbe, isAwake ? times(1) : never()).enable(); + verify(mLuxProbe, isAwake ? never() : times(1)).disable(); } @Test diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/camera/VirtualCameraControllerTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/camera/VirtualCameraControllerTest.java index 4505a8b88b25..627ca03c8d96 100644 --- a/services/tests/servicestests/src/com/android/server/companion/virtual/camera/VirtualCameraControllerTest.java +++ b/services/tests/servicestests/src/com/android/server/companion/virtual/camera/VirtualCameraControllerTest.java @@ -95,7 +95,7 @@ public class VirtualCameraControllerTest { mVirtualCameraController = new VirtualCameraController(mVirtualCameraServiceMock, DEVICE_POLICY_CUSTOM, DEVICE_ID); when(mVirtualCameraServiceMock.registerCamera(any(), any(), anyInt())).thenReturn(true); - when(mVirtualCameraServiceMock.getCameraId(any())).thenReturn(0); + when(mVirtualCameraServiceMock.getCameraId(any())).thenReturn("0"); } @After diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageTest.java index 2984cfa06550..2984cfa06550 100755..100644 --- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageTest.java +++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageTest.java 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 3cab75b5d320..3d6884925098 100644 --- a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java @@ -18,11 +18,13 @@ package com.android.server.net; import static android.Manifest.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS; import static android.Manifest.permission.NETWORK_STACK; +import static android.app.ActivityManager.MAX_PROCESS_STATE; import static android.app.ActivityManager.PROCESS_CAPABILITY_ALL; import static android.app.ActivityManager.PROCESS_CAPABILITY_NONE; import static android.app.ActivityManager.PROCESS_CAPABILITY_POWER_RESTRICTED_NETWORK; import static android.app.ActivityManager.PROCESS_CAPABILITY_USER_RESTRICTED_NETWORK; import static android.app.ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND; +import static android.app.ActivityManager.PROCESS_STATE_LAST_ACTIVITY; import static android.app.ActivityManager.PROCESS_STATE_SERVICE; import static android.app.ActivityManager.PROCESS_STATE_TOP; import static android.net.ConnectivityManager.BLOCKED_METERED_REASON_DATA_SAVER; @@ -165,9 +167,11 @@ import android.os.PowerManagerInternal; import android.os.PowerSaveState; import android.os.RemoteException; import android.os.SimpleClock; +import android.os.SystemClock; import android.os.UserHandle; import android.os.UserManager; 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; @@ -2158,7 +2162,8 @@ public class NetworkPolicyManagerServiceTest { @Test @RequiresFlagsEnabled(Flags.FLAG_NETWORK_BLOCKED_FOR_TOP_SLEEPING_AND_ABOVE) - public void testBackgroundChainOnProcStateChange() throws Exception { + @RequiresFlagsDisabled(Flags.FLAG_USE_DIFFERENT_DELAYS_FOR_BACKGROUND_CHAIN) + public void testBackgroundChainOnProcStateChangeSameDelay() throws Exception { // initialization calls setFirewallChainEnabled, so we want to reset the invocations. clearInvocations(mNetworkManager); @@ -2186,6 +2191,59 @@ public class NetworkPolicyManagerServiceTest { } @Test + @RequiresFlagsEnabled({ + Flags.FLAG_NETWORK_BLOCKED_FOR_TOP_SLEEPING_AND_ABOVE, + 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)); + + // Tweak delays to avoid waiting too long in tests. + mService.mBackgroundRestrictionShortDelayMs = 50; + mService.mBackgroundRestrictionLongDelayMs = 1000; + + int procStateSeq = 231; // Any arbitrary starting sequence. + for (int ps = BACKGROUND_THRESHOLD_STATE; ps <= MAX_PROCESS_STATE; ps++) { + clearInvocations(mNetworkManager); + + // Make sure app is in correct process-state to access network. + callAndWaitOnUidStateChanged(UID_A, BACKGROUND_THRESHOLD_STATE - 1, procStateSeq++); + verify(mNetworkManager).setFirewallUidRule(FIREWALL_CHAIN_BACKGROUND, UID_A, + FIREWALL_RULE_ALLOW); + assertFalse(mService.isUidNetworkingBlocked(UID_A, false)); + + // Now put the app into the background and test that it eventually loses network. + callAndWaitOnUidStateChanged(UID_A, ps, procStateSeq++); + + final long uidStateChangeTime = SystemClock.uptimeMillis(); + if (ps <= PROCESS_STATE_LAST_ACTIVITY) { + // Verify that the app is blocked after long delay but not after short delay. + waitForDelayedMessageOnHandler(mService.mBackgroundRestrictionShortDelayMs + 1); + verify(mNetworkManager, never()).setFirewallUidRule(FIREWALL_CHAIN_BACKGROUND, + UID_A, FIREWALL_RULE_DEFAULT); + assertFalse(mService.isUidNetworkingBlocked(UID_A, false)); + + final long timeUntilLongDelay = uidStateChangeTime + + mService.mBackgroundRestrictionLongDelayMs - SystemClock.uptimeMillis(); + assertTrue("No time left to verify long delay in background transition", + timeUntilLongDelay >= 0); + + waitForDelayedMessageOnHandler(timeUntilLongDelay + 1); + verify(mNetworkManager).setFirewallUidRule(FIREWALL_CHAIN_BACKGROUND, UID_A, + FIREWALL_RULE_DEFAULT); + assertTrue(mService.isUidNetworkingBlocked(UID_A, false)); + } else { + // Verify that the app is blocked after short delay. + waitForDelayedMessageOnHandler(mService.mBackgroundRestrictionShortDelayMs + 1); + verify(mNetworkManager).setFirewallUidRule(FIREWALL_CHAIN_BACKGROUND, UID_A, + FIREWALL_RULE_DEFAULT); + assertTrue(mService.isUidNetworkingBlocked(UID_A, false)); + } + } + } + + @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. @@ -2881,6 +2939,11 @@ public class NetworkPolicyManagerServiceTest { } } + /** + * This posts a blocking message to the service handler with the given delayMs and waits for it + * to complete. This ensures that all messages posted before the given delayMs will also + * have been executed before this method returns and can be verified in subsequent code. + */ private void waitForDelayedMessageOnHandler(long delayMs) throws InterruptedException { final CountDownLatch latch = new CountDownLatch(1); mService.getHandlerForTesting().postDelayed(latch::countDown, delayMs); 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 32bbc7a618d1..6d79ae467bf0 100644 --- a/services/tests/servicestests/src/com/android/server/power/ThermalManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/power/ThermalManagerServiceTest.java @@ -551,7 +551,7 @@ public class ThermalManagerServiceTest { // Add some time-series data for (int i = 1; i < 20; ++i) { - samples.add(0, watcher.createSampleForTesting(1000 * i, 25.0f + 0.5f * i)); + samples.add(watcher.createSampleForTesting(1000 * i, 25.0f + 0.5f * i)); } // Now the forecast should vary depending on how far ahead we are trying to predict 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 15c9bfb74d92..f07e5bca4f70 100755..100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java @@ -511,7 +511,8 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { TestableNotificationManagerService.StrongAuthTrackerFake mStrongAuthTracker; TestableFlagResolver mTestFlagResolver = new TestableFlagResolver(); - @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(DEVICE_DEFAULT); + @Rule + public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); private InstanceIdSequence mNotificationInstanceIdSequence = new InstanceIdSequenceFake( 1 << 30); @Mock @@ -626,7 +627,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { when(mPackageManagerClient.getPackageUidAsUser(any(), anyInt())).thenReturn(mUid); when(mPackageManagerInternal.isSameApp(anyString(), anyInt(), anyInt())).thenAnswer( (Answer<Boolean>) invocation -> { - // TODO: b/317957802 - This is overly broad and basically makes ANY + // TODO: b/317957802 - This is overly broad and basically makes ANY // isSameApp() check pass, requiring Mockito.reset() for meaningful // tests! Make it more precise. Object[] args = invocation.getArguments(); @@ -6892,22 +6893,15 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { verify(visitor, times(1)).accept(eq(personIcon3.getUri())); } - private PendingIntent getPendingIntentWithUri(Uri uri) { - return PendingIntent.getActivity(mContext, 0, - new Intent("action", uri), - PendingIntent.FLAG_IMMUTABLE); - } - @Test - @EnableFlags({android.app.Flags.FLAG_VISIT_RISKY_URIS}) - public void testVisitUris_callStyle_ongoingCall() { + public void testVisitUris_callStyle() { Icon personIcon = Icon.createWithContentUri("content://media/person"); Icon verificationIcon = Icon.createWithContentUri("content://media/verification"); Person callingPerson = new Person.Builder().setName("Someone") .setIcon(personIcon) .build(); - Uri hangUpUri = Uri.parse("content://intent/hangup"); - PendingIntent hangUpIntent = getPendingIntentWithUri(hangUpUri); + PendingIntent hangUpIntent = PendingIntent.getActivity(mContext, 0, new Intent(), + PendingIntent.FLAG_IMMUTABLE); Notification n = new Notification.Builder(mContext, "a") .setStyle(Notification.CallStyle.forOngoingCall(callingPerson, hangUpIntent) .setVerificationIcon(verificationIcon)) @@ -6920,42 +6914,10 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { verify(visitor, times(1)).accept(eq(personIcon.getUri())); verify(visitor, times(1)).accept(eq(verificationIcon.getUri())); - verify(visitor, times(1)).accept(eq(hangUpUri)); hangUpIntent.cancel(); } @Test - @EnableFlags({android.app.Flags.FLAG_VISIT_RISKY_URIS}) - public void testVisitUris_callStyle_incomingCall() { - Icon personIcon = Icon.createWithContentUri("content://media/person"); - Icon verificationIcon = Icon.createWithContentUri("content://media/verification"); - Person callingPerson = new Person.Builder().setName("Someone") - .setIcon(personIcon) - .build(); - Uri answerUri = Uri.parse("content://intent/answer"); - PendingIntent answerIntent = getPendingIntentWithUri(answerUri); - Uri declineUri = Uri.parse("content://intent/decline"); - PendingIntent declineIntent = getPendingIntentWithUri(declineUri); - Notification n = new Notification.Builder(mContext, "a") - .setStyle(Notification.CallStyle.forIncomingCall(callingPerson, declineIntent, - answerIntent) - .setVerificationIcon(verificationIcon)) - .setContentTitle("Calling...") - .setSmallIcon(android.R.drawable.sym_def_app_icon) - .build(); - - Consumer<Uri> visitor = (Consumer<Uri>) spy(Consumer.class); - n.visitUris(visitor); - - verify(visitor, times(1)).accept(eq(personIcon.getUri())); - verify(visitor, times(1)).accept(eq(verificationIcon.getUri())); - verify(visitor, times(1)).accept(eq(answerIntent.getIntent().getData())); - verify(visitor, times(1)).accept(eq(declineUri)); - answerIntent.cancel(); - declineIntent.cancel(); - } - - @Test public void testVisitUris_styleExtrasWithoutStyle() { Notification.Builder notification = new Notification.Builder(mContext, "a") .setSmallIcon(android.R.drawable.sym_def_app_icon); @@ -7001,87 +6963,23 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { } @Test - @EnableFlags({android.app.Flags.FLAG_VISIT_RISKY_URIS}) public void testVisitUris_wearableExtender() { Icon actionIcon = Icon.createWithContentUri("content://media/action"); Icon wearActionIcon = Icon.createWithContentUri("content://media/wearAction"); - Uri displayIntentUri = Uri.parse("content://intent/display"); - PendingIntent displayIntent = getPendingIntentWithUri(displayIntentUri); - Uri actionIntentUri = Uri.parse("content://intent/action"); - PendingIntent actionIntent = getPendingIntentWithUri(actionIntentUri); - Uri wearActionIntentUri = Uri.parse("content://intent/wear"); - PendingIntent wearActionIntent = getPendingIntentWithUri(wearActionIntentUri); + PendingIntent intent = PendingIntent.getActivity(mContext, 0, new Intent(), + PendingIntent.FLAG_IMMUTABLE); Notification n = new Notification.Builder(mContext, "a") .setSmallIcon(android.R.drawable.sym_def_app_icon) - .addAction( - new Notification.Action.Builder(actionIcon, "Hey!", actionIntent).build()) - .extend(new Notification.WearableExtender() - .setDisplayIntent(displayIntent) - .addAction(new Notification.Action.Builder(wearActionIcon, "Wear!", - wearActionIntent) - .build())) + .addAction(new Notification.Action.Builder(actionIcon, "Hey!", intent).build()) + .extend(new Notification.WearableExtender().addAction( + new Notification.Action.Builder(wearActionIcon, "Wear!", intent).build())) .build(); Consumer<Uri> visitor = (Consumer<Uri>) spy(Consumer.class); n.visitUris(visitor); verify(visitor).accept(eq(actionIcon.getUri())); - verify(visitor, times(1)).accept(eq(actionIntentUri)); verify(visitor).accept(eq(wearActionIcon.getUri())); - verify(visitor, times(1)).accept(eq(wearActionIntentUri)); - displayIntent.cancel(); - actionIntent.cancel(); - wearActionIntent.cancel(); - } - - @Test - @EnableFlags({android.app.Flags.FLAG_VISIT_RISKY_URIS}) - public void testVisitUris_tvExtender() { - Uri contentIntentUri = Uri.parse("content://intent/content"); - PendingIntent contentIntent = getPendingIntentWithUri(contentIntentUri); - Uri deleteIntentUri = Uri.parse("content://intent/delete"); - PendingIntent deleteIntent = getPendingIntentWithUri(deleteIntentUri); - Notification n = new Notification.Builder(mContext, "a") - .extend( - new Notification.TvExtender() - .setContentIntent(contentIntent) - .setDeleteIntent(deleteIntent)) - .build(); - - Consumer<Uri> visitor = (Consumer<Uri>) spy(Consumer.class); - n.visitUris(visitor); - - verify(visitor, times(1)).accept(eq(contentIntentUri)); - verify(visitor, times(1)).accept(eq(deleteIntentUri)); - contentIntent.cancel(); - deleteIntent.cancel(); - } - - @Test - @EnableFlags({android.app.Flags.FLAG_VISIT_RISKY_URIS}) - public void testVisitUris_carExtender() { - final String testParticipant = "testParticipant"; - Uri readPendingIntentUri = Uri.parse("content://intent/read"); - PendingIntent readPendingIntent = getPendingIntentWithUri(readPendingIntentUri); - Uri replyPendingIntentUri = Uri.parse("content://intent/reply"); - PendingIntent replyPendingIntent = getPendingIntentWithUri(replyPendingIntentUri); - final RemoteInput testRemoteInput = new RemoteInput.Builder("key").build(); - - Notification n = new Notification.Builder(mContext, "a") - .extend(new Notification.CarExtender().setUnreadConversation( - new Notification.CarExtender.Builder(testParticipant) - .setReadPendingIntent(readPendingIntent) - .setReplyAction(replyPendingIntent, testRemoteInput) - .build())) - .build(); - - Consumer<Uri> visitor = (Consumer<Uri>) spy(Consumer.class); - n.visitUris(visitor); - - verify(visitor, times(1)).accept(eq(readPendingIntentUri)); - verify(visitor, times(1)).accept(eq(replyPendingIntentUri)); - readPendingIntent.cancel(); - replyPendingIntent.cancel(); } @Test 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 863cda4905f1..594d6f2ebb98 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationVisitUrisTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationVisitUrisTest.java @@ -34,6 +34,7 @@ import android.net.Uri; import android.os.Bundle; import android.os.IBinder; import android.os.Parcel; +import android.platform.test.annotations.EnableFlags; import android.platform.test.flag.junit.SetFlagsRule; import android.util.Log; import android.view.LayoutInflater; @@ -87,6 +88,7 @@ import java.util.stream.Stream; import javax.annotation.Nullable; @RunWith(AndroidJUnit4.class) +@EnableFlags(Flags.FLAG_VISIT_PERSON_URI) public class NotificationVisitUrisTest extends UiServiceTestCase { @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); @@ -153,10 +155,6 @@ public class NotificationVisitUrisTest extends UiServiceTestCase { .put(Notification.Action.Builder.class, "extend") // Overwrites icon supplied to constructor. .put(Notification.BubbleMetadata.Builder.class, "setIcon") - // Overwrites intent supplied to constructor. - .put(Notification.BubbleMetadata.Builder.class, "setIntent") - // Overwrites intent supplied to constructor. - .put(Notification.BubbleMetadata.Builder.class, "setDeleteIntent") // Discards previously-added actions. .put(RemoteViews.class, "mergeRemoteViews") .build(); @@ -172,7 +170,6 @@ public class NotificationVisitUrisTest extends UiServiceTestCase { @Before public void setUp() { mContext = InstrumentationRegistry.getInstrumentation().getContext(); - mSetFlagsRule.enableFlags(Flags.FLAG_VISIT_RISKY_URIS); } @After @@ -700,14 +697,13 @@ public class NotificationVisitUrisTest extends UiServiceTestCase { } if (clazz == Intent.class) { - return new Intent("action", generateUri(where.plus(Intent.class))); + return new Intent("action"); } if (clazz == PendingIntent.class) { - // PendingIntent can have an Intent with a Uri. - Uri intentUri = generateUri(where.plus(PendingIntent.class)); - return PendingIntent.getActivity(mContext, 0, - new Intent("action", intentUri), + // PendingIntent can have an Intent with a Uri but those are inaccessible and + // not inspected. + return PendingIntent.getActivity(mContext, 0, new Intent("action"), PendingIntent.FLAG_IMMUTABLE); } diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java index abfb95c9431a..9352c1287ee1 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java @@ -17,7 +17,16 @@ package com.android.server.notification; import static android.app.AutomaticZenRule.TYPE_BEDTIME; +import static android.app.Flags.FLAG_MODES_UI; +import static android.provider.Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS; +import static android.provider.Settings.Global.ZEN_MODE_OFF; +import static android.service.notification.Condition.SOURCE_USER_ACTION; +import static android.service.notification.Condition.STATE_FALSE; +import static android.service.notification.Condition.STATE_TRUE; import static android.service.notification.ZenPolicy.CONVERSATION_SENDERS_IMPORTANT; +import static android.service.notification.ZenPolicy.CONVERSATION_SENDERS_NONE; +import static android.service.notification.ZenPolicy.PEOPLE_TYPE_ANYONE; +import static android.service.notification.ZenPolicy.STATE_ALLOW; import static com.google.common.truth.Truth.assertThat; @@ -33,6 +42,9 @@ import android.app.NotificationManager.Policy; import android.content.ComponentName; import android.net.Uri; import android.os.Parcel; +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.Settings; import android.service.notification.Condition; @@ -43,7 +55,6 @@ import android.service.notification.ZenPolicy; import android.util.Xml; import androidx.test.filters.SmallTest; -import androidx.test.runner.AndroidJUnit4; import com.android.modules.utils.TypedXmlPullParser; import com.android.modules.utils.TypedXmlSerializer; @@ -63,9 +74,13 @@ import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.time.Instant; +import java.util.List; + +import platform.test.runner.parameterized.ParameterizedAndroidJunit4; +import platform.test.runner.parameterized.Parameters; @SmallTest -@RunWith(AndroidJUnit4.class) +@RunWith(ParameterizedAndroidJunit4.class) public class ZenModeConfigTest extends UiServiceTestCase { private final String NAME = "name"; @@ -91,6 +106,16 @@ public class ZenModeConfigTest extends UiServiceTestCase { public final SetFlagsRule mSetFlagsRule = new SetFlagsRule( SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT); + @Parameters(name = "{0}") + public static List<FlagsParameterization> getParams() { + return FlagsParameterization.allCombinationsOf( + FLAG_MODES_UI); + } + + public ZenModeConfigTest(FlagsParameterization flags) { + mSetFlagsRule.setFlagsParameterization(flags); + } + @Before public final void setUp() { mSetFlagsRule.enableFlags(Flags.FLAG_MODES_API); @@ -101,13 +126,44 @@ public class ZenModeConfigTest extends UiServiceTestCase { ZenModeConfig config = getMutedRingerConfig(); assertTrue(ZenModeConfig.areAllPriorityOnlyRingerSoundsMuted(config)); - config.allowReminders = true; + if (Flags.modesUi()) { + config.manualRule.zenPolicy = new ZenPolicy.Builder(config.manualRule.zenPolicy) + .allowReminders(true) + .build(); + } else { + config.setAllowReminders(true); + } assertFalse(ZenModeConfig.areAllPriorityOnlyRingerSoundsMuted(config)); - config.allowReminders = false; + if (Flags.modesUi()) { + config.manualRule.zenPolicy = new ZenPolicy.Builder(config.manualRule.zenPolicy) + .allowReminders(false) + .build(); + } else { + config.setAllowReminders(false); + } + assertTrue(ZenModeConfig.areAllPriorityOnlyRingerSoundsMuted(config)); config.areChannelsBypassingDnd = true; + assertTrue(ZenModeConfig.areAllPriorityOnlyRingerSoundsMuted(config)); + + if (Flags.modesUi()) { + config.manualRule.zenPolicy = new ZenPolicy.Builder(config.manualRule.zenPolicy) + .allowPriorityChannels(true) + .build(); + } else { + config.setAllowPriorityChannels(true); + } + assertFalse(ZenModeConfig.areAllPriorityOnlyRingerSoundsMuted(config)); + config.areChannelsBypassingDnd = false; + if (Flags.modesUi()) { + config.manualRule.zenPolicy = new ZenPolicy.Builder(config.manualRule.zenPolicy) + .allowPriorityChannels(false) + .build(); + } else { + config.setAllowPriorityChannels(false); + } assertTrue(ZenModeConfig.areAllPriorityOnlyRingerSoundsMuted(config)); } @@ -122,6 +178,8 @@ public class ZenModeConfigTest extends UiServiceTestCase { @Test public void testZenPolicyToNotificationPolicy_classic() { ZenModeConfig config = getMutedAllConfig(); + // this shouldn't usually be directly set, but since it's a test that involved the default + // policy, calling setNotificationPolicy as a precondition may obscure issues config.suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_BADGE; // Explicitly allow conversations from priority senders to make sure that goes through @@ -154,8 +212,9 @@ public class ZenModeConfigTest extends UiServiceTestCase { @Test public void testZenPolicyToNotificationPolicy() { - mSetFlagsRule.enableFlags(Flags.FLAG_MODES_API); ZenModeConfig config = getMutedAllConfig(); + // this shouldn't usually be directly set, but since it's a test that involved the default + // policy, calling setNotificationPolicy as a precondition may obscure issues config.suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_BADGE; // Explicitly allow conversations from priority senders to make sure that goes through @@ -194,14 +253,16 @@ public class ZenModeConfigTest extends UiServiceTestCase { @Test public void testZenPolicyToNotificationPolicy_unsetChannelsTakesDefault() { - mSetFlagsRule.enableFlags(Flags.FLAG_MODES_API); ZenModeConfig config = new ZenModeConfig(); ZenPolicy zenPolicy = new ZenPolicy.Builder().build(); // When allowChannels is not set to anything in the ZenPolicy builder, make sure it takes // the default value from the zen mode config. Policy policy = config.toNotificationPolicy(zenPolicy); - assertEquals(config.allowPriorityChannels, policy.allowPriorityChannels()); + assertEquals(Flags.modesUi() + ? config.manualRule.zenPolicy.getPriorityChannelsAllowed() == STATE_ALLOW + : config.isAllowPriorityChannels(), + policy.allowPriorityChannels()); } @Test @@ -219,18 +280,22 @@ public class ZenModeConfigTest extends UiServiceTestCase { .build(); ZenModeConfig config = getMutedAllConfig(); - config.allowAlarms = true; - config.allowReminders = true; - config.allowEvents = true; - config.allowCalls = true; - config.allowCallsFrom = Policy.PRIORITY_SENDERS_CONTACTS; - config.allowMessages = true; - config.allowMessagesFrom = Policy.PRIORITY_SENDERS_STARRED; - config.allowConversations = false; - config.suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_BADGE; - config.suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_LIGHTS; - config.suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_AMBIENT; - ZenPolicy actual = config.toZenPolicy(); + if (Flags.modesUi()) { + config.manualRule.zenPolicy = expected.copy(); + } else { + config.setAllowAlarms(true); + config.setAllowReminders(true); + config.setAllowEvents(true); + config.setAllowCalls(true); + config.setAllowCallsFrom(Policy.PRIORITY_SENDERS_CONTACTS); + config.setAllowMessages(true); + config.setAllowMessagesFrom(Policy.PRIORITY_SENDERS_STARRED); + config.setAllowConversationsFrom(CONVERSATION_SENDERS_NONE); + config.setSuppressedVisualEffects(config.getSuppressedVisualEffects() + | Policy.SUPPRESSED_EFFECT_BADGE | Policy.SUPPRESSED_EFFECT_LIGHTS + | Policy.SUPPRESSED_EFFECT_AMBIENT); + } + ZenPolicy actual = config.getZenPolicy(); assertEquals(expected.getVisualEffectBadge(), actual.getVisualEffectBadge()); assertEquals(expected.getPriorityCategoryAlarms(), actual.getPriorityCategoryAlarms()); @@ -247,7 +312,6 @@ public class ZenModeConfigTest extends UiServiceTestCase { @Test public void testZenConfigToZenPolicy() { - mSetFlagsRule.enableFlags(Flags.FLAG_MODES_API); ZenPolicy expected = new ZenPolicy.Builder() .allowAlarms(true) .allowReminders(true) @@ -262,19 +326,24 @@ public class ZenModeConfigTest extends UiServiceTestCase { .build(); ZenModeConfig config = getMutedAllConfig(); - config.allowAlarms = true; - config.allowReminders = true; - config.allowEvents = true; - config.allowCalls = true; - config.allowCallsFrom = Policy.PRIORITY_SENDERS_CONTACTS; - config.allowMessages = true; - config.allowMessagesFrom = Policy.PRIORITY_SENDERS_STARRED; - config.allowConversations = false; - config.allowPriorityChannels = false; - config.suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_BADGE; - config.suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_LIGHTS; - config.suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_AMBIENT; - ZenPolicy actual = config.toZenPolicy(); + if (Flags.modesUi()) { + config.manualRule.zenPolicy = expected.copy(); + } else { + config.setAllowAlarms(true); + config.setAllowReminders(true); + + config.setAllowEvents(true); + config.setAllowCalls(true); + config.setAllowCallsFrom(Policy.PRIORITY_SENDERS_CONTACTS); + config.setAllowMessages(true); + config.setAllowMessagesFrom(Policy.PRIORITY_SENDERS_STARRED); + config.setAllowConversationsFrom(CONVERSATION_SENDERS_NONE); + config.setAllowPriorityChannels(false); + config.setSuppressedVisualEffects(config.getSuppressedVisualEffects() + | Policy.SUPPRESSED_EFFECT_BADGE | Policy.SUPPRESSED_EFFECT_LIGHTS + | Policy.SUPPRESSED_EFFECT_AMBIENT); + } + ZenPolicy actual = config.getZenPolicy(); assertEquals(expected.getVisualEffectBadge(), actual.getVisualEffectBadge()); assertEquals(expected.getPriorityCategoryAlarms(), actual.getPriorityCategoryAlarms()); @@ -296,20 +365,64 @@ public class ZenModeConfigTest extends UiServiceTestCase { assertTrue(ZenModeConfig.areAllPriorityOnlyRingerSoundsMuted(config)); assertTrue(ZenModeConfig.areAllZenBehaviorSoundsMuted(config)); - config.allowReminders = true; + if (Flags.modesUi()) { + config.manualRule.zenPolicy = new ZenPolicy.Builder(config.manualRule.zenPolicy) + .allowReminders(true) + .build(); + } else { + config.setAllowReminders(true); + } assertFalse(ZenModeConfig.areAllPriorityOnlyRingerSoundsMuted(config)); assertFalse(ZenModeConfig.areAllZenBehaviorSoundsMuted(config)); - config.allowReminders = false; + + if (Flags.modesUi()) { + config.manualRule.zenPolicy = new ZenPolicy.Builder(config.manualRule.zenPolicy) + .allowReminders(false) + .build(); + } else { + config.setAllowReminders(false); + } + + assertTrue(ZenModeConfig.areAllPriorityOnlyRingerSoundsMuted(config)); + assertTrue(ZenModeConfig.areAllZenBehaviorSoundsMuted(config)); config.areChannelsBypassingDnd = true; + if (Flags.modesUi()) { + config.manualRule.zenPolicy = new ZenPolicy.Builder(config.manualRule.zenPolicy) + .allowPriorityChannels(true) + .build(); + } else { + config.setAllowPriorityChannels(true); + } + assertFalse(ZenModeConfig.areAllPriorityOnlyRingerSoundsMuted(config)); assertFalse(ZenModeConfig.areAllZenBehaviorSoundsMuted(config)); - config.areChannelsBypassingDnd = false; - config.allowAlarms = true; + config.areChannelsBypassingDnd = false; + if (Flags.modesUi()) { + config.manualRule.zenPolicy = new ZenPolicy.Builder(config.manualRule.zenPolicy) + .allowPriorityChannels(false) + .build(); + } else { + config.setAllowPriorityChannels(false); + } + + if (Flags.modesUi()) { + config.manualRule.zenPolicy = new ZenPolicy.Builder(config.manualRule.zenPolicy) + .allowAlarms(true) + .build(); + } else { + config.setAllowAlarms(true); + } assertTrue(ZenModeConfig.areAllPriorityOnlyRingerSoundsMuted(config)); assertFalse(ZenModeConfig.areAllZenBehaviorSoundsMuted(config)); - config.allowAlarms = false; + if (Flags.modesUi()) { + config.manualRule.zenPolicy = new ZenPolicy.Builder(config.manualRule.zenPolicy) + .allowAlarms(false) + .build(); + } else { + config.setAllowAlarms(false); + } assertTrue(ZenModeConfig.areAllPriorityOnlyRingerSoundsMuted(config)); assertTrue(ZenModeConfig.areAllZenBehaviorSoundsMuted(config)); @@ -375,8 +488,6 @@ public class ZenModeConfigTest extends UiServiceTestCase { @Test public void testWriteToParcel() { - mSetFlagsRule.enableFlags(Flags.FLAG_MODES_API); - ZenModeConfig.ZenRule rule = new ZenModeConfig.ZenRule(); rule.configurationActivity = CONFIG_ACTIVITY; rule.component = OWNER; @@ -473,8 +584,6 @@ public class ZenModeConfigTest extends UiServiceTestCase { @Test public void testRuleXml() throws Exception { - mSetFlagsRule.enableFlags(Flags.FLAG_MODES_API); - ZenModeConfig.ZenRule rule = new ZenModeConfig.ZenRule(); rule.configurationActivity = CONFIG_ACTIVITY; rule.component = OWNER; @@ -719,8 +828,6 @@ public class ZenModeConfigTest extends UiServiceTestCase { @Test public void testZenPolicyXml() throws Exception { - mSetFlagsRule.enableFlags(Flags.FLAG_MODES_API); - ZenPolicy policy = new ZenPolicy.Builder() .allowCalls(ZenPolicy.PEOPLE_TYPE_CONTACTS) .allowMessages(ZenPolicy.PEOPLE_TYPE_NONE) @@ -770,63 +877,173 @@ public class ZenModeConfigTest extends UiServiceTestCase { fromXml.getVisualEffectNotificationList()); } + @Test + @EnableFlags(Flags.FLAG_MODES_UI) + public void testisManualActive_stateTrue() { + ZenModeConfig config = getMutedAllConfig(); + final ZenModeConfig.ZenRule newRule = new ZenModeConfig.ZenRule(); + newRule.type = AutomaticZenRule.TYPE_OTHER; + newRule.enabled = true; + newRule.conditionId = Uri.EMPTY; + newRule.allowManualInvocation = true; + config.manualRule = newRule; + config.manualRule.pkg = "android"; + config.manualRule.zenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS; + config.manualRule.condition = new Condition(Uri.EMPTY, "", STATE_TRUE, SOURCE_USER_ACTION); + + assertThat(config.isManualActive()).isTrue(); + } + + @Test + @EnableFlags(Flags.FLAG_MODES_UI) + public void testisManualActive_stateFalse() { + ZenModeConfig config = getMutedAllConfig(); + final ZenModeConfig.ZenRule newRule = new ZenModeConfig.ZenRule(); + newRule.type = AutomaticZenRule.TYPE_OTHER; + newRule.enabled = true; + newRule.conditionId = Uri.EMPTY; + newRule.allowManualInvocation = true; + config.manualRule = newRule; + config.manualRule.pkg = "android"; + config.manualRule.zenMode = ZEN_MODE_OFF; + config.manualRule.condition = new Condition(Uri.EMPTY, "", STATE_FALSE, SOURCE_USER_ACTION); + + assertThat(config.isManualActive()).isFalse(); + } + + @Test + @DisableFlags(Flags.FLAG_MODES_UI) + public void testisManualActive_noState() { + ZenModeConfig config = getMutedAllConfig(); + final ZenModeConfig.ZenRule newRule = new ZenModeConfig.ZenRule(); + newRule.type = AutomaticZenRule.TYPE_OTHER; + newRule.enabled = true; + newRule.conditionId = Uri.EMPTY; + newRule.allowManualInvocation = true; + config.manualRule = newRule; + config.manualRule.pkg = "android"; + config.manualRule.zenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS; + + assertThat(config.isManualActive()).isTrue(); + } + + @Test + public void testisManualActive_noRule() { + ZenModeConfig config = getMutedAllConfig(); + + assertThat(config.isManualActive()).isFalse(); + } + + @Test + @EnableFlags(Flags.FLAG_MODES_UI) + public void testRuleXml_manual_upgrade() throws Exception { + ZenModeConfig config = getMutedAllConfig(); + final ZenModeConfig.ZenRule newRule = new ZenModeConfig.ZenRule(); + newRule.type = AutomaticZenRule.TYPE_OTHER; + newRule.enabled = true; + newRule.conditionId = Uri.EMPTY; + newRule.allowManualInvocation = true; + newRule.pkg = "android"; + newRule.zenMode = ZEN_MODE_OFF; + config.manualRule = newRule; + + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + writeRuleXml(newRule, baos); + ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); + ZenModeConfig.ZenRule fromXml = readRuleXml(bais); + + assertThat(fromXml.zenPolicy).isEqualTo(config.getZenPolicy()); + } + private ZenModeConfig getMutedRingerConfig() { ZenModeConfig config = new ZenModeConfig(); - // Allow alarms, media - config.allowAlarms = true; - config.allowMedia = true; - - // All sounds that respect the ringer are not allowed - config.allowSystem = false; - config.allowCalls = false; - config.allowRepeatCallers = false; - config.allowMessages = false; - config.allowReminders = false; - config.allowEvents = false; - config.areChannelsBypassingDnd = false; - config.suppressedVisualEffects = 0; + if (Flags.modesUi()) { + config.manualRule.zenPolicy = new ZenPolicy.Builder() + .disallowAllSounds() + .allowAlarms(true) + .allowMedia(true) + .allowPriorityChannels(false) + .showAllVisualEffects() + .build(); + } else { + // Allow alarms, media + config.setAllowAlarms(true); + config.setAllowMedia(true); + + // All sounds that respect the ringer are not allowed + config.setAllowSystem(false); + config.setAllowCalls(false); + config.setAllowRepeatCallers(false); + config.setAllowMessages(false); + config.setAllowReminders(false); + config.setAllowEvents(false); + config.setSuppressedVisualEffects(0); + config.setAllowPriorityChannels(false); + } + config.areChannelsBypassingDnd = false; return config; } private ZenModeConfig getCustomConfig() { ZenModeConfig config = new ZenModeConfig(); - // Some sounds allowed - config.allowAlarms = true; - config.allowMedia = false; - config.allowSystem = false; - config.allowCalls = true; - config.allowRepeatCallers = true; - config.allowMessages = false; - config.allowReminders = false; - config.allowEvents = false; - config.areChannelsBypassingDnd = false; - config.allowCallsFrom = ZenModeConfig.SOURCE_ANYONE; - config.allowMessagesFrom = ZenModeConfig.SOURCE_ANYONE; - config.allowConversations = true; - config.allowConversationsFrom = CONVERSATION_SENDERS_IMPORTANT; - config.suppressedVisualEffects = 0; + if (Flags.modesUi()) { + config.manualRule.zenPolicy = new ZenPolicy.Builder() + .disallowAllSounds() + .allowAlarms(true) + .allowCalls(PEOPLE_TYPE_ANYONE) + .allowRepeatCallers(true) + .allowConversations(CONVERSATION_SENDERS_IMPORTANT) + .allowPriorityChannels(true) + .showAllVisualEffects() + .build(); + } else { + // Some sounds allowed + config.setAllowAlarms(true); + config.setAllowMedia(false); + config.setAllowSystem(false); + config.setAllowCalls(true); + config.setAllowRepeatCallers(true); + config.setAllowMessages(false); + config.setAllowReminders(false); + config.setAllowEvents(false); + config.setAllowCallsFrom(ZenModeConfig.SOURCE_ANYONE); + config.setAllowMessagesFrom(ZenModeConfig.SOURCE_ANYONE); + config.setAllowConversations(true); + config.setAllowConversationsFrom(CONVERSATION_SENDERS_IMPORTANT); + config.setSuppressedVisualEffects(0); + config.setAllowPriorityChannels(true); + } + config.areChannelsBypassingDnd = false; return config; } private ZenModeConfig getMutedAllConfig() { ZenModeConfig config = new ZenModeConfig(); - // No sounds allowed - config.allowAlarms = false; - config.allowMedia = false; - config.allowSystem = false; - config.allowCalls = false; - config.allowRepeatCallers = false; - config.allowMessages = false; - config.allowReminders = false; - config.allowEvents = false; - config.areChannelsBypassingDnd = false; - config.allowConversations = false; - config.allowConversationsFrom = ZenPolicy.CONVERSATION_SENDERS_NONE; - config.suppressedVisualEffects = 0; + if (Flags.modesUi()) { + config.manualRule.zenPolicy = new ZenPolicy.Builder() + .disallowAllSounds() + .showAllVisualEffects() + .allowPriorityChannels(false) + .build(); + } else { + // No sounds allowed + config.setAllowAlarms(false); + config.setAllowMedia(false); + config.setAllowSystem(false); + config.setAllowCalls(false); + config.setAllowRepeatCallers(false); + config.setAllowMessages(false); + config.setAllowReminders(false); + config.setAllowEvents(false); + config.setAllowConversations(false); + config.setAllowConversationsFrom(CONVERSATION_SENDERS_NONE); + config.setSuppressedVisualEffects(0); + } + config.areChannelsBypassingDnd = false; return config; } diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeDiffTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeDiffTest.java index 2e64645ecade..26a13cb47563 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeDiffTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeDiffTest.java @@ -16,6 +16,8 @@ package com.android.server.notification; +import static android.app.Flags.FLAG_MODES_UI; + import static com.google.common.truth.Truth.assertThat; import static junit.framework.Assert.assertEquals; @@ -27,8 +29,10 @@ import static junit.framework.Assert.fail; import android.app.AutomaticZenRule; import android.app.Flags; +import android.app.NotificationManager; import android.content.ComponentName; import android.net.Uri; +import android.platform.test.flag.junit.FlagsParameterization; import android.platform.test.flag.junit.SetFlagsRule; import android.provider.Settings; import android.service.notification.Condition; @@ -37,7 +41,6 @@ import android.service.notification.ZenModeConfig; import android.service.notification.ZenModeDiff; import android.service.notification.ZenModeDiff.RuleDiff; import android.service.notification.ZenPolicy; -import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.util.ArrayMap; @@ -60,8 +63,11 @@ import java.util.List; import java.util.Optional; import java.util.Set; +import platform.test.runner.parameterized.ParameterizedAndroidJunit4; +import platform.test.runner.parameterized.Parameters; + @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(ParameterizedAndroidJunit4.class) @TestableLooper.RunWithLooper public class ZenModeDiffTest extends UiServiceTestCase { // Base set of exempt fields independent of fields that are enabled/disabled via flags. @@ -91,6 +97,16 @@ public class ZenModeDiffTest extends UiServiceTestCase { @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); + @Parameters(name = "{0}") + public static List<FlagsParameterization> getParams() { + return FlagsParameterization.allCombinationsOf( + FLAG_MODES_UI); + } + + public ZenModeDiffTest(FlagsParameterization flags) { + mSetFlagsRule.setFlagsParameterization(flags); + } + @Test public void testRuleDiff_addRemoveSame() { // Test add, remove, and both sides same @@ -220,21 +236,35 @@ public class ZenModeDiffTest extends UiServiceTestCase { ZenModeConfig c2 = new ZenModeConfig(); // set c1 and c2 to have some different senders - c1.allowMessagesFrom = ZenModeConfig.SOURCE_STAR; - c2.allowMessagesFrom = ZenModeConfig.SOURCE_CONTACT; - c1.allowConversationsFrom = ZenPolicy.CONVERSATION_SENDERS_IMPORTANT; - c2.allowConversationsFrom = ZenPolicy.CONVERSATION_SENDERS_NONE; + NotificationManager.Policy c1Policy = c1.toNotificationPolicy(); + c1.applyNotificationPolicy(new NotificationManager.Policy( + c1Policy.priorityCategories, c1Policy.priorityCallSenders, + c1Policy.PRIORITY_SENDERS_STARRED, c1Policy.suppressedVisualEffects, + c1Policy.state, ZenPolicy.CONVERSATION_SENDERS_IMPORTANT)); + + NotificationManager.Policy c2Policy = c1.toNotificationPolicy(); + c2.applyNotificationPolicy(new NotificationManager.Policy( + c2Policy.priorityCategories, c2Policy.priorityCallSenders, + c2Policy.PRIORITY_SENDERS_CONTACTS, c2Policy.suppressedVisualEffects, + c2Policy.state, ZenPolicy.CONVERSATION_SENDERS_NONE)); ZenModeDiff.ConfigDiff d = new ZenModeDiff.ConfigDiff(c1, c2); assertTrue(d.hasDiff()); - // Diff in top-level fields - assertTrue(d.getDiffForField("allowMessagesFrom").hasDiff()); - assertTrue(d.getDiffForField("allowConversationsFrom").hasDiff()); - - // Bonus testing of stringification of people senders and conversation senders - assertTrue(d.toString().contains("allowMessagesFrom:stars->contacts")); - assertTrue(d.toString().contains("allowConversationsFrom:important->none")); + if (!Flags.modesUi()) { + // Diff in top-level fields + assertTrue(d.getDiffForField("allowMessagesFrom").hasDiff()); + assertTrue(d.getDiffForField("allowConversationsFrom").hasDiff()); + + // Bonus testing of stringification of people senders and conversation senders + assertTrue(d.toString().contains("allowMessagesFrom:stars->contacts")); + assertTrue(d.toString().contains("allowConversationsFrom:important->none")); + } else { + RuleDiff r = d.getManualRuleDiff(); + assertNotNull(r); + ZenModeDiff.FieldDiff p = r.getDiffForField(RuleDiff.FIELD_ZEN_POLICY); + assertNotNull(p); + } } @Test 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 72ace84b855b..201b2868b3b8 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java @@ -18,6 +18,8 @@ package com.android.server.notification; import static android.app.AutomaticZenRule.TYPE_BEDTIME; import static android.app.AutomaticZenRule.TYPE_IMMERSIVE; +import static android.app.Flags.FLAG_MODES_API; +import static android.app.Flags.FLAG_MODES_UI; import static android.app.NotificationManager.AUTOMATIC_RULE_STATUS_ACTIVATED; import static android.app.NotificationManager.AUTOMATIC_RULE_STATUS_DEACTIVATED; import static android.app.NotificationManager.AUTOMATIC_RULE_STATUS_DISABLED; @@ -41,11 +43,9 @@ import static android.app.NotificationManager.Policy.PRIORITY_CATEGORY_SYSTEM; import static android.app.NotificationManager.Policy.PRIORITY_SENDERS_ANY; import static android.app.NotificationManager.Policy.PRIORITY_SENDERS_CONTACTS; import static android.app.NotificationManager.Policy.PRIORITY_SENDERS_STARRED; -import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_AMBIENT; +import static android.app.NotificationManager.Policy.STATE_CHANNELS_BYPASSING_DND; +import static android.app.NotificationManager.Policy.STATE_PRIORITY_CHANNELS_BLOCKED; import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_BADGE; -import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_FULL_SCREEN_INTENT; -import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_LIGHTS; -import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_PEEK; import static android.content.pm.PackageManager.PERMISSION_GRANTED; import static android.provider.Settings.Global.ZEN_MODE_ALARMS; import static android.provider.Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS; @@ -64,6 +64,11 @@ import static android.service.notification.ZenModeConfig.UPDATE_ORIGIN_USER; import static android.service.notification.ZenPolicy.PEOPLE_TYPE_CONTACTS; import static android.service.notification.ZenPolicy.PEOPLE_TYPE_NONE; import static android.service.notification.ZenPolicy.PEOPLE_TYPE_STARRED; +import static android.service.notification.ZenPolicy.VISUAL_EFFECT_AMBIENT; +import static android.service.notification.ZenPolicy.VISUAL_EFFECT_BADGE; +import static android.service.notification.ZenPolicy.VISUAL_EFFECT_FULL_SCREEN_INTENT; +import static android.service.notification.ZenPolicy.VISUAL_EFFECT_LIGHTS; +import static android.service.notification.ZenPolicy.VISUAL_EFFECT_PEEK; import static com.android.internal.config.sysui.SystemUiSystemPropertiesFlags.NotificationFlags.LOG_DND_STATE_EVENTS; import static com.android.os.dnd.DNDProtoEnums.PEOPLE_STARRED; @@ -75,6 +80,7 @@ import static com.android.server.notification.ZenModeHelper.RULE_LIMIT_PER_PACKA import static com.google.common.collect.Iterables.getOnlyElement; import static com.google.common.truth.Truth.assertThat; +import static com.google.common.truth.Truth.assertWithMessage; import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertFalse; @@ -131,6 +137,7 @@ import android.os.SimpleClock; 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.Settings; import android.provider.Settings.Global; @@ -140,7 +147,6 @@ import android.service.notification.SystemZenRules; import android.service.notification.ZenAdapters; import android.service.notification.ZenDeviceEffects; import android.service.notification.ZenModeConfig; -import android.service.notification.ZenModeConfig.ConfigChangeOrigin; import android.service.notification.ZenModeConfig.ScheduleInfo; import android.service.notification.ZenModeConfig.ZenRule; import android.service.notification.ZenModeDiff; @@ -172,8 +178,6 @@ import com.google.common.collect.ImmutableSet; import com.google.common.truth.Correspondence; import com.google.common.util.concurrent.SettableFuture; import com.google.protobuf.InvalidProtocolBufferException; -import com.google.testing.junit.testparameterinjector.TestParameter; -import com.google.testing.junit.testparameterinjector.TestParameterInjector; import org.junit.Before; import org.junit.Rule; @@ -202,9 +206,12 @@ import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; +import platform.test.runner.parameterized.ParameterizedAndroidJunit4; +import platform.test.runner.parameterized.Parameters; + @SmallTest @SuppressLint("GuardedBy") // It's ok for this test to access guarded methods from the service. -@RunWith(TestParameterInjector.class) +@RunWith(ParameterizedAndroidJunit4.class) @TestableLooper.RunWithLooper public class ZenModeHelperTest extends UiServiceTestCase { @@ -268,6 +275,16 @@ public class ZenModeHelperTest extends UiServiceTestCase { ZenModeEventLoggerFake mZenModeEventLogger; private String mPkg; + @Parameters(name = "{0}") + public static List<FlagsParameterization> getParams() { + return FlagsParameterization.progressionOf(FLAG_MODES_API, + FLAG_MODES_UI); + } + + public ZenModeHelperTest(FlagsParameterization flags) { + mSetFlagsRule.setFlagsParameterization(flags); + } + @Before public void setUp() throws PackageManager.NameNotFoundException { MockitoAnnotations.initMocks(this); @@ -677,7 +694,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test - @EnableFlags(Flags.FLAG_MODES_API) + @EnableFlags(FLAG_MODES_API) public void testTotalSilence_consolidatedPolicyDisallowsAll() { // Start with zen mode off just to make sure global/manual mode isn't doing anything. mZenModeHelper.mZenMode = ZEN_MODE_OFF; @@ -711,7 +728,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test - @EnableFlags(Flags.FLAG_MODES_API) + @EnableFlags(FLAG_MODES_API) public void testAlarmsOnly_consolidatedPolicyOnlyAllowsAlarmsAndMedia() { // Start with zen mode off just to make sure global/manual mode isn't doing anything. mZenModeHelper.mZenMode = ZEN_MODE_OFF; @@ -802,15 +819,11 @@ public class ZenModeHelperTest extends UiServiceTestCase { // 1. Current ringer is normal when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_NORMAL); // Set zen to priority-only with all notification sounds muted (so ringer will be muted) + Policy totalSilence = new Policy(0,0,0); + mZenModeHelper.setNotificationPolicy(totalSilence, UPDATE_ORIGIN_APP, 1); mZenModeHelper.mZenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS; - mZenModeHelper.mConfig.allowReminders = false; - mZenModeHelper.mConfig.allowCalls = false; - mZenModeHelper.mConfig.allowMessages = false; - mZenModeHelper.mConfig.allowEvents = false; - mZenModeHelper.mConfig.allowRepeatCallers = false; - mZenModeHelper.mConfig.allowConversations = false; - - // 2. apply priority only zen - verify ringer is unchanged + + // 2. verify ringer is unchanged mZenModeHelper.applyZenToRingerMode(); verify(mAudioManager, never()).setRingerModeInternal(AudioManager.RINGER_MODE_SILENT, mZenModeHelper.TAG); @@ -844,9 +857,8 @@ public class ZenModeHelperTest extends UiServiceTestCase { public void testRingerAffectedStreamsPriorityOnly() { // in priority only mode: // ringtone, notification and system streams are affected by ringer mode - mZenModeHelper.mConfig.allowAlarms = true; - mZenModeHelper.mConfig.allowReminders = true; - mZenModeHelper.mZenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS; + mZenModeHelper.setManualZenMode(ZEN_MODE_IMPORTANT_INTERRUPTIONS, Uri.EMPTY, + UPDATE_ORIGIN_APP, "test", "caller", 1); ZenModeHelper.RingerModeDelegate ringerModeDelegateRingerMuted = mZenModeHelper.new RingerModeDelegate(); @@ -862,13 +874,9 @@ public class ZenModeHelperTest extends UiServiceTestCase { // even when ringer is muted (since all ringer sounds cannot bypass DND), // system stream is still affected by ringer mode - mZenModeHelper.mConfig.allowSystem = false; - mZenModeHelper.mConfig.allowReminders = false; - mZenModeHelper.mConfig.allowCalls = false; - mZenModeHelper.mConfig.allowMessages = false; - mZenModeHelper.mConfig.allowEvents = false; - mZenModeHelper.mConfig.allowRepeatCallers = false; - mZenModeHelper.mConfig.allowConversations = false; + mZenModeHelper.setNotificationPolicy(new Policy(0,0,0), UPDATE_ORIGIN_APP, 1); + mZenModeHelper.setManualZenMode(ZEN_MODE_IMPORTANT_INTERRUPTIONS, Uri.EMPTY, + UPDATE_ORIGIN_APP, "test", "caller", 1); ZenModeHelper.RingerModeDelegate ringerModeDelegateRingerNotMuted = mZenModeHelper.new RingerModeDelegate(); @@ -885,7 +893,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test - public void testZenSetInternalRinger_NotAllPriorityNotificationSoundsMuted_StartNormal() { + public void applyZenToRingerMode_ZEN_MODE_IMPORTANT_INTERRUPTIONS() { AudioManagerInternal mAudioManager = mock(AudioManagerInternal.class); mZenModeHelper.mAudioManager = mAudioManager; Global.putString(mContext.getContentResolver(), Global.ZEN_MODE_RINGER_LEVEL, @@ -894,7 +902,6 @@ public class ZenModeHelperTest extends UiServiceTestCase { // 1. Current ringer is normal when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_NORMAL); mZenModeHelper.mZenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS; - mZenModeHelper.mConfig.allowReminders = true; // 2. apply priority only zen - verify ringer is normal mZenModeHelper.applyZenToRingerMode(); @@ -919,7 +926,6 @@ public class ZenModeHelperTest extends UiServiceTestCase { // 1. Current ringer is silent when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_SILENT); mZenModeHelper.mZenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS; - mZenModeHelper.mConfig.allowReminders = true; // 2. apply priority only zen - verify ringer is silent mZenModeHelper.applyZenToRingerMode(); @@ -945,7 +951,6 @@ public class ZenModeHelperTest extends UiServiceTestCase { when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_NORMAL); // Set zen to priority-only with all notification sounds muted (so ringer will be muted) mZenModeHelper.mZenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS; - mZenModeHelper.mConfig.allowReminders = true; // 2. apply priority only zen - verify zen will still be normal mZenModeHelper.applyZenToRingerMode(); @@ -977,11 +982,9 @@ public class ZenModeHelperTest extends UiServiceTestCase { // apply zen off multiple times - verify ringer is not set to normal when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_SILENT); - mZenModeHelper.mZenMode = ZEN_MODE_OFF; - mZenModeHelper.mConfig = null; // will evaluate config to zen mode off for (int i = 0; i < 3; i++) { - // if zen doesn't change, zen should not reapply itself to the ringer - mZenModeHelper.evaluateZenModeLocked(UPDATE_ORIGIN_UNKNOWN, "test", true); + mZenModeHelper.setManualZenMode(ZEN_MODE_OFF, Uri.EMPTY, + UPDATE_ORIGIN_APP, "test", "caller", 1); } verify(mAudioManager, never()).setRingerModeInternal(AudioManager.RINGER_MODE_NORMAL, mZenModeHelper.TAG); @@ -991,8 +994,6 @@ public class ZenModeHelperTest extends UiServiceTestCase { public void testSilentRingerSavedOnZenOff_startsZenOn() { AudioManagerInternal mAudioManager = mock(AudioManagerInternal.class); mZenModeHelper.mAudioManager = mAudioManager; - mZenModeHelper.mZenMode = ZEN_MODE_OFF; - mZenModeHelper.mConfig = new ZenModeConfig(); // previously set silent ringer ZenModeHelper.RingerModeDelegate ringerModeDelegate = @@ -1003,12 +1004,12 @@ public class ZenModeHelperTest extends UiServiceTestCase { assertEquals(AudioManager.RINGER_MODE_SILENT, Global.getInt(mContext.getContentResolver(), Global.ZEN_MODE_RINGER_LEVEL, AudioManager.RINGER_MODE_NORMAL)); - // apply zen off multiple times - verify ringer is not set to normal + // apply zen on multiple times - verify ringer is not set to normal when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_SILENT); - mZenModeHelper.mZenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS; for (int i = 0; i < 3; i++) { // if zen doesn't change, zen should not reapply itself to the ringer - mZenModeHelper.evaluateZenModeLocked(UPDATE_ORIGIN_UNKNOWN, "test", true); + mZenModeHelper.setManualZenMode(ZEN_MODE_OFF, Uri.EMPTY, + UPDATE_ORIGIN_APP, "test", "caller", 1); } verify(mAudioManager, never()).setRingerModeInternal(AudioManager.RINGER_MODE_NORMAL, mZenModeHelper.TAG); @@ -1018,8 +1019,6 @@ public class ZenModeHelperTest extends UiServiceTestCase { public void testVibrateRingerSavedOnZenOff_startsZenOn() { AudioManagerInternal mAudioManager = mock(AudioManagerInternal.class); mZenModeHelper.mAudioManager = mAudioManager; - mZenModeHelper.mZenMode = ZEN_MODE_OFF; - mZenModeHelper.mConfig = new ZenModeConfig(); // previously set silent ringer ZenModeHelper.RingerModeDelegate ringerModeDelegate = @@ -1032,10 +1031,10 @@ public class ZenModeHelperTest extends UiServiceTestCase { // apply zen off multiple times - verify ringer is not set to normal when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_VIBRATE); - mZenModeHelper.mZenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS; for (int i = 0; i < 3; i++) { // if zen doesn't change, zen should not reapply itself to the ringer - mZenModeHelper.evaluateZenModeLocked(UPDATE_ORIGIN_UNKNOWN, "test", true); + mZenModeHelper.setManualZenMode(ZEN_MODE_OFF, Uri.EMPTY, + UPDATE_ORIGIN_APP, "test", "caller", 1); } verify(mAudioManager, never()).setRingerModeInternal(AudioManager.RINGER_MODE_NORMAL, mZenModeHelper.TAG); @@ -1066,22 +1065,18 @@ public class ZenModeHelperTest extends UiServiceTestCase { @Test public void testParcelConfig() { - mZenModeHelper.mZenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS; - mZenModeHelper.mConfig.allowAlarms = false; - mZenModeHelper.mConfig.allowMedia = false; - mZenModeHelper.mConfig.allowSystem = false; - mZenModeHelper.mConfig.allowReminders = true; - mZenModeHelper.mConfig.allowCalls = true; - mZenModeHelper.mConfig.allowMessages = true; - mZenModeHelper.mConfig.allowEvents = true; - mZenModeHelper.mConfig.allowRepeatCallers = true; - mZenModeHelper.mConfig.allowConversations = true; - mZenModeHelper.mConfig.allowConversationsFrom = ZenPolicy.CONVERSATION_SENDERS_ANYONE; - mZenModeHelper.mConfig.suppressedVisualEffects = SUPPRESSED_EFFECT_BADGE; - mZenModeHelper.mConfig.manualRule = new ZenModeConfig.ZenRule(); - mZenModeHelper.mConfig.manualRule.component = new ComponentName("a", "a"); - mZenModeHelper.mConfig.manualRule.enabled = true; - mZenModeHelper.mConfig.manualRule.snoozing = true; + mZenModeHelper.setNotificationPolicy(new Policy(PRIORITY_CATEGORY_EVENTS + | PRIORITY_CATEGORY_MESSAGES | PRIORITY_CATEGORY_REPEAT_CALLERS + | PRIORITY_CATEGORY_CONVERSATIONS, PRIORITY_SENDERS_STARRED, + PRIORITY_SENDERS_STARRED, 0, CONVERSATION_SENDERS_ANYONE), UPDATE_ORIGIN_UNKNOWN, + 1); + mZenModeHelper.setManualZenRuleDeviceEffects(new ZenDeviceEffects.Builder() + .setShouldDimWallpaper(true) + .setShouldDisplayGrayscale(true) + .setShouldUseNightMode(true) + .build(), UPDATE_ORIGIN_UNKNOWN, "test", 1); + mZenModeHelper.setManualZenMode(ZEN_MODE_IMPORTANT_INTERRUPTIONS, Uri.EMPTY, + UPDATE_ORIGIN_UNKNOWN, "test", "me", 1); ZenModeConfig actual = mZenModeHelper.mConfig.copy(); @@ -1090,24 +1085,17 @@ public class ZenModeHelperTest extends UiServiceTestCase { @Test public void testWriteXml() throws Exception { - mZenModeHelper.mZenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS; - mZenModeHelper.mConfig.allowAlarms = false; - mZenModeHelper.mConfig.allowMedia = false; - mZenModeHelper.mConfig.allowSystem = false; - mZenModeHelper.mConfig.allowReminders = true; - mZenModeHelper.mConfig.allowCalls = true; - mZenModeHelper.mConfig.allowMessages = true; - mZenModeHelper.mConfig.allowEvents = true; - mZenModeHelper.mConfig.allowRepeatCallers = true; - mZenModeHelper.mConfig.allowConversations = true; - mZenModeHelper.mConfig.allowConversationsFrom = ZenPolicy.CONVERSATION_SENDERS_ANYONE; - mZenModeHelper.mConfig.suppressedVisualEffects = SUPPRESSED_EFFECT_BADGE; - mZenModeHelper.mConfig.manualRule = new ZenModeConfig.ZenRule(); - mZenModeHelper.mConfig.manualRule.zenMode = - ZEN_MODE_IMPORTANT_INTERRUPTIONS; - mZenModeHelper.mConfig.manualRule.component = new ComponentName("a", "a"); - mZenModeHelper.mConfig.manualRule.pkg = "a"; - mZenModeHelper.mConfig.manualRule.enabled = true; + mZenModeHelper.setNotificationPolicy(new Policy(PRIORITY_CATEGORY_EVENTS + | PRIORITY_CATEGORY_MESSAGES | PRIORITY_CATEGORY_REPEAT_CALLERS + | PRIORITY_CATEGORY_CONVERSATIONS, PRIORITY_SENDERS_STARRED, + PRIORITY_SENDERS_STARRED, SUPPRESSED_EFFECT_BADGE, CONVERSATION_SENDERS_ANYONE), + UPDATE_ORIGIN_UNKNOWN, 1); + mZenModeHelper.setManualZenRuleDeviceEffects(new ZenDeviceEffects.Builder() + .setShouldDimWallpaper(true) + .setShouldDisplayGrayscale(true) + .build(), UPDATE_ORIGIN_UNKNOWN, "test", 1); + mZenModeHelper.setManualZenMode(ZEN_MODE_IMPORTANT_INTERRUPTIONS, Uri.EMPTY, + UPDATE_ORIGIN_UNKNOWN, "test", "me", 1); ZenModeConfig expected = mZenModeHelper.mConfig.copy(); if (Flags.modesUi()) { @@ -1127,10 +1115,10 @@ public class ZenModeHelperTest extends UiServiceTestCase { @Test public void testProto() throws InvalidProtocolBufferException { - mZenModeHelper.mZenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS; - // existence of manual rule means it should be in output - mZenModeHelper.mConfig.manualRule = new ZenModeConfig.ZenRule(); - mZenModeHelper.mConfig.manualRule.pkg = "android"; // system + mZenModeHelper.setManualZenMode(ZEN_MODE_IMPORTANT_INTERRUPTIONS, null, + Flags.modesApi() ? UPDATE_ORIGIN_USER : UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, + null, "test", CUSTOM_PKG_UID); + mZenModeHelper.mConfig.automaticRules = new ArrayMap<>(); // no automatic rules List<String> ids = new ArrayList<>(); @@ -1151,7 +1139,6 @@ public class ZenModeHelperTest extends UiServiceTestCase { assertTrue(cfg.getEnabled()); assertFalse(cfg.getChannelsBypassing()); } - assertEquals(Process.SYSTEM_UID, cfg.getUid()); String name = cfg.getId(); assertTrue("unexpected rule id", ids.contains(name)); ids.remove(name); @@ -1255,10 +1242,8 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test + @EnableFlags(FLAG_MODES_API) public void testProtoWithAutoRuleCustomPolicy() throws Exception { - // allowChannels is only valid under modes_api. - mSetFlagsRule.enableFlags(Flags.FLAG_MODES_API); - setupZenConfig(); // clear any automatic rules just to make sure mZenModeHelper.mConfig.automaticRules = new ArrayMap<>(); @@ -1299,7 +1284,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test - @EnableFlags(Flags.FLAG_MODES_API) + @EnableFlags(FLAG_MODES_API) public void testProtoWithAutoRuleWithModifiedFields() throws Exception { setupZenConfig(); mZenModeHelper.mConfig.automaticRules = new ArrayMap<>(); @@ -1384,8 +1369,8 @@ public class ZenModeHelperTest extends UiServiceTestCase { public void testProtoWithManualRule() throws Exception { setupZenConfig(); mZenModeHelper.mConfig.automaticRules = getCustomAutomaticRules(); - mZenModeHelper.mConfig.manualRule = new ZenModeConfig.ZenRule(); - mZenModeHelper.mConfig.manualRule.enabled = true; + mZenModeHelper.setManualZenMode(INTERRUPTION_FILTER_PRIORITY, Uri.EMPTY, UPDATE_ORIGIN_APP, + "test", "me", 1); List<StatsEvent> events = new LinkedList<>(); mZenModeHelper.pullRules(events); @@ -1408,15 +1393,15 @@ public class ZenModeHelperTest extends UiServiceTestCase { // Setup configs for user 10 and 11. setupZenConfig(); ZenModeConfig config10 = mZenModeHelper.mConfig.copy(); + Policy policy = new Policy(PRIORITY_CATEGORY_MEDIA | PRIORITY_CATEGORY_ALARMS, 0, 0); + config10.applyNotificationPolicy(policy); config10.user = 10; - config10.allowAlarms = true; - config10.allowMedia = true; mZenModeHelper.setConfig(config10, null, UPDATE_ORIGIN_INIT, "writeXml", Process.SYSTEM_UID); ZenModeConfig config11 = mZenModeHelper.mConfig.copy(); config11.user = 11; - config11.allowAlarms = false; - config11.allowMedia = false; + policy = new Policy(0, 0, 0); + config11.applyNotificationPolicy(policy); mZenModeHelper.setConfig(config11, null, UPDATE_ORIGIN_INIT, "writeXml", Process.SYSTEM_UID); @@ -1583,8 +1568,9 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test - public void testReadXmlRulesNotOverriden() throws Exception { + public void testReadXmlRulesNotOverridden() throws Exception { setupZenConfig(); + Policy originalPolicy = mZenModeHelper.getNotificationPolicy(); // automatic zen rule is enabled on upgrade so rules should not be overriden to default ArrayMap<String, ZenModeConfig.ZenRule> enabledAutoRule = new ArrayMap<>(); @@ -1607,7 +1593,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { mZenModeHelper.readXml(parser, false, UserHandle.USER_ALL); assertTrue(mZenModeHelper.mConfig.automaticRules.containsKey("customRule")); - setupZenConfigMaintained(); + assertEquals(originalPolicy, mZenModeHelper.getNotificationPolicy()); } @Test @@ -1626,7 +1612,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { parser.nextTag(); mZenModeHelper.readXml(parser, false, UserHandle.USER_ALL); - assertEquals(0, mZenModeHelper.mConfig.suppressedVisualEffects); + assertTrue(mZenModeHelper.mConfig.getZenPolicy().shouldShowAllVisualEffects()); xml = "<zen version=\"6\" user=\"0\">\n" + "<allow calls=\"false\" repeatCallers=\"false\" messages=\"true\" " @@ -1642,7 +1628,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { parser.nextTag(); mZenModeHelper.readXml(parser, false, UserHandle.USER_ALL); - assertEquals(0, mZenModeHelper.mConfig.suppressedVisualEffects); + assertTrue(mZenModeHelper.mConfig.getZenPolicy().shouldShowAllVisualEffects()); } @Test @@ -1661,7 +1647,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { parser.nextTag(); mZenModeHelper.readXml(parser, false, UserHandle.USER_ALL); - assertEquals(0, mZenModeHelper.mConfig.suppressedVisualEffects); + assertTrue(mZenModeHelper.mConfig.getZenPolicy().shouldShowAllVisualEffects()); } @Test @@ -1680,11 +1666,16 @@ public class ZenModeHelperTest extends UiServiceTestCase { parser.nextTag(); mZenModeHelper.readXml(parser, false, UserHandle.USER_ALL); - assertEquals(SUPPRESSED_EFFECT_FULL_SCREEN_INTENT - | SUPPRESSED_EFFECT_LIGHTS - | SUPPRESSED_EFFECT_AMBIENT - | SUPPRESSED_EFFECT_PEEK, - mZenModeHelper.mConfig.suppressedVisualEffects); + assertThat(mZenModeHelper.mConfig.getZenPolicy() + .isVisualEffectAllowed(VISUAL_EFFECT_FULL_SCREEN_INTENT, true)).isFalse(); + assertThat(mZenModeHelper.mConfig.getZenPolicy() + .isVisualEffectAllowed(VISUAL_EFFECT_LIGHTS, true)).isFalse(); + assertThat(mZenModeHelper.mConfig.getZenPolicy() + .isVisualEffectAllowed(VISUAL_EFFECT_PEEK, true)).isFalse(); + assertThat(mZenModeHelper.mConfig.getZenPolicy() + .isVisualEffectAllowed(VISUAL_EFFECT_AMBIENT, true)).isFalse(); + assertThat(mZenModeHelper.mConfig.getZenPolicy() + .isVisualEffectAllowed(VISUAL_EFFECT_BADGE, true)).isTrue(); xml = "<zen version=\"6\" user=\"0\">\n" + "<allow calls=\"false\" repeatCallers=\"false\" messages=\"true\" " @@ -1700,7 +1691,10 @@ public class ZenModeHelperTest extends UiServiceTestCase { parser.nextTag(); mZenModeHelper.readXml(parser, false, UserHandle.USER_ALL); - assertEquals(SUPPRESSED_EFFECT_PEEK, mZenModeHelper.mConfig.suppressedVisualEffects); + assertThat(mZenModeHelper.mConfig.getZenPolicy() + .isVisualEffectAllowed(VISUAL_EFFECT_PEEK, true)).isFalse(); + assertThat(mZenModeHelper.mConfig.getZenPolicy() + .isVisualEffectAllowed(VISUAL_EFFECT_AMBIENT, true)).isTrue(); xml = "<zen version=\"6\" user=\"0\">\n" + "<allow calls=\"false\" repeatCallers=\"false\" messages=\"true\" " @@ -1716,18 +1710,23 @@ public class ZenModeHelperTest extends UiServiceTestCase { parser.nextTag(); mZenModeHelper.readXml(parser, false, UserHandle.USER_ALL); - assertEquals(SUPPRESSED_EFFECT_FULL_SCREEN_INTENT - | SUPPRESSED_EFFECT_LIGHTS - | SUPPRESSED_EFFECT_AMBIENT, - mZenModeHelper.mConfig.suppressedVisualEffects); + assertThat(mZenModeHelper.mConfig.getZenPolicy() + .isVisualEffectAllowed(VISUAL_EFFECT_FULL_SCREEN_INTENT, true)).isFalse(); + assertThat(mZenModeHelper.mConfig.getZenPolicy() + .isVisualEffectAllowed(VISUAL_EFFECT_LIGHTS, true)).isFalse(); + assertThat(mZenModeHelper.mConfig.getZenPolicy() + .isVisualEffectAllowed(VISUAL_EFFECT_AMBIENT, true)).isFalse(); + assertThat(mZenModeHelper.mConfig.getZenPolicy() + .isVisualEffectAllowed(VISUAL_EFFECT_BADGE, true)).isTrue(); } @Test public void testReadXmlResetDefaultRules() throws Exception { setupZenConfig(); + Policy originalPolicy = mZenModeHelper.getNotificationPolicy(); // no enabled automatic zen rules and no default rules - // so rules should be overriden by default rules + // so rules should be overridden by default rules mZenModeHelper.mConfig.automaticRules = new ArrayMap<>(); // set previous version @@ -1745,13 +1744,14 @@ public class ZenModeHelperTest extends UiServiceTestCase { assertTrue(rules.containsKey(defaultId)); } - setupZenConfigMaintained(); + assertEquals(originalPolicy, mZenModeHelper.getNotificationPolicy()); } @Test public void testReadXmlAllDisabledRulesResetDefaultRules() throws Exception { setupZenConfig(); + Policy originalPolicy = mZenModeHelper.getNotificationPolicy(); // all automatic zen rules are disabled on upgrade (and default rules don't already exist) // so rules should be overriden by default rules @@ -1782,12 +1782,13 @@ public class ZenModeHelperTest extends UiServiceTestCase { } assertFalse(rules.containsKey("customRule")); - setupZenConfigMaintained(); + assertEquals(originalPolicy, mZenModeHelper.getNotificationPolicy()); } @Test public void testReadXmlOnlyOneDefaultRuleExists() throws Exception { setupZenConfig(); + Policy originalPolicy = mZenModeHelper.getNotificationPolicy(); // all automatic zen rules are disabled on upgrade and only one default rule exists // so rules should be overriden to the default rules @@ -1834,12 +1835,13 @@ public class ZenModeHelperTest extends UiServiceTestCase { } assertFalse(rules.containsKey("customRule")); - setupZenConfigMaintained(); + assertEquals(originalPolicy, mZenModeHelper.getNotificationPolicy()); } @Test public void testReadXmlDefaultRulesExist() throws Exception { setupZenConfig(); + Policy originalPolicy = mZenModeHelper.getNotificationPolicy(); // Default rules exist so rules should not be overridden by defaults ArrayMap<String, ZenModeConfig.ZenRule> automaticRules = new ArrayMap<>(); @@ -1897,13 +1899,13 @@ public class ZenModeHelperTest extends UiServiceTestCase { // check default rules ArrayMap<String, ZenModeConfig.ZenRule> rules = mZenModeHelper.mConfig.automaticRules; - assertTrue(rules.size() != 0); + assertEquals(3, rules.size()); for (String defaultId : ZenModeConfig.DEFAULT_RULE_IDS) { assertTrue(rules.containsKey(defaultId)); } assertTrue(rules.containsKey("customRule")); - setupZenConfigMaintained(); + assertEquals(originalPolicy, mZenModeHelper.getNotificationPolicy()); List<StatsEvent> events = new LinkedList<>(); mZenModeHelper.pullRules(events); @@ -1911,11 +1913,12 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test - @EnableFlags(Flags.FLAG_MODES_API) + @EnableFlags(FLAG_MODES_API) public void testReadXml_onModesApi_noUpgrade() throws Exception { // When reading XML for something that is already on the modes API system, make sure no // rules' policies get changed. setupZenConfig(); + Policy originalPolicy = mZenModeHelper.getNotificationPolicy(); // Shared for rules ArrayMap<String, ZenModeConfig.ZenRule> enabledAutoRules = new ArrayMap<>(); @@ -1947,7 +1950,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { mZenModeHelper.readXml(parser, false, UserHandle.USER_ALL); // basic check: global config maintained - setupZenConfigMaintained(); + assertEquals(originalPolicy, mZenModeHelper.getNotificationPolicy()); // Find our automatic rules. ArrayMap<String, ZenModeConfig.ZenRule> rules = mZenModeHelper.mConfig.automaticRules; @@ -1958,12 +1961,13 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test - @EnableFlags(Flags.FLAG_MODES_API) + @EnableFlags(FLAG_MODES_API) public void testReadXml_upgradeToModesApi_makesCustomPolicies() throws Exception { // When reading in an XML file written from a pre-modes-API version, confirm that we create // a custom policy matching the global config for any automatic rule with no specified // policy. setupZenConfig(); + Policy originalPolicy = mZenModeHelper.getNotificationPolicy(); ArrayMap<String, ZenModeConfig.ZenRule> enabledAutoRule = new ArrayMap<>(); ZenModeConfig.ZenRule customRule = new ZenModeConfig.ZenRule(); @@ -1985,7 +1989,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { mZenModeHelper.readXml(parser, false, UserHandle.USER_ALL); // basic check: global config maintained - setupZenConfigMaintained(); + assertEquals(originalPolicy, mZenModeHelper.getNotificationPolicy()); // Find our automatic rule and check that it has a policy set now ArrayMap<String, ZenModeConfig.ZenRule> rules = mZenModeHelper.mConfig.automaticRules; @@ -2009,12 +2013,13 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test - @EnableFlags(Flags.FLAG_MODES_API) + @EnableFlags(FLAG_MODES_API) public void testReadXml_upgradeToModesApi_fillsInCustomPolicies() throws Exception { // When reading in an XML file written from a pre-modes-API version, confirm that for an // underspecified ZenPolicy, we fill in all of the gaps with things from the global config // in order to maintain consistency of behavior. setupZenConfig(); + Policy originalPolicy = mZenModeHelper.getNotificationPolicy(); ArrayMap<String, ZenModeConfig.ZenRule> enabledAutoRule = new ArrayMap<>(); ZenModeConfig.ZenRule customRule = new ZenModeConfig.ZenRule(); @@ -2041,7 +2046,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { mZenModeHelper.readXml(parser, false, UserHandle.USER_ALL); // basic check: global config maintained - setupZenConfigMaintained(); + assertEquals(originalPolicy, mZenModeHelper.getNotificationPolicy()); // Find our automatic rule and check that it has a policy set now ArrayMap<String, ZenModeConfig.ZenRule> rules = mZenModeHelper.mConfig.automaticRules; @@ -2068,7 +2073,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test - @EnableFlags(Flags.FLAG_MODES_API) + @EnableFlags(FLAG_MODES_API) public void testReadXml_upgradeToModesApi_existingDefaultRulesGetCustomPolicy() throws Exception { setupZenConfig(); @@ -2230,7 +2235,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test - @EnableFlags(Flags.FLAG_MODES_API) + @EnableFlags(FLAG_MODES_API) public void testDefaultRulesFromConfig_modesApi_getPolicies() { // After mZenModeHelper was created, set some things in the policy so it's changed from // default. @@ -2389,7 +2394,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test - @EnableFlags(Flags.FLAG_MODES_API) + @EnableFlags(FLAG_MODES_API) public void testAddAutomaticZenRule_modesApi_fillsInDefaultValues() { // When a new automatic zen rule is added with only some fields filled in, ensure that // all unset fields are filled in with device defaults. @@ -2436,19 +2441,19 @@ public class ZenModeHelperTest extends UiServiceTestCase { assertThat(rule2InConfig.zenPolicy.getPriorityMessageSenders()) .isEqualTo(PEOPLE_TYPE_CONTACTS); assertThat(rule2InConfig.zenPolicy.getVisualEffectFullScreenIntent()) - .isEqualTo(ZenPolicy.STATE_ALLOW); + .isEqualTo(STATE_ALLOW); // the rest of rule 2's settings should be the device defaults assertThat(rule2InConfig.zenPolicy.getPriorityConversationSenders()) .isEqualTo(CONVERSATION_SENDERS_IMPORTANT); assertThat(rule2InConfig.zenPolicy.getPriorityCategorySystem()) - .isEqualTo(ZenPolicy.STATE_DISALLOW); + .isEqualTo(STATE_DISALLOW); assertThat(rule2InConfig.zenPolicy.getPriorityCategoryAlarms()) - .isEqualTo(ZenPolicy.STATE_ALLOW); + .isEqualTo(STATE_ALLOW); assertThat(rule2InConfig.zenPolicy.getVisualEffectPeek()) - .isEqualTo(ZenPolicy.STATE_DISALLOW); + .isEqualTo(STATE_DISALLOW); assertThat(rule2InConfig.zenPolicy.getVisualEffectNotificationList()) - .isEqualTo(ZenPolicy.STATE_ALLOW); + .isEqualTo(STATE_ALLOW); } @Test @@ -2590,9 +2595,8 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test + @EnableFlags(FLAG_MODES_API) public void addAutomaticZenRule_fromApp_ignoresHiddenEffects() { - mSetFlagsRule.enableFlags(Flags.FLAG_MODES_API); - ZenDeviceEffects zde = new ZenDeviceEffects.Builder() .setShouldDisplayGrayscale(true) .setShouldSuppressAmbientDisplay(true) @@ -2624,9 +2628,8 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test + @EnableFlags(FLAG_MODES_API) public void addAutomaticZenRule_fromSystem_respectsHiddenEffects() { - mSetFlagsRule.enableFlags(Flags.FLAG_MODES_API); - ZenDeviceEffects zde = new ZenDeviceEffects.Builder() .setShouldDisplayGrayscale(true) .setShouldSuppressAmbientDisplay(true) @@ -2652,9 +2655,8 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test + @EnableFlags(FLAG_MODES_API) public void addAutomaticZenRule_fromUser_respectsHiddenEffects() throws Exception { - mSetFlagsRule.enableFlags(Flags.FLAG_MODES_API); - ZenDeviceEffects zde = new ZenDeviceEffects.Builder() .setShouldDisplayGrayscale(true) .setShouldSuppressAmbientDisplay(true) @@ -2682,8 +2684,8 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test + @EnableFlags(FLAG_MODES_API) public void updateAutomaticZenRule_fromApp_preservesPreviousHiddenEffects() { - mSetFlagsRule.enableFlags(Flags.FLAG_MODES_API); ZenDeviceEffects original = new ZenDeviceEffects.Builder() .setShouldDisableTapToWake(true) .addExtraEffect("extra") @@ -2717,8 +2719,8 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test + @EnableFlags(FLAG_MODES_API) public void updateAutomaticZenRule_fromSystem_updatesHiddenEffects() { - mSetFlagsRule.enableFlags(Flags.FLAG_MODES_API); ZenDeviceEffects original = new ZenDeviceEffects.Builder() .setShouldDisableTapToWake(true) .build(); @@ -2744,8 +2746,8 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test + @EnableFlags(FLAG_MODES_API) public void updateAutomaticZenRule_fromUser_updatesHiddenEffects() { - mSetFlagsRule.enableFlags(Flags.FLAG_MODES_API); ZenDeviceEffects original = new ZenDeviceEffects.Builder() .setShouldDisableTapToWake(true) .build(); @@ -2775,7 +2777,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test - @EnableFlags(Flags.FLAG_MODES_API) + @EnableFlags(FLAG_MODES_API) public void updateAutomaticZenRule_nullPolicy_doesNothing() { // Test that when updateAutomaticZenRule is called with a null policy, nothing changes // about the existing policy. @@ -2796,11 +2798,11 @@ public class ZenModeHelperTest extends UiServiceTestCase { AutomaticZenRule savedRule = mZenModeHelper.getAutomaticZenRule(ruleId); assertThat(savedRule.getZenPolicy().getPriorityCategoryCalls()) - .isEqualTo(ZenPolicy.STATE_DISALLOW); + .isEqualTo(STATE_DISALLOW); } @Test - @EnableFlags(Flags.FLAG_MODES_API) + @EnableFlags(FLAG_MODES_API) public void updateAutomaticZenRule_overwritesExistingPolicy() { // Test that when updating an automatic zen rule with an existing policy, the newly set // fields overwrite those from the previous policy, but unset fields in the new policy @@ -2826,18 +2828,18 @@ public class ZenModeHelperTest extends UiServiceTestCase { AutomaticZenRule savedRule = mZenModeHelper.getAutomaticZenRule(ruleId); assertThat(savedRule.getZenPolicy().getPriorityCategoryCalls()) - .isEqualTo(ZenPolicy.STATE_ALLOW); // from update + .isEqualTo(STATE_ALLOW); // from update assertThat(savedRule.getZenPolicy().getPriorityCallSenders()) .isEqualTo(ZenPolicy.PEOPLE_TYPE_CONTACTS); // from update assertThat(savedRule.getZenPolicy().getPriorityCategoryAlarms()) - .isEqualTo(ZenPolicy.STATE_DISALLOW); // from original + .isEqualTo(STATE_DISALLOW); // from original assertThat(savedRule.getZenPolicy().getPriorityCategoryReminders()) - .isEqualTo(ZenPolicy.STATE_ALLOW); // from original + .isEqualTo(STATE_ALLOW); // from original } @Test - @EnableFlags(Flags.FLAG_MODES_API) + @EnableFlags(FLAG_MODES_API) public void addAutomaticZenRule_withTypeBedtime_replacesDisabledSleeping() { ZenRule sleepingRule = createCustomAutomaticRule(ZEN_MODE_IMPORTANT_INTERRUPTIONS, ZenModeConfig.EVERY_NIGHT_DEFAULT_RULE_ID); @@ -2857,7 +2859,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test - @EnableFlags(Flags.FLAG_MODES_API) + @EnableFlags(FLAG_MODES_API) public void addAutomaticZenRule_withTypeBedtime_keepsEnabledSleeping() { ZenRule sleepingRule = createCustomAutomaticRule(ZEN_MODE_IMPORTANT_INTERRUPTIONS, ZenModeConfig.EVERY_NIGHT_DEFAULT_RULE_ID); @@ -2878,7 +2880,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test - @EnableFlags(Flags.FLAG_MODES_API) + @EnableFlags(FLAG_MODES_API) public void addAutomaticZenRule_withTypeBedtime_keepsCustomizedSleeping() { ZenRule sleepingRule = createCustomAutomaticRule(ZEN_MODE_IMPORTANT_INTERRUPTIONS, ZenModeConfig.EVERY_NIGHT_DEFAULT_RULE_ID); @@ -2899,8 +2901,8 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test + @EnableFlags(FLAG_MODES_API) public void testSetManualZenMode() { - mSetFlagsRule.enableFlags(Flags.FLAG_MODES_API); setupZenConfig(); // note that caller=null because that's how it comes in from NMS.setZenMode @@ -2919,68 +2921,83 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test - @EnableFlags(Flags.FLAG_MODES_API) - @DisableFlags(Flags.FLAG_MODES_UI) - public void setManualZenMode_off_snoozesActiveRules(@TestParameter ChangeOrigin setZenOrigin) { - // Start with an active rule and an inactive rule. - mZenModeHelper.mConfig.automaticRules.clear(); - AutomaticZenRule activeRule = new AutomaticZenRule.Builder("Test", CONDITION_ID) - .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY) - .build(); - String activeRuleId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), - activeRule, UPDATE_ORIGIN_APP, "add it", CUSTOM_PKG_UID); - mZenModeHelper.setAutomaticZenRuleState(activeRuleId, CONDITION_TRUE, UPDATE_ORIGIN_APP, - CUSTOM_PKG_UID); - AutomaticZenRule inactiveRule = new AutomaticZenRule.Builder("Test", CONDITION_ID) - .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY) - .build(); - String inactiveRuleId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), - inactiveRule, UPDATE_ORIGIN_APP, "add it", CUSTOM_PKG_UID); - - assertThat(mZenModeHelper.getZenMode()).isEqualTo(ZEN_MODE_IMPORTANT_INTERRUPTIONS); - - // User turns DND off. - mZenModeHelper.setManualZenMode(ZEN_MODE_OFF, null, setZenOrigin.value(), - "snoozing", "systemui", Process.SYSTEM_UID); - assertThat(mZenModeHelper.getZenMode()).isEqualTo(ZEN_MODE_OFF); - assertThat(mZenModeHelper.mConfig.automaticRules.get(activeRuleId).snoozing).isTrue(); - assertThat(mZenModeHelper.mConfig.automaticRules.get(inactiveRuleId).snoozing).isFalse(); + @EnableFlags(FLAG_MODES_API) + @DisableFlags(FLAG_MODES_UI) + public void setManualZenMode_off_snoozesActiveRules() { + for (ChangeOrigin origin : ChangeOrigin.values()) { + // Start with an active rule and an inactive rule. + mZenModeHelper.mConfig.automaticRules.clear(); + AutomaticZenRule activeRule = new AutomaticZenRule.Builder("Test", CONDITION_ID) + .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY) + .build(); + String activeRuleId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), + activeRule, UPDATE_ORIGIN_APP, "add it", CUSTOM_PKG_UID); + mZenModeHelper.setAutomaticZenRuleState(activeRuleId, CONDITION_TRUE, UPDATE_ORIGIN_APP, + CUSTOM_PKG_UID); + AutomaticZenRule inactiveRule = new AutomaticZenRule.Builder("Test", CONDITION_ID) + .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY) + .build(); + String inactiveRuleId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), + inactiveRule, UPDATE_ORIGIN_APP, "add it", CUSTOM_PKG_UID); + + assertWithMessage("Failure for origin " + origin.name()) + .that(mZenModeHelper.getZenMode()).isEqualTo(ZEN_MODE_IMPORTANT_INTERRUPTIONS); + + // User turns DND off. + mZenModeHelper.setManualZenMode(ZEN_MODE_OFF, null, origin.value(), + "snoozing", "systemui", Process.SYSTEM_UID); + assertWithMessage("Failure for origin " + origin.name()) + .that(mZenModeHelper.getZenMode()).isEqualTo(ZEN_MODE_OFF); + assertWithMessage("Failure for origin " + origin.name()) + .that(mZenModeHelper.mConfig.automaticRules.get(activeRuleId).snoozing) + .isTrue(); + assertWithMessage("Failure for origin " + origin.name()) + .that(mZenModeHelper.mConfig.automaticRules.get(inactiveRuleId).snoozing) + .isFalse(); + } } @Test - @EnableFlags({Flags.FLAG_MODES_API, Flags.FLAG_MODES_UI}) - public void setManualZenMode_off_doesNotSnoozeRulesIfFromUser( - @TestParameter ChangeOrigin setZenOrigin) { - // Start with an active rule and an inactive rule - mZenModeHelper.mConfig.automaticRules.clear(); - AutomaticZenRule activeRule = new AutomaticZenRule.Builder("Test", CONDITION_ID) - .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY) - .build(); - String activeRuleId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), - activeRule, UPDATE_ORIGIN_APP, "add it", CUSTOM_PKG_UID); - mZenModeHelper.setAutomaticZenRuleState(activeRuleId, CONDITION_TRUE, UPDATE_ORIGIN_APP, - CUSTOM_PKG_UID); - AutomaticZenRule inactiveRule = new AutomaticZenRule.Builder("Test", CONDITION_ID) - .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY) - .build(); - String inactiveRuleId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), - inactiveRule, UPDATE_ORIGIN_APP, "add it", CUSTOM_PKG_UID); + @EnableFlags({FLAG_MODES_API, FLAG_MODES_UI}) + public void setManualZenMode_off_doesNotSnoozeRulesIfFromUser() { + for (ChangeOrigin origin : ChangeOrigin.values()) { + // Start with an active rule and an inactive rule + mZenModeHelper.mConfig.automaticRules.clear(); + AutomaticZenRule activeRule = new AutomaticZenRule.Builder("Test", CONDITION_ID) + .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY) + .build(); + String activeRuleId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), + activeRule, UPDATE_ORIGIN_APP, "add it", CUSTOM_PKG_UID); + mZenModeHelper.setAutomaticZenRuleState(activeRuleId, CONDITION_TRUE, UPDATE_ORIGIN_APP, + CUSTOM_PKG_UID); + AutomaticZenRule inactiveRule = new AutomaticZenRule.Builder("Test", CONDITION_ID) + .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY) + .build(); + String inactiveRuleId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), + inactiveRule, UPDATE_ORIGIN_APP, "add it", CUSTOM_PKG_UID); - assertThat(mZenModeHelper.getZenMode()).isEqualTo(ZEN_MODE_IMPORTANT_INTERRUPTIONS); - - // User turns DND off. - mZenModeHelper.setManualZenMode(ZEN_MODE_OFF, null, setZenOrigin.value(), - "snoozing", "systemui", Process.SYSTEM_UID); - ZenModeConfig config = mZenModeHelper.mConfig; - if (setZenOrigin == ChangeOrigin.ORIGIN_USER) { - // Other rule was unaffected. assertThat(mZenModeHelper.getZenMode()).isEqualTo(ZEN_MODE_IMPORTANT_INTERRUPTIONS); - assertThat(config.automaticRules.get(activeRuleId).snoozing).isFalse(); - assertThat(config.automaticRules.get(inactiveRuleId).snoozing).isFalse(); - } else { - assertThat(mZenModeHelper.getZenMode()).isEqualTo(ZEN_MODE_OFF); - assertThat(config.automaticRules.get(activeRuleId).snoozing).isTrue(); - assertThat(config.automaticRules.get(inactiveRuleId).snoozing).isFalse(); + + // User turns DND off. + mZenModeHelper.setManualZenMode(ZEN_MODE_OFF, null, origin.value(), + "snoozing", "systemui", Process.SYSTEM_UID); + ZenModeConfig config = mZenModeHelper.mConfig; + if (origin == ChangeOrigin.ORIGIN_USER) { + // Other rule was unaffected. + assertWithMessage("Failure for origin " + origin.name()).that( + mZenModeHelper.getZenMode()).isEqualTo(ZEN_MODE_IMPORTANT_INTERRUPTIONS); + assertWithMessage("Failure for origin " + origin.name()).that( + config.automaticRules.get(activeRuleId).snoozing).isFalse(); + assertWithMessage("Failure for origin " + origin.name()).that( + config.automaticRules.get(inactiveRuleId).snoozing).isFalse(); + } else { + assertWithMessage("Failure for origin " + origin.name()).that( + mZenModeHelper.getZenMode()).isEqualTo(ZEN_MODE_OFF); + assertWithMessage("Failure for origin " + origin.name()).that( + config.automaticRules.get(activeRuleId).snoozing).isTrue(); + assertWithMessage("Failure for origin " + origin.name()).that( + config.automaticRules.get(inactiveRuleId).snoozing).isFalse(); + } } } @@ -3002,45 +3019,17 @@ public class ZenModeHelperTest extends UiServiceTestCase { assertEquals(ZEN_MODE_OFF, mZenModeHelper.mZenMode); } - private enum ModesFlag { - MODES_UI(2, /* originForUserActionInSystemUi= */ UPDATE_ORIGIN_USER), - MODES_API(1, /* originForUserActionInSystemUi= */ UPDATE_ORIGIN_USER), - DISABLED(0, /* originForUserActionInSystemUi= */ UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI); - - private final int mFlagsEnabled; - @ConfigChangeOrigin - private final int mOriginForUserActionInSystemUi; - - ModesFlag(int flagsEnabled, @ConfigChangeOrigin int originForUserActionInSystemUi) { - this.mFlagsEnabled = flagsEnabled; - this.mOriginForUserActionInSystemUi = originForUserActionInSystemUi; - } - - void applyFlags(SetFlagsRule setFlagsRule) { - if (mFlagsEnabled >= 1) { - setFlagsRule.enableFlags(Flags.FLAG_MODES_API); - } else { - setFlagsRule.disableFlags(Flags.FLAG_MODES_API); - } - if (mFlagsEnabled >= 2) { - setFlagsRule.enableFlags(Flags.FLAG_MODES_UI); - } else { - setFlagsRule.disableFlags(Flags.FLAG_MODES_UI); - } - } - } - @Test - public void testZenModeEventLog_setManualZenMode(@TestParameter ModesFlag modesFlag) + public void testZenModeEventLog_setManualZenMode() throws IllegalArgumentException { - modesFlag.applyFlags(mSetFlagsRule); mTestFlagResolver.setFlagOverride(LOG_DND_STATE_EVENTS, true); setupZenConfig(); // Turn zen mode on (to important_interruptions) // Need to additionally call the looper in order to finish the post-apply-config process mZenModeHelper.setManualZenMode(ZEN_MODE_IMPORTANT_INTERRUPTIONS, null, - modesFlag.mOriginForUserActionInSystemUi, "", null, Process.SYSTEM_UID); + Flags.modesApi() ? UPDATE_ORIGIN_USER: UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, "", null, + Process.SYSTEM_UID); // Now turn zen mode off, but via a different package UID -- this should get registered as // "not an action by the user" because some other app is changing zen mode @@ -3068,7 +3057,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { assertEquals(DNDProtoEnums.MANUAL_RULE, mZenModeEventLogger.getChangedRuleType(0)); assertEquals(1, mZenModeEventLogger.getNumRulesActive(0)); assertThat(mZenModeEventLogger.getFromSystemOrSystemUi(0)).isEqualTo( - modesFlag == ModesFlag.DISABLED); + !(Flags.modesUi() || Flags.modesApi())); assertTrue(mZenModeEventLogger.getIsUserAction(0)); assertEquals(Process.SYSTEM_UID, mZenModeEventLogger.getPackageUid(0)); checkDndProtoMatchesSetupZenConfig(mZenModeEventLogger.getPolicyProto(0)); @@ -3097,9 +3086,8 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test - public void testZenModeEventLog_automaticRules(@TestParameter ModesFlag modesFlag) + public void testZenModeEventLog_automaticRules() throws IllegalArgumentException { - modesFlag.applyFlags(mSetFlagsRule); mTestFlagResolver.setFlagOverride(LOG_DND_STATE_EVENTS, true); setupZenConfig(); @@ -3116,15 +3104,14 @@ public class ZenModeHelperTest extends UiServiceTestCase { // Event 1: Mimic the rule coming on automatically by setting the Condition to STATE_TRUE mZenModeHelper.setAutomaticZenRuleState(id, new Condition(zenRule.getConditionId(), "", STATE_TRUE), - UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, - Process.SYSTEM_UID); + UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, Process.SYSTEM_UID); // Event 2: "User" turns off the automatic rule (sets it to not enabled) zenRule.setEnabled(false); mZenModeHelper.updateAutomaticZenRule(id, zenRule, - modesFlag.mOriginForUserActionInSystemUi, "", Process.SYSTEM_UID); + Flags.modesApi() ? UPDATE_ORIGIN_USER: UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, "", + Process.SYSTEM_UID); - // Add a new system rule AutomaticZenRule systemRule = new AutomaticZenRule("systemRule", null, new ComponentName("android", "ScheduleConditionProvider"), @@ -3132,7 +3119,8 @@ public class ZenModeHelperTest extends UiServiceTestCase { null, NotificationManager.INTERRUPTION_FILTER_PRIORITY, true); String systemId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), systemRule, - UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, "test", Process.SYSTEM_UID); + Flags.modesApi() ? UPDATE_ORIGIN_USER: UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, "test", + Process.SYSTEM_UID); // Event 3: turn on the system rule mZenModeHelper.setAutomaticZenRuleState(systemId, @@ -3140,9 +3128,9 @@ public class ZenModeHelperTest extends UiServiceTestCase { UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, Process.SYSTEM_UID); // Event 4: "User" deletes the rule - mZenModeHelper.removeAutomaticZenRule(systemId, modesFlag.mOriginForUserActionInSystemUi, - "", Process.SYSTEM_UID); - + mZenModeHelper.removeAutomaticZenRule(systemId, + Flags.modesApi() ? UPDATE_ORIGIN_USER: UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, "", + Process.SYSTEM_UID); // In total, this represents 4 events assertEquals(4, mZenModeEventLogger.numLoggedChanges()); @@ -3201,7 +3189,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test - @EnableFlags(Flags.FLAG_MODES_API) + @EnableFlags(FLAG_MODES_API) public void testZenModeEventLog_automaticRuleActivatedFromAppByAppAndUser() throws IllegalArgumentException { mTestFlagResolver.setFlagOverride(LOG_DND_STATE_EVENTS, true); @@ -3288,22 +3276,19 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test - public void testZenModeEventLog_policyChanges(@TestParameter ModesFlag modesFlag) + public void testZenModeEventLog_policyChanges() throws IllegalArgumentException { - modesFlag.applyFlags(mSetFlagsRule); mTestFlagResolver.setFlagOverride(LOG_DND_STATE_EVENTS, true); setupZenConfig(); // First just turn zen mode on mZenModeHelper.setManualZenMode(ZEN_MODE_IMPORTANT_INTERRUPTIONS, null, - modesFlag.mOriginForUserActionInSystemUi, "", null, Process.SYSTEM_UID); + UPDATE_ORIGIN_USER, "", null, Process.SYSTEM_UID); // Now change the policy slightly; want to confirm that this'll be reflected in the logs ZenModeConfig newConfig = mZenModeHelper.mConfig.copy(); - newConfig.allowAlarms = true; - newConfig.allowRepeatCallers = false; - mZenModeHelper.setNotificationPolicy(newConfig.toNotificationPolicy(), - modesFlag.mOriginForUserActionInSystemUi, Process.SYSTEM_UID); + mZenModeHelper.setNotificationPolicy(new Policy(PRIORITY_CATEGORY_ALARMS, 0, 0), + UPDATE_ORIGIN_USER, Process.SYSTEM_UID); // Turn zen mode off; we want to make sure policy changes do not get logged when zen mode // is off. @@ -3311,10 +3296,8 @@ public class ZenModeHelperTest extends UiServiceTestCase { null, Process.SYSTEM_UID); // Change the policy again - newConfig.allowMessages = false; - newConfig.allowRepeatCallers = true; - mZenModeHelper.setNotificationPolicy(newConfig.toNotificationPolicy(), - modesFlag.mOriginForUserActionInSystemUi, Process.SYSTEM_UID); + mZenModeHelper.setNotificationPolicy(new Policy(PRIORITY_CATEGORY_REPEAT_CALLERS, 0, 0), + UPDATE_ORIGIN_USER, Process.SYSTEM_UID); // Total events: we only expect ones for turning on, changing policy, and turning off assertEquals(3, mZenModeEventLogger.numLoggedChanges()); @@ -3347,9 +3330,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test - public void testZenModeEventLog_ruleCounts(@TestParameter ModesFlag modesFlag) - throws IllegalArgumentException { - modesFlag.applyFlags(mSetFlagsRule); + public void testZenModeEventLog_ruleCounts() throws IllegalArgumentException { mTestFlagResolver.setFlagOverride(LOG_DND_STATE_EVENTS, true); setupZenConfig(); @@ -3374,14 +3355,12 @@ public class ZenModeHelperTest extends UiServiceTestCase { // Rule 3, has stricter settings than the default settings ZenModeConfig ruleConfig = mZenModeHelper.mConfig.copy(); - ruleConfig.allowReminders = false; - ruleConfig.allowCalls = false; - ruleConfig.allowMessages = false; + ruleConfig.applyNotificationPolicy(new Policy(0, 0, 0)); AutomaticZenRule zenRule3 = new AutomaticZenRule("name3", null, new ComponentName("android", "ScheduleConditionProvider"), ZenModeConfig.toScheduleConditionId(new ScheduleInfo()), - ruleConfig.toZenPolicy(), + ruleConfig.getZenPolicy(), NotificationManager.INTERRUPTION_FILTER_PRIORITY, true); String id3 = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), zenRule3, UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, "test", Process.SYSTEM_UID); @@ -3452,10 +3431,8 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test - public void testZenModeEventLog_noLogWithNoConfigChange( - @TestParameter ModesFlag modesFlag) throws IllegalArgumentException { + public void testZenModeEventLog_noLogWithNoConfigChange() throws IllegalArgumentException { // If evaluateZenMode is called independently of a config change, don't log. - modesFlag.applyFlags(mSetFlagsRule); mTestFlagResolver.setFlagOverride(LOG_DND_STATE_EVENTS, true); setupZenConfig(); @@ -3472,17 +3449,16 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test - public void testZenModeEventLog_reassignUid(@TestParameter ModesFlag modesFlag) + public void testZenModeEventLog_reassignUid() throws IllegalArgumentException { // Test that, only in specific cases, we reassign the calling UID to one associated with // the automatic rule owner. - modesFlag.applyFlags(mSetFlagsRule); mTestFlagResolver.setFlagOverride(LOG_DND_STATE_EVENTS, true); setupZenConfig(); // Explicitly set up all rules with the same policy as the manual rule so there will be // no policy changes in this test case. - ZenPolicy manualRulePolicy = mZenModeHelper.mConfig.toZenPolicy(); + ZenPolicy manualRulePolicy = mZenModeHelper.mConfig.getZenPolicy(); // Rule 1, owned by a package AutomaticZenRule zenRule = new AutomaticZenRule("name", @@ -3502,7 +3478,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { manualRulePolicy, NotificationManager.INTERRUPTION_FILTER_PRIORITY, true); String id2 = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), zenRule2, - modesFlag.mOriginForUserActionInSystemUi, "test", Process.SYSTEM_UID); + UPDATE_ORIGIN_USER, "test", Process.SYSTEM_UID); // Turn on rule 1; call looks like it's from the system. Because setting a condition is // typically an automatic (non-user-initiated) action, expect the calling UID to be @@ -3521,7 +3497,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { // from the system-provided one. zenRule.setEnabled(false); mZenModeHelper.updateAutomaticZenRule(id, zenRule, - modesFlag.mOriginForUserActionInSystemUi, "", Process.SYSTEM_UID); + UPDATE_ORIGIN_USER, "", Process.SYSTEM_UID); // Add a manual rule. Any manual rule changes should not get calling uids reassigned. mZenModeHelper.setManualZenMode(ZEN_MODE_IMPORTANT_INTERRUPTIONS, null, UPDATE_ORIGIN_APP, @@ -3578,10 +3554,8 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test - public void testZenModeEventLog_channelsBypassingChanges( - @TestParameter ModesFlag modesFlag) { + public void testZenModeEventLog_channelsBypassingChanges() { // Verify that the right thing happens when the canBypassDnd value changes. - modesFlag.applyFlags(mSetFlagsRule); mTestFlagResolver.setFlagOverride(LOG_DND_STATE_EVENTS, true); setupZenConfig(); @@ -3589,21 +3563,25 @@ public class ZenModeHelperTest extends UiServiceTestCase { // as a user action, and *should* get its UID reassigned. mZenModeHelper.setManualZenMode(ZEN_MODE_IMPORTANT_INTERRUPTIONS, null, UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, "", CUSTOM_PKG_NAME, Process.SYSTEM_UID); + assertEquals(1, mZenModeEventLogger.numLoggedChanges()); // Now change apps bypassing to true ZenModeConfig newConfig = mZenModeHelper.mConfig.copy(); newConfig.areChannelsBypassingDnd = true; mZenModeHelper.setNotificationPolicy(newConfig.toNotificationPolicy(), UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, Process.SYSTEM_UID); + assertEquals(2, mZenModeEventLogger.numLoggedChanges()); // and then back to false, all without changing anything else newConfig.areChannelsBypassingDnd = false; mZenModeHelper.setNotificationPolicy(newConfig.toNotificationPolicy(), UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, Process.SYSTEM_UID); + assertEquals(3, mZenModeEventLogger.numLoggedChanges()); // Turn off manual mode, call from a package: don't reset UID even though enabler is set mZenModeHelper.setManualZenMode(ZEN_MODE_OFF, null, UPDATE_ORIGIN_APP, "", CUSTOM_PKG_NAME, 12345); + assertEquals(4, mZenModeEventLogger.numLoggedChanges()); // And likewise when turning it back on again mZenModeHelper.setManualZenMode(ZEN_MODE_IMPORTANT_INTERRUPTIONS, null, UPDATE_ORIGIN_APP, @@ -3642,11 +3620,11 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test + @EnableFlags(FLAG_MODES_API) public void testZenModeEventLog_policyAllowChannels() { // when modes_api flag is on, ensure that any change in allow_channels gets logged, // even when there are no other changes. mTestFlagResolver.setFlagOverride(LOG_DND_STATE_EVENTS, true); - mSetFlagsRule.enableFlags(Flags.FLAG_MODES_API); // Default zen config has allow channels = priority (aka on) setupZenConfig(); @@ -3658,8 +3636,12 @@ public class ZenModeHelperTest extends UiServiceTestCase { // Now change only the channels part of the policy; want to confirm that this'll be // reflected in the logs ZenModeConfig newConfig = mZenModeHelper.mConfig.copy(); - newConfig.allowPriorityChannels = false; - mZenModeHelper.setNotificationPolicy(newConfig.toNotificationPolicy(), + Policy oldPolicy = newConfig.toNotificationPolicy(); + Policy newPolicy = new Policy(oldPolicy.priorityCategories, oldPolicy.priorityCallSenders, + oldPolicy.priorityMessageSenders, oldPolicy.suppressedVisualEffects, + STATE_PRIORITY_CHANNELS_BLOCKED, + oldPolicy.priorityConversationSenders); + mZenModeHelper.setNotificationPolicy(newPolicy, UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, Process.SYSTEM_UID); // Total events: one for turning on, one for changing policy @@ -3688,7 +3670,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test - @EnableFlags(Flags.FLAG_MODES_API) + @EnableFlags(FLAG_MODES_API) public void testZenModeEventLog_ruleWithInterruptionFilterAll_notLoggedAsDndChange() { mTestFlagResolver.setFlagOverride(LOG_DND_STATE_EVENTS, true); setupZenConfig(); @@ -3730,7 +3712,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test - @EnableFlags(Flags.FLAG_MODES_API) + @EnableFlags(FLAG_MODES_API) public void testZenModeEventLog_activeRuleTypes() { mTestFlagResolver.setFlagOverride(LOG_DND_STATE_EVENTS, true); setupZenConfig(); @@ -3748,7 +3730,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { // Create immersive rule AutomaticZenRule immersive = new AutomaticZenRule.Builder("Immersed", CONDITION_ID) .setType(TYPE_IMMERSIVE) - .setZenPolicy(mZenModeHelper.mConfig.toZenPolicy()) // same as the manual rule + .setZenPolicy(mZenModeHelper.mConfig.getZenPolicy()) // same as the manual rule .build(); String immersiveId = mZenModeHelper.addAutomaticZenRule(mPkg, immersive, UPDATE_ORIGIN_APP, "reason", CUSTOM_PKG_UID); @@ -3819,10 +3801,9 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test - @DisableFlags(Flags.FLAG_MODES_API) + @DisableFlags(FLAG_MODES_API) public void testUpdateConsolidatedPolicy_preModesApiDefaultRulesOnly_takesGlobalDefault() { setupZenConfig(); - // When there's one automatic rule active and it doesn't specify a policy, test that the // resulting consolidated policy is one that matches the default rule settings. AutomaticZenRule zenRule = new AutomaticZenRule("name", @@ -3839,6 +3820,9 @@ public class ZenModeHelperTest extends UiServiceTestCase { new Condition(zenRule.getConditionId(), "", STATE_TRUE), UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, Process.SYSTEM_UID); + assertEquals(mZenModeHelper.getNotificationPolicy(), + mZenModeHelper.getConsolidatedNotificationPolicy()); + // inspect the consolidated policy. Based on setupZenConfig() values. assertFalse(mZenModeHelper.mConsolidatedPolicy.allowAlarms()); assertFalse(mZenModeHelper.mConsolidatedPolicy.allowMedia()); @@ -3853,9 +3837,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test - public void testUpdateConsolidatedPolicy_modesApiDefaultRulesOnly_takesDefault( - @TestParameter({"MODES_UI", "MODES_API"}) ModesFlag modesFlag) { - modesFlag.applyFlags(mSetFlagsRule); + public void testUpdateConsolidatedPolicy_modesApiDefaultRulesOnly_takesDefault() { setupZenConfig(); // When there's one automatic rule active and it doesn't specify a policy, test that the @@ -3876,13 +3858,13 @@ public class ZenModeHelperTest extends UiServiceTestCase { // inspect the consolidated policy, which should match the device default settings. assertThat(ZenAdapters.notificationPolicyToZenPolicy(mZenModeHelper.mConsolidatedPolicy)) - .isEqualTo(modesFlag == ModesFlag.MODES_UI + .isEqualTo(Flags.modesUi() ? mZenModeHelper.getDefaultZenPolicy() - : mZenModeHelper.mConfig.toZenPolicy()); + : mZenModeHelper.mConfig.getZenPolicy()); } @Test - @DisableFlags(Flags.FLAG_MODES_API) + @DisableFlags(FLAG_MODES_API) public void testUpdateConsolidatedPolicy_preModesApiCustomPolicyOnly_fillInWithGlobal() { setupZenConfig(); @@ -3928,9 +3910,8 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test - public void testUpdateConsolidatedPolicy_modesApiCustomPolicyOnly_fillInWithDefault( - @TestParameter({"MODES_UI", "MODES_API"}) ModesFlag modesFlag) { - modesFlag.applyFlags(mSetFlagsRule); + @EnableFlags(FLAG_MODES_API) + public void testUpdateConsolidatedPolicy_modesApiCustomPolicyOnly_fillInWithDefault() { setupZenConfig(); // when there's only one automatic rule active and it has a custom policy, make sure that's @@ -3962,12 +3943,12 @@ public class ZenModeHelperTest extends UiServiceTestCase { // policy for every field specified, and take default values (from either device default // policy or manual rule) for unspecified things assertThat(mZenModeHelper.mConsolidatedPolicy.allowAlarms()).isEqualTo( - modesFlag == ModesFlag.MODES_UI ? true : false); // default + Flags.modesUi() ? true : false); // default assertThat(mZenModeHelper.mConsolidatedPolicy.allowMedia()).isEqualTo( - modesFlag == ModesFlag.MODES_UI ? true : false); // default + Flags.modesUi() ? true : false); // default assertThat(mZenModeHelper.mConsolidatedPolicy.allowSystem()).isTrue(); // custom assertThat(mZenModeHelper.mConsolidatedPolicy.allowReminders()).isEqualTo( - modesFlag == ModesFlag.MODES_UI ? false : true); // default + Flags.modesUi() ? false : true); // default assertThat(mZenModeHelper.mConsolidatedPolicy.allowCalls()).isFalse(); // custom assertThat(mZenModeHelper.mConsolidatedPolicy.allowMessages()).isTrue(); // default assertThat(mZenModeHelper.mConsolidatedPolicy.allowRepeatCallers()).isFalse(); // custom @@ -3976,7 +3957,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test - @DisableFlags(Flags.FLAG_MODES_API) + @DisableFlags(FLAG_MODES_API) public void testUpdateConsolidatedPolicy_preModesApiDefaultAndCustomActive_mergesWithGlobal() { setupZenConfig(); @@ -4037,9 +4018,8 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test - public void testUpdateConsolidatedPolicy_modesApiDefaultAndCustomActive_mergesWithDefault( - @TestParameter({"MODES_UI", "MODES_API"}) ModesFlag modesFlag) { - modesFlag.applyFlags(mSetFlagsRule); + @EnableFlags(FLAG_MODES_API) + public void testUpdateConsolidatedPolicy_modesApiDefaultAndCustomActive_mergesWithDefault() { setupZenConfig(); // when there are two rules active, one inheriting the default policy and one setting its @@ -4088,10 +4068,10 @@ public class ZenModeHelperTest extends UiServiceTestCase { // restrictive option of each of the two assertThat(mZenModeHelper.mConsolidatedPolicy.allowAlarms()).isFalse(); // custom stricter assertThat(mZenModeHelper.mConsolidatedPolicy.allowMedia()).isEqualTo( - modesFlag == ModesFlag.MODES_UI ? true : false); // default + Flags.modesUi() ? true : false); // default assertThat(mZenModeHelper.mConsolidatedPolicy.allowSystem()).isFalse(); // default stricter assertThat(mZenModeHelper.mConsolidatedPolicy.allowReminders()).isEqualTo( - modesFlag == ModesFlag.MODES_UI ? false : true); // default + Flags.modesUi() ? false : true); // default assertThat(mZenModeHelper.mConsolidatedPolicy.allowCalls()).isFalse(); // custom stricter assertThat(mZenModeHelper.mConsolidatedPolicy.allowMessages()).isTrue(); // default assertThat(mZenModeHelper.mConsolidatedPolicy.allowConversations()).isTrue(); // default @@ -4099,12 +4079,12 @@ public class ZenModeHelperTest extends UiServiceTestCase { .isFalse(); // custom stricter assertThat(mZenModeHelper.mConsolidatedPolicy.showBadges()).isFalse(); // custom stricter assertThat(mZenModeHelper.mConsolidatedPolicy.showPeeking()).isEqualTo( - modesFlag == ModesFlag.MODES_UI ? false : true); // default + Flags.modesUi() ? false : true); // default } @Test + @EnableFlags(FLAG_MODES_API) public void testUpdateConsolidatedPolicy_allowChannels() { - mSetFlagsRule.enableFlags(Flags.FLAG_MODES_API); setupZenConfig(); // one rule, custom policy, allows channels @@ -4153,9 +4133,8 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test - public void testUpdateConsolidatedPolicy_ignoresActiveRulesWithInterruptionFilterAll( - @TestParameter({"MODES_UI", "MODES_API"}) ModesFlag modesFlag) { - modesFlag.applyFlags(mSetFlagsRule); + @EnableFlags(FLAG_MODES_API) + public void testUpdateConsolidatedPolicy_ignoresActiveRulesWithInterruptionFilterAll() { setupZenConfig(); // Rules with INTERRUPTION_FILTER_ALL are skipped when calculating consolidated policy. @@ -4193,11 +4172,11 @@ public class ZenModeHelperTest extends UiServiceTestCase { // Consolidated Policy should be default + rule1. assertThat(mZenModeHelper.mConsolidatedPolicy.allowAlarms()).isEqualTo( - modesFlag == ModesFlag.MODES_UI ? true : false); // default + Flags.modesUi() ? true : false); // default assertThat(mZenModeHelper.mConsolidatedPolicy.allowMedia()).isTrue(); // priority rule assertThat(mZenModeHelper.mConsolidatedPolicy.allowSystem()).isTrue(); // priority rule assertThat(mZenModeHelper.mConsolidatedPolicy.allowReminders()).isEqualTo( - modesFlag == ModesFlag.MODES_UI ? false : true); // default + Flags.modesUi() ? false : true); // default assertThat(mZenModeHelper.mConsolidatedPolicy.allowCalls()).isTrue(); // default assertThat(mZenModeHelper.mConsolidatedPolicy.allowMessages()).isTrue(); // default assertThat(mZenModeHelper.mConsolidatedPolicy.allowConversations()).isTrue(); // default @@ -4205,8 +4184,8 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test + @EnableFlags(FLAG_MODES_API) public void zenRuleToAutomaticZenRule_allFields() { - mSetFlagsRule.enableFlags(Flags.FLAG_MODES_API); when(mPackageManager.getPackagesForUid(anyInt())).thenReturn( new String[]{OWNER.getPackageName()}); @@ -4249,8 +4228,8 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test + @EnableFlags(FLAG_MODES_API) public void automaticZenRuleToZenRule_allFields() { - mSetFlagsRule.enableFlags(Flags.FLAG_MODES_API); when(mPackageManager.getPackagesForUid(anyInt())).thenReturn( new String[]{OWNER.getPackageName()}); @@ -4291,7 +4270,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test - @EnableFlags(Flags.FLAG_MODES_API) + @EnableFlags(FLAG_MODES_API) public void updateAutomaticZenRule_fromApp_updatesNameUnlessUserModified() { // Add a starting rule with the name OriginalName. AutomaticZenRule azrBase = new AutomaticZenRule.Builder("OriginalName", CONDITION_ID) @@ -4348,7 +4327,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test - @EnableFlags(Flags.FLAG_MODES_API) + @EnableFlags(FLAG_MODES_API) public void updateAutomaticZenRule_fromUser_updatesBitmaskAndValue() { // Adds a starting rule with empty zen policies and device effects AutomaticZenRule azrBase = new AutomaticZenRule.Builder(NAME, CONDITION_ID) @@ -4385,7 +4364,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { assertThat(rule.getInterruptionFilter()).isEqualTo(INTERRUPTION_FILTER_PRIORITY); assertThat(rule.getIconResId()).isEqualTo(ICON_RES_ID); assertThat(rule.getZenPolicy().getPriorityChannelsAllowed()).isEqualTo( - ZenPolicy.STATE_DISALLOW); + STATE_DISALLOW); assertThat(rule.getDeviceEffects().shouldDisplayGrayscale()).isTrue(); @@ -4401,7 +4380,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test - @EnableFlags(Flags.FLAG_MODES_API) + @EnableFlags(FLAG_MODES_API) public void updateAutomaticZenRule_fromSystemUi_updatesValues() { // Adds a starting rule with empty zen policies and device effects AutomaticZenRule azrBase = new AutomaticZenRule.Builder(NAME, CONDITION_ID) @@ -4441,7 +4420,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { // UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI should change the value but NOT update the bitmask. assertThat(rule.getIconResId()).isEqualTo(ICON_RES_ID); assertThat(rule.getZenPolicy().getPriorityCategoryReminders()) - .isEqualTo(ZenPolicy.STATE_ALLOW); + .isEqualTo(STATE_ALLOW); assertThat(rule.getDeviceEffects().shouldDisplayGrayscale()).isTrue(); ZenRule storedRule = mZenModeHelper.mConfig.automaticRules.get(ruleId); @@ -4451,7 +4430,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test - @EnableFlags(Flags.FLAG_MODES_API) + @EnableFlags(FLAG_MODES_API) public void updateAutomaticZenRule_fromApp_updatesValuesIfRuleNotUserModified() { // Adds a starting rule with empty zen policies and device effects AutomaticZenRule azrBase = new AutomaticZenRule.Builder(NAME, CONDITION_ID) @@ -4490,7 +4469,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { assertThat(storedRule.zenMode).isEqualTo(ZEN_MODE_ALARMS); assertThat(storedRule.zenPolicy.getPriorityCategoryReminders()) - .isEqualTo(ZenPolicy.STATE_ALLOW); + .isEqualTo(STATE_ALLOW); assertThat(storedRule.zenDeviceEffects.shouldDisplayGrayscale()).isTrue(); assertThat(storedRule.userModifiedFields).isEqualTo(0); assertThat(storedRule.zenPolicyUserModifiedFields).isEqualTo(0); @@ -4514,7 +4493,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { // so the rule is not changed, and neither is the bitmask. assertThat(ruleUser.getInterruptionFilter()).isEqualTo(INTERRUPTION_FILTER_ALL); assertThat(ruleUser.getZenPolicy().getPriorityCategoryReminders()) - .isEqualTo(ZenPolicy.STATE_DISALLOW); + .isEqualTo(STATE_DISALLOW); assertThat(ruleUser.getDeviceEffects().shouldDisplayGrayscale()).isFalse(); storedRule = mZenModeHelper.mConfig.automaticRules.get(ruleIdUser); @@ -4525,7 +4504,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test - @EnableFlags(Flags.FLAG_MODES_API) + @EnableFlags(FLAG_MODES_API) public void addAutomaticZenRule_updatesValues() { // Adds a starting rule with empty zen policies and device effects AutomaticZenRule azrBase = new AutomaticZenRule.Builder(NAME, CONDITION_ID) @@ -4543,7 +4522,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { // The values are modified but the bitmask is not. assertThat(rule.getZenPolicy().getPriorityCategoryReminders()) - .isEqualTo(ZenPolicy.STATE_ALLOW); + .isEqualTo(STATE_ALLOW); assertThat(rule.getDeviceEffects().shouldDisplayGrayscale()).isTrue(); ZenRule storedRule = mZenModeHelper.mConfig.automaticRules.get(ruleId); @@ -4551,7 +4530,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test - @EnableFlags(Flags.FLAG_MODES_API) + @EnableFlags(FLAG_MODES_API) public void updateAutomaticZenRule_nullDeviceEffectsUpdate() { // Adds a starting rule with empty zen policies and device effects ZenDeviceEffects zde = new ZenDeviceEffects.Builder().setShouldUseNightMode(true).build(); @@ -4578,7 +4557,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test - @EnableFlags(Flags.FLAG_MODES_API) + @EnableFlags(FLAG_MODES_API) public void updateAutomaticZenRule_nullPolicyUpdate() { // Adds a starting rule with set zen policy and empty device effects AutomaticZenRule azrBase = new AutomaticZenRule.Builder(NAME, CONDITION_ID) @@ -4607,7 +4586,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test - @EnableFlags(Flags.FLAG_MODES_API) + @EnableFlags(FLAG_MODES_API) public void automaticZenRuleToZenRule_nullToNonNullPolicyUpdate() { when(mContext.checkCallingPermission(anyString())) .thenReturn(PackageManager.PERMISSION_GRANTED); @@ -4654,7 +4633,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { // New ZenPolicy differs from the default config assertThat(rule.getZenPolicy()).isNotNull(); assertThat(rule.getZenPolicy().getPriorityChannelsAllowed()).isEqualTo( - ZenPolicy.STATE_DISALLOW); + STATE_DISALLOW); ZenRule storedRule = mZenModeHelper.mConfig.automaticRules.get(ruleId); assertThat(storedRule.canBeUpdatedByApp()).isFalse(); @@ -4671,7 +4650,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test - @EnableFlags(Flags.FLAG_MODES_API) + @EnableFlags(FLAG_MODES_API) public void automaticZenRuleToZenRule_nullToNonNullDeviceEffectsUpdate() { // Adds a starting rule with empty zen policies and device effects AutomaticZenRule azrBase = new AutomaticZenRule.Builder(NAME, CONDITION_ID) @@ -4775,8 +4754,8 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test + @EnableFlags(FLAG_MODES_API) public void testUpdateAutomaticRule_activated_triggersBroadcast() throws Exception { - mSetFlagsRule.enableFlags(Flags.FLAG_MODES_API); setupZenConfig(); // Add a new automatic zen rule that's enabled @@ -4815,8 +4794,8 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test + @EnableFlags(FLAG_MODES_API) public void testUpdateAutomaticRule_deactivatedByUser_triggersBroadcast() throws Exception { - mSetFlagsRule.enableFlags(Flags.FLAG_MODES_API); setupZenConfig(); // Add a new automatic zen rule that's enabled @@ -4860,8 +4839,8 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test + @EnableFlags(FLAG_MODES_API) public void testUpdateAutomaticRule_deactivatedByApp_triggersBroadcast() throws Exception { - mSetFlagsRule.enableFlags(Flags.FLAG_MODES_API); setupZenConfig(); // Add a new automatic zen rule that's enabled @@ -4937,7 +4916,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test - @EnableFlags(Flags.FLAG_MODES_API) + @EnableFlags(FLAG_MODES_API) public void updateAutomaticZenRule_ruleChanged_deactivatesRule() { assertThat(mZenModeHelper.getZenMode()).isEqualTo(ZEN_MODE_OFF); AutomaticZenRule rule = new AutomaticZenRule.Builder("rule", CONDITION_ID) @@ -4961,7 +4940,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test - @EnableFlags(Flags.FLAG_MODES_API) + @EnableFlags(FLAG_MODES_API) public void updateAutomaticZenRule_ruleNotChanged_doesNotDeactivateRule() { assertThat(mZenModeHelper.getZenMode()).isEqualTo(ZEN_MODE_OFF); AutomaticZenRule rule = new AutomaticZenRule.Builder("rule", CONDITION_ID) @@ -4984,7 +4963,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test - @EnableFlags({Flags.FLAG_MODES_API, Flags.FLAG_MODES_UI}) + @EnableFlags({FLAG_MODES_API, FLAG_MODES_UI}) public void updateAutomaticZenRule_ruleChangedByUser_doesNotDeactivateRule() { assertThat(mZenModeHelper.getZenMode()).isEqualTo(ZEN_MODE_OFF); AutomaticZenRule rule = new AutomaticZenRule.Builder("rule", CONDITION_ID) @@ -5009,7 +4988,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test - @EnableFlags({Flags.FLAG_MODES_API, Flags.FLAG_MODES_UI}) + @EnableFlags({FLAG_MODES_API, FLAG_MODES_UI}) public void updateAutomaticZenRule_ruleDisabledByUser_doesNotReactivateOnReenable() { assertThat(mZenModeHelper.getZenMode()).isEqualTo(ZEN_MODE_OFF); AutomaticZenRule rule = new AutomaticZenRule.Builder("rule", CONDITION_ID) @@ -5034,7 +5013,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test - @EnableFlags(Flags.FLAG_MODES_API) + @EnableFlags(FLAG_MODES_API) public void removeAutomaticZenRule_propagatesOriginToEffectsApplier() { mZenModeHelper.setDeviceEffectsApplier(mDeviceEffectsApplier); reset(mDeviceEffectsApplier); @@ -5057,8 +5036,8 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test + @EnableFlags(FLAG_MODES_API) public void testDeviceEffects_applied() { - mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API); mZenModeHelper.setDeviceEffectsApplier(mDeviceEffectsApplier); verify(mDeviceEffectsApplier).apply(eq(NO_EFFECTS), eq(UPDATE_ORIGIN_INIT)); @@ -5077,8 +5056,8 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test + @EnableFlags(FLAG_MODES_API) public void testDeviceEffects_onDeactivateRule_applied() { - mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API); mZenModeHelper.setDeviceEffectsApplier(mDeviceEffectsApplier); ZenDeviceEffects zde = new ZenDeviceEffects.Builder().setShouldUseNightMode(true).build(); @@ -5096,8 +5075,8 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test + @EnableFlags(FLAG_MODES_API) public void testDeviceEffects_changeToConsolidatedEffects_applied() { - mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API); mZenModeHelper.setDeviceEffectsApplier(mDeviceEffectsApplier); verify(mDeviceEffectsApplier).apply(eq(NO_EFFECTS), eq(UPDATE_ORIGIN_INIT)); @@ -5136,8 +5115,8 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test + @EnableFlags(FLAG_MODES_API) public void testDeviceEffects_noChangeToConsolidatedEffects_notApplied() { - mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API); mZenModeHelper.setDeviceEffectsApplier(mDeviceEffectsApplier); verify(mDeviceEffectsApplier).apply(eq(NO_EFFECTS), eq(UPDATE_ORIGIN_INIT)); @@ -5161,9 +5140,8 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test + @EnableFlags(FLAG_MODES_API) public void testDeviceEffects_activeBeforeApplierProvided_appliedWhenProvided() { - mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API); - ZenDeviceEffects zde = new ZenDeviceEffects.Builder().setShouldUseNightMode(true).build(); String ruleId = addRuleWithEffects(zde); verify(mDeviceEffectsApplier, never()).apply(any(), anyInt()); @@ -5178,8 +5156,8 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test + @EnableFlags(FLAG_MODES_API) public void testDeviceEffects_onUserSwitch_appliedImmediately() { - mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API); mZenModeHelper.setDeviceEffectsApplier(mDeviceEffectsApplier); verify(mDeviceEffectsApplier).apply(eq(NO_EFFECTS), eq(UPDATE_ORIGIN_INIT)); @@ -5214,9 +5192,8 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test + @EnableFlags(FLAG_MODES_API) public void removeAndAddAutomaticZenRule_wasCustomized_isRestored() { - mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API); - // Start with a rule. mZenModeHelper.mConfig.automaticRules.clear(); mTestClock.setNowMillis(1000); @@ -5257,7 +5234,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { assertThat(newRuleId).isEqualTo(ruleId); assertThat(finalRule.getInterruptionFilter()).isEqualTo(INTERRUPTION_FILTER_ALARMS); assertThat(finalRule.getZenPolicy().getPriorityCategoryRepeatCallers()).isEqualTo( - ZenPolicy.STATE_ALLOW); + STATE_ALLOW); ZenRule storedRule = mZenModeHelper.mConfig.automaticRules.get(ruleId); assertThat(storedRule.userModifiedFields).isEqualTo( @@ -5270,9 +5247,8 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test + @EnableFlags(FLAG_MODES_API) public void removeAndAddAutomaticZenRule_wasNotCustomized_isNotRestored() { - mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API); - // Start with a single rule. mZenModeHelper.mConfig.automaticRules.clear(); mTestClock.setNowMillis(1000); @@ -5303,9 +5279,8 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test + @EnableFlags(FLAG_MODES_API) public void removeAndAddAutomaticZenRule_recreatedButNotByApp_isNotRestored() { - mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API); - // Start with a single rule. mZenModeHelper.mConfig.automaticRules.clear(); mTestClock.setNowMillis(1000); @@ -5345,16 +5320,15 @@ public class ZenModeHelperTest extends UiServiceTestCase { assertThat(newRuleId).isNotEqualTo(ruleId); assertThat(finalRule.getInterruptionFilter()).isEqualTo(INTERRUPTION_FILTER_PRIORITY); assertThat(finalRule.getZenPolicy().getPriorityCategoryRepeatCallers()).isEqualTo( - ZenPolicy.STATE_DISALLOW); + STATE_DISALLOW); // Also, we discarded the "deleted rule" since we're not interested in recreating it. assertThat(mZenModeHelper.mConfig.deletedRules).hasSize(0); } @Test + @EnableFlags(FLAG_MODES_API) public void removeAndAddAutomaticZenRule_removedByUser_isNotRestored() { - mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API); - // Start with a single rule. mZenModeHelper.mConfig.automaticRules.clear(); mTestClock.setNowMillis(1000); @@ -5394,8 +5368,8 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test + @EnableFlags(FLAG_MODES_API) public void removeAutomaticZenRule_preservedForRestoringByPackageAndConditionId() { - mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API); mContext.getTestablePermissions().setPermission(Manifest.permission.MANAGE_NOTIFICATIONS, PERMISSION_GRANTED); // So that canManageAZR passes although packages don't match. mZenModeHelper.mConfig.automaticRules.clear(); @@ -5434,8 +5408,8 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test + @EnableFlags(FLAG_MODES_API) public void removeAllZenRules_preservedForRestoring() { - mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API); mZenModeHelper.mConfig.automaticRules.clear(); mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), @@ -5456,8 +5430,8 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test + @EnableFlags(FLAG_MODES_API) public void removeAllZenRules_fromSystem_deletesPreservedRulesToo() { - mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API); mZenModeHelper.mConfig.automaticRules.clear(); // Start with deleted rules from 2 different packages. @@ -5476,7 +5450,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test - @EnableFlags(Flags.FLAG_MODES_API) + @EnableFlags(FLAG_MODES_API) public void removeAndAddAutomaticZenRule_wasActive_isRestoredAsInactive() { // Start with a rule. mZenModeHelper.mConfig.automaticRules.clear(); @@ -5525,7 +5499,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test - @EnableFlags(Flags.FLAG_MODES_API) + @EnableFlags(FLAG_MODES_API) public void removeAndAddAutomaticZenRule_wasSnoozed_isRestoredAsInactive() { // Start with a rule. mZenModeHelper.mConfig.automaticRules.clear(); @@ -5579,8 +5553,8 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test + @EnableFlags(FLAG_MODES_API) public void testRuleCleanup() throws Exception { - mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API); Instant now = Instant.ofEpochMilli(1701796461000L); Instant yesterday = now.minus(1, ChronoUnit.DAYS); Instant aWeekAgo = now.minus(7, ChronoUnit.DAYS); @@ -5637,7 +5611,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test - @EnableFlags(Flags.FLAG_MODES_API) + @EnableFlags(FLAG_MODES_API) public void getAutomaticZenRuleState_ownedRule_returnsRuleState() { String id = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), new AutomaticZenRule.Builder("Rule", CONDITION_ID) @@ -5662,7 +5636,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test - @EnableFlags(Flags.FLAG_MODES_API) + @EnableFlags(FLAG_MODES_API) public void getAutomaticZenRuleState_notOwnedRule_returnsStateUnknown() { // Assume existence of a system-owned rule that is currently ACTIVE. ZenRule systemRule = newZenRule("android", Instant.now(), null); @@ -5678,7 +5652,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test - @EnableFlags(Flags.FLAG_MODES_API) + @EnableFlags(FLAG_MODES_API) public void setAutomaticZenRuleState_idForNotOwnedRule_ignored() { // Assume existence of an other-package-owned rule that is currently ACTIVE. assertThat(mZenModeHelper.getZenMode()).isEqualTo(ZEN_MODE_OFF); @@ -5699,7 +5673,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test - @EnableFlags(Flags.FLAG_MODES_API) + @EnableFlags(FLAG_MODES_API) public void setAutomaticZenRuleState_conditionForNotOwnedRule_ignored() { // Assume existence of an other-package-owned rule that is currently ACTIVE. assertThat(mZenModeHelper.getZenMode()).isEqualTo(ZEN_MODE_OFF); @@ -5720,7 +5694,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test - @EnableFlags(android.app.Flags.FLAG_MODES_API) + @EnableFlags(FLAG_MODES_API) public void testCallbacks_policy() throws Exception { setupZenConfig(); assertThat(mZenModeHelper.getNotificationPolicy().allowReminders()).isTrue(); @@ -5740,10 +5714,9 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test - @EnableFlags(android.app.Flags.FLAG_MODES_API) + @EnableFlags(FLAG_MODES_API) public void testCallbacks_consolidatedPolicy() throws Exception { - setupZenConfig(); - assertThat(mZenModeHelper.getConsolidatedNotificationPolicy().allowAlarms()).isTrue(); + assertThat(mZenModeHelper.getConsolidatedNotificationPolicy().allowMedia()).isTrue(); SettableFuture<Policy> futureConsolidatedPolicy = SettableFuture.create(); mZenModeHelper.addCallback(new ZenModeHelper.Callback() { @Override @@ -5762,12 +5735,12 @@ public class ZenModeHelperTest extends UiServiceTestCase { new Condition(CONDITION_ID, "", STATE_TRUE), UPDATE_ORIGIN_APP, CUSTOM_PKG_UID); Policy callbackPolicy = futureConsolidatedPolicy.get(1, TimeUnit.SECONDS); - assertThat(callbackPolicy.allowAlarms()).isFalse(); + assertThat(callbackPolicy.allowMedia()).isFalse(); } @Test + @EnableFlags(FLAG_MODES_API) public void applyGlobalZenModeAsImplicitZenRule_createsImplicitRuleAndActivatesIt() { - mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API); mZenModeHelper.mConfig.automaticRules.clear(); mZenModeHelper.applyGlobalZenModeAsImplicitZenRule(CUSTOM_PKG_NAME, CUSTOM_PKG_UID, @@ -5777,13 +5750,13 @@ public class ZenModeHelperTest extends UiServiceTestCase { .comparingElementsUsing(IGNORE_METADATA) .containsExactly( expectedImplicitRule(CUSTOM_PKG_NAME, ZEN_MODE_IMPORTANT_INTERRUPTIONS, - mZenModeHelper.mConfig.toZenPolicy(), // copy of global config + mZenModeHelper.mConfig.getZenPolicy(), // copy of global config true)); } @Test + @EnableFlags(FLAG_MODES_API) public void applyGlobalZenModeAsImplicitZenRule_updatesImplicitRuleAndActivatesIt() { - mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API); mZenModeHelper.mConfig.automaticRules.clear(); mZenModeHelper.applyGlobalZenModeAsImplicitZenRule(CUSTOM_PKG_NAME, CUSTOM_PKG_UID, @@ -5798,12 +5771,12 @@ public class ZenModeHelperTest extends UiServiceTestCase { .comparingElementsUsing(IGNORE_METADATA) .containsExactly( expectedImplicitRule(CUSTOM_PKG_NAME, ZEN_MODE_ALARMS, - mZenModeHelper.mConfig.toZenPolicy(), // copy of global config + mZenModeHelper.mConfig.getZenPolicy(), // copy of global config true)); } @Test - @EnableFlags(android.app.Flags.FLAG_MODES_API) + @EnableFlags(FLAG_MODES_API) public void applyGlobalZenModeAsImplicitZenRule_ruleCustomized_doesNotUpdateRule() { mZenModeHelper.mConfig.automaticRules.clear(); String pkg = mContext.getPackageName(); @@ -5835,7 +5808,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test - @EnableFlags(android.app.Flags.FLAG_MODES_API) + @EnableFlags(FLAG_MODES_API) public void applyGlobalZenModeAsImplicitZenRule_ruleCustomizedButNotFilter_updatesRule() { mZenModeHelper.mConfig.automaticRules.clear(); String pkg = mContext.getPackageName(); @@ -5866,8 +5839,8 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test + @EnableFlags(FLAG_MODES_API) public void applyGlobalZenModeAsImplicitZenRule_modeOff_deactivatesImplicitRule() { - mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API); mZenModeHelper.mConfig.automaticRules.clear(); mZenModeHelper.applyGlobalZenModeAsImplicitZenRule(mPkg, CUSTOM_PKG_UID, ZEN_MODE_IMPORTANT_INTERRUPTIONS); @@ -5883,8 +5856,8 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test + @EnableFlags(FLAG_MODES_API) public void applyGlobalZenModeAsImplicitZenRule_modeOffButNoPreviousRule_ignored() { - mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API); mZenModeHelper.mConfig.automaticRules.clear(); mZenModeHelper.applyGlobalZenModeAsImplicitZenRule(CUSTOM_PKG_NAME, CUSTOM_PKG_UID, @@ -5894,8 +5867,8 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test + @EnableFlags(FLAG_MODES_API) public void applyGlobalZenModeAsImplicitZenRule_update_unsnoozesRule() { - mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API); mZenModeHelper.mConfig.automaticRules.clear(); mZenModeHelper.applyGlobalZenModeAsImplicitZenRule(CUSTOM_PKG_NAME, CUSTOM_PKG_UID, @@ -5915,8 +5888,8 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test + @DisableFlags(FLAG_MODES_API) public void applyGlobalZenModeAsImplicitZenRule_flagOff_ignored() { - mSetFlagsRule.disableFlags(android.app.Flags.FLAG_MODES_API); mZenModeHelper.mConfig.automaticRules.clear(); withoutWtfCrash( @@ -5928,8 +5901,8 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test + @EnableFlags(FLAG_MODES_API) public void applyGlobalPolicyAsImplicitZenRule_createsImplicitRule() { - mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API); mZenModeHelper.mConfig.automaticRules.clear(); Policy policy = new Policy(PRIORITY_CATEGORY_CALLS | PRIORITY_CATEGORY_CONVERSATIONS, @@ -5952,8 +5925,8 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test + @EnableFlags(FLAG_MODES_API) public void applyGlobalPolicyAsImplicitZenRule_updatesImplicitRule() { - mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API); mZenModeHelper.mConfig.automaticRules.clear(); Policy original = new Policy(PRIORITY_CATEGORY_CALLS | PRIORITY_CATEGORY_CONVERSATIONS, @@ -5983,7 +5956,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test - @EnableFlags(android.app.Flags.FLAG_MODES_API) + @EnableFlags(FLAG_MODES_API) public void applyGlobalPolicyAsImplicitZenRule_ruleCustomized_doesNotUpdateRule() { mZenModeHelper.mConfig.automaticRules.clear(); String pkg = mContext.getPackageName(); @@ -5995,7 +5968,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { // Store this for checking later. ZenPolicy originalEffectiveZenPolicy = new ZenPolicy.Builder( - mZenModeHelper.mConfig.toZenPolicy()).allowMedia(true).build(); + mZenModeHelper.mConfig.getZenPolicy()).allowMedia(true).build(); // From user, update that rule's policy. AutomaticZenRule rule = mZenModeHelper.getAutomaticZenRule(ruleId); @@ -6023,7 +5996,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test - @EnableFlags(android.app.Flags.FLAG_MODES_API) + @EnableFlags(FLAG_MODES_API) public void applyGlobalPolicyAsImplicitZenRule_ruleCustomizedButNotZenPolicy_updatesRule() { mZenModeHelper.mConfig.automaticRules.clear(); String pkg = mContext.getPackageName(); @@ -6035,7 +6008,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { // Store this for checking later. ZenPolicy originalEffectiveZenPolicy = new ZenPolicy.Builder( - mZenModeHelper.mConfig.toZenPolicy()).allowMedia(true).build(); + mZenModeHelper.mConfig.getZenPolicy()).allowMedia(true).build(); // From user, update something in that rule, but not the ZenPolicy. AutomaticZenRule rule = mZenModeHelper.getAutomaticZenRule(ruleId); @@ -6060,8 +6033,8 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test + @DisableFlags(FLAG_MODES_API) public void applyGlobalPolicyAsImplicitZenRule_flagOff_ignored() { - mSetFlagsRule.disableFlags(android.app.Flags.FLAG_MODES_API); mZenModeHelper.mConfig.automaticRules.clear(); withoutWtfCrash( @@ -6072,8 +6045,8 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test + @EnableFlags(FLAG_MODES_API) public void getNotificationPolicyFromImplicitZenRule_returnsSetPolicy() { - mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API); Policy writtenPolicy = new Policy(PRIORITY_CATEGORY_CALLS | PRIORITY_CATEGORY_CONVERSATIONS, PRIORITY_SENDERS_CONTACTS, PRIORITY_SENDERS_STARRED, Policy.getAllSuppressedVisualEffects(), STATE_FALSE, @@ -6088,32 +6061,30 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test + @EnableFlags(FLAG_MODES_API) + @DisableFlags(FLAG_MODES_UI) public void getNotificationPolicyFromImplicitZenRule_ruleWithoutPolicy_copiesGlobalPolicy() { - mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API); - mZenModeHelper.mConfig.allowCalls = true; - mZenModeHelper.mConfig.allowConversations = false; - // Implicit rule will get the global policy at the time of rule creation. mZenModeHelper.applyGlobalZenModeAsImplicitZenRule(CUSTOM_PKG_NAME, CUSTOM_PKG_UID, - ZEN_MODE_ALARMS); - - // If the policy then changes afterwards, we should keep the snapshotted version. - mZenModeHelper.mConfig.allowCalls = false; + ZEN_MODE_IMPORTANT_INTERRUPTIONS); + // If the policy then changes afterwards, it should inherit updates because user cannot + // edit the policy in the UI. + mZenModeHelper.setNotificationPolicy(new Policy(PRIORITY_CATEGORY_ALARMS, 0, 0), + UPDATE_ORIGIN_APP, 1); Policy readPolicy = mZenModeHelper.getNotificationPolicyFromImplicitZenRule( CUSTOM_PKG_NAME); assertThat(readPolicy).isNotNull(); - assertThat(readPolicy.allowCalls()).isTrue(); - assertThat(readPolicy.allowConversations()).isFalse(); + assertThat(readPolicy.allowCalls()).isFalse(); + assertThat(readPolicy.allowAlarms()).isTrue(); } @Test + @EnableFlags(FLAG_MODES_API) public void getNotificationPolicyFromImplicitZenRule_noImplicitRule_returnsGlobalPolicy() { - mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API); - - mZenModeHelper.mConfig.allowCalls = true; - mZenModeHelper.mConfig.allowConversations = false; + Policy policy = new Policy(PRIORITY_CATEGORY_CALLS, PRIORITY_SENDERS_STARRED, 0); + mZenModeHelper.setNotificationPolicy(policy, UPDATE_ORIGIN_USER, 1); Policy readPolicy = mZenModeHelper.getNotificationPolicyFromImplicitZenRule( CUSTOM_PKG_NAME); @@ -6124,8 +6095,8 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test - @EnableFlags(Flags.FLAG_MODES_API) - @DisableFlags(Flags.FLAG_MODES_UI) + @EnableFlags(FLAG_MODES_API) + @DisableFlags(FLAG_MODES_UI) public void setNotificationPolicy_updatesRulePolicies_ifRulePolicyIsDefaultOrGlobalPolicy() { ZenPolicy defaultZenPolicy = mZenModeHelper.getDefaultZenPolicy(); Policy previousManualPolicy = mZenModeHelper.mConfig.toNotificationPolicy(); @@ -6185,6 +6156,35 @@ public class ZenModeHelperTest extends UiServiceTestCase { assertThat(storedRule.getIconResId()).isEqualTo(0); } + @Test + @EnableFlags({FLAG_MODES_API, FLAG_MODES_UI}) + public void setManualZenRuleDeviceEffects_noPreexistingMode() { + ZenDeviceEffects effects = new ZenDeviceEffects.Builder() + .setShouldDimWallpaper(true) + .build(); + mZenModeHelper.setManualZenRuleDeviceEffects(effects, UPDATE_ORIGIN_USER, "settings", 1000); + + assertThat(mZenModeHelper.getConfig().manualRule).isNotNull(); + assertThat(mZenModeHelper.getConfig().isManualActive()).isFalse(); + assertThat(mZenModeHelper.getConfig().manualRule.zenDeviceEffects).isEqualTo(effects); + } + + @Test + @EnableFlags({FLAG_MODES_API, FLAG_MODES_UI}) + public void setManualZenRuleDeviceEffects_preexistingMode() { + mZenModeHelper.setManualZenMode(ZEN_MODE_OFF, Uri.EMPTY, UPDATE_ORIGIN_USER, + "create manual rule", "settings", 1000); + + ZenDeviceEffects effects = new ZenDeviceEffects.Builder() + .setShouldDimWallpaper(true) + .build(); + mZenModeHelper.setManualZenRuleDeviceEffects(effects, UPDATE_ORIGIN_USER, "settings", 1000); + + assertThat(mZenModeHelper.getConfig().manualRule).isNotNull(); + assertThat(mZenModeHelper.getConfig().isManualActive()).isFalse(); + assertThat(mZenModeHelper.getConfig().manualRule.zenDeviceEffects).isEqualTo(effects); + } + private static void addZenRule(ZenModeConfig config, String id, String ownerPkg, int zenMode, @Nullable ZenPolicy zenPolicy) { ZenRule rule = new ZenRule(); @@ -6246,34 +6246,19 @@ public class ZenModeHelperTest extends UiServiceTestCase { // TODO: b/310620812 - Update setup methods to include allowChannels() when MODES_API is inlined private void setupZenConfig() { - mZenModeHelper.mZenMode = ZEN_MODE_OFF; - mZenModeHelper.mConfig.allowAlarms = false; - mZenModeHelper.mConfig.allowMedia = false; - mZenModeHelper.mConfig.allowSystem = false; - mZenModeHelper.mConfig.allowReminders = true; - mZenModeHelper.mConfig.allowCalls = true; - mZenModeHelper.mConfig.allowCallsFrom = PRIORITY_SENDERS_STARRED; - mZenModeHelper.mConfig.allowMessages = true; - mZenModeHelper.mConfig.allowConversations = true; - mZenModeHelper.mConfig.allowEvents = true; - mZenModeHelper.mConfig.allowRepeatCallers = true; - mZenModeHelper.mConfig.suppressedVisualEffects = SUPPRESSED_EFFECT_BADGE; - mZenModeHelper.mConfig.manualRule = null; - } - - private void setupZenConfigMaintained() { - // config is still the same as when it was setup (setupZenConfig) - assertFalse(mZenModeHelper.mConfig.allowAlarms); - assertFalse(mZenModeHelper.mConfig.allowMedia); - assertFalse(mZenModeHelper.mConfig.allowSystem); - assertTrue(mZenModeHelper.mConfig.allowReminders); - assertTrue(mZenModeHelper.mConfig.allowCalls); - assertEquals(PRIORITY_SENDERS_STARRED, mZenModeHelper.mConfig.allowCallsFrom); - assertTrue(mZenModeHelper.mConfig.allowMessages); - assertTrue(mZenModeHelper.mConfig.allowConversations); - assertTrue(mZenModeHelper.mConfig.allowEvents); - assertTrue(mZenModeHelper.mConfig.allowRepeatCallers); - assertEquals(SUPPRESSED_EFFECT_BADGE, mZenModeHelper.mConfig.suppressedVisualEffects); + Policy customPolicy = new Policy(PRIORITY_CATEGORY_REMINDERS + | PRIORITY_CATEGORY_CALLS | PRIORITY_CATEGORY_MESSAGES + | PRIORITY_CATEGORY_EVENTS | PRIORITY_CATEGORY_REPEAT_CALLERS + | PRIORITY_CATEGORY_CONVERSATIONS, + PRIORITY_SENDERS_STARRED, + PRIORITY_SENDERS_STARRED, + SUPPRESSED_EFFECT_BADGE, + 0, + CONVERSATION_SENDERS_IMPORTANT); + mZenModeHelper.setNotificationPolicy(customPolicy, UPDATE_ORIGIN_UNKNOWN, 1); + if (!Flags.modesUi()) { + mZenModeHelper.mConfig.manualRule = null; + } } private void checkDndProtoMatchesSetupZenConfig(DNDPolicyProto dndProto) { diff --git a/services/tests/vibrator/src/com/android/server/vibrator/VibrationThreadTest.java b/services/tests/vibrator/src/com/android/server/vibrator/VibrationThreadTest.java index 19ce217e581c..9dac23f075e6 100644 --- a/services/tests/vibrator/src/com/android/server/vibrator/VibrationThreadTest.java +++ b/services/tests/vibrator/src/com/android/server/vibrator/VibrationThreadTest.java @@ -1681,7 +1681,7 @@ public class VibrationThreadTest { .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK) .compose(); VibrationEffect effect4 = VibrationEffect.createOneShot(8000, 100); - VibrationEffect effect5 = VibrationEffect.createOneShot(20, 222); + VibrationEffect effect5 = VibrationEffect.get(VibrationEffect.EFFECT_CLICK); long vibrationId1 = startThreadAndDispatcher(effect1); waitForCompletion(); @@ -1745,13 +1745,12 @@ public class VibrationThreadTest { verifyCallbacksTriggered(vibrationId4, Vibration.Status.CANCELLED_BY_SCREEN_OFF); assertTrue("Tested duration=" + duration4, duration4 < 2000); - // Effect5: normal oneshot. Don't worry about amplitude, as effect4 may or may not have - // started. + // Effect5: played normally after effect4, which may or may not have played. verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibrationId5)); verifyCallbacksTriggered(vibrationId5, Vibration.Status.FINISHED); - assertEquals(Arrays.asList(expectedOneShot(20)), + assertEquals(Arrays.asList(expectedPrebaked(VibrationEffect.EFFECT_CLICK)), fakeVibrator.getEffectSegments(vibrationId5)); } diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java index 89140a2c0b44..ff1c6c8fc70c 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java @@ -16,10 +16,8 @@ package com.android.server.wm; -import static android.Manifest.permission.START_ACTIVITIES_FROM_BACKGROUND; import static android.app.Activity.RESULT_CANCELED; import static android.app.ActivityManager.PROCESS_STATE_BOUND_TOP; -import static android.app.ActivityManager.PROCESS_STATE_TOP; import static android.app.ActivityManager.START_ABORTED; import static android.app.ActivityManager.START_CANCELED; import static android.app.ActivityManager.START_CLASS_NOT_FOUND; @@ -44,7 +42,6 @@ import static android.content.pm.ActivityInfo.FLAG_ALLOW_UNTRUSTED_ACTIVITY_EMBE import static android.content.pm.ActivityInfo.LAUNCH_MULTIPLE; import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_INSTANCE; import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_TASK; -import static android.content.pm.PackageManager.PERMISSION_GRANTED; import static android.os.Process.SYSTEM_UID; import static com.android.dx.mockito.inline.extended.ExtendedMockito.clearInvocations; @@ -52,7 +49,6 @@ 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; import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock; -import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession; import static com.android.dx.mockito.inline.extended.ExtendedMockito.never; import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy; import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; @@ -79,15 +75,12 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyLong; -import static org.mockito.ArgumentMatchers.anyObject; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.notNull; import android.app.ActivityOptions; -import android.app.ActivityOptions.BackgroundActivityStartMode; import android.app.AppOpsManager; -import android.app.BackgroundStartPrivileges; import android.app.IApplicationThread; import android.app.PictureInPictureParams; import android.content.ComponentName; @@ -101,10 +94,10 @@ import android.content.pm.SigningDetails; import android.graphics.Rect; import android.os.Binder; import android.os.IBinder; -import android.os.Process; import android.os.RemoteException; +import android.platform.test.annotations.DisableFlags; +import android.platform.test.annotations.EnableFlags; import android.platform.test.annotations.Presubmit; -import android.platform.test.annotations.RequiresFlagsDisabled; import android.provider.DeviceConfig; import android.service.voice.IVoiceInteractionSession; import android.util.Pair; @@ -116,21 +109,17 @@ import android.window.TaskFragmentOrganizerToken; import androidx.test.filters.SmallTest; import com.android.compatibility.common.util.DeviceConfigStateHelper; -import com.android.internal.util.FrameworkStatsLog; -import com.android.server.am.PendingIntentRecord; import com.android.server.pm.PackageArchiver; import com.android.server.pm.pkg.AndroidPackage; import com.android.server.wm.BackgroundActivityStartController.BalVerdict; import com.android.server.wm.LaunchParamsController.LaunchParamsModifier; import com.android.server.wm.utils.MockTracker; +import com.android.wm.shell.Flags; import org.junit.After; import org.junit.Before; -import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; -import org.mockito.MockitoSession; -import org.mockito.quality.Strictness; import java.util.ArrayList; import java.util.Arrays; @@ -416,9 +405,7 @@ public class ActivityStarterTests extends WindowTestsBase { doReturn("packageName").when(mMockPackageManager).getNameForUid(anyInt()); doReturn(false).when(mMockPackageManager).isInstantAppInstallerComponent(any()); doReturn(null).when(mMockPackageManager).resolveIntent(any(), any(), anyLong(), anyLong(), - anyInt(), anyBoolean(), anyInt()); - doReturn(null).when(mMockPackageManager).resolveIntentExported(any(), any(), - anyLong(), anyLong(), anyInt(), anyBoolean(), anyInt(), anyInt()); + anyInt(), anyBoolean(), anyInt(), anyInt()); doReturn(new ComponentName("", "")).when(mMockPackageManager).getSystemUiServiceComponent(); // Never review permissions @@ -508,7 +495,8 @@ public class ActivityStarterTests extends WindowTestsBase { // Start activity and delivered new intent. starter.getIntent().setComponent(splitSecondReusableActivity.mActivityComponent); - doReturn(splitSecondReusableActivity).when(mRootWindowContainer).findTask(any(), any()); + doReturn(splitSecondReusableActivity) + .when(mRootWindowContainer).findTask(any(), any(), anyBoolean()); final int result = starter.setReason("testSplitScreenDeliverToTop").execute(); // Ensure result is delivering intent to top. @@ -535,7 +523,8 @@ public class ActivityStarterTests extends WindowTestsBase { // Start activity and delivered new intent. starter.getIntent().setComponent(splitSecondReusableActivity.mActivityComponent); - doReturn(splitSecondReusableActivity).when(mRootWindowContainer).findTask(any(), any()); + doReturn(splitSecondReusableActivity) + .when(mRootWindowContainer).findTask(any(), any(), anyBoolean()); final int result = starter.setReason("testSplitScreenMoveToFront").execute(); // Ensure result is moving task to front. @@ -582,7 +571,7 @@ public class ActivityStarterTests extends WindowTestsBase { // Start activity and delivered new intent. starter.getIntent().setComponent(activities.get(3).mActivityComponent); - doReturn(activities.get(3)).when(mRootWindowContainer).findTask(any(), any()); + doReturn(activities.get(3)).when(mRootWindowContainer).findTask(any(), any(), anyBoolean()); final int result = starter.setReason("testDesktopModeDeliverToTop").execute(); // Ensure result is delivering intent to top. @@ -609,7 +598,8 @@ public class ActivityStarterTests extends WindowTestsBase { // Start activity and delivered new intent. starter.getIntent().setComponent(desktopModeReusableActivity.mActivityComponent); - doReturn(desktopModeReusableActivity).when(mRootWindowContainer).findTask(any(), any()); + doReturn(desktopModeReusableActivity) + .when(mRootWindowContainer).findTask(any(), any(), anyBoolean()); final int result = starter.setReason("testDesktopModeMoveToFront").execute(); // Ensure result is moving task to front. @@ -728,497 +718,68 @@ public class ActivityStarterTests extends WindowTestsBase { eq(FAKE_REAL_CALLING_UID), anyInt(), anyBoolean(), eq(false)); } - /** - * This test ensures that unsupported usecases aren't aborted when background starts are - * allowed. - */ - @Test - public void testBackgroundActivityStartsAllowed_noStartsAborted() { - doReturn(true).when(mAtm).isBackgroundActivityStartsEnabled(); - runAndVerifyBackgroundActivityStartsSubtest("allowed_noStartsAborted", false, - UNIMPORTANT_UID, false, PROCESS_STATE_BOUND_TOP, - UNIMPORTANT_UID2, false, PROCESS_STATE_BOUND_TOP, - false, false, false, false, false, false, false, false); - } - - /** - * This test ensures that unsupported usecases are aborted when background starts are - * disallowed. - */ - @Test - public void testBackgroundActivityStartsDisallowed_unsupportedUsecaseAborted() { - doReturn(false).when(mAtm).isBackgroundActivityStartsEnabled(); - runAndVerifyBackgroundActivityStartsSubtest( - "disallowed_unsupportedUsecase_aborted", true, - UNIMPORTANT_UID, false, PROCESS_STATE_BOUND_TOP, - UNIMPORTANT_UID2, false, PROCESS_STATE_BOUND_TOP, - false, false, false, false, false, false, false, false); - } - - /** - * This test ensures that unsupported usecases are aborted when background starts are - * disallowed. - */ - @Test - public void testBackgroundActivityStartsDisallowed_callingUidProcessStateTopAborted() { - doReturn(false).when(mAtm).isBackgroundActivityStartsEnabled(); - runAndVerifyBackgroundActivityStartsSubtest( - "disallowed_callingUidProcessStateTop_aborted", true, - UNIMPORTANT_UID, false, PROCESS_STATE_TOP, - UNIMPORTANT_UID2, false, PROCESS_STATE_BOUND_TOP, - false, false, false, false, false, false, false, false); - } - - /** - * This test ensures that unsupported usecases are aborted when background starts are - * disallowed. - */ - @Test - public void testBackgroundActivityStartsDisallowed_realCallingUidProcessStateTopAborted() { - doReturn(false).when(mAtm).isBackgroundActivityStartsEnabled(); - runAndVerifyBackgroundActivityStartsSubtest( - "disallowed_realCallingUidProcessStateTop_aborted", true, - UNIMPORTANT_UID, false, PROCESS_STATE_BOUND_TOP, - UNIMPORTANT_UID2, false, PROCESS_STATE_TOP, - false, false, false, false, false, false, false, false); - } /** * This test ensures that unsupported usecases are aborted when background starts are * disallowed. */ @Test - public void testBackgroundActivityStartsDisallowed_hasForegroundActivitiesAborted() { - doReturn(false).when(mAtm).isBackgroundActivityStartsEnabled(); - runAndVerifyBackgroundActivityStartsSubtest( - "disallowed_hasForegroundActivities_aborted", true, - UNIMPORTANT_UID, false, PROCESS_STATE_BOUND_TOP, - UNIMPORTANT_UID2, false, PROCESS_STATE_BOUND_TOP, - true, false, false, false, false, false, false, false); - } - - /** - * This test ensures that unsupported usecases are aborted when background starts are - * disallowed. - */ - @Test - public void testBackgroundActivityStartsDisallowed_pinnedSingleInstanceAborted() { - doReturn(false).when(mAtm).isBackgroundActivityStartsEnabled(); - runAndVerifyBackgroundActivityStartsSubtest( - "disallowed_pinned_singleinstance_aborted", true, - UNIMPORTANT_UID, false, PROCESS_STATE_BOUND_TOP, - UNIMPORTANT_UID2, false, PROCESS_STATE_BOUND_TOP, - false, false, false, false, false, false, true, false); - } - - /** - * This test ensures that supported usecases aren't aborted when background starts are - * disallowed. Each scenarios tests one condition that makes them supported in isolation. In - * this case the calling process runs as ROOT_UID. - */ - @Test - public void testBackgroundActivityStartsDisallowed_rootUidNotAborted() { - doReturn(false).when(mAtm).isBackgroundActivityStartsEnabled(); - runAndVerifyBackgroundActivityStartsSubtest("disallowed_rootUid_notAborted", false, - Process.ROOT_UID, false, PROCESS_STATE_BOUND_TOP, - UNIMPORTANT_UID2, false, PROCESS_STATE_BOUND_TOP, - false, false, false, false, false, false, false, false); - } - - /** - * This test ensures that supported usecases aren't aborted when background starts are - * disallowed. Each scenarios tests one condition that makes them supported in isolation. In - * this case the calling process is running as SYSTEM_UID. - */ - @Test - public void testBackgroundActivityStartsDisallowed_systemUidNotAborted() { - doReturn(false).when(mAtm).isBackgroundActivityStartsEnabled(); - runAndVerifyBackgroundActivityStartsSubtest("disallowed_systemUid_notAborted", false, - Process.SYSTEM_UID, false, PROCESS_STATE_BOUND_TOP, - UNIMPORTANT_UID2, false, PROCESS_STATE_BOUND_TOP, - false, false, false, false, false, false, false, false); - } - - /** - * This test ensures that supported usecases aren't aborted when background starts are - * disallowed. Each scenarios tests one condition that makes them supported in isolation. In - * this case the calling process is running as NFC_UID. - */ - @Test - public void testBackgroundActivityStartsDisallowed_nfcUidNotAborted() { - doReturn(false).when(mAtm).isBackgroundActivityStartsEnabled(); - runAndVerifyBackgroundActivityStartsSubtest("disallowed_nfcUid_notAborted", false, - Process.NFC_UID, false, PROCESS_STATE_BOUND_TOP, - UNIMPORTANT_UID2, false, PROCESS_STATE_BOUND_TOP, - false, false, false, false, false, false, false, false); - } - - /** - * This test ensures that supported usecases aren't aborted when background starts are - * disallowed. Each scenarios tests one condition that makes them supported in isolation. In - * this case the calling process has a visible window. - */ - @Test - public void testBackgroundActivityStartsDisallowed_callingUidHasVisibleWindowNotAborted() { - doReturn(false).when(mAtm).isBackgroundActivityStartsEnabled(); - runAndVerifyBackgroundActivityStartsSubtest( - "disallowed_callingUidHasVisibleWindow_notAborted", false, - UNIMPORTANT_UID, true, PROCESS_STATE_BOUND_TOP, - UNIMPORTANT_UID2, false, PROCESS_STATE_BOUND_TOP, - false, false, false, false, false, false, false, false); - } - - /** - * The sending app has a visible window, but does not (by default) allow the pending intent to - * start the background activity. - */ - @Test - @Ignore("b/266015587") - public void testBackgroundActivityStartsDisallowed_realCallingUidHasVisibleWindowAborted() { - doReturn(false).when(mAtm).isBackgroundActivityStartsEnabled(); - - runAndVerifyBackgroundActivityStartsSubtest( - "disallowed_realCallingUidHasVisibleWindow_abortedInU", true, - UNIMPORTANT_UID, false, PROCESS_STATE_BOUND_TOP, - UNIMPORTANT_UID2, true, PROCESS_STATE_BOUND_TOP, - false, false, false, false, false, false, false, false); - } - - /** - * This test ensures that supported usecases aren't aborted when background starts are - * disallowed. Each scenarios tests one condition that makes them supported in isolation. In - * this case the caller is in the recent activity list. - */ - @Test - public void testBackgroundActivityStartsDisallowed_callerIsRecentsNotAborted() { - doReturn(false).when(mAtm).isBackgroundActivityStartsEnabled(); - runAndVerifyBackgroundActivityStartsSubtest( - "disallowed_callerIsRecents_notAborted", false, - UNIMPORTANT_UID, false, PROCESS_STATE_BOUND_TOP, - UNIMPORTANT_UID2, false, PROCESS_STATE_BOUND_TOP, - false, true, false, false, false, false, false, false); - } - - /** - * This test ensures that supported usecases aren't aborted when background starts are - * disallowed. Each scenarios tests one condition that makes them supported in isolation. In - * this case the caller is temporarily (10s) allowed to start. - */ - @Test - public void testBackgroundActivityStartsDisallowed_callerIsAllowedNotAborted() { + public void testPinnedSingleInstanceAborted() { doReturn(false).when(mAtm).isBackgroundActivityStartsEnabled(); - runAndVerifyBackgroundActivityStartsSubtest( - "disallowed_callerIsAllowed_notAborted", false, - UNIMPORTANT_UID, false, PROCESS_STATE_BOUND_TOP, - UNIMPORTANT_UID2, false, PROCESS_STATE_BOUND_TOP, - false, false, true, false, false, false, false, false); - } - - /** - * This test ensures that supported usecases aren't aborted when background starts are - * disallowed. Each scenarios tests one condition that makes them supported in isolation. In - * this case the caller explicitly has background activity start privilege. - */ - @Test - public void testBackgroundActivityStartsDisallowed_callerIsInstrumentingWithBASPnotAborted() { - doReturn(false).when(mAtm).isBackgroundActivityStartsEnabled(); - runAndVerifyBackgroundActivityStartsSubtest( - "disallowed_callerIsInstrumentingWithBackgroundActivityStartPrivileges_notAborted", - false, - UNIMPORTANT_UID, false, PROCESS_STATE_BOUND_TOP, - UNIMPORTANT_UID2, false, PROCESS_STATE_BOUND_TOP, - false, false, false, true, false, false, false, false); - } - - /** - * This test ensures that supported usecases aren't aborted when background starts are - * disallowed. Each scenarios tests one condition that makes them supported in isolation. In - * this case the caller is a device owner. - */ - @Test - public void - testBackgroundActivityStartsDisallowed_callingPackageNameIsDeviceOwnerNotAborted() { - doReturn(false).when(mAtm).isBackgroundActivityStartsEnabled(); - runAndVerifyBackgroundActivityStartsSubtest( - "disallowed_callingPackageNameIsDeviceOwner_notAborted", false, - UNIMPORTANT_UID, false, PROCESS_STATE_BOUND_TOP, - UNIMPORTANT_UID2, false, PROCESS_STATE_BOUND_TOP, - false, false, false, false, true, false, false, false); - } - - /** - * This test ensures that supported usecases aren't aborted when background starts are - * disallowed. Each scenarios tests one condition that makes them supported in isolation. In - * this case the caller is a affiliated profile owner. - */ - @Test - public void - testBackgroundActivityStartsDisallowed_isAffiliatedProfileOwnerNotAborted() { - doReturn(false).when(mAtm).isBackgroundActivityStartsEnabled(); - runAndVerifyBackgroundActivityStartsSubtest( - "disallowed_callingUidIsAffiliatedProfileOwner_notAborted", false, - UNIMPORTANT_UID, false, PROCESS_STATE_BOUND_TOP, - UNIMPORTANT_UID2, false, PROCESS_STATE_BOUND_TOP, - false, false, false, false, false, true, false, false); - } - - /** - * This test ensures that supported usecases aren't aborted when background starts are - * disallowed. Each scenarios tests one condition that makes them supported in isolation. In - * this case the caller has the OP_SYSTEM_EXEMPT_FROM_ACTIVITY_BG_START_RESTRICTION appop. - */ - @Test - public void testBackgroundActivityStartsDisallowed_callerHasSystemExemptAppOpNotAborted() { - doReturn(false).when(mAtm).isBackgroundActivityStartsEnabled(); - runAndVerifyBackgroundActivityStartsSubtest( - "disallowed_callerHasSystemExemptAppOpNotAborted", false, - UNIMPORTANT_UID, false, PROCESS_STATE_BOUND_TOP, - UNIMPORTANT_UID2, false, PROCESS_STATE_BOUND_TOP, - false, false, false, false, false, false, false, true); - } - - /** - * This test ensures that supported usecases aren't aborted when background starts are - * disallowed. Each scenarios tests one condition that makes them supported in isolation. In - * this case the caller is an IME. - */ - @Test - public void testBackgroundActivityStartsDisallowed_callingPackageNameIsImeNotAborted() { - doReturn(false).when(mAtm).isBackgroundActivityStartsEnabled(); - setupImeWindow(); - runAndVerifyBackgroundActivityStartsSubtest( - "disallowed_callingPackageNameIsIme_notAborted", false, - CURRENT_IME_UID, false, PROCESS_STATE_BOUND_TOP, - UNIMPORTANT_UID2, false, PROCESS_STATE_BOUND_TOP, - false, false, false, false, false, false, false, false); - } - - /** - * This test ensures proper logging for BAL_ALLOW_PERMISSION. - */ - @Test - public void testBackgroundActivityStartsAllowed_logging() { - doReturn(false).when(mAtm).isBackgroundActivityStartsEnabled(); - MockitoSession mockingSession = mockitoSession() - .mockStatic(ActivityTaskManagerService.class) - .mockStatic(FrameworkStatsLog.class) - .strictness(Strictness.LENIENT) - .startMocking(); - try { - doReturn(PERMISSION_GRANTED).when(() -> ActivityTaskManagerService.checkPermission( - eq(START_ACTIVITIES_FROM_BACKGROUND), - anyInt(), anyInt())); - runAndVerifyBackgroundActivityStartsSubtest( - "allowed_notAborted", false, - UNIMPORTANT_UID, false, PROCESS_STATE_BOUND_TOP, - UNIMPORTANT_UID2, false, PROCESS_STATE_BOUND_TOP, - false, true, false, false, false, false, false, false); - verify(() -> FrameworkStatsLog.write(FrameworkStatsLog.BAL_ALLOWED, - "", // activity name - BackgroundActivityStartController.BAL_ALLOW_PERMISSION, - UNIMPORTANT_UID, - UNIMPORTANT_UID2, - BackgroundActivityStartController.BAL_ALLOW_PERMISSION, - true, // opt in - false, // but no explicit opt in - BackgroundActivityStartController.BAL_BLOCK, - true, // opt in - false // but no explicit opt in - )); - } finally { - mockingSession.finishMocking(); - } - } - - /** - * This test ensures proper logging for BAL_ALLOW_PENDING_INTENT, when the PendingIntent sender - * is the only reason BAL is allowed. - */ - @Test - @RequiresFlagsDisabled(com.android.window.flags.Flags.FLAG_BAL_IMPROVED_METRICS) - public void testBackgroundActivityStartsAllowed_loggingOnlyPendingIntentAllowed() { - doReturn(false).when(mAtm).isBackgroundActivityStartsEnabled(); - MockitoSession mockingSession = mockitoSession() - .mockStatic(ActivityTaskManagerService.class) - .mockStatic(FrameworkStatsLog.class) - .mockStatic(PendingIntentRecord.class) - .strictness(Strictness.LENIENT) - .startMocking(); - try { - doReturn(PERMISSION_GRANTED).when(() -> ActivityTaskManagerService.checkPermission( - eq(START_ACTIVITIES_FROM_BACKGROUND), - anyInt(), anyInt())); - doReturn(BackgroundStartPrivileges.allowBackgroundActivityStarts(null)).when( - () -> PendingIntentRecord.getBackgroundStartPrivilegesAllowedByCaller( - anyObject(), anyInt(), anyObject())); - runAndVerifyBackgroundActivityStartsSubtest( - "allowed_notAborted", false, - UNIMPORTANT_UID, false, PROCESS_STATE_BOUND_TOP, - Process.SYSTEM_UID, true, PROCESS_STATE_BOUND_TOP, - false, true, false, false, false, false, false, false, - ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_DENIED); - verify(() -> FrameworkStatsLog.write(FrameworkStatsLog.BAL_ALLOWED, - DEFAULT_COMPONENT_PACKAGE_NAME + "/" + DEFAULT_COMPONENT_PACKAGE_NAME, - BackgroundActivityStartController.BAL_ALLOW_PENDING_INTENT, - UNIMPORTANT_UID, - Process.SYSTEM_UID, - BackgroundActivityStartController.BAL_ALLOW_PERMISSION, - false, // opt in - true, // explicit opt out - BackgroundActivityStartController.BAL_ALLOW_VISIBLE_WINDOW, - true, // opt in - false // but no explicit opt in - )); - } finally { - mockingSession.finishMocking(); - } - } - - /** - * This test ensures proper logging for BAL_ALLOW_PENDING_INTENT, when the PendingIntent sender - * is not the primary reason to allow BAL (but the creator). - */ - @Test - @RequiresFlagsDisabled(com.android.window.flags.Flags.FLAG_BAL_IMPROVED_METRICS) - public void testBackgroundActivityStartsAllowed_loggingPendingIntentAllowed() { - doReturn(false).when(mAtm).isBackgroundActivityStartsEnabled(); - MockitoSession mockingSession = mockitoSession() - .mockStatic(ActivityTaskManagerService.class) - .mockStatic(FrameworkStatsLog.class) - .mockStatic(PendingIntentRecord.class) - .strictness(Strictness.LENIENT) - .startMocking(); - try { - doReturn(PERMISSION_GRANTED).when(() -> ActivityTaskManagerService.checkPermission( - eq(START_ACTIVITIES_FROM_BACKGROUND), - anyInt(), anyInt())); - doReturn(BackgroundStartPrivileges.allowBackgroundActivityStarts(null)).when( - () -> PendingIntentRecord.getBackgroundStartPrivilegesAllowedByCaller( - anyObject(), anyInt(), anyObject())); - runAndVerifyBackgroundActivityStartsSubtest( - "allowed_notAborted", false, - UNIMPORTANT_UID, false, PROCESS_STATE_BOUND_TOP, - Process.SYSTEM_UID, true, PROCESS_STATE_BOUND_TOP, - false, true, false, false, false, false, false, false, - ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED); - verify(() -> FrameworkStatsLog.write(FrameworkStatsLog.BAL_ALLOWED, - "", - BackgroundActivityStartController.BAL_ALLOW_PERMISSION, - UNIMPORTANT_UID, - Process.SYSTEM_UID, - BackgroundActivityStartController.BAL_ALLOW_PERMISSION, - true, // opt in - true, // explicit opt in - BackgroundActivityStartController.BAL_ALLOW_VISIBLE_WINDOW, - true, // opt in - false // but no explicit opt in - )); - } finally { - mockingSession.finishMocking(); - } - } - - private void runAndVerifyBackgroundActivityStartsSubtest(String name, boolean shouldHaveAborted, - int callingUid, boolean callingUidHasVisibleWindow, int callingUidProcState, - int realCallingUid, boolean realCallingUidHasVisibleWindow, int realCallingUidProcState, - boolean hasForegroundActivities, boolean callerIsRecents, - boolean callerIsTempAllowed, - boolean callerIsInstrumentingWithBackgroundActivityStartPrivileges, - boolean isCallingUidDeviceOwner, - boolean isCallingUidAffiliatedProfileOwner, - boolean isPinnedSingleInstance, - boolean hasSystemExemptAppOp) { - runAndVerifyBackgroundActivityStartsSubtest(name, shouldHaveAborted, callingUid, - callingUidHasVisibleWindow, callingUidProcState, realCallingUid, - realCallingUidHasVisibleWindow, realCallingUidProcState, hasForegroundActivities, - callerIsRecents, callerIsTempAllowed, - callerIsInstrumentingWithBackgroundActivityStartPrivileges, - isCallingUidDeviceOwner, isCallingUidAffiliatedProfileOwner, isPinnedSingleInstance, - hasSystemExemptAppOp, - ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_SYSTEM_DEFINED); - } - - private void runAndVerifyBackgroundActivityStartsSubtest(String name, boolean shouldHaveAborted, - int callingUid, boolean callingUidHasVisibleWindow, int callingUidProcState, - int realCallingUid, boolean realCallingUidHasVisibleWindow, int realCallingUidProcState, - boolean hasForegroundActivities, boolean callerIsRecents, - boolean callerIsTempAllowed, - boolean callerIsInstrumentingWithBackgroundActivityStartPrivileges, - boolean isCallingUidDeviceOwner, - boolean isCallingUidAffiliatedProfileOwner, - boolean isPinnedSingleInstance, - boolean hasSystemExemptAppOp, - @BackgroundActivityStartMode int pendingIntentCreatorBackgroundActivityStartMode) { // window visibility - doReturn(callingUidHasVisibleWindow).when(mAtm).hasActiveVisibleWindow(callingUid); - doReturn(realCallingUidHasVisibleWindow).when(mAtm).hasActiveVisibleWindow(realCallingUid); + doReturn(false).when(mAtm).hasActiveVisibleWindow(UNIMPORTANT_UID); + doReturn(false).when(mAtm).hasActiveVisibleWindow(UNIMPORTANT_UID2); // process importance - mAtm.mActiveUids.onUidActive(callingUid, callingUidProcState); - mAtm.mActiveUids.onUidActive(realCallingUid, realCallingUidProcState); + mAtm.mActiveUids.onUidActive(UNIMPORTANT_UID, PROCESS_STATE_BOUND_TOP); + mAtm.mActiveUids.onUidActive(UNIMPORTANT_UID2, PROCESS_STATE_BOUND_TOP); // foreground activities final IApplicationThread caller = mock(IApplicationThread.class); final WindowProcessListener listener = mock(WindowProcessListener.class); final ApplicationInfo ai = new ApplicationInfo(); - ai.uid = callingUid; + ai.uid = UNIMPORTANT_UID; ai.packageName = "com.android.test.package"; - final WindowProcessController callerApp = - spy(new WindowProcessController(mAtm, ai, null, callingUid, -1, null, listener)); - doReturn(hasForegroundActivities).when(callerApp).hasForegroundActivities(); + final WindowProcessController callerApp = spy(new WindowProcessController( + mAtm, ai, null, UNIMPORTANT_UID, -1, null, listener)); + doReturn(false).when(callerApp).hasForegroundActivities(); doReturn(callerApp).when(mAtm).getProcessController(caller); // caller is recents RecentTasks recentTasks = mock(RecentTasks.class); mAtm.mTaskSupervisor.setRecentTasks(recentTasks); - doReturn(callerIsRecents).when(recentTasks).isCallerRecents(callingUid); + doReturn(false).when(recentTasks).isCallerRecents(UNIMPORTANT_UID); // caller is temp allowed - if (callerIsTempAllowed) { - callerApp.addOrUpdateBackgroundStartPrivileges(new Binder(), - BackgroundStartPrivileges.ALLOW_BAL); - } // caller is instrumenting with background activity starts privileges - callerApp.setInstrumenting(callerIsInstrumentingWithBackgroundActivityStartPrivileges, - callerIsInstrumentingWithBackgroundActivityStartPrivileges ? Process.SHELL_UID : -1, - callerIsInstrumentingWithBackgroundActivityStartPrivileges); + callerApp.setInstrumenting(false, -1, false); // callingUid is the device owner - doReturn(isCallingUidDeviceOwner).when(mAtm).isDeviceOwner(callingUid); + doReturn(false).when(mAtm).isDeviceOwner(UNIMPORTANT_UID); // callingUid is the affiliated profile owner - doReturn(isCallingUidAffiliatedProfileOwner).when(mAtm) - .isAffiliatedProfileOwner(callingUid); + doReturn(false).when(mAtm).isAffiliatedProfileOwner(UNIMPORTANT_UID); // caller has OP_SYSTEM_EXEMPT_FROM_ACTIVITY_BG_START_RESTRICTION appop - doReturn(hasSystemExemptAppOp ? AppOpsManager.MODE_ALLOWED - : AppOpsManager.MODE_DEFAULT).when(mAppOpsManager).checkOpNoThrow( + doReturn(AppOpsManager.MODE_DEFAULT).when(mAppOpsManager).checkOpNoThrow( eq(AppOpsManager.OP_SYSTEM_EXEMPT_FROM_ACTIVITY_BG_START_RESTRICTION), anyInt(), any()); - int launchMode = LAUNCH_MULTIPLE; - if (isPinnedSingleInstance) { - final ActivityRecord baseActivity = - new ActivityBuilder(mAtm).setCreateTask(true).build(); - baseActivity.getRootTask() - .setWindowingMode(WINDOWING_MODE_PINNED); - doReturn(baseActivity).when(mRootWindowContainer).findTask(any(), any()); - launchMode = LAUNCH_SINGLE_INSTANCE; - } + final ActivityRecord baseActivity = new ActivityBuilder(mAtm).setCreateTask(true).build(); + baseActivity.getRootTask().setWindowingMode(WINDOWING_MODE_PINNED); + doReturn(baseActivity).when(mRootWindowContainer).findTask(any(), any(), anyBoolean()); ActivityOptions rawOptions = ActivityOptions.makeBasic() .setPendingIntentCreatorBackgroundActivityStartMode( - pendingIntentCreatorBackgroundActivityStartMode); + ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_SYSTEM_DEFINED); final ActivityOptions options = spy(rawOptions); ActivityRecord[] outActivity = new ActivityRecord[1]; ActivityStarter starter = prepareStarter( - FLAG_ACTIVITY_NEW_TASK, true, launchMode) + FLAG_ACTIVITY_NEW_TASK, true, LAUNCH_SINGLE_INSTANCE) .setCallingPackage("com.whatever.dude") .setCaller(caller) - .setCallingUid(callingUid) - .setRealCallingUid(realCallingUid) + .setCallingUid(UNIMPORTANT_UID) + .setRealCallingUid(UNIMPORTANT_UID2) .setActivityOptions(new SafeActivityOptions(options)) .setOutActivity(outActivity); - final int result = starter.setReason("testBackgroundActivityStarts_" + name).execute(); - - assertEquals(ActivityStarter.getExternalResult( - shouldHaveAborted ? START_ABORTED : START_SUCCESS), result); - verify(options, times(shouldHaveAborted ? 1 : 0)).abort(); + final int result = starter.setReason("testPinnedSingleInstanceAborted").execute(); + assertEquals(ActivityStarter.getExternalResult(START_ABORTED), result); + verify(options, times(1)).abort(); final ActivityRecord startedActivity = outActivity[0]; if (startedActivity != null && startedActivity.getTask() != null) { @@ -2093,6 +1654,120 @@ public class ActivityStarterTests extends WindowTestsBase { assertNotEquals(inTask, target.getTask()); } + @EnableFlags(Flags.FLAG_ONLY_REUSE_BUBBLED_TASK_WHEN_LAUNCHED_FROM_BUBBLE) + @Test + public void launchActivity_reusesBubbledTask() { + final ActivityStarter starter = prepareStarter(0, false); + final ActivityRecord bubbledActivity = createBubbledActivity(); + + // create the target activity to be launched with the same component as the bubbled activity + final ActivityRecord targetRecord = new ActivityBuilder(mAtm) + .setLaunchMode(LAUNCH_SINGLE_TASK) + .setComponent(ActivityBuilder.getDefaultComponent()).build(); + starter.getIntent().setComponent(bubbledActivity.mActivityComponent); + startActivityInner(starter, targetRecord, bubbledActivity, null /* options */, + null /* inTask */, null /* inTaskFragment */); + + assertEquals(bubbledActivity.getTask(), targetRecord.getTask()); + } + + @EnableFlags(Flags.FLAG_ONLY_REUSE_BUBBLED_TASK_WHEN_LAUNCHED_FROM_BUBBLE) + @Test + public void launchActivity_nullSourceRecord_doesNotReuseBubbledTask() { + final ActivityStarter starter = prepareStarter(0, false); + final ActivityRecord bubbledActivity = createBubbledActivity(); + + // create the target activity to be launched + final ActivityRecord targetRecord = + new ActivityBuilder(mAtm) + .setLaunchMode(LAUNCH_SINGLE_TASK) + .setComponent(ActivityBuilder.getDefaultComponent()).build(); + starter.getIntent().setComponent(bubbledActivity.mActivityComponent); + + // pass null as the source record + startActivityInner(starter, targetRecord, null, null /* options */, + null /* inTask */, null /* inTaskFragment */); + + assertNotEquals(bubbledActivity.getTask(), targetRecord.getTask()); + } + + @EnableFlags(Flags.FLAG_ONLY_REUSE_BUBBLED_TASK_WHEN_LAUNCHED_FROM_BUBBLE) + @Test + public void launchActivity_nonBubbledSourceRecord_doesNotReuseBubbledTask() { + final ActivityStarter starter = prepareStarter(0, false); + final ActivityRecord bubbledActivity = createBubbledActivity(); + + // create a non bubbled activity + final ActivityRecord nonBubbleSourceRecord = + new ActivityBuilder(mAtm).setCreateTask(true) + .setLaunchMode(LAUNCH_SINGLE_TASK) + .setComponent(ActivityBuilder.getDefaultComponent()) + .build(); + + // create the target activity to be launched + final ActivityRecord targetRecord = + new ActivityBuilder(mAtm) + .setLaunchMode(LAUNCH_SINGLE_TASK) + .setComponent(ActivityBuilder.getDefaultComponent()).build(); + starter.getIntent().setComponent(bubbledActivity.mActivityComponent); + + // use the non bubbled activity as the source + startActivityInner(starter, targetRecord, nonBubbleSourceRecord, null /* options */, + null /* inTask */, null /* inTaskFragment*/); + + assertNotEquals(bubbledActivity.getTask(), targetRecord.getTask()); + } + + @DisableFlags(Flags.FLAG_ONLY_REUSE_BUBBLED_TASK_WHEN_LAUNCHED_FROM_BUBBLE) + @Test + public void launchActivity_nullSourceRecord_flagDisabled_reusesBubbledTask() { + final ActivityStarter starter = prepareStarter(0, false); + final ActivityRecord bubbledActivity = createBubbledActivity(); + + // create the target activity to be launched + final ActivityRecord targetRecord = + new ActivityBuilder(mAtm) + .setLaunchMode(LAUNCH_SINGLE_TASK) + .setComponent(ActivityBuilder.getDefaultComponent()).build(); + starter.getIntent().setComponent(bubbledActivity.mActivityComponent); + + // pass null as the source record + startActivityInner(starter, targetRecord, null, null /* options */, + null /* inTask */, null /* inTaskFragment */); + + assertEquals(bubbledActivity.getTask(), targetRecord.getTask()); + } + + @DisableFlags(Flags.FLAG_ONLY_REUSE_BUBBLED_TASK_WHEN_LAUNCHED_FROM_BUBBLE) + @Test + public void launchActivity_fromBubble_flagDisabled_reusesBubbledTask() { + final ActivityStarter starter = prepareStarter(0, false); + final ActivityRecord bubbledActivity = createBubbledActivity(); + + // create the target activity to be launched with the same component as the bubbled activity + final ActivityRecord targetRecord = + new ActivityBuilder(mAtm) + .setLaunchMode(LAUNCH_SINGLE_TASK) + .setComponent(ActivityBuilder.getDefaultComponent()).build(); + starter.getIntent().setComponent(bubbledActivity.mActivityComponent); + startActivityInner(starter, targetRecord, bubbledActivity, null /* options */, + null /* inTask */, null /* inTaskFragment */); + + assertEquals(bubbledActivity.getTask(), targetRecord.getTask()); + } + + private ActivityRecord createBubbledActivity() { + final ActivityOptions opts = ActivityOptions.makeBasic(); + opts.setTaskAlwaysOnTop(true); + opts.setLaunchedFromBubble(true); + opts.setLaunchBounds(new Rect(10, 10, 100, 100)); + return new ActivityBuilder(mAtm) + .setCreateTask(true) + .setComponent(ActivityBuilder.getDefaultComponent()) + .setActivityOptions(opts) + .build(); + } + private static void startActivityInner(ActivityStarter starter, ActivityRecord target, ActivityRecord source, ActivityOptions options, Task inTask, TaskFragment inTaskFragment) { 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 c67d1ec63827..a39a1a8637df 100644 --- a/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java @@ -550,7 +550,7 @@ public class BackNavigationControllerTests extends WindowTestsBase { }).when(appWindow.mSession).setOnBackInvokedCallbackInfo(eq(appWindow.mClient), any()); addToWindowMap(appWindow, true); - dispatcher.attachToWindow(appWindow.mSession, appWindow.mClient, null, null); + dispatcher.attachToWindow(appWindow.mSession, appWindow.mClient, null); OnBackInvokedCallback appCallback = createBackCallback(appLatch); 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 4afc8ac6c599..366e519fb063 100644 --- a/services/tests/wmtests/src/com/android/server/wm/BackgroundActivityStartControllerExemptionTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/BackgroundActivityStartControllerExemptionTests.java @@ -29,6 +29,7 @@ import static com.google.common.truth.Truth.assertWithMessage; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import android.app.ActivityOptions; @@ -564,4 +565,136 @@ public class BackgroundActivityStartControllerExemptionTests { assertWithMessage(balState.toString()).that(callerVerdict.getCode()).isEqualTo( BAL_ALLOW_SAW_PERMISSION); } + + @Test + public void testCaller_isRecents() { + 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 + //if (mSupervisor.mRecentTasks.isCallerRecents(state.mCallingUid)) + RecentTasks recentTasks = mock(RecentTasks.class); + when(recentTasks.isCallerRecents(eq(callingUid))).thenReturn(true); + mSupervisor.mRecentTasks = recentTasks; + + // prepare call + PendingIntentRecord originatingPendingIntent = mPendingIntentRecord; + BackgroundStartPrivileges forcedBalByPiSender = BackgroundStartPrivileges.NONE; + Intent intent = TEST_INTENT; + ActivityOptions checkedOptions = mCheckedOptions; + BackgroundActivityStartController.BalState balState = mController.new BalState(callingUid, + callingPid, callingPackage, realCallingUid, realCallingPid, null, + originatingPendingIntent, forcedBalByPiSender, mResultRecord, intent, + checkedOptions); + + // call + BalVerdict callerVerdict = mController.checkBackgroundActivityStartAllowedByCaller( + balState); + balState.setResultForCaller(callerVerdict); + + // assertions + assertWithMessage(balState.toString()).that(callerVerdict.getCode()).isEqualTo( + BAL_ALLOW_ALLOWLISTED_COMPONENT); + } + + @Test + public void testCaller_isDeviceOwner() { + 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 + when(mService.isDeviceOwner(eq(callingUid))).thenReturn(true); + + // prepare call + PendingIntentRecord originatingPendingIntent = mPendingIntentRecord; + BackgroundStartPrivileges forcedBalByPiSender = BackgroundStartPrivileges.NONE; + Intent intent = TEST_INTENT; + ActivityOptions checkedOptions = mCheckedOptions; + BackgroundActivityStartController.BalState balState = mController.new BalState(callingUid, + callingPid, callingPackage, realCallingUid, realCallingPid, null, + originatingPendingIntent, forcedBalByPiSender, mResultRecord, intent, + checkedOptions); + + // call + BalVerdict callerVerdict = mController.checkBackgroundActivityStartAllowedByCaller( + balState); + balState.setResultForCaller(callerVerdict); + + // assertions + assertWithMessage(balState.toString()).that(callerVerdict.getCode()).isEqualTo( + BAL_ALLOW_ALLOWLISTED_COMPONENT); + } + + @Test + public void testCaller_isAffiliatedProfileOwner() { + 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 + when(mService.isAffiliatedProfileOwner(eq(callingUid))).thenReturn(true); + + // prepare call + PendingIntentRecord originatingPendingIntent = mPendingIntentRecord; + BackgroundStartPrivileges forcedBalByPiSender = BackgroundStartPrivileges.NONE; + Intent intent = TEST_INTENT; + ActivityOptions checkedOptions = mCheckedOptions; + BackgroundActivityStartController.BalState balState = mController.new BalState(callingUid, + callingPid, callingPackage, realCallingUid, realCallingPid, null, + originatingPendingIntent, forcedBalByPiSender, mResultRecord, intent, + checkedOptions); + + // call + BalVerdict callerVerdict = mController.checkBackgroundActivityStartAllowedByCaller( + balState); + balState.setResultForCaller(callerVerdict); + + // assertions + assertWithMessage(balState.toString()).that(callerVerdict.getCode()).isEqualTo( + BAL_ALLOW_ALLOWLISTED_COMPONENT); + } + + @Test + public void testCaller_isExemptFromBgStartRestriction() { + 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; + + mDeviceConfig.set("system_exempt_from_activity_bg_start_restriction_enabled", "true"); + AppOpsManager appOpsManager = mock(AppOpsManager.class); + when(mService.getAppOpsManager()).thenReturn(appOpsManager); + when(appOpsManager.checkOpNoThrow(eq( + AppOpsManager.OP_SYSTEM_EXEMPT_FROM_ACTIVITY_BG_START_RESTRICTION), + eq(callingUid), eq(callingPackage))).thenReturn(AppOpsManager.MODE_ALLOWED); + + + // prepare call + PendingIntentRecord originatingPendingIntent = mPendingIntentRecord; + BackgroundStartPrivileges forcedBalByPiSender = BackgroundStartPrivileges.NONE; + Intent intent = TEST_INTENT; + ActivityOptions checkedOptions = mCheckedOptions; + BackgroundActivityStartController.BalState balState = mController.new BalState(callingUid, + callingPid, callingPackage, realCallingUid, realCallingPid, null, + originatingPendingIntent, forcedBalByPiSender, mResultRecord, intent, + checkedOptions); + + // call + BalVerdict callerVerdict = mController.checkBackgroundActivityStartAllowedByCaller( + balState); + balState.setResultForCaller(callerVerdict); + + // assertions + assertWithMessage(balState.toString()).that(callerVerdict.getCode()).isEqualTo( + BAL_ALLOW_PERMISSION); + } } diff --git a/services/tests/wmtests/src/com/android/server/wm/BackgroundLaunchProcessControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/BackgroundLaunchProcessControllerTests.java new file mode 100644 index 000000000000..a4df03447754 --- /dev/null +++ b/services/tests/wmtests/src/com/android/server/wm/BackgroundLaunchProcessControllerTests.java @@ -0,0 +1,189 @@ +/* + * 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. + */ + +package com.android.server.wm; + +import static com.android.server.wm.ActivityTaskManagerService.APP_SWITCH_ALLOW; +import static com.android.server.wm.ActivityTaskManagerService.APP_SWITCH_DISALLOW; +import static com.android.server.wm.BackgroundActivityStartController.BAL_ALLOW_FOREGROUND; +import static com.android.server.wm.BackgroundActivityStartController.BAL_ALLOW_GRACE_PERIOD; +import static com.android.server.wm.BackgroundActivityStartController.BAL_ALLOW_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 android.app.BackgroundStartPrivileges; +import android.content.Context; +import android.os.Binder; +import android.os.IBinder; +import android.platform.test.annotations.Presubmit; + +import androidx.test.filters.SmallTest; + +import com.android.server.wm.BackgroundActivityStartController.BalVerdict; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +import java.util.Collection; +import java.util.HashSet; +import java.util.Set; + +/** + * Tests for the {@link BackgroundLaunchProcessController} class. + * + * Build/Install/Run: + * atest WmTests:BackgroundLaunchProcessControllerTests + */ +@SmallTest +@Presubmit +@RunWith(JUnit4.class) +public class BackgroundLaunchProcessControllerTests { + + Set<IBinder> mActivityStartAllowed = new HashSet<>(); + Set<Integer> mHasActiveVisibleWindow = new HashSet<>(); + + BackgroundActivityStartCallback mCallback = new BackgroundActivityStartCallback() { + @Override + public boolean isActivityStartAllowed(Collection<IBinder> tokens, int uid, + String packageName) { + for (IBinder token : tokens) { + if (token == null || mActivityStartAllowed.contains(token)) { + return true; + } + } + return false; + } + + @Override + public boolean canCloseSystemDialogs(Collection<IBinder> tokens, int uid) { + return false; + } + }; + BackgroundLaunchProcessController mController = new BackgroundLaunchProcessController( + mHasActiveVisibleWindow::contains, mCallback); + + int mPid = 123; + int mUid = 234; + String mPackageName = "package.name"; + int mAppSwitchState = APP_SWITCH_DISALLOW; + boolean mIsCheckingForFgsStart = false; + boolean mHasActivityInVisibleTask = false; + boolean mHasBackgroundActivityStartPrivileges = false; + long mLastStopAppSwitchesTime = 0L; + long mLastActivityLaunchTime = 0L; + long mLastActivityFinishTime = 0L; + + @Test + public void testNothingAllows() { + BalVerdict balVerdict = mController.areBackgroundActivityStartsAllowed( + mPid, mUid, mPackageName, + mAppSwitchState, mIsCheckingForFgsStart, + mHasActivityInVisibleTask, mHasBackgroundActivityStartPrivileges, + mLastStopAppSwitchesTime, mLastActivityLaunchTime, + mLastActivityFinishTime); + assertThat(balVerdict.getCode()).isEqualTo(BAL_BLOCK); + } + + @Test + public void testInstrumenting() { + mHasBackgroundActivityStartPrivileges = true; + BalVerdict balVerdict = mController.areBackgroundActivityStartsAllowed( + mPid, mUid, mPackageName, + mAppSwitchState, mIsCheckingForFgsStart, + mHasActivityInVisibleTask, mHasBackgroundActivityStartPrivileges, + mLastStopAppSwitchesTime, mLastActivityLaunchTime, + mLastActivityFinishTime); + assertThat(balVerdict.getCode()).isEqualTo(BAL_ALLOW_PERMISSION); + } + + @Test + public void testAllowedByTokenNoCallback() { + mController = new BackgroundLaunchProcessController(mHasActiveVisibleWindow::contains, + null); + Binder token = new Binder(); + mActivityStartAllowed.add(token); + mController.addOrUpdateAllowBackgroundStartPrivileges(token, + BackgroundStartPrivileges.ALLOW_BAL); + BalVerdict balVerdict = mController.areBackgroundActivityStartsAllowed( + mPid, mUid, mPackageName, + mAppSwitchState, mIsCheckingForFgsStart, + mHasActivityInVisibleTask, mHasBackgroundActivityStartPrivileges, + mLastStopAppSwitchesTime, mLastActivityLaunchTime, + mLastActivityFinishTime); + assertThat(balVerdict.getCode()).isEqualTo(BAL_ALLOW_PERMISSION); + } + + @Test + public void testAllowedByToken() { + Binder token = new Binder(); + mActivityStartAllowed.add(token); + mController.addOrUpdateAllowBackgroundStartPrivileges(token, + BackgroundStartPrivileges.ALLOW_BAL); + BalVerdict balVerdict = mController.areBackgroundActivityStartsAllowed( + mPid, mUid, mPackageName, + mAppSwitchState, mIsCheckingForFgsStart, + mHasActivityInVisibleTask, mHasBackgroundActivityStartPrivileges, + mLastStopAppSwitchesTime, mLastActivityLaunchTime, + mLastActivityFinishTime); + assertThat(balVerdict.getCode()).isEqualTo(BAL_ALLOW_PERMISSION); + } + + @Test + public void testBoundByForeground() { + mAppSwitchState = APP_SWITCH_ALLOW; + mController.addBoundClientUid(999, "visible.package", Context.BIND_ALLOW_ACTIVITY_STARTS); + mHasActiveVisibleWindow.add(999); + BalVerdict balVerdict = mController.areBackgroundActivityStartsAllowed( + mPid, mUid, mPackageName, + mAppSwitchState, mIsCheckingForFgsStart, + mHasActivityInVisibleTask, mHasBackgroundActivityStartPrivileges, + mLastStopAppSwitchesTime, mLastActivityLaunchTime, + mLastActivityFinishTime); + assertThat(balVerdict.getCode()).isEqualTo(BAL_ALLOW_VISIBLE_WINDOW); + } + + @Test + public void testForegroundTask() { + mAppSwitchState = APP_SWITCH_ALLOW; + mHasActivityInVisibleTask = true; + BalVerdict balVerdict = mController.areBackgroundActivityStartsAllowed( + mPid, mUid, mPackageName, + mAppSwitchState, mIsCheckingForFgsStart, + mHasActivityInVisibleTask, mHasBackgroundActivityStartPrivileges, + mLastStopAppSwitchesTime, mLastActivityLaunchTime, + mLastActivityFinishTime); + assertThat(balVerdict.getCode()).isEqualTo(BAL_ALLOW_FOREGROUND); + } + + @Test + public void testGracePeriod() { + mAppSwitchState = APP_SWITCH_ALLOW; + long now = System.currentTimeMillis(); + mLastStopAppSwitchesTime = now - 10000; + mLastActivityLaunchTime = now - 9000; + mLastActivityFinishTime = now - 100; + BalVerdict balVerdict = mController.areBackgroundActivityStartsAllowed( + mPid, mUid, mPackageName, + mAppSwitchState, mIsCheckingForFgsStart, + mHasActivityInVisibleTask, mHasBackgroundActivityStartPrivileges, + mLastStopAppSwitchesTime, mLastActivityLaunchTime, + mLastActivityFinishTime); + assertThat(balVerdict.getCode()).isEqualTo(BAL_ALLOW_GRACE_PERIOD); + } +} diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java index 87395a17698d..417ee6be17bc 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java @@ -1343,6 +1343,27 @@ public class DisplayContentTests extends WindowTestsBase { assertEquals(mAppWindow, mDisplayContent.computeImeControlTarget()); } + @SetupWindows(addWindows = W_ACTIVITY) + @Test + public void testShouldImeAttachedToApp_targetBoundsDifferentFromImeContainer_returnsFalse() + throws Exception { + Rect imeContainerBounds = new Rect(0, 0, 100, 100); + Rect imeTargetBounds = new Rect(0, 0, 100, 200); + spyOn(mAppWindow); + spyOn(mAppWindow.mActivityRecord); + doReturn(imeTargetBounds).when(mAppWindow).getBounds(); + doReturn(true).when(mAppWindow.mActivityRecord).matchParentBounds(); + mDisplayContent.setImeInputTarget(mAppWindow); + mDisplayContent.setImeLayeringTarget( + mDisplayContent.getImeInputTarget().getWindowState()); + mDisplayContent.setRemoteInsetsController(createDisplayWindowInsetsController()); + final DisplayArea.Tokens imeContainer = mDisplayContent.getImeContainer(); + spyOn(imeContainer); + doReturn(imeContainerBounds).when(imeContainer).getBounds(); + + assertFalse(mDisplayContent.shouldImeAttachedToApp()); + } + @Test public void testUpdateSystemGestureExclusion() throws Exception { final DisplayContent dc = createNewDisplay(); diff --git a/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java index c7f502045ac8..8129c3d030be 100644 --- a/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java @@ -77,6 +77,7 @@ import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; import android.annotation.Nullable; import android.compat.testing.PlatformCompatChangeRule; @@ -110,7 +111,7 @@ import org.junit.rules.TestRule; import org.junit.runner.RunWith; /** - * Test class for {@link LetterboxUiControllerTest}. + * Test class for {@link LetterboxUiController}. * * Build/Install/Run: * atest WmTests:LetterboxUiControllerTest @@ -521,8 +522,8 @@ public class LetterboxUiControllerTest extends WindowTestsBase { final Rect opaqueBounds = new Rect(0, 0, 500, 300); doReturn(opaqueBounds).when(mActivity).getBounds(); // Activity is translucent - spyOn(mActivity.mLetterboxUiController); - doReturn(true).when(mActivity.mLetterboxUiController).hasInheritedLetterboxBehavior(); + spyOn(mActivity.mTransparentPolicy); + when(mActivity.mTransparentPolicy.isRunning()).thenReturn(true); // Makes requested sizes different mainWindow.mRequestedWidth = opaqueBounds.width() - 1; diff --git a/services/tests/wmtests/src/com/android/server/wm/PersisterQueueTests.java b/services/tests/wmtests/src/com/android/server/wm/PersisterQueueTests.java index 8cf2776b85e9..3e87f1f96fcd 100644 --- a/services/tests/wmtests/src/com/android/server/wm/PersisterQueueTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/PersisterQueueTests.java @@ -125,7 +125,7 @@ public class PersisterQueueTests { processDuration < PRE_TASK_DELAY_MS); assertTrue("Target didn't call callback enough times.", - mFactory.waitForAllExpectedItemsProcessed(TIMEOUT_ALLOWANCE)); + mListener.waitForAllExpectedCallbackDone(TIMEOUT_ALLOWANCE)); // Once before processing this item, once after that. assertEquals(2, mListener.mProbablyDoneResults.size()); // The last one must be called with probably done being true. diff --git a/services/tests/wmtests/src/com/android/server/wm/RootTaskTests.java b/services/tests/wmtests/src/com/android/server/wm/RootTaskTests.java index ce9050456681..e019a416c069 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RootTaskTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/RootTaskTests.java @@ -405,11 +405,12 @@ public class RootTaskTests extends WindowTestsBase { final RootWindowContainer.FindTaskResult result = new RootWindowContainer.FindTaskResult(); - result.init(r.getActivityType(), r.taskAffinity, r.intent, r.info); + result.init(r.getActivityType(), r.taskAffinity, r.intent, r.info, true); result.process(task); - assertEquals(r, task.getTopNonFinishingActivity(false /* includeOverlays */)); - assertEquals(taskOverlay, task.getTopNonFinishingActivity(true /* includeOverlays */)); + assertEquals(r, task.getTopNonFinishingActivity(false /* includeOverlays */, true)); + assertEquals( + taskOverlay, task.getTopNonFinishingActivity(true /* includeOverlays */, true)); assertNotNull(result.mIdealRecord); } @@ -432,7 +433,7 @@ public class RootTaskTests extends WindowTestsBase { final ActivityRecord r1 = new ActivityBuilder(mAtm).setComponent( target).setTargetActivity(targetActivity).build(); RootWindowContainer.FindTaskResult result = new RootWindowContainer.FindTaskResult(); - result.init(r1.getActivityType(), r1.taskAffinity, r1.intent, r1.info); + result.init(r1.getActivityType(), r1.taskAffinity, r1.intent, r1.info, true); result.process(parentTask); assertThat(result.mIdealRecord).isNotNull(); @@ -440,7 +441,7 @@ public class RootTaskTests extends WindowTestsBase { final ActivityRecord r2 = new ActivityBuilder(mAtm).setComponent( alias).setTargetActivity(targetActivity).build(); result = new RootWindowContainer.FindTaskResult(); - result.init(r2.getActivityType(), r2.taskAffinity, r2.intent, r2.info); + result.init(r2.getActivityType(), r2.taskAffinity, r2.intent, r2.info, true); result.process(parentTask); assertThat(result.mIdealRecord).isNotNull(); } @@ -1234,12 +1235,18 @@ public class RootTaskTests extends WindowTestsBase { assertEquals(STOPPING, activity2.getState()); assertThat(mSupervisor.mStoppingActivities).contains(activity2); + registerTestTransitionPlayer(); + final Transition transition = display.mTransitionController + .requestCloseTransitionIfNeeded(rootTask1); + transition.collectClose(rootTask1); // The display becomes empty. Since there is no next activity to be idle, the activity // should be destroyed immediately with updating configuration to restore original state. final ActivityRecord activity1 = finishTopActivity(rootTask1); assertEquals(DESTROYING, activity1.getState()); verify(mRootWindowContainer).ensureVisibilityAndConfig(eq(null) /* starting */, eq(display), anyBoolean()); + assertTrue("Transition must be ready if there is no next running activity", + transition.allReady()); } private ActivityRecord finishTopActivity(Task task) { diff --git a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java index 7756edd68eda..eb79118fe1c7 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java @@ -147,6 +147,40 @@ public class RootWindowContainerTests extends WindowTestsBase { } @Test + public void testFindTask_includeLaunchedFromBubbled() { + final ComponentName component = ComponentName.createRelative( + DEFAULT_COMPONENT_PACKAGE_NAME, ".BubbledActivity"); + final ActivityOptions opts = ActivityOptions.makeBasic(); + opts.setTaskAlwaysOnTop(true); + opts.setLaunchedFromBubble(true); + final ActivityRecord activity = new ActivityBuilder(mWm.mAtmService) + .setComponent(component) + .setActivityOptions(opts) + .setCreateTask(true) + .build(); + + assertEquals(activity, mWm.mRoot.findTask(activity, activity.getTaskDisplayArea(), + true /* includeLaunchedFromBubble */)); + } + + @Test + public void testFindTask_ignoreLaunchedFromBubbled() { + final ComponentName component = ComponentName.createRelative( + DEFAULT_COMPONENT_PACKAGE_NAME, ".BubbledActivity"); + final ActivityOptions opts = ActivityOptions.makeBasic(); + opts.setTaskAlwaysOnTop(true); + opts.setLaunchedFromBubble(true); + final ActivityRecord activity = new ActivityBuilder(mWm.mAtmService) + .setComponent(component) + .setActivityOptions(opts) + .setCreateTask(true) + .build(); + + assertNull(mWm.mRoot.findTask(activity, activity.getTaskDisplayArea(), + false /* includeLaunchedFromBubble */)); + } + + @Test public void testAllPausedActivitiesComplete() { DisplayContent displayContent = mWm.mRoot.getDisplayContent(DEFAULT_DISPLAY); ActivityRecord activity = createActivityRecord(displayContent); @@ -906,6 +940,24 @@ public class RootWindowContainerTests extends WindowTestsBase { } /** + * Tests whether home can be started if it's not allowed by policy. + */ + @Test + public void testCanStartHome_returnsFalse_ifDisallowedByPolicy() { + final ActivityInfo info = new ActivityInfo(); + info.applicationInfo = new ApplicationInfo(); + final WindowProcessController app = mock(WindowProcessController.class); + doReturn(app).when(mAtm).getProcessController(any(), anyInt()); + doReturn(false).when(app).isInstrumenting(); + final TaskDisplayArea taskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea(); + doReturn(false).when(taskDisplayArea).canHostHomeTask(); + + assertFalse(mRootWindowContainer.canStartHomeOnDisplayArea(info, taskDisplayArea, + false /* allowInstrumenting*/)); + } + + + /** * Tests that secondary home activity should not be resolved if device is still locked. */ @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 7ced9d50ab3f..1a366b3e3a4f 100644 --- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java @@ -16,8 +16,6 @@ package com.android.server.wm; -import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; -import static android.app.WindowConfiguration.ROTATION_UNDEFINED; 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; @@ -62,7 +60,6 @@ import static com.android.internal.util.FrameworkStatsLog.APP_COMPAT_STATE_CHANG import static com.android.internal.util.FrameworkStatsLog.APP_COMPAT_STATE_CHANGED__STATE__LETTERBOXED_FOR_SIZE_COMPAT_MODE; import static com.android.internal.util.FrameworkStatsLog.APP_COMPAT_STATE_CHANGED__STATE__NOT_LETTERBOXED; import static com.android.internal.util.FrameworkStatsLog.APP_COMPAT_STATE_CHANGED__STATE__NOT_VISIBLE; -import static com.android.server.wm.ActivityRecord.State.DESTROYED; import static com.android.server.wm.ActivityRecord.State.PAUSED; import static com.android.server.wm.ActivityRecord.State.RESTARTING_PROCESS; import static com.android.server.wm.ActivityRecord.State.RESUMED; @@ -77,7 +74,6 @@ 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.assertNull; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; @@ -109,7 +105,6 @@ import android.os.RemoteException; import android.os.UserHandle; import android.platform.test.annotations.EnableFlags; import android.platform.test.annotations.Presubmit; -import android.platform.test.annotations.RequiresFlagsDisabled; import android.provider.DeviceConfig; import android.provider.DeviceConfig.Properties; import android.view.InsetsFrameProvider; @@ -199,26 +194,6 @@ public class SizeCompatTests extends WindowTestsBase { } @Test - public void testCleanLetterboxConfigListenerWhenTranslucentIsDestroyed() { - mWm.mLetterboxConfiguration.setTranslucentLetterboxingOverrideEnabled(true); - setUpDisplaySizeWithApp(2000, 1000); - prepareUnresizable(mActivity, SCREEN_ORIENTATION_PORTRAIT); - mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); - // Translucent Activity - final ActivityRecord translucentActivity = new ActivityBuilder(mAtm) - .setActivityTheme(android.R.style.Theme_Translucent) - .setLaunchedFromUid(mActivity.getUid()) - .setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT) - .build(); - mTask.addChild(translucentActivity); - - translucentActivity.setState(DESTROYED, "testing"); - translucentActivity.removeImmediately(); - - assertFalse(translucentActivity.mLetterboxUiController.hasInheritedLetterboxBehavior()); - } - - @Test public void testHorizontalReachabilityEnabledForTranslucentActivities() { testReachabilityEnabledForTranslucentActivity(/* dw */ 2500, /* dh */1000, SCREEN_ORIENTATION_PORTRAIT, /* minAspectRatio */ 0f, @@ -364,45 +339,8 @@ public class SizeCompatTests extends WindowTestsBase { } @Test - public void testApplyStrategyAgainWhenOpaqueIsDestroyed() { - mWm.mLetterboxConfiguration.setTranslucentLetterboxingOverrideEnabled(true); - setUpDisplaySizeWithApp(2000, 1000); - prepareUnresizable(mActivity, SCREEN_ORIENTATION_PORTRAIT); - mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); - // Launch another opaque activity - final ActivityRecord opaqueActivity = new ActivityBuilder(mAtm) - .setLaunchedFromUid(mActivity.getUid()) - .setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT) - .build(); - mTask.addChild(opaqueActivity); - // Transparent activity strategy not applied - assertFalse(opaqueActivity.mLetterboxUiController.hasInheritedLetterboxBehavior()); - - // Launch translucent Activity - final ActivityRecord translucentActivity = new ActivityBuilder(mAtm) - .setActivityTheme(android.R.style.Theme_Translucent) - .setLaunchedFromUid(mActivity.getUid()) - .setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT) - .build(); - mTask.addChild(translucentActivity); - // Transparent strategy applied - assertTrue(translucentActivity.mLetterboxUiController.hasInheritedLetterboxBehavior()); - - spyOn(translucentActivity.mLetterboxUiController); - clearInvocations(translucentActivity.mLetterboxUiController); - - // We destroy the first opaque activity - opaqueActivity.setState(DESTROYED, "testing"); - opaqueActivity.removeImmediately(); - - // Check that updateInheritedLetterbox() is invoked again - verify(translucentActivity.mLetterboxUiController).updateInheritedLetterbox(); - } - - // TODO(b/333663877): Enable test after fix - @Test - @RequiresFlagsDisabled({Flags.FLAG_INSETS_DECOUPLED_CONFIGURATION}) @EnableFlags(Flags.FLAG_IMMERSIVE_APP_REPOSITIONING) + @DisableCompatChanges({ActivityInfo.INSETS_DECOUPLED_CONFIGURATION_ENFORCED}) public void testRepositionLandscapeImmersiveAppWithDisplayCutout() { final int dw = 2100; final int dh = 2000; @@ -416,11 +354,14 @@ public class SizeCompatTests extends WindowTestsBase { mWm.mLetterboxConfiguration.setLetterboxVerticalPositionMultiplier(0.5f); mWm.mLetterboxConfiguration.setIsVerticalReachabilityEnabled(true); - doReturn(true).when(mActivity).isImmersiveMode(any()); - prepareMinAspectRatio(mActivity, OVERRIDE_MIN_ASPECT_RATIO_LARGE_VALUE, - SCREEN_ORIENTATION_LANDSCAPE); - addWindowToActivity(mActivity); - mActivity.mRootWindowContainer.performSurfacePlacement(); + final ActivityRecord activity = getActivityBuilderOnSameTask() + .setScreenOrientation(SCREEN_ORIENTATION_LANDSCAPE) + .setMinAspectRatio(OVERRIDE_MIN_ASPECT_RATIO_LARGE_VALUE) + .build(); + + doReturn(true).when(activity).isImmersiveMode(any()); + addWindowToActivity(activity); + activity.mRootWindowContainer.performSurfacePlacement(); final Function<ActivityRecord, Rect> innerBoundsOf = (ActivityRecord a) -> { @@ -431,299 +372,22 @@ public class SizeCompatTests extends WindowTestsBase { final Consumer<Integer> doubleClick = (Integer y) -> { - mActivity.mLetterboxUiController.handleVerticalDoubleTap(y); - mActivity.mRootWindowContainer.performSurfacePlacement(); + activity.mLetterboxUiController.handleVerticalDoubleTap(y); + activity.mRootWindowContainer.performSurfacePlacement(); }; - final Rect bounds = mActivity.getBounds(); + final Rect bounds = activity.getBounds(); assertTrue(bounds.top > cutoutHeight && bounds.bottom < dh); assertEquals(dw, bounds.width()); // Double click bottom. doubleClick.accept(dh - 10); - assertEquals(dh, innerBoundsOf.apply(mActivity).bottom); + assertEquals(dh, innerBoundsOf.apply(activity).bottom); // Double click top. doubleClick.accept(10); doubleClick.accept(10); - assertEquals(cutoutHeight, innerBoundsOf.apply(mActivity).top); - } - - @Test - public void testResetOpaqueReferenceWhenOpaqueIsDestroyed() { - mWm.mLetterboxConfiguration.setTranslucentLetterboxingOverrideEnabled(true); - setUpDisplaySizeWithApp(2000, 1000); - prepareUnresizable(mActivity, SCREEN_ORIENTATION_PORTRAIT); - mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); - - // Launch translucent Activity - final ActivityRecord translucentActivity = new ActivityBuilder(mAtm) - .setActivityTheme(android.R.style.Theme_Translucent) - .setLaunchedFromUid(mActivity.getUid()) - .setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT) - .build(); - mTask.addChild(translucentActivity); - // Transparent strategy applied - assertTrue(translucentActivity.mLetterboxUiController.hasInheritedLetterboxBehavior()); - assertNotNull(translucentActivity.mLetterboxUiController.mFirstOpaqueActivityBeneath); - - spyOn(translucentActivity.mLetterboxUiController); - clearInvocations(translucentActivity.mLetterboxUiController); - - // We destroy the first opaque activity - mActivity.removeImmediately(); - - // Check that updateInheritedLetterbox() is invoked again - verify(translucentActivity.mLetterboxUiController).updateInheritedLetterbox(); - assertNull(translucentActivity.mLetterboxUiController.mFirstOpaqueActivityBeneath); - } - - @Test - public void testNotApplyStrategyAgainWhenOpaqueIsNotDestroyed() { - mWm.mLetterboxConfiguration.setTranslucentLetterboxingOverrideEnabled(true); - setUpDisplaySizeWithApp(2000, 1000); - prepareUnresizable(mActivity, SCREEN_ORIENTATION_PORTRAIT); - mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); - // Launch another opaque activity - final ActivityRecord opaqueActivity = new ActivityBuilder(mAtm) - .setLaunchedFromUid(mActivity.getUid()) - .setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT) - .build(); - mTask.addChild(opaqueActivity); - // Transparent activity strategy not applied - assertFalse(opaqueActivity.mLetterboxUiController.hasInheritedLetterboxBehavior()); - - // Launch translucent Activity - final ActivityRecord translucentActivity = new ActivityBuilder(mAtm) - .setActivityTheme(android.R.style.Theme_Translucent) - .setLaunchedFromUid(mActivity.getUid()) - .setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT) - .build(); - mTask.addChild(translucentActivity); - // Transparent strategy applied - assertTrue(translucentActivity.mLetterboxUiController.hasInheritedLetterboxBehavior()); - - spyOn(translucentActivity.mLetterboxUiController); - clearInvocations(translucentActivity.mLetterboxUiController); - - // Check that updateInheritedLetterbox() is invoked again - verify(translucentActivity.mLetterboxUiController, never()).updateInheritedLetterbox(); - } - - @Test - public void testApplyStrategyToTranslucentActivities() { - mWm.mLetterboxConfiguration.setTranslucentLetterboxingOverrideEnabled(true); - setUpDisplaySizeWithApp(2000, 1000); - prepareUnresizable(mActivity, 1.5f /* maxAspect */, SCREEN_ORIENTATION_PORTRAIT); - mActivity.info.setMinAspectRatio(1.2f); - mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); - // Translucent Activity - final ActivityRecord translucentActivity = new ActivityBuilder(mAtm) - .setActivityTheme(android.R.style.Theme_Translucent) - .setLaunchedFromUid(mActivity.getUid()) - .setScreenOrientation(SCREEN_ORIENTATION_LANDSCAPE) - .setMinAspectRatio(1.1f) - .setMaxAspectRatio(3f) - .build(); - mTask.addChild(translucentActivity); - // We check bounds - final Rect opaqueBounds = mActivity.getConfiguration().windowConfiguration.getBounds(); - final Rect translucentRequestedBounds = translucentActivity.getRequestedOverrideBounds(); - assertEquals(opaqueBounds, translucentRequestedBounds); - // We check orientation - final int translucentOrientation = - translucentActivity.getRequestedConfigurationOrientation(); - assertEquals(ORIENTATION_PORTRAIT, translucentOrientation); - // We check aspect ratios - assertEquals(1.2f, translucentActivity.getMinAspectRatio(), 0.00001f); - assertEquals(1.5f, translucentActivity.getMaxAspectRatio(), 0.00001f); - } - - @Test - public void testApplyStrategyToTranslucentActivitiesRetainsWindowConfigurationProperties() { - mWm.mLetterboxConfiguration.setTranslucentLetterboxingOverrideEnabled(true); - setUpDisplaySizeWithApp(2000, 1000); - prepareUnresizable(mActivity, SCREEN_ORIENTATION_PORTRAIT); - mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); - // Translucent Activity - final ActivityRecord translucentActivity = new ActivityBuilder(mAtm) - .setActivityTheme(android.R.style.Theme_Translucent) - .setLaunchedFromUid(mActivity.getUid()) - .build(); - final Configuration requestedConfig = - translucentActivity.getRequestedOverrideConfiguration(); - final WindowConfiguration translucentWinConf = requestedConfig.windowConfiguration; - translucentWinConf.setActivityType(ACTIVITY_TYPE_STANDARD); - translucentWinConf.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW); - translucentWinConf.setAlwaysOnTop(true); - translucentActivity.onRequestedOverrideConfigurationChanged(requestedConfig); - - mTask.addChild(translucentActivity); - - // The original override of WindowConfiguration should keep. - assertEquals(ACTIVITY_TYPE_STANDARD, translucentActivity.getActivityType()); - assertEquals(WINDOWING_MODE_MULTI_WINDOW, translucentWinConf.getWindowingMode()); - assertTrue(translucentWinConf.isAlwaysOnTop()); - // Unless display is going to be rotated, it should always inherit from parent. - assertEquals(ROTATION_UNDEFINED, translucentWinConf.getDisplayRotation()); - } - - @Test - public void testApplyStrategyToMultipleTranslucentActivities() { - mWm.mLetterboxConfiguration.setTranslucentLetterboxingOverrideEnabled(true); - setUpDisplaySizeWithApp(2000, 1000); - prepareUnresizable(mActivity, 1.5f /* maxAspect */, SCREEN_ORIENTATION_PORTRAIT); - mActivity.info.setMinAspectRatio(1.2f); - mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); - // Translucent Activity - final ActivityRecord translucentActivity = new ActivityBuilder(mAtm) - .setActivityTheme(android.R.style.Theme_Translucent) - .setLaunchedFromUid(mActivity.getUid()) - .setScreenOrientation(SCREEN_ORIENTATION_LANDSCAPE) - .setMinAspectRatio(1.1f) - .setMaxAspectRatio(3f) - .build(); - mTask.addChild(translucentActivity); - // We check bounds - final Rect opaqueBounds = mActivity.getConfiguration().windowConfiguration.getBounds(); - final Rect translucentRequestedBounds = translucentActivity.getRequestedOverrideBounds(); - assertEquals(opaqueBounds, translucentRequestedBounds); - // Launch another translucent activity - final ActivityRecord translucentActivity2 = new ActivityBuilder(mAtm) - .setActivityTheme(android.R.style.Theme_Translucent) - .setLaunchedFromUid(mActivity.getUid()) - .setScreenOrientation(SCREEN_ORIENTATION_LANDSCAPE) - .build(); - mTask.addChild(translucentActivity2); - // We check bounds - final Rect translucent2RequestedBounds = translucentActivity2.getRequestedOverrideBounds(); - assertEquals(opaqueBounds, translucent2RequestedBounds); - } - - @Test - public void testNotApplyStrategyToTranslucentActivitiesOverEmbeddedActivities() { - mWm.mLetterboxConfiguration.setTranslucentLetterboxingOverrideEnabled(true); - setUpDisplaySizeWithApp(2000, 1000); - mActivity.info.screenOrientation = SCREEN_ORIENTATION_PORTRAIT; - mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); - // Mock the activity as embedded without additional TaskFragment layer in the task for - // simplicity. - doReturn(true).when(mActivity).isEmbedded(); - // Translucent Activity - final ActivityRecord translucentActivity = new ActivityBuilder(mAtm) - .setActivityTheme(android.R.style.Theme_Translucent).build(); - doReturn(false).when(translucentActivity).matchParentBounds(); - mTask.addChild(translucentActivity); - // Check the strategy has not being applied - assertFalse(translucentActivity.mLetterboxUiController.hasInheritedLetterboxBehavior()); - } - - @Test - public void testTranslucentActivitiesDontGoInSizeCompatMode() { - mWm.mLetterboxConfiguration.setTranslucentLetterboxingOverrideEnabled(true); - setUpDisplaySizeWithApp(2800, 1400); - mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); - prepareUnresizable(mActivity, -1f /* maxAspect */, SCREEN_ORIENTATION_PORTRAIT); - // Rotate to put activity in size compat mode. - rotateDisplay(mActivity.mDisplayContent, ROTATION_90); - assertTrue(mActivity.inSizeCompatMode()); - // Rotate back - rotateDisplay(mActivity.mDisplayContent, ROTATION_0); - assertFalse(mActivity.inSizeCompatMode()); - // We launch a transparent activity - final ActivityRecord translucentActivity = new ActivityBuilder(mAtm) - .setActivityTheme(android.R.style.Theme_Translucent) - .setLaunchedFromUid(mActivity.getUid()) - .setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT) - .build(); - mTask.addChild(translucentActivity); - // It should not be in SCM - assertFalse(translucentActivity.inSizeCompatMode()); - // We rotate again - rotateDisplay(translucentActivity.mDisplayContent, ROTATION_90); - assertFalse(translucentActivity.inSizeCompatMode()); - } - - @Test - public void testCheckOpaqueIsLetterboxedWhenStrategyIsApplied() { - mWm.mLetterboxConfiguration.setTranslucentLetterboxingOverrideEnabled(true); - setUpDisplaySizeWithApp(2000, 1000); - prepareUnresizable(mActivity, SCREEN_ORIENTATION_PORTRAIT); - mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); - // Translucent Activity - final ActivityRecord translucentActivity = new ActivityBuilder(mAtm) - .setActivityTheme(android.R.style.Theme_Translucent) - .setLaunchedFromUid(mActivity.getUid()) - .build(); - assertFalse(translucentActivity.fillsParent()); - assertTrue(mActivity.fillsParent()); - mActivity.finishing = true; - assertFalse(mActivity.occludesParent()); - mTask.addChild(translucentActivity); - // The translucent activity won't inherit letterbox behavior from a finishing activity. - assertFalse(translucentActivity.mLetterboxUiController.hasInheritedLetterboxBehavior()); - } - - @Test - public void testTranslucentActivitiesWhenUnfolding() { - mWm.mLetterboxConfiguration.setTranslucentLetterboxingOverrideEnabled(true); - setUpDisplaySizeWithApp(2800, 1400); - mActivity.mDisplayContent.setIgnoreOrientationRequest( - true /* ignoreOrientationRequest */); - mActivity.mWmService.mLetterboxConfiguration.setLetterboxHorizontalPositionMultiplier( - 1.0f /*letterboxVerticalPositionMultiplier*/); - prepareUnresizable(mActivity, SCREEN_ORIENTATION_PORTRAIT); - // We launch a transparent activity - final ActivityRecord translucentActivity = new ActivityBuilder(mAtm) - .setActivityTheme(android.R.style.Theme_Translucent) - .setLaunchedFromUid(mActivity.getUid()) - .setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT) - .build(); - mTask.addChild(translucentActivity); - assertEquals(translucentActivity.getBounds(), mActivity.getBounds()); - - mTask.setWindowingMode(WINDOWING_MODE_FULLSCREEN); - spyOn(mActivity); - - // Halffold - setFoldablePosture(translucentActivity, true /* isHalfFolded */, - false /* isTabletop */); - verify(mActivity).recomputeConfiguration(); - assertEquals(translucentActivity.getBounds(), mActivity.getBounds()); - clearInvocations(mActivity); - - // Unfold - setFoldablePosture(translucentActivity, false /* isHalfFolded */, - false /* isTabletop */); - verify(mActivity).recomputeConfiguration(); - assertEquals(translucentActivity.getBounds(), mActivity.getBounds()); - } - - @Test - public void testTranslucentActivity_clearSizeCompatMode_inheritedCompatDisplayInsetsCleared() { - mWm.mLetterboxConfiguration.setTranslucentLetterboxingOverrideEnabled(true); - setUpDisplaySizeWithApp(2800, 1400); - mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); - prepareUnresizable(mActivity, -1f /* maxAspect */, SCREEN_ORIENTATION_PORTRAIT); - // Rotate to put activity in size compat mode. - rotateDisplay(mActivity.mDisplayContent, ROTATION_90); - assertTrue(mActivity.inSizeCompatMode()); - - // We launch a transparent activity - final ActivityRecord translucentActivity = new ActivityBuilder(mAtm) - .setActivityTheme(android.R.style.Theme_Translucent) - .setLaunchedFromUid(mActivity.getUid()) - .setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT) - .build(); - mTask.addChild(translucentActivity); - - // The transparent activity inherits the compat display insets of the opaque activity - // beneath it - assertNotNull(translucentActivity.getCompatDisplayInsets()); - - // Clearing SCM should also clear the inherited compat display insets - translucentActivity.clearSizeCompatMode(); - assertNull(translucentActivity.getCompatDisplayInsets()); + assertEquals(cutoutHeight, innerBoundsOf.apply(activity).top); } @Test @@ -754,26 +418,25 @@ public class SizeCompatTests extends WindowTestsBase { } @Test + @DisableCompatChanges({ActivityInfo.INSETS_DECOUPLED_CONFIGURATION_ENFORCED}) public void testFixedAspectRatioBoundsWithDecorInSquareDisplay() { - if (Flags.insetsDecoupledConfiguration()) { - // TODO (b/151861875): Re-enable it. This is disabled temporarily because the config - // bounds no longer contains display cutout. - return; - } final int notchHeight = 100; setUpApp(new TestDisplayContent.Builder(mAtm, 600, 800).setNotch(notchHeight).build()); final Rect displayBounds = mActivity.mDisplayContent.getWindowConfiguration().getBounds(); final float aspectRatio = 1.2f; - mActivity.info.setMaxAspectRatio(aspectRatio); - mActivity.info.setMinAspectRatio(aspectRatio); - prepareUnresizable(mActivity, -1f, SCREEN_ORIENTATION_UNSPECIFIED); - final Rect appBounds = mActivity.getWindowConfiguration().getAppBounds(); + final ActivityRecord activity = getActivityBuilderOnSameTask() + .setScreenOrientation(SCREEN_ORIENTATION_UNSPECIFIED) + .setMinAspectRatio(aspectRatio) + .setMaxAspectRatio(aspectRatio) + .setResizeMode(RESIZE_MODE_UNRESIZEABLE) + .build(); + final Rect appBounds = activity.getWindowConfiguration().getAppBounds(); // The parent configuration doesn't change since the first resolved configuration, so the // activity should fit in the parent naturally (size=583x700, appBounds=[9, 100 - 592, 800], // horizontal offset = round((600 - 583) / 2) = 9)). - assertFitted(); + assertFitted(activity); final int offsetX = (int) ((1f + displayBounds.width() - appBounds.width()) / 2); // The bounds must be horizontal centered. assertEquals(offsetX, appBounds.left); @@ -781,30 +444,30 @@ public class SizeCompatTests extends WindowTestsBase { // Ensure the app bounds keep the declared aspect ratio. assertEquals(appBounds.height(), appBounds.width() * aspectRatio, 0.5f /* delta */); // The decor height should be a part of the effective bounds. - assertEquals(mActivity.getBounds().height(), appBounds.height() + notchHeight); + assertEquals(activity.getBounds().height(), appBounds.height() + notchHeight); // Activity max bounds should be sandboxed; activity is letterboxed due to aspect ratio. - assertActivityMaxBoundsSandboxed(); + assertActivityMaxBoundsSandboxed(activity); // Activity max bounds ignore notch, since an app can be shown past the notch (although app // is currently limited by the notch). - assertThat(mActivity.getWindowConfiguration().getMaxBounds().height()) + assertThat(activity.getWindowConfiguration().getMaxBounds().height()) .isEqualTo(displayBounds.height()); - mActivity.setRequestedOrientation(SCREEN_ORIENTATION_LANDSCAPE); - assertFitted(); + activity.setRequestedOrientation(SCREEN_ORIENTATION_LANDSCAPE); + assertFitted(activity); // After the orientation of activity is changed, the display is rotated, the aspect // ratio should be the same (bounds=[0, 0 - 800, 583], appBounds=[100, 0 - 800, 583]). assertEquals(appBounds.width(), appBounds.height() * aspectRatio, 0.5f /* delta */); // Activity max bounds are sandboxed. - assertActivityMaxBoundsSandboxed(); + assertActivityMaxBoundsSandboxed(activity); - mActivity.setRequestedOrientation(SCREEN_ORIENTATION_PORTRAIT); - assertFitted(); + activity.setRequestedOrientation(SCREEN_ORIENTATION_PORTRAIT); + assertFitted(activity); // Activity max bounds should be sandboxed; activity is letterboxed due to aspect ratio. - assertActivityMaxBoundsSandboxed(); + assertActivityMaxBoundsSandboxed(activity); // Activity max bounds ignore notch, since an app can be shown past the notch (although app // is currently limited by the notch). - assertThat(mActivity.getWindowConfiguration().getMaxBounds().height()) + assertThat(activity.getWindowConfiguration().getMaxBounds().height()) .isEqualTo(displayBounds.height()); } @@ -1011,12 +674,8 @@ public class SizeCompatTests extends WindowTestsBase { @Test public void testMoveToDifferentOrientationDisplay() { - if (Flags.insetsDecoupledConfiguration()) { - // TODO (b/151861875): Re-enable it. This is disabled temporarily because the config - // bounds no longer contains display cutout. - return; - } setUpDisplaySizeWithApp(1000, 2500); + mActivity.mResolveConfigHint.mUseOverrideInsetsForConfig = true; prepareUnresizable(mActivity, -1.f /* maxAspect */, SCREEN_ORIENTATION_PORTRAIT); assertFitted(); @@ -1063,16 +722,12 @@ public class SizeCompatTests extends WindowTestsBase { @Test public void testFixedOrientationRotateCutoutDisplay() { - if (Flags.insetsDecoupledConfiguration()) { - // TODO (b/151861875): Re-enable it. This is disabled temporarily because the config - // bounds no longer contains display cutout. - return; - } // Create a display with a notch/cutout final int notchHeight = 60; final int width = 1000; setUpApp(new TestDisplayContent.Builder(mAtm, width, 2500) .setNotch(notchHeight).build()); + mActivity.mResolveConfigHint.mUseOverrideInsetsForConfig = true; // Bounds=[0, 0 - 1000, 1400], AppBounds=[0, 60 - 1000, 1460]. final float maxAspect = 1.4f; prepareUnresizable(mActivity, 1.4f /* maxAspect */, SCREEN_ORIENTATION_PORTRAIT); @@ -1665,12 +1320,8 @@ public class SizeCompatTests extends WindowTestsBase { setUpDisplaySizeWithApp(1000, 1200); // Create a size compat activity on the same task. - final ActivityRecord activity = new ActivityBuilder(mAtm) - .setTask(mTask) + final ActivityRecord activity = getActivityBuilderOnSameTask() .setScreenOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT) - .setComponent(ComponentName.createRelative(mContext, - SizeCompatTests.class.getName())) - .setUid(android.os.Process.myUid()) .build(); // The per-package override forces the activity into a 3:2 aspect ratio @@ -1682,24 +1333,18 @@ public class SizeCompatTests extends WindowTestsBase { @Test @EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO, ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_MEDIUM}) + @DisableCompatChanges({ActivityInfo.INSETS_DECOUPLED_CONFIGURATION_ENFORCED}) public void testOverrideMinAspectRatioLowerThanManifest() { - if (Flags.insetsDecoupledConfiguration()) { - // TODO (b/151861875): Re-enable it. This is disabled temporarily because the config - // bounds no longer contains display cutout. - return; - } - final DisplayContent display = new TestDisplayContent.Builder(mAtm, 1400, 1800) - .setNotch(200).setSystemDecorations(true).build(); + final int dh = 1800; + final int notchHeight = 200; + final DisplayContent display = new TestDisplayContent.Builder(mAtm, 1400, dh) + .setNotch(notchHeight).setSystemDecorations(true).build(); mTask = new TaskBuilder(mSupervisor).setDisplay(display).build(); // Create a size compat activity on the same task. - final ActivityRecord activity = new ActivityBuilder(mAtm) - .setTask(mTask) + final ActivityRecord activity = getActivityBuilderOnSameTask() .setScreenOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT) - .setComponent(ComponentName.createRelative(mContext, - SizeCompatTests.class.getName())) .setMinAspectRatio(2f) - .setUid(android.os.Process.myUid()) .build(); // The per-package override should have no effect, because the manifest aspect ratio is @@ -1708,7 +1353,7 @@ public class SizeCompatTests extends WindowTestsBase { assertEquals("App bounds must have min aspect ratio", 2f, (float) appBounds.height() / appBounds.width(), 0.0001f /* delta */); assertEquals("Long side must fit task", - mTask.getWindowConfiguration().getAppBounds().height(), appBounds.height()); + dh - notchHeight, appBounds.height()); assertEquals("Bounds can include insets", mTask.getBounds().height(), activity.getBounds().height()); } @@ -1720,13 +1365,9 @@ public class SizeCompatTests extends WindowTestsBase { setUpDisplaySizeWithApp(1400, 1600); // Create a size compat activity on the same task. - final ActivityRecord activity = new ActivityBuilder(mAtm) - .setTask(mTask) + final ActivityRecord activity = getActivityBuilderOnSameTask() .setScreenOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT) - .setComponent(ComponentName.createRelative(mContext, - SizeCompatTests.class.getName())) .setMinAspectRatio(1.1f) - .setUid(android.os.Process.myUid()) .build(); // The per-package override should have no effect, because the manifest aspect ratio is @@ -1743,12 +1384,8 @@ public class SizeCompatTests extends WindowTestsBase { setUpDisplaySizeWithApp(1500, 1600); // Create a size compat activity on the same task. - final ActivityRecord activity = new ActivityBuilder(mAtm) - .setTask(mTask) + final ActivityRecord activity = getActivityBuilderOnSameTask() .setScreenOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT) - .setComponent(ComponentName.createRelative(mContext, - SizeCompatTests.class.getName())) - .setUid(android.os.Process.myUid()) .build(); // The per-package override forces the activity into a 16:9 aspect ratio @@ -1767,12 +1404,8 @@ public class SizeCompatTests extends WindowTestsBase { setUpDisplaySizeWithApp(1400, 1600); // Create a size compat activity on the same task. - final ActivityRecord activity = new ActivityBuilder(mAtm) - .setTask(mTask) + final ActivityRecord activity = getActivityBuilderOnSameTask() .setScreenOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT) - .setComponent(ComponentName.createRelative(mContext, - SizeCompatTests.class.getName())) - .setUid(android.os.Process.myUid()) .build(); // The per-package override forces the activity into a 16:9 aspect ratio @@ -1792,12 +1425,7 @@ public class SizeCompatTests extends WindowTestsBase { setUpDisplaySizeWithApp(1000, 1200); // Create a size compat activity on the same task. - final ActivityRecord activity = new ActivityBuilder(mAtm) - .setTask(mTask) - .setComponent(ComponentName.createRelative(mContext, - SizeCompatTests.class.getName())) - .setUid(android.os.Process.myUid()) - .build(); + final ActivityRecord activity = getActivityBuilderOnSameTask().build(); // The per-package override should have no effect assertEquals(1200, activity.getBounds().height()); @@ -1824,12 +1452,8 @@ public class SizeCompatTests extends WindowTestsBase { setUpDisplaySizeWithApp(1000, 1200); // Create a size compat activity on the same task. - final ActivityRecord activity = new ActivityBuilder(mAtm) - .setTask(mTask) + final ActivityRecord activity = getActivityBuilderOnSameTask() .setScreenOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE) - .setComponent(ComponentName.createRelative(mContext, - SizeCompatTests.class.getName())) - .setUid(android.os.Process.myUid()) .build(); // The per-package override should have no effect @@ -1854,12 +1478,8 @@ public class SizeCompatTests extends WindowTestsBase { setUpDisplaySizeWithApp(1000, 1200); // Create a size compat activity on the same task. - final ActivityRecord activity = new ActivityBuilder(mAtm) - .setTask(mTask) + final ActivityRecord activity = getActivityBuilderOnSameTask() .setScreenOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT) - .setComponent(ComponentName.createRelative(mContext, - SizeCompatTests.class.getName())) - .setUid(android.os.Process.myUid()) .build(); // The per-package override forces the activity into a 3:2 aspect ratio @@ -1884,12 +1504,7 @@ public class SizeCompatTests extends WindowTestsBase { setUpDisplaySizeWithApp(1000, 1200); // Create a size compat activity on the same task. - final ActivityRecord activity = new ActivityBuilder(mAtm) - .setTask(mTask) - .setComponent(ComponentName.createRelative(mContext, - SizeCompatTests.class.getName())) - .setUid(android.os.Process.myUid()) - .build(); + final ActivityRecord activity = getActivityBuilderOnSameTask().build(); // The per-package override forces the activity into a 3:2 aspect ratio assertEquals(1200, activity.getBounds().height()); @@ -1908,12 +1523,8 @@ public class SizeCompatTests extends WindowTestsBase { setUpDisplaySizeWithApp(1000, 1200); // Create a size compat activity on the same task. - final ActivityRecord activity = new ActivityBuilder(mAtm) - .setTask(mTask) + final ActivityRecord activity = getActivityBuilderOnSameTask() .setScreenOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE) - .setComponent(ComponentName.createRelative(mContext, - SizeCompatTests.class.getName())) - .setUid(android.os.Process.myUid()) .build(); // The per-package override forces the activity into a 3:2 aspect ratio @@ -1931,12 +1542,8 @@ public class SizeCompatTests extends WindowTestsBase { setUpDisplaySizeWithApp(1000, 1200); // Create a size compat activity on the same task. - final ActivityRecord activity = new ActivityBuilder(mAtm) - .setTask(mTask) + final ActivityRecord activity = getActivityBuilderOnSameTask() .setScreenOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT) - .setComponent(ComponentName.createRelative(mContext, - SizeCompatTests.class.getName())) - .setUid(android.os.Process.myUid()) .build(); // The per-package override should have no effect @@ -1951,12 +1558,8 @@ public class SizeCompatTests extends WindowTestsBase { setUpDisplaySizeWithApp(/* dw= */ 1000, /* dh= */ 2800); // Create a size compat activity on the same task. - final ActivityRecord activity = new ActivityBuilder(mAtm) - .setTask(mTask) + final ActivityRecord activity = getActivityBuilderOnSameTask() .setScreenOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT) - .setComponent(ComponentName.createRelative(mContext, - SizeCompatTests.class.getName())) - .setUid(android.os.Process.myUid()) .build(); final TestSplitOrganizer organizer = @@ -2034,15 +1637,11 @@ public class SizeCompatTests extends WindowTestsBase { @Test public void testLaunchWithFixedRotationTransform() { - if (Flags.insetsDecoupledConfiguration()) { - // TODO (b/151861875): Re-enable it. This is disabled temporarily because the config - // bounds no longer contains display cutout. - return; - } final int dw = 1000; final int dh = 2500; final int notchHeight = 200; setUpApp(new TestDisplayContent.Builder(mAtm, dw, dh).setNotch(notchHeight).build()); + mActivity.mResolveConfigHint.mUseOverrideInsetsForConfig = true; // The test assumes the notch will be at left side when the orientation is landscape. if (mContext.getResources().getBoolean( com.android.internal.R.bool.config_reverseDefaultRotation)) { @@ -2652,12 +2251,7 @@ public class SizeCompatTests extends WindowTestsBase { private void testUserOverrideAspectRatio(boolean isUnresizable, int screenOrientation, float expectedAspectRatio, @PackageManager.UserMinAspectRatio int aspectRatio, boolean enabled) { - final ActivityRecord activity = new ActivityBuilder(mAtm) - .setTask(mTask) - .setComponent(ComponentName.createRelative(mContext, - SizeCompatTests.class.getName())) - .setUid(android.os.Process.myUid()) - .build(); + final ActivityRecord activity = getActivityBuilderOnSameTask().build(); activity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); spyOn(activity.mWmService.mLetterboxConfiguration); doReturn(enabled).when(activity.mWmService.mLetterboxConfiguration) @@ -2688,12 +2282,8 @@ public class SizeCompatTests extends WindowTestsBase { final int displayWidth = 1400; final int displayHeight = 1600; setUpDisplaySizeWithApp(displayWidth, displayHeight); - final ActivityRecord activity = new ActivityBuilder(mAtm) - .setTask(mTask) - .setComponent(ComponentName.createRelative(mContext, - SizeCompatTests.class.getName())) + final ActivityRecord activity = getActivityBuilderOnSameTask() .setMinAspectRatio(1.1f) - .setUid(android.os.Process.myUid()) .build(); // Setup Letterbox Configuration activity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); @@ -2713,12 +2303,8 @@ public class SizeCompatTests extends WindowTestsBase { final int displayWidth = 1600; final int displayHeight = 1400; setUpDisplaySizeWithApp(displayWidth, displayHeight); - final ActivityRecord activity = new ActivityBuilder(mAtm) - .setTask(mTask) - .setComponent(ComponentName.createRelative(mContext, - SizeCompatTests.class.getName())) + final ActivityRecord activity = getActivityBuilderOnSameTask() .setMinAspectRatio(1.1f) - .setUid(android.os.Process.myUid()) .build(); // Setup Letterbox Configuration activity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); @@ -2739,12 +2325,8 @@ public class SizeCompatTests extends WindowTestsBase { final int displayWidth = 1400; final int displayHeight = 1600; setUpDisplaySizeWithApp(displayWidth, displayHeight); - final ActivityRecord activity = new ActivityBuilder(mAtm) - .setTask(mTask) - .setComponent(ComponentName.createRelative(mContext, - SizeCompatTests.class.getName())) + final ActivityRecord activity = getActivityBuilderOnSameTask() .setMinAspectRatio(1.1f) - .setUid(android.os.Process.myUid()) .build(); // Setup Letterbox Configuration activity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); @@ -2765,12 +2347,8 @@ public class SizeCompatTests extends WindowTestsBase { final int displayWidth = 1600; final int displayHeight = 1400; setUpDisplaySizeWithApp(displayWidth, displayHeight); - final ActivityRecord activity = new ActivityBuilder(mAtm) - .setTask(mTask) - .setComponent(ComponentName.createRelative(mContext, - SizeCompatTests.class.getName())) + final ActivityRecord activity = getActivityBuilderOnSameTask() .setMinAspectRatio(1.1f) - .setUid(android.os.Process.myUid()) .build(); // Setup Letterbox Configuration activity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); @@ -2791,12 +2369,7 @@ public class SizeCompatTests extends WindowTestsBase { final int screenWidth = 1800; final int screenHeight = 1000; setUpDisplaySizeWithApp(screenWidth, screenHeight); - final ActivityRecord activity = new ActivityBuilder(mAtm) - .setTask(mTask) - .setComponent(ComponentName.createRelative(mContext, - SizeCompatTests.class.getName())) - .setUid(android.os.Process.myUid()) - .build(); + final ActivityRecord activity = getActivityBuilderOnSameTask().build(); activity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); // Simulate real display with top insets. @@ -2832,12 +2405,7 @@ public class SizeCompatTests extends WindowTestsBase { final int screenWidth = 1000; final int screenHeight = 1800; setUpDisplaySizeWithApp(screenWidth, screenHeight); - final ActivityRecord activity = new ActivityBuilder(mAtm) - .setTask(mTask) - .setComponent(ComponentName.createRelative(mContext, - SizeCompatTests.class.getName())) - .setUid(android.os.Process.myUid()) - .build(); + final ActivityRecord activity = getActivityBuilderOnSameTask().build(); activity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); // Simulate real display with top insets. @@ -2875,12 +2443,7 @@ public class SizeCompatTests extends WindowTestsBase { mActivity.mWmService.mLetterboxConfiguration.setFixedOrientationLetterboxAspectRatio(1.33f); // Create a size compat activity on the same task. - final ActivityRecord activity = new ActivityBuilder(mAtm) - .setTask(mTask) - .setComponent(ComponentName.createRelative(mContext, - SizeCompatTests.class.getName())) - .setUid(android.os.Process.myUid()) - .build(); + final ActivityRecord activity = getActivityBuilderOnSameTask().build(); // Non-resizable portrait activity prepareUnresizable(activity, 0f, ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); @@ -2910,12 +2473,7 @@ public class SizeCompatTests extends WindowTestsBase { mActivity.mWmService.mLetterboxConfiguration.setFixedOrientationLetterboxAspectRatio(1.33f); // Create a size compat activity on the same task. - final ActivityRecord activity = new ActivityBuilder(mAtm) - .setTask(mTask) - .setComponent(ComponentName.createRelative(mContext, - SizeCompatTests.class.getName())) - .setUid(android.os.Process.myUid()) - .build(); + final ActivityRecord activity = getActivityBuilderOnSameTask().build(); final TestSplitOrganizer organizer = new TestSplitOrganizer(mAtm, activity.getDisplayContent()); @@ -3973,17 +3531,13 @@ public class SizeCompatTests extends WindowTestsBase { @Test public void testLetterboxDetailsForStatusBar_letterboxNotOverlappingStatusBar() { - if (Flags.insetsDecoupledConfiguration()) { - // TODO (b/151861875): Re-enable it. This is disabled temporarily because the config - // bounds no longer contains display cutout. - return; - } // Align to center so that we don't overlap with the status bar mAtm.mWindowManager.mLetterboxConfiguration.setLetterboxVerticalPositionMultiplier(0.5f); final DisplayContent display = new TestDisplayContent.Builder(mAtm, 1000, 2800) .setNotch(100) .build(); setUpApp(display); + mActivity.mResolveConfigHint.mUseOverrideInsetsForConfig = true; TestWindowState statusBar = addStatusBar(mActivity.mDisplayContent); spyOn(statusBar); doReturn(new Rect(0, 0, statusBar.mRequestedWidth, statusBar.mRequestedHeight)) @@ -3995,12 +3549,12 @@ public class SizeCompatTests extends WindowTestsBase { // Refresh the letterbox mActivity.mRootWindowContainer.performSurfacePlacement(); - Rect mBounds = new Rect(mActivity.getWindowConfiguration().getBounds()); - assertEquals(mBounds, new Rect(0, 900, 1000, 2000)); + Rect bounds = new Rect(mActivity.getWindowConfiguration().getBounds()); + assertEquals(new Rect(0, 900, 1000, 2000), bounds); DisplayPolicy displayPolicy = mActivity.getDisplayContent().getDisplayPolicy(); LetterboxDetails[] expectedLetterboxDetails = {new LetterboxDetails( - mBounds, + bounds, mActivity.getDisplayContent().getBounds(), mActivity.findMainWindow().mAttrs.insetsFlags.appearance )}; @@ -4289,12 +3843,8 @@ public class SizeCompatTests extends WindowTestsBase { assertTrue(display.getDisplayPolicy().updateDecorInsetsInfo()); display.sendNewConfiguration(); - final ActivityRecord activity = new ActivityBuilder(mAtm) - .setTask(mTask) + final ActivityRecord activity = getActivityBuilderOnSameTask() .setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT) - .setComponent(ComponentName.createRelative(mContext, - SizeCompatTests.class.getName())) - .setUid(android.os.Process.myUid()) .build(); // Activity should not be letterboxed and should have portrait app bounds even though @@ -4326,12 +3876,8 @@ public class SizeCompatTests extends WindowTestsBase { assertTrue(display.getDisplayPolicy().updateDecorInsetsInfo()); display.sendNewConfiguration(); - final ActivityRecord activity = new ActivityBuilder(mAtm) - .setTask(mTask) + final ActivityRecord activity = getActivityBuilderOnSameTask() .setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT) - .setComponent(ComponentName.createRelative(mContext, - SizeCompatTests.class.getName())) - .setUid(android.os.Process.myUid()) .build(); final Rect bounds = activity.getBounds(); @@ -4358,14 +3904,11 @@ public class SizeCompatTests extends WindowTestsBase { assertTrue(dc.getDisplayPolicy().updateDecorInsetsInfo()); dc.sendNewConfiguration(); - final ActivityRecord activity = new ActivityBuilder(mAtm) - .setTask(mTask) - .setComponent(ComponentName.createRelative(mContext, - SizeCompatTests.class.getName())) - .setUid(android.os.Process.myUid()) + final ActivityRecord activity = getActivityBuilderOnSameTask() + .setResizeMode(RESIZE_MODE_UNRESIZEABLE) + .setScreenOrientation(SCREEN_ORIENTATION_LANDSCAPE) + .setMinAspectRatio(OVERRIDE_MIN_ASPECT_RATIO_LARGE_VALUE) .build(); - prepareMinAspectRatio(activity, OVERRIDE_MIN_ASPECT_RATIO_LARGE_VALUE, - SCREEN_ORIENTATION_LANDSCAPE); // To force config to update again but with the same landscape orientation. activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE); @@ -4379,11 +3922,6 @@ public class SizeCompatTests extends WindowTestsBase { @Test public void testApplyAspectRatio_activityAlignWithParentAppVertical() { - if (Flags.insetsDecoupledConfiguration()) { - // TODO (b/151861875): Re-enable it. This is disabled temporarily because the config - // bounds no longer contains display cutout. - return; - } // The display's app bounds will be (0, 100, 1000, 2350) final DisplayContent display = new TestDisplayContent.Builder(mAtm, 1000, 2500) .setCanRotate(false) @@ -4391,19 +3929,16 @@ public class SizeCompatTests extends WindowTestsBase { .build(); setUpApp(display); - prepareUnresizable(mActivity, 2.1f /* maxAspect */, SCREEN_ORIENTATION_UNSPECIFIED); + mActivity.mResolveConfigHint.mUseOverrideInsetsForConfig = true; + prepareUnresizable(mActivity, 2.1f, SCREEN_ORIENTATION_UNSPECIFIED); // The activity height is 2100 and the display's app bounds height is 2250, so the activity // can be aligned inside parentAppBounds - assertEquals(mActivity.getBounds(), new Rect(0, 0, 1000, 2200)); + assertEquals(new Rect(0, 0, 1000, 2200), mActivity.getBounds()); } @Test + @DisableCompatChanges({ActivityInfo.INSETS_DECOUPLED_CONFIGURATION_ENFORCED}) public void testApplyAspectRatio_activityCannotAlignWithParentAppVertical() { - if (Flags.insetsDecoupledConfiguration()) { - // TODO (b/151861875): Re-enable it. This is disabled temporarily because the config - // bounds no longer contains display cutout. - return; - } // The display's app bounds will be (0, 100, 1000, 2150) final DisplayContent display = new TestDisplayContent.Builder(mAtm, 1000, 2300) .setCanRotate(false) @@ -4411,19 +3946,21 @@ public class SizeCompatTests extends WindowTestsBase { .build(); setUpApp(display); - prepareUnresizable(mActivity, 2.1f /* maxAspect */, SCREEN_ORIENTATION_UNSPECIFIED); + + final ActivityRecord activity = getActivityBuilderOnSameTask() + .setResizeMode(RESIZE_MODE_UNRESIZEABLE) + .setMaxAspectRatio(2.1f) + .setScreenOrientation(SCREEN_ORIENTATION_UNSPECIFIED) + .build(); + // The activity height is 2100 and the display's app bounds height is 2050, so the activity // cannot be aligned inside parentAppBounds and it will fill the parentBounds of the display - assertEquals(mActivity.getBounds(), display.getBounds()); + assertEquals(activity.getBounds(), display.getBounds()); } @Test + @DisableCompatChanges({ActivityInfo.INSETS_DECOUPLED_CONFIGURATION_ENFORCED}) public void testApplyAspectRatio_activityAlignWithParentAppHorizontal() { - if (Flags.insetsDecoupledConfiguration()) { - // TODO (b/151861875): Re-enable it. This is disabled temporarily because the config - // bounds no longer contains display cutout. - return; - } // The display's app bounds will be (100, 0, 2350, 1000) final DisplayContent display = new TestDisplayContent.Builder(mAtm, 2500, 1000) .setCanRotate(false) @@ -4431,18 +3968,19 @@ public class SizeCompatTests extends WindowTestsBase { .build(); setUpApp(display); - prepareUnresizable(mActivity, 2.1f /* maxAspect */, SCREEN_ORIENTATION_UNSPECIFIED); + + final ActivityRecord activity = getActivityBuilderOnSameTask() + .setResizeMode(RESIZE_MODE_UNRESIZEABLE) + .setMaxAspectRatio(2.1f) + .setScreenOrientation(SCREEN_ORIENTATION_UNSPECIFIED) + .build(); // The activity width is 2100 and the display's app bounds width is 2250, so the activity // can be aligned inside parentAppBounds - assertEquals(mActivity.getBounds(), new Rect(175, 0, 2275, 1000)); + assertEquals(activity.getBounds(), new Rect(175, 0, 2275, 1000)); } @Test + @DisableCompatChanges({ActivityInfo.INSETS_DECOUPLED_CONFIGURATION_ENFORCED}) public void testApplyAspectRatio_activityCannotAlignWithParentAppHorizontal() { - if (Flags.insetsDecoupledConfiguration()) { - // TODO (b/151861875): Re-enable it. This is disabled temporarily because the config - // bounds no longer contains display cutout. - return; - } // The display's app bounds will be (100, 0, 2150, 1000) final DisplayContent display = new TestDisplayContent.Builder(mAtm, 2300, 1000) .setCanRotate(false) @@ -4450,10 +3988,14 @@ public class SizeCompatTests extends WindowTestsBase { .build(); setUpApp(display); - prepareUnresizable(mActivity, 2.1f /* maxAspect */, SCREEN_ORIENTATION_UNSPECIFIED); + final ActivityRecord activity = getActivityBuilderOnSameTask() + .setResizeMode(RESIZE_MODE_UNRESIZEABLE) + .setMaxAspectRatio(2.1f) + .setScreenOrientation(SCREEN_ORIENTATION_UNSPECIFIED) + .build(); // The activity width is 2100 and the display's app bounds width is 2050, so the activity // cannot be aligned inside parentAppBounds and it will fill the parentBounds of the display - assertEquals(mActivity.getBounds(), display.getBounds()); + assertEquals(activity.getBounds(), display.getBounds()); } @Test @@ -4681,26 +4223,23 @@ public class SizeCompatTests extends WindowTestsBase { @Test public void testUpdateResolvedBoundsPosition_alignToTop() { - if (Flags.insetsDecoupledConfiguration()) { - // TODO (b/151861875): Re-enable it. This is disabled temporarily because the config - // bounds no longer contains display cutout. - return; - } final int notchHeight = 100; final DisplayContent display = new TestDisplayContent.Builder(mAtm, 1000, 2800) .setNotch(notchHeight) .build(); setUpApp(display); + mActivity.mResolveConfigHint.mUseOverrideInsetsForConfig = true; // Prepare unresizable activity with max aspect ratio - prepareUnresizable(mActivity, /* maxAspect */ 1.1f, SCREEN_ORIENTATION_UNSPECIFIED); + prepareUnresizable(mActivity, 1.1f, SCREEN_ORIENTATION_UNSPECIFIED); - Rect mBounds = new Rect(mActivity.getWindowConfiguration().getBounds()); + Rect bounds = new Rect(mActivity.getWindowConfiguration().getBounds()); Rect appBounds = new Rect(mActivity.getWindowConfiguration().getAppBounds()); // The insets should be cut for aspect ratio and then added back because the appBounds // are aligned to the top of the parentAppBounds - assertEquals(mBounds, new Rect(0, 0, 1000, 1200)); - assertEquals(appBounds, new Rect(0, notchHeight, 1000, 1200)); + assertEquals(new Rect(0, notchHeight, 1000, 1200), appBounds); + assertEquals(new Rect(0, 0, 1000, 1200), bounds); + } private void assertVerticalPositionForDifferentDisplayConfigsForLandscapeActivity( @@ -5196,15 +4735,19 @@ public class SizeCompatTests extends WindowTestsBase { */ private ActivityRecord buildActivityRecord(boolean supportsSizeChanges, int resizeMode, @ScreenOrientation int screenOrientation) { - return new ActivityBuilder(mAtm) - .setTask(mTask) + return getActivityBuilderOnSameTask() .setResizeMode(resizeMode) .setSupportsSizeChanges(supportsSizeChanges) .setScreenOrientation(screenOrientation) + .build(); + } + + private ActivityBuilder getActivityBuilderOnSameTask() { + return new ActivityBuilder(mAtm) + .setTask(mTask) .setComponent(ComponentName.createRelative(mContext, SizeCompatTests.class.getName())) - .setUid(android.os.Process.myUid()) - .build(); + .setUid(android.os.Process.myUid()); } static void prepareMinAspectRatio(ActivityRecord activity, float minAspect, @@ -5268,21 +4811,29 @@ public class SizeCompatTests extends WindowTestsBase { } } - /** Asserts that the size of activity is larger than its parent so it is scaling. */ private void assertScaled() { - assertTrue(mActivity.inSizeCompatMode()); - assertNotEquals(1f, mActivity.getCompatScale(), 0.0001f /* delta */); + assertScaled(mActivity); + } + + /** Asserts that the size of activity is larger than its parent so it is scaling. */ + private void assertScaled(ActivityRecord activity) { + assertTrue(activity.inSizeCompatMode()); + assertNotEquals(1f, activity.getCompatScale(), 0.0001f /* delta */); } - /** Asserts that the activity is best fitted in the parent. */ private void assertFitted() { - final boolean inSizeCompatMode = mActivity.inSizeCompatMode(); + assertFitted(mActivity); + } + + /** Asserts that the activity is best fitted in the parent. */ + private void assertFitted(ActivityRecord activity) { + final boolean inSizeCompatMode = activity.inSizeCompatMode(); final String failedConfigInfo = inSizeCompatMode - ? ("ParentConfig=" + mActivity.getParent().getConfiguration() - + " ActivityConfig=" + mActivity.getConfiguration()) + ? ("ParentConfig=" + activity.getParent().getConfiguration() + + " ActivityConfig=" + activity.getConfiguration()) : ""; assertFalse(failedConfigInfo, inSizeCompatMode); - assertFalse(mActivity.hasSizeCompatBounds()); + assertFalse(activity.hasSizeCompatBounds()); } /** Asserts the activity max bounds inherit from the TaskDisplayArea. */ diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java index d9fd312423d1..9670a9a44eb7 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java @@ -1902,7 +1902,7 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase { assertApplyTransactionAllowed(mTransaction); - verify(task).moveOrCreateDecorSurfaceFor(tf); + verify(task).moveOrCreateDecorSurfaceFor(tf, true /* visible */); } @Test diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java index d57a7e61ad63..f94e5e3c38ed 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java @@ -56,6 +56,7 @@ import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.mock; +import android.app.ActivityOptions; import android.content.pm.SigningDetails; import android.content.res.Configuration; import android.graphics.Color; @@ -291,6 +292,30 @@ public class TaskFragmentTest extends WindowTestsBase { } @Test + public void testFindTopNonFinishingActivity_ignoresLaunchedFromBubbleActivities() { + final ActivityOptions opts = ActivityOptions.makeBasic(); + opts.setTaskAlwaysOnTop(true); + opts.setLaunchedFromBubble(true); + ActivityRecord activity = new ActivityBuilder(mAtm) + .setUid(DEFAULT_TASK_FRAGMENT_ORGANIZER_UID).setActivityOptions(opts).build(); + mTaskFragment.addChild(activity); + + assertNull(mTaskFragment.getTopNonFinishingActivity(true, false)); + } + + @Test + public void testFindTopNonFinishingActivity_includesLaunchedFromBubbleActivities() { + final ActivityOptions opts = ActivityOptions.makeBasic(); + opts.setTaskAlwaysOnTop(true); + opts.setLaunchedFromBubble(true); + ActivityRecord activity = new ActivityBuilder(mAtm) + .setUid(DEFAULT_TASK_FRAGMENT_ORGANIZER_UID).setActivityOptions(opts).build(); + mTaskFragment.addChild(activity); + + assertEquals(mTaskFragment.getTopNonFinishingActivity(true, true), activity); + } + + @Test public void testMoveTaskToFront_supportsEnterPipOnTaskSwitchForAdjacentTaskFragment() { final Task bottomTask = createTask(mDisplayContent); final ActivityRecord bottomActivity = createActivityRecord(bottomTask); diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java index 225e85e03b26..6ecaea90b85b 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java @@ -1695,7 +1695,7 @@ public class TaskTests extends WindowTestsBase { // Decor surface should be created. clearInvocations(task); - task.moveOrCreateDecorSurfaceFor(fragment); + task.moveOrCreateDecorSurfaceFor(fragment, true /* visible */); assertNotNull(task.mDecorSurfaceContainer); assertNotNull(task.getDecorSurface()); @@ -1722,14 +1722,14 @@ public class TaskTests extends WindowTestsBase { final TaskFragment fragment2 = createTaskFragmentWithEmbeddedActivity(task, organizer); doNothing().when(task).sendTaskFragmentParentInfoChangedIfNeeded(); - task.moveOrCreateDecorSurfaceFor(fragment1); + task.moveOrCreateDecorSurfaceFor(fragment1, true /* visible */); assertNotNull(task.mDecorSurfaceContainer); assertNotNull(task.getDecorSurface()); assertEquals(fragment1, task.mDecorSurfaceContainer.mOwnerTaskFragment); // Transfer ownership - task.moveOrCreateDecorSurfaceFor(fragment2); + task.moveOrCreateDecorSurfaceFor(fragment2, true /* visible */); assertNotNull(task.mDecorSurfaceContainer); assertNotNull(task.getDecorSurface()); @@ -1775,7 +1775,7 @@ public class TaskTests extends WindowTestsBase { doReturn(true).when(fragment2).isAllowedToBeEmbeddedInTrustedMode(); doReturn(true).when(fragment1).isVisible(); - task.moveOrCreateDecorSurfaceFor(fragment1); + task.moveOrCreateDecorSurfaceFor(fragment1, true /* visible */); task.assignChildLayers(t); verify(unembeddedActivity).assignLayer(t, 0); @@ -1880,7 +1880,7 @@ public class TaskTests extends WindowTestsBase { doReturn(false).when(fragment2).isAllowedToBeEmbeddedInTrustedMode(); doReturn(true).when(fragment1).isVisible(); - task.moveOrCreateDecorSurfaceFor(fragment1); + task.moveOrCreateDecorSurfaceFor(fragment1, true /* visible */); clearInvocations(t); clearInvocations(unembeddedActivity); @@ -1889,7 +1889,8 @@ public class TaskTests extends WindowTestsBase { // The decor surface should be placed above all the windows when boosted and the cover // surface should show. - task.setDecorSurfaceBoosted(fragment1, true /* isBoosted */, clientTransaction); + task.requestDecorSurfaceBoosted(fragment1, true /* isBoosted */, clientTransaction); + task.commitDecorSurfaceBoostedState(); verify(unembeddedActivity).assignLayer(t, 0); verify(fragment1).assignLayer(t, 1); @@ -1906,8 +1907,9 @@ public class TaskTests extends WindowTestsBase { // The decor surface should be placed just above the owner TaskFragment and the cover // surface should hide. - task.moveOrCreateDecorSurfaceFor(fragment1); - task.setDecorSurfaceBoosted(fragment1, false /* isBoosted */, clientTransaction); + task.moveOrCreateDecorSurfaceFor(fragment1, true /* visible */); + task.requestDecorSurfaceBoosted(fragment1, false /* isBoosted */, clientTransaction); + task.commitDecorSurfaceBoostedState(); verify(unembeddedActivity).assignLayer(t, 0); verify(fragment1).assignLayer(t, 1); diff --git a/services/tests/wmtests/src/com/android/server/wm/TransparentPolicyTest.java b/services/tests/wmtests/src/com/android/server/wm/TransparentPolicyTest.java new file mode 100644 index 000000000000..1d6e30777922 --- /dev/null +++ b/services/tests/wmtests/src/com/android/server/wm/TransparentPolicyTest.java @@ -0,0 +1,637 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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.wm; + +import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; +import static android.app.WindowConfiguration.ROTATION_UNDEFINED; +import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; +import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW; +import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE; +import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE; +import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE; +import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT; +import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; +import static android.view.Surface.ROTATION_0; +import static android.view.Surface.ROTATION_90; + +import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; + +import static org.junit.Assert.assertEquals; +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.verify; +import static org.mockito.Mockito.when; + +import android.app.WindowConfiguration; +import android.content.res.Configuration; +import android.graphics.Rect; +import android.platform.test.annotations.Presubmit; + +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.function.Predicate; + +/** + * Test class for {@link TransparentPolicy}. + * + * Build/Install/Run: + * atest WmTests:TransparentPolicyTest + */ +@Presubmit +@RunWith(WindowTestRunner.class) +public class TransparentPolicyTest extends WindowTestsBase { + + @Test + public void testNotStartingWhenDisabled() { + runTestScenario((robot) -> { + robot.launchTransparentActivityInTask(); + + robot.checkTopActivityPolicyStateIsNotRunning(); + }, /* policyEnabled */ false); + } + + @Test + public void testNotStartingWithoutTask() { + runTestScenario((robot) -> { + robot.launchTransparentActivity(); + + robot.checkTopActivityPolicyStartNotInvoked(); + robot.checkTopActivityPolicyStateIsNotRunning(); + }); + } + + @Test + public void testPolicyRunningWhenTransparentIsUsed() { + runTestScenario((robot) -> { + robot.launchTransparentActivityInTask(); + + robot.checkTopActivityPolicyStartNotInvoked(); + robot.checkTopActivityPolicyStateIsRunning(); + }); + } + + @Test + public void testCleanLetterboxConfigListenerWhenTranslucentIsDestroyed() { + runTestScenario((robot) -> { + robot.launchTransparentActivityInTask(); + robot.checkTopActivityPolicyStartNotInvoked(); + robot.checkTopActivityPolicyStateIsRunning(); + + robot.clearInteractions(); + robot.destroyTopActivity(); + + robot.checkTopActivityPolicyStopInvoked(); + robot.checkTopActivityPolicyStateIsNotRunning(); + }); + } + + @Test + public void testApplyStrategyAgainWhenOpaqueIsDestroyed() { + runTestScenario((robot) -> { + robot.launchOpaqueActivityInTask(); + robot.checkTopActivityPolicyStateIsNotRunning(); + + robot.launchTransparentActivityInTask(); + robot.checkTopActivityPolicyStateIsRunning(); + + robot.destroyActivity(/* fromTop */ 1); + robot.checkTopActivityPolicyStartInvoked(); + }); + } + + @Test + public void testResetOpaqueReferenceWhenOpaqueIsDestroyed() { + runTestScenario((robot) -> { + robot.launchTransparentActivityInTask(); + + robot.clearInteractions(); + robot.destroyActivity(/* fromTop */ 1); + + robot.checkTopActivityPolicyStartInvoked(); + robot.checkTopActivityPolicyStateIsNotRunning(); + }); + } + + @Test + public void testNotApplyStrategyAgainWhenOpaqueIsNotDestroyed() { + runTestScenario((robot) -> { + robot.launchOpaqueActivityInTask(); + robot.checkTopActivityPolicyStateIsNotRunning(); + + robot.launchTransparentActivityInTask(); + robot.checkTopActivityPolicyStateIsRunning(); + + robot.clearInteractions(); + robot.checkTopActivityPolicyStopNotInvoked(); + }); + } + + @Test + public void testApplyStrategyToTranslucentActivities() { + runTestScenario((robot) -> { + robot.configureTopActivity(/* minAspect */ 1.2f, /* maxAspect */ 1.5f, + SCREEN_ORIENTATION_PORTRAIT, /* isUnresizable */ true); + robot.configureTopActivityIgnoreOrientationRequest(true); + robot.launchActivity(/* minAspect */ 1.1f, /* maxAspect */ 3f, + SCREEN_ORIENTATION_LANDSCAPE, /* transparent */true, /* addToTask */true); + robot.checkTopActivityPolicyStateIsRunning(); + robot.checkTopActivityHasInheritedBoundsFrom(/* fromTop */ 1); + robot.checkTopOrientation(SCREEN_ORIENTATION_PORTRAIT); + robot.checkTopAspectRatios(/* minAspectRatio */ 1.2f, /* maxAspectRatio */ 1.5f); + }); + } + + @Test + public void testApplyStrategyToTransparentActivitiesRetainsWindowConfigurationProperties() { + runTestScenario((robot) -> { + robot.launchTransparentActivity(); + + robot.forceChangeInTopActivityConfiguration(); + robot.attachTopActivityToTask(); + + robot.checkTopActivityConfigurationConfiguration(); + }); + } + + @Test + public void testApplyStrategyToMultipleTranslucentActivities() { + runTestScenario((robot) -> { + robot.launchTransparentActivityInTask(); + robot.checkTopActivityPolicyStateIsRunning(); + robot.checkTopActivityHasInheritedBoundsFrom(/* fromTop */ 1); + + robot.launchTransparentActivityInTask(); + robot.checkTopActivityPolicyStateIsRunning(); + robot.checkTopActivityHasInheritedBoundsFrom(/* fromTop */ 2); + }); + } + + @Test + public void testNotApplyStrategyToTranslucentActivitiesOverEmbeddedActivities() { + runTestScenario((robot) -> { + robot.configureTopActivityAsEmbedded(); + robot.launchTransparentActivityInTask(); + + robot.checkTopActivityPolicyStartNotInvoked(); + robot.checkTopActivityPolicyStateIsNotRunning(); + }); + } + + @Test + public void testTranslucentActivitiesDontGoInSizeCompatMode() { + runTestScenario((robot) -> { + robot.configureTopActivityIgnoreOrientationRequest(true); + robot.configureUnresizableTopActivity(SCREEN_ORIENTATION_PORTRAIT); + robot.rotateDisplayForTopActivity(ROTATION_90); + robot.checkTopActivitySizeCompatMode(/* inScm */ true); + robot.rotateDisplayForTopActivity(ROTATION_0); + robot.checkTopActivitySizeCompatMode(/* inScm */ false); + + robot.launchTransparentActivityInTask(); + robot.checkTopActivitySizeCompatMode(/* inScm */ false); + robot.rotateDisplayForTopActivity(ROTATION_90); + robot.checkTopActivitySizeCompatMode(/* inScm */ false); + }, /* displayWidth */ 2800, /* displayHeight */ 1400); + } + + @Test + public void testCheckOpaqueIsLetterboxedWhenStrategyIsApplied() { + runTestScenario((robot) -> { + robot.configureUnresizableTopActivity(SCREEN_ORIENTATION_PORTRAIT); + robot.configureTopActivityIgnoreOrientationRequest(true); + robot.launchTransparentActivity(); + + robot.assertFalseOnTopActivity(ActivityRecord::fillsParent); + robot.assertTrueOnActivity(/* fromTop */ 1, ActivityRecord::fillsParent); + robot.applyTo(/* fromTop */ 1, (activity) -> { + activity.finishing = true; + }); + robot.assertFalseOnActivity(/* fromTop */ 1, ActivityRecord::occludesParent); + robot.attachTopActivityToTask(); + + robot.checkTopActivityPolicyStateIsNotRunning(); + }); + } + + @Test + public void testTranslucentActivitiesWhenUnfolding() { + runTestScenario((robot) -> { + robot.applyToTop((activity) -> { + activity.mWmService.mLetterboxConfiguration + .setLetterboxHorizontalPositionMultiplier(1.0f); + }); + robot.configureUnresizableTopActivity(SCREEN_ORIENTATION_PORTRAIT); + robot.configureTopActivityIgnoreOrientationRequest(true); + robot.launchTransparentActivityInTask(); + robot.checkTopActivityHasInheritedBoundsFrom(/* fromTop */ 1); + + robot.configureTaskWindowingMode(WINDOWING_MODE_FULLSCREEN); + + robot.configureTopActivityFoldablePosture(/* isHalfFolded */ true, + /* isTabletop */ false); + robot.checkTopActivityRecomputedConfiguration(); + robot.checkTopActivityHasInheritedBoundsFrom(/* fromTop */ 1); + robot.clearInteractions(); + + robot.configureTopActivityFoldablePosture(/* isHalfFolded */ false, + /* isTabletop */ false); + robot.checkTopActivityRecomputedConfiguration(); + robot.checkTopActivityHasInheritedBoundsFrom(/* fromTop */ 1); + }, /* displayWidth */ 2800, /* displayHeight */ 1400); + } + + + @Test + public void testTranslucentActivity_clearSizeCompatMode_inheritedCompatDisplayInsetsCleared() { + runTestScenario((robot) -> { + robot.configureTopActivityIgnoreOrientationRequest(true); + robot.configureUnresizableTopActivity(SCREEN_ORIENTATION_PORTRAIT); + // Rotate to put activity in size compat mode. + robot.rotateDisplayForTopActivity(ROTATION_90); + robot.checkTopActivitySizeCompatMode(/* inScm */ true); + + robot.launchTransparentActivityInTask(); + robot.assertNotNullOnTopActivity(ActivityRecord::getCompatDisplayInsets); + robot.applyToTop(ActivityRecord::clearSizeCompatMode); + robot.assertNullOnTopActivity(ActivityRecord::getCompatDisplayInsets); + }); + } + + private void runTestScenario(Consumer<TransparentPolicyRobotTest> consumer, + boolean policyEnabled, int displayWidth, int displayHeight) { + spyOn(mWm.mLetterboxConfiguration); + when(mWm.mLetterboxConfiguration.isTranslucentLetterboxingEnabled()) + .thenReturn(policyEnabled); + final TestDisplayContent.Builder builder = new TestDisplayContent.Builder(mAtm, + displayWidth, displayHeight); + final Task task = new TaskBuilder(mSupervisor).setDisplay(builder.build()) + .setCreateActivity(true).build(); + final ActivityRecord opaqueActivity = task.getTopNonFinishingActivity(); + final TransparentPolicyRobotTest robot = new TransparentPolicyRobotTest(mAtm, task, + opaqueActivity); + consumer.accept(robot); + } + + private void runTestScenario(Consumer<TransparentPolicyRobotTest> consumer, + int displayWidth, int displayHeight) { + runTestScenario(consumer, /* policyEnabled */ true, displayWidth, displayHeight); + } + + private void runTestScenario(Consumer<TransparentPolicyRobotTest> consumer, + boolean policyEnabled) { + runTestScenario(consumer, policyEnabled, /* displayWidth */ 2000, + /* displayHeight */ 1000); + } + + private void runTestScenario(Consumer<TransparentPolicyRobotTest> consumer) { + runTestScenario(consumer, /* policyEnabled */ true); + } + + /** + * Robot pattern implementation for TransparentPolicy + * TODO(b/344587983): Extract Robot to be reused in different test classes. + */ + private static class TransparentPolicyRobotTest { + + private final ActivityTaskManagerService mAtm; + + private final Task mTask; + + private final ActivityStackTest mActivityStack; + + private WindowConfiguration mTopActivityWindowConfiguration; + + private TransparentPolicyRobotTest(ActivityTaskManagerService atm, Task task, + ActivityRecord opaqueActivity) { + mAtm = atm; + mTask = task; + mActivityStack = new ActivityStackTest(); + mActivityStack.pushActivity(opaqueActivity); + spyOn(opaqueActivity.mTransparentPolicy); + } + + void configureTopActivityAsEmbedded() { + final ActivityRecord topActivity = mActivityStack.top(); + spyOn(topActivity); + doReturn(true).when(topActivity).isEmbedded(); + } + + private void launchActivity(float minAspectRatio, float maxAspectRatio, + @Configuration.Orientation int orientation, boolean transparent, + boolean addToTask) { + final ActivityBuilder activityBuilder = new ActivityBuilder(mAtm) + .setScreenOrientation(orientation) + .setLaunchedFromUid(mActivityStack.base().getUid()); + if (transparent) { + activityBuilder.setActivityTheme(android.R.style.Theme_Translucent); + } + if (minAspectRatio >= 0) { + activityBuilder.setMinAspectRatio(minAspectRatio); + } + if (maxAspectRatio >= 0) { + activityBuilder.setMaxAspectRatio(maxAspectRatio); + } + final ActivityRecord newActivity = activityBuilder.build(); + if (addToTask) { + mTask.addChild(newActivity); + } + spyOn(newActivity.mTransparentPolicy); + mActivityStack.pushActivity(newActivity); + } + + void attachTopActivityToTask() { + mTask.addChild(mActivityStack.top()); + } + + void launchTransparentActivity() { + launchActivity(/*minAspectRatio */ -1, /* maxAspectRatio */ -1, + SCREEN_ORIENTATION_PORTRAIT, /* transparent */ true, + /* addToTask */ false); + } + + void launchTransparentActivityInTask() { + launchActivity(/*minAspectRatio */ -1, /* maxAspectRatio */ -1, + SCREEN_ORIENTATION_PORTRAIT, /* transparent */ true, + /* addToTask */true); + } + + void launchOpaqueActivityInTask() { + launchActivity(/*minAspectRatio */ -1, /* maxAspectRatio */ -1, + SCREEN_ORIENTATION_PORTRAIT, /* transparent */ false, + /* addToTask */true); + } + + void destroyTopActivity() { + mActivityStack.top().removeImmediately(); + } + + void destroyActivity(int fromTop) { + mActivityStack.applyTo(/* fromTop */ fromTop, ActivityRecord::removeImmediately); + } + + void forceChangeInTopActivityConfiguration() { + mActivityStack.applyToTop((activity) -> { + final Configuration requestedConfig = activity.getRequestedOverrideConfiguration(); + mTopActivityWindowConfiguration = requestedConfig.windowConfiguration; + mTopActivityWindowConfiguration.setActivityType(ACTIVITY_TYPE_STANDARD); + mTopActivityWindowConfiguration.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW); + mTopActivityWindowConfiguration.setAlwaysOnTop(true); + activity.onRequestedOverrideConfigurationChanged(requestedConfig); + }); + } + + void checkTopActivityPolicyStateIsRunning() { + assertTrue(mActivityStack.top().mTransparentPolicy.isRunning()); + } + + void checkTopActivityPolicyStateIsNotRunning() { + assertFalse(mActivityStack.top().mTransparentPolicy.isRunning()); + } + + void checkTopActivityPolicyStopInvoked() { + verify(mActivityStack.top().mTransparentPolicy).stop(); + } + + void checkTopActivityPolicyStopNotInvoked() { + mActivityStack.applyToTop((activity) -> { + verify(activity.mTransparentPolicy, never()).stop(); + }); + } + + void checkTopActivityPolicyStartInvoked() { + mActivityStack.applyToTop((activity) -> { + verify(activity.mTransparentPolicy).start(); + }); + } + + void checkTopActivityPolicyStartNotInvoked() { + verify(mActivityStack.top().mTransparentPolicy, never()).start(); + } + + void assertTrueOnActivity(int fromTop, Predicate<ActivityRecord> predicate) { + mActivityStack.applyTo(fromTop, (activity) -> { + Assert.assertTrue(predicate.test(activity)); + }); + } + + void assertFalseOnTopActivity(Predicate<ActivityRecord> predicate) { + Assert.assertFalse(predicate.test(mActivityStack.top())); + } + + void assertFalseOnActivity(int fromTop, Predicate<ActivityRecord> predicate) { + mActivityStack.applyTo(fromTop, (activity) -> { + Assert.assertFalse(predicate.test(activity)); + }); + } + + void assertNotNullOnTopActivity(Function<ActivityRecord, Object> getter) { + Assert.assertNotNull(getter.apply(mActivityStack.top())); + } + + void assertNullOnTopActivity(Function<ActivityRecord, Object> getter) { + Assert.assertNull(getter.apply(mActivityStack.top())); + } + + void checkTopActivityConfigurationConfiguration() { + mActivityStack.applyToTop((activity) -> { + // The original override of WindowConfiguration should keep. + assertEquals(ACTIVITY_TYPE_STANDARD, activity.getActivityType()); + assertEquals(WINDOWING_MODE_MULTI_WINDOW, + mTopActivityWindowConfiguration.getWindowingMode()); + assertTrue(mTopActivityWindowConfiguration.isAlwaysOnTop()); + // Unless display is going to be rotated, it should always inherit from parent. + assertEquals(ROTATION_UNDEFINED, + mTopActivityWindowConfiguration.getDisplayRotation()); + }); + } + + void checkTopActivityHasInheritedBoundsFrom(int fromTop) { + final ActivityRecord topActivity = mActivityStack.top(); + final ActivityRecord otherActivity = mActivityStack.getFromTop(/* fromTop */ fromTop); + final Rect opaqueBounds = otherActivity.getConfiguration().windowConfiguration + .getBounds(); + final Rect translucentRequestedBounds = topActivity.getRequestedOverrideBounds(); + Assert.assertEquals(opaqueBounds, translucentRequestedBounds); + } + + void checkTopActivityRecomputedConfiguration() { + verify(mActivityStack.top()).recomputeConfiguration(); + } + + void checkTopOrientation(int orientation) { + Assert.assertEquals(orientation, mActivityStack.top() + .getRequestedConfigurationOrientation()); + } + + void configureTaskWindowingMode(int windowingMode) { + mTask.setWindowingMode(windowingMode); + } + + void checkTopAspectRatios(float minAspectRatio, float maxAspectRatio) { + final ActivityRecord topActivity = mActivityStack.top(); + Assert.assertEquals(minAspectRatio, topActivity.getMinAspectRatio(), 0.0001); + Assert.assertEquals(maxAspectRatio, topActivity.getMaxAspectRatio(), 0.0001); + } + + void checkTopActivitySizeCompatMode(boolean inScm) { + Assert.assertEquals(inScm, mActivityStack.top().inSizeCompatMode()); + } + + void clearInteractions() { + mActivityStack.applyToAll((activity) -> { + clearInvocations(activity); + clearInvocations(activity.mTransparentPolicy); + }); + } + + void configureTopActivity(float minAspect, float maxAspect, int screenOrientation, + boolean isUnresizable) { + prepareLimitedBounds(mActivityStack.top(), minAspect, maxAspect, screenOrientation, + isUnresizable); + } + + void configureTopActivityIgnoreOrientationRequest(boolean ignoreOrientationRequest) { + mActivityStack.top().mDisplayContent + .setIgnoreOrientationRequest(ignoreOrientationRequest); + } + + void configureUnresizableTopActivity(int screenOrientation) { + configureTopActivity(-1, -1, screenOrientation, true); + } + + void applyToTop(Consumer<ActivityRecord> consumer) { + consumer.accept(mActivityStack.top()); + } + + void applyTo(int fromTop, Consumer<ActivityRecord> consumer) { + mActivityStack.applyTo(fromTop, consumer); + } + + void rotateDisplayForTopActivity(int rotation) { + rotateDisplay(mActivityStack.top().mDisplayContent, rotation); + } + + /** + * Setups activity with restriction on its bounds, such as maxAspect, minAspect, + * fixed orientation, and/or whether it is resizable. + */ + void prepareLimitedBounds(ActivityRecord activity, float minAspect, float maxAspect, + int screenOrientation, boolean isUnresizable) { + activity.info.resizeMode = isUnresizable + ? RESIZE_MODE_UNRESIZEABLE + : RESIZE_MODE_RESIZEABLE; + final Task task = activity.getTask(); + if (task != null) { + // Update the Task resize value as activity will follow the task. + task.mResizeMode = activity.info.resizeMode; + task.getRootActivity().info.resizeMode = activity.info.resizeMode; + } + activity.setVisibleRequested(true); + if (maxAspect >= 0) { + activity.info.setMaxAspectRatio(maxAspect); + } + if (minAspect >= 0) { + activity.info.setMinAspectRatio(minAspect); + } + if (screenOrientation != SCREEN_ORIENTATION_UNSPECIFIED) { + activity.info.screenOrientation = screenOrientation; + activity.setRequestedOrientation(screenOrientation); + } + // Make sure to use the provided configuration to construct the size compat fields. + activity.clearSizeCompatMode(); + activity.ensureActivityConfiguration(); + // Make sure the display configuration reflects the change of activity. + if (activity.mDisplayContent.updateOrientation()) { + activity.mDisplayContent.sendNewConfiguration(); + } + } + + void configureTopActivityFoldablePosture(boolean isHalfFolded, boolean isTabletop) { + mActivityStack.applyToTop((activity) -> { + final DisplayRotation r = activity.mDisplayContent.getDisplayRotation(); + doReturn(isHalfFolded).when(r).isDisplaySeparatingHinge(); + doReturn(false).when(r) + .isDeviceInPosture(any(DeviceStateController.DeviceState.class), + anyBoolean()); + if (isHalfFolded) { + doReturn(true).when(r) + .isDeviceInPosture(DeviceStateController.DeviceState.HALF_FOLDED, + isTabletop); + } + activity.recomputeConfiguration(); + }); + } + + private static void rotateDisplay(DisplayContent display, int rotation) { + final Configuration c = new Configuration(); + display.getDisplayRotation().setRotation(rotation); + display.computeScreenConfiguration(c); + display.onRequestedOverrideConfigurationChanged(c); + } + + /** + * Contains all the ActivityRecord launched in the test. This is different from what's in + * the Task because activities are added here even if not added to tasks. + */ + private static class ActivityStackTest { + private final List<ActivityRecord> mActivities = new ArrayList<>(); + + void pushActivity(ActivityRecord activityRecord) { + mActivities.add(activityRecord); + } + + void applyToTop(Consumer<ActivityRecord> consumer) { + consumer.accept(top()); + } + + ActivityRecord getFromTop(int fromTop) { + return mActivities.get(mActivities.size() - fromTop - 1); + } + + ActivityRecord base() { + return mActivities.get(0); + } + + ActivityRecord top() { + return mActivities.get(mActivities.size() - 1); + } + + // Allows access to the activity at position beforeLast from the top. + // If fromTop = 0 the activity used is the top one. + void applyTo(int fromTop, Consumer<ActivityRecord> consumer) { + consumer.accept(getFromTop(fromTop)); + } + + void applyToAll(Consumer<ActivityRecord> consumer) { + for (int i = mActivities.size() - 1; i >= 0; i--) { + consumer.accept(mActivities.get(i)); + } + } + } + } +} diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java index 9f85acb98817..4ebbf496b2a4 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java @@ -960,10 +960,20 @@ public class WindowContainerTests extends WindowTestsBase { assertTrue(child.handlesOrientationChangeFromDescendant(orientation)); } + private static void addLocalInsets(WindowContainer wc) { + final Binder owner = new Binder(); + Rect genericOverlayInsetsRect1 = new Rect(0, 200, 1080, 700); + final InsetsFrameProvider provider1 = + new InsetsFrameProvider(owner, 1, WindowInsets.Type.systemOverlays()) + .setArbitraryRectangle(genericOverlayInsetsRect1); + wc.addLocalInsetsFrameProvider(provider1, owner); + } + @Test public void testOnDisplayChanged() { final Task rootTask = createTask(mDisplayContent); final Task task = createTaskInRootTask(rootTask, 0 /* userId */); + addLocalInsets(task); final ActivityRecord activity = createActivityRecord(mDisplayContent, task); final DisplayContent newDc = createNewDisplay(); @@ -972,6 +982,7 @@ public class WindowContainerTests extends WindowTestsBase { verify(rootTask).onDisplayChanged(newDc); verify(task).onDisplayChanged(newDc); + assertTrue(task.mLocalInsetsSources.size() == 1); verify(activity).onDisplayChanged(newDc); assertEquals(newDc, rootTask.mDisplayContent); assertEquals(newDc, task.mDisplayContent); @@ -981,6 +992,7 @@ public class WindowContainerTests extends WindowTestsBase { @Test public void testOnDisplayChanged_cleanupChanging() { final Task task = createTask(mDisplayContent); + addLocalInsets(task); spyOn(task.mSurfaceFreezer); mDisplayContent.mChangingContainers.add(task); @@ -988,6 +1000,7 @@ public class WindowContainerTests extends WindowTestsBase { // This happens on display info changed. task.onDisplayChanged(mDisplayContent); + assertTrue(task.mLocalInsetsSources.size() == 1); assertTrue(mDisplayContent.mChangingContainers.contains(task)); verify(task.mSurfaceFreezer, never()).unfreeze(any()); diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java index fb854c5bda68..ab4decacf0ca 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java @@ -31,6 +31,7 @@ import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; import static android.content.res.Configuration.SCREEN_HEIGHT_DP_UNDEFINED; import static android.content.res.Configuration.SCREEN_WIDTH_DP_UNDEFINED; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION; +import static android.window.DisplayAreaOrganizer.FEATURE_VENDOR_FIRST; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; @@ -1455,6 +1456,53 @@ public class WindowOrganizerTests extends WindowTestsBase { } @Test + public void testReorderWithParents() { + /* + root + ____|______ + | | + firstTda secondTda + | | + firstRootTask secondRootTask + + */ + final TaskDisplayArea firstTaskDisplayArea = mDisplayContent.getDefaultTaskDisplayArea(); + final TaskDisplayArea secondTaskDisplayArea = createTaskDisplayArea( + mDisplayContent, mRootWindowContainer.mWmService, "TestTaskDisplayArea", + FEATURE_VENDOR_FIRST); + final Task firstRootTask = firstTaskDisplayArea.createRootTask( + WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, false /* onTop */); + final Task secondRootTask = secondTaskDisplayArea.createRootTask( + WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, false /* onTop */); + final ActivityRecord firstActivity = new ActivityBuilder(mAtm) + .setTask(firstRootTask).build(); + final ActivityRecord secondActivity = new ActivityBuilder(mAtm) + .setTask(secondRootTask).build(); + // This assertion is just a defense to ensure that firstRootTask is not the top most + // by default + assertThat(mDisplayContent.getTopRootTask()).isEqualTo(secondRootTask); + WindowContainerTransaction wct = new WindowContainerTransaction(); + + // Reorder to top + wct.reorder(firstRootTask.mRemoteToken.toWindowContainerToken(), true /* onTop */, + true /* includingParents */); + mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct); + + // firstRootTask can only be on the top if its TDA was also reordered to the Top which + // in-turn ensures that the reorder happened including the parents. + assertThat(mDisplayContent.getTopRootTask()).isEqualTo(firstRootTask); + + // Reorder to bottom + wct.reorder(firstRootTask.mRemoteToken.toWindowContainerToken(), false /* onTop */, + true /* includingParents */); + mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct); + + // firstRootTask can only be on the bottom if its TDA was also reordered to the bottom + // which in-turn ensures that the reorder happened including the parents. + assertThat(mDisplayContent.getBottomMostTask()).isEqualTo(firstRootTask); + } + + @Test public void testAppearDeferThenVanish() { final ITaskOrganizer organizer = registerMockOrganizer(); final Task rootTask = createRootTask(); diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java index e1357a9c0953..b512aa852ddb 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java @@ -1016,6 +1016,10 @@ class WindowTestsBase extends SystemServiceTestsBase { public void topFocusedWindowChanged(ComponentName component, int requestedVisibleTypes) { } + + @Override + public void setImeInputTargetRequestedVisibility(boolean visible) { + } }; } diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java index 2e93cba80386..27c383c283a1 100644 --- a/services/usage/java/com/android/server/usage/UsageStatsService.java +++ b/services/usage/java/com/android/server/usage/UsageStatsService.java @@ -403,7 +403,9 @@ public class UsageStatsService extends SystemService implements mAppStandby.addListener(mStandbyChangeListener); - mPackageMonitor.register(getContext(), null, UserHandle.ALL, true); + mPackageMonitor.register(getContext(), + /* thread= */ USE_DEDICATED_HANDLER_THREAD ? mHandler.getLooper() : null, + UserHandle.ALL, true); IntentFilter filter = new IntentFilter(Intent.ACTION_USER_REMOVED); filter.addAction(Intent.ACTION_USER_STARTED); diff --git a/services/usb/java/com/android/server/usb/UsbDeviceManager.java b/services/usb/java/com/android/server/usb/UsbDeviceManager.java index e8bac66a724a..175a09db54e3 100644 --- a/services/usb/java/com/android/server/usb/UsbDeviceManager.java +++ b/services/usb/java/com/android/server/usb/UsbDeviceManager.java @@ -80,6 +80,7 @@ import android.service.usb.UsbDeviceManagerProto; import android.service.usb.UsbHandlerProto; import android.util.Pair; import android.util.Slog; +import android.text.TextUtils; import com.android.internal.annotations.GuardedBy; import com.android.internal.logging.MetricsLogger; @@ -110,6 +111,8 @@ import java.util.NoSuchElementException; import java.util.Scanner; import java.util.Set; import java.util.concurrent.atomic.AtomicInteger; +import java.util.regex.Matcher; +import java.util.regex.Pattern; /** * UsbDeviceManager manages USB state in device mode. @@ -135,6 +138,11 @@ public class UsbDeviceManager implements ActivityTaskManagerInternal.ScreenObser */ private static final String NORMAL_BOOT = "normal"; + /** + * UDC controller for the ConfigFS USB Gadgets. + */ + private static final String USB_CONTROLLER_NAME_PROPERTY = "sys.usb.controller"; + private static final String USB_STATE_MATCH = "DEVPATH=/devices/virtual/android_usb/android0"; private static final String ACCESSORY_START_MATCH = @@ -932,6 +940,42 @@ public class UsbDeviceManager implements ActivityTaskManagerInternal.ScreenObser sEventLogger.enqueue(new EventLogger.StringEvent("USB intent: " + intent)); } + private void getMidiCardDevice() throws FileNotFoundException { + String controllerName = getSystemProperty(USB_CONTROLLER_NAME_PROPERTY, ""); + if (TextUtils.isEmpty(controllerName)) { + throw new FileNotFoundException("controller name not found"); + } + + File soundDir = new File("/sys/class/udc/" + controllerName + "/gadget/sound"); + if (!soundDir.exists()) { + throw new FileNotFoundException("sound device not found"); + } + + // There should be exactly one sound card + File[] cardDirs = FileUtils.listFilesOrEmpty(soundDir, + (dir, file) -> file.startsWith("card")); + if (cardDirs.length != 1) { + throw new FileNotFoundException("sound card not match"); + } + + // There should be exactly one midi device + File[] midis = FileUtils.listFilesOrEmpty(cardDirs[0], + (dir, file) -> file.startsWith("midi")); + if (midis.length != 1) { + throw new FileNotFoundException("MIDI device not match"); + } + + Pattern pattern = Pattern.compile("midiC(\\d+)D(\\d+)"); + Matcher matcher = pattern.matcher(midis[0].getName()); + if (matcher.matches()) { + mMidiCard = Integer.parseInt(matcher.group(1)); + mMidiDevice = Integer.parseInt(matcher.group(2)); + Slog.i(TAG, "Found MIDI card " + mMidiCard + " device " + mMidiDevice); + } else { + throw new FileNotFoundException("MIDI name not match"); + } + } + private void updateUsbFunctions() { updateMidiFunction(); updateMtpFunction(); @@ -941,17 +985,26 @@ public class UsbDeviceManager implements ActivityTaskManagerInternal.ScreenObser boolean enabled = (mCurrentFunctions & UsbManager.FUNCTION_MIDI) != 0; if (enabled != mMidiEnabled) { if (enabled) { - Scanner scanner = null; - try { - scanner = new Scanner(new File(MIDI_ALSA_PATH)); - mMidiCard = scanner.nextInt(); - mMidiDevice = scanner.nextInt(); - } catch (FileNotFoundException e) { - Slog.e(TAG, "could not open MIDI file", e); - enabled = false; - } finally { - if (scanner != null) { - scanner.close(); + if (android.hardware.usb.flags.Flags.enableUsbSysfsMidiIdentification()) { + try { + getMidiCardDevice(); + } catch (FileNotFoundException e) { + Slog.e(TAG, "could not identify MIDI device", e); + enabled = false; + } + } else { + Scanner scanner = null; + try { + scanner = new Scanner(new File(MIDI_ALSA_PATH)); + mMidiCard = scanner.nextInt(); + mMidiDevice = scanner.nextInt(); + } catch (FileNotFoundException e) { + Slog.e(TAG, "could not open MIDI file", e); + enabled = false; + } finally { + if (scanner != null) { + scanner.close(); + } } } } diff --git a/telecomm/java/android/telecom/ConnectionService.java b/telecomm/java/android/telecom/ConnectionService.java index 01448c3bcd9e..01448c3bcd9e 100755..100644 --- a/telecomm/java/android/telecom/ConnectionService.java +++ b/telecomm/java/android/telecom/ConnectionService.java diff --git a/telecomm/java/android/telecom/InCallAdapter.java b/telecomm/java/android/telecom/InCallAdapter.java index 77701457484a..77701457484a 100755..100644 --- a/telecomm/java/android/telecom/InCallAdapter.java +++ b/telecomm/java/android/telecom/InCallAdapter.java diff --git a/telecomm/java/com/android/internal/telecom/IInCallAdapter.aidl b/telecomm/java/com/android/internal/telecom/IInCallAdapter.aidl index e381ce8c080f..e381ce8c080f 100755..100644 --- a/telecomm/java/com/android/internal/telecom/IInCallAdapter.aidl +++ b/telecomm/java/com/android/internal/telecom/IInCallAdapter.aidl diff --git a/telephony/common/com/google/android/mms/package.html b/telephony/common/com/google/android/mms/package.html index c9f96a66ab3b..c9f96a66ab3b 100755..100644 --- a/telephony/common/com/google/android/mms/package.html +++ b/telephony/common/com/google/android/mms/package.html diff --git a/telephony/common/com/google/android/mms/pdu/PduParser.java b/telephony/common/com/google/android/mms/pdu/PduParser.java index 62eac7ac942a..62eac7ac942a 100755..100644 --- a/telephony/common/com/google/android/mms/pdu/PduParser.java +++ b/telephony/common/com/google/android/mms/pdu/PduParser.java diff --git a/telephony/common/com/google/android/mms/pdu/PduPersister.java b/telephony/common/com/google/android/mms/pdu/PduPersister.java index b61ad3680c6c..b61ad3680c6c 100755..100644 --- a/telephony/common/com/google/android/mms/pdu/PduPersister.java +++ b/telephony/common/com/google/android/mms/pdu/PduPersister.java diff --git a/telephony/common/com/google/android/mms/pdu/package.html b/telephony/common/com/google/android/mms/pdu/package.html index c9f96a66ab3b..c9f96a66ab3b 100755..100644 --- a/telephony/common/com/google/android/mms/pdu/package.html +++ b/telephony/common/com/google/android/mms/pdu/package.html diff --git a/telephony/common/com/google/android/mms/util/package.html b/telephony/common/com/google/android/mms/util/package.html index c9f96a66ab3b..c9f96a66ab3b 100755..100644 --- a/telephony/common/com/google/android/mms/util/package.html +++ b/telephony/common/com/google/android/mms/util/package.html diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java index bc8f65edaa12..09cb464198b5 100644 --- a/telephony/java/android/telephony/CarrierConfigManager.java +++ b/telephony/java/android/telephony/CarrierConfigManager.java @@ -9842,6 +9842,43 @@ public class CarrierConfigManager { public static final String KEY_REMOVE_SATELLITE_PLMN_IN_MANUAL_NETWORK_SCAN_BOOL = "remove_satellite_plmn_in_manual_network_scan_bool"; + + /** @hide */ + @IntDef({ + SATELLITE_DATA_SUPPORT_ONLY_RESTRICTED, + SATELLITE_DATA_SUPPORT_BANDWIDTH_CONSTRAINED, + SATELLITE_DATA_SUPPORT_ALL, + }) + public @interface SATELLITE_DATA_SUPPORT_MODE {} + + /** + * Doesn't support unrestricted traffic on satellite network. + * @hide + */ + public static final int SATELLITE_DATA_SUPPORT_ONLY_RESTRICTED = 0; + /** + * Support unrestricted but bandwidth_constrained traffic on satellite network. + * @hide + */ + public static final int SATELLITE_DATA_SUPPORT_BANDWIDTH_CONSTRAINED = 1; + /** + * Support unrestricted satellite network that serves all traffic. + * @hide + */ + public static final int SATELLITE_DATA_SUPPORT_ALL = 2; + /** + * Indicates what kind of traffic an {@link NetworkCapabilities#NET_CAPABILITY_NOT_RESTRICTED} + * satellite network can possibly support. The network may subject to further + * restrictions such as entitlement etc. + * If no data is allowed on satellite network, exclude + * {@link ApnSetting#INFRASTRUCTURE_SATELLITE} from APN infrastructure_bitmask, and this + * configuration is ignored. + * By default it only supports restricted data. + * @hide + */ + public static final String KEY_SATELLITE_DATA_SUPPORT_MODE_INT = + "satellite_data_support_mode_int"; + /** * Determine whether to override roaming Wi-Fi Calling preference when device is connected to * non-terrestrial network. @@ -11084,6 +11121,8 @@ public class CarrierConfigManager { sDefaults.putInt(KEY_PARAMETERS_USED_FOR_NTN_LTE_SIGNAL_BAR_INT, CellSignalStrengthLte.USE_RSRP); sDefaults.putBoolean(KEY_REMOVE_SATELLITE_PLMN_IN_MANUAL_NETWORK_SCAN_BOOL, true); + sDefaults.putInt(KEY_SATELLITE_DATA_SUPPORT_MODE_INT, + CarrierConfigManager.SATELLITE_DATA_SUPPORT_ONLY_RESTRICTED); sDefaults.putBoolean(KEY_OVERRIDE_WFC_ROAMING_MODE_WHILE_USING_NTN_BOOL, true); sDefaults.putInt(KEY_SATELLITE_ENTITLEMENT_STATUS_REFRESH_DAYS_INT, 7); sDefaults.putBoolean(KEY_SATELLITE_ENTITLEMENT_SUPPORTED_BOOL, false); diff --git a/telephony/java/android/telephony/SubscriptionInfo.aidl b/telephony/java/android/telephony/SubscriptionInfo.aidl index 1e13732bb465..1e13732bb465 100755..100644 --- a/telephony/java/android/telephony/SubscriptionInfo.aidl +++ b/telephony/java/android/telephony/SubscriptionInfo.aidl diff --git a/telephony/java/android/telephony/TelephonyFrameworkInitializer.java b/telephony/java/android/telephony/TelephonyFrameworkInitializer.java index f5688bfffa02..7356cdc8889b 100644 --- a/telephony/java/android/telephony/TelephonyFrameworkInitializer.java +++ b/telephony/java/android/telephony/TelephonyFrameworkInitializer.java @@ -83,7 +83,7 @@ public class TelephonyFrameworkInitializer { // Check SDK version of the vendor partition. final int vendorApiLevel = SystemProperties.getInt( "ro.vendor.api_level", Build.VERSION.DEVICE_INITIAL_SDK_INT); - if (vendorApiLevel < Build.VERSION_CODES.VANILLA_ICE_CREAM) return true; + if (vendorApiLevel < Build.VENDOR_API_2024_Q2) return true; // Check SDK version of the client app. if (!Compatibility.isChangeEnabled(ENABLE_CHECKING_TELEPHONY_FEATURES)) return true; diff --git a/telephony/java/android/telephony/euicc/EuiccRulesAuthTable.java b/telephony/java/android/telephony/euicc/EuiccRulesAuthTable.java index c35242d0bad8..5f9a524cd78e 100644 --- a/telephony/java/android/telephony/euicc/EuiccRulesAuthTable.java +++ b/telephony/java/android/telephony/euicc/EuiccRulesAuthTable.java @@ -242,6 +242,16 @@ public final class EuiccRulesAuthTable implements Parcelable { && Arrays.equals(mPolicyRuleFlags, that.mPolicyRuleFlags); } + @Override + public int hashCode() { + int result = Arrays.hashCode(mPolicyRules); + result = 31 * result + Arrays.hashCode(mPolicyRuleFlags); + for (int i = 0; i < mCarrierIds.length; i++) { + result = 31 * result + Arrays.hashCode(mCarrierIds[i]); + } + return result; + } + private EuiccRulesAuthTable(Parcel source) { mPolicyRules = source.createIntArray(); int len = mPolicyRules.length; diff --git a/telephony/java/android/telephony/ims/ImsCallSession.java b/telephony/java/android/telephony/ims/ImsCallSession.java index d30078d9cd97..d30078d9cd97 100755..100644 --- a/telephony/java/android/telephony/ims/ImsCallSession.java +++ b/telephony/java/android/telephony/ims/ImsCallSession.java diff --git a/telephony/java/android/telephony/ims/ImsRegistrationAttributes.java b/telephony/java/android/telephony/ims/ImsRegistrationAttributes.java index f548dbada3cb..c7af66167a09 100644 --- a/telephony/java/android/telephony/ims/ImsRegistrationAttributes.java +++ b/telephony/java/android/telephony/ims/ImsRegistrationAttributes.java @@ -256,6 +256,26 @@ public final class ImsRegistrationAttributes implements Parcelable { } /** + * Get the attribute flag ATTR_REGISTRATION_TYPE_EMERGENCY. + * @return {@code true} if the ATTR_REGISTRATION_TYPE_EMERGENCY attribute has been set, or + * {@code false} if it has not been set. + */ + @FlaggedApi(Flags.FLAG_EMERGENCY_REGISTRATION_STATE) + public boolean getFlagRegistrationTypeEmergency() { + return (mImsAttributeFlags & ATTR_REGISTRATION_TYPE_EMERGENCY) != 0; + } + + /** + * Get the attribute flag ATTR_VIRTUAL_FOR_ANONYMOUS_EMERGENCY_CALL. + * @return {@code true} if the ATTR_VIRTUAL_FOR_ANONYMOUS_EMERGENCY_CALL attribute has been set, + * or {@code false} if it has not been set. + */ + @FlaggedApi(Flags.FLAG_EMERGENCY_REGISTRATION_STATE) + public boolean getFlagVirtualRegistrationForEmergencyCall() { + return (mImsAttributeFlags & ATTR_VIRTUAL_FOR_ANONYMOUS_EMERGENCY_CALL) != 0; + } + + /** * Gets the Set of feature tags associated with the current IMS registration, if the IMS * service supports supplying this information. * <p> diff --git a/telephony/java/android/telephony/ims/compat/stub/ImsCallSessionImplBase.java b/telephony/java/android/telephony/ims/compat/stub/ImsCallSessionImplBase.java index 798e8019502f..798e8019502f 100755..100644 --- a/telephony/java/android/telephony/ims/compat/stub/ImsCallSessionImplBase.java +++ b/telephony/java/android/telephony/ims/compat/stub/ImsCallSessionImplBase.java diff --git a/telephony/java/android/telephony/mbms/DownloadRequest.aidl b/telephony/java/android/telephony/mbms/DownloadRequest.aidl index ece577d1a739..ece577d1a739 100755..100644 --- a/telephony/java/android/telephony/mbms/DownloadRequest.aidl +++ b/telephony/java/android/telephony/mbms/DownloadRequest.aidl diff --git a/telephony/java/android/telephony/mbms/FileInfo.aidl b/telephony/java/android/telephony/mbms/FileInfo.aidl index 62926e199a7a..62926e199a7a 100755..100644 --- a/telephony/java/android/telephony/mbms/FileInfo.aidl +++ b/telephony/java/android/telephony/mbms/FileInfo.aidl diff --git a/telephony/java/android/telephony/mbms/FileServiceInfo.aidl b/telephony/java/android/telephony/mbms/FileServiceInfo.aidl index 4646bad8f5db..4646bad8f5db 100755..100644 --- a/telephony/java/android/telephony/mbms/FileServiceInfo.aidl +++ b/telephony/java/android/telephony/mbms/FileServiceInfo.aidl diff --git a/telephony/java/android/telephony/mbms/IDownloadProgressListener.aidl b/telephony/java/android/telephony/mbms/IDownloadProgressListener.aidl index d0adcb5f4862..d0adcb5f4862 100755..100644 --- a/telephony/java/android/telephony/mbms/IDownloadProgressListener.aidl +++ b/telephony/java/android/telephony/mbms/IDownloadProgressListener.aidl diff --git a/telephony/java/android/telephony/mbms/IDownloadStatusListener.aidl b/telephony/java/android/telephony/mbms/IDownloadStatusListener.aidl index 799290afc9b7..799290afc9b7 100755..100644 --- a/telephony/java/android/telephony/mbms/IDownloadStatusListener.aidl +++ b/telephony/java/android/telephony/mbms/IDownloadStatusListener.aidl diff --git a/telephony/java/android/telephony/mbms/IGroupCallCallback.aidl b/telephony/java/android/telephony/mbms/IGroupCallCallback.aidl index 844b6344a34c..844b6344a34c 100755..100644 --- a/telephony/java/android/telephony/mbms/IGroupCallCallback.aidl +++ b/telephony/java/android/telephony/mbms/IGroupCallCallback.aidl diff --git a/telephony/java/android/telephony/mbms/IMbmsDownloadSessionCallback.aidl b/telephony/java/android/telephony/mbms/IMbmsDownloadSessionCallback.aidl index 0d813a7ceea0..0d813a7ceea0 100755..100644 --- a/telephony/java/android/telephony/mbms/IMbmsDownloadSessionCallback.aidl +++ b/telephony/java/android/telephony/mbms/IMbmsDownloadSessionCallback.aidl diff --git a/telephony/java/android/telephony/mbms/IMbmsGroupCallSessionCallback.aidl b/telephony/java/android/telephony/mbms/IMbmsGroupCallSessionCallback.aidl index 43273a41fbd5..43273a41fbd5 100755..100644 --- a/telephony/java/android/telephony/mbms/IMbmsGroupCallSessionCallback.aidl +++ b/telephony/java/android/telephony/mbms/IMbmsGroupCallSessionCallback.aidl diff --git a/telephony/java/android/telephony/mbms/IMbmsStreamingSessionCallback.aidl b/telephony/java/android/telephony/mbms/IMbmsStreamingSessionCallback.aidl index 0bf0ebc484ea..0bf0ebc484ea 100755..100644 --- a/telephony/java/android/telephony/mbms/IMbmsStreamingSessionCallback.aidl +++ b/telephony/java/android/telephony/mbms/IMbmsStreamingSessionCallback.aidl diff --git a/telephony/java/android/telephony/mbms/IStreamingServiceCallback.aidl b/telephony/java/android/telephony/mbms/IStreamingServiceCallback.aidl index 164cefb2d5ef..164cefb2d5ef 100755..100644 --- a/telephony/java/android/telephony/mbms/IStreamingServiceCallback.aidl +++ b/telephony/java/android/telephony/mbms/IStreamingServiceCallback.aidl diff --git a/telephony/java/android/telephony/mbms/ServiceInfo.aidl b/telephony/java/android/telephony/mbms/ServiceInfo.aidl index 6661c26d5a79..6661c26d5a79 100755..100644 --- a/telephony/java/android/telephony/mbms/ServiceInfo.aidl +++ b/telephony/java/android/telephony/mbms/ServiceInfo.aidl diff --git a/telephony/java/android/telephony/mbms/StreamingServiceInfo.aidl b/telephony/java/android/telephony/mbms/StreamingServiceInfo.aidl index b902f271a96c..b902f271a96c 100755..100644 --- a/telephony/java/android/telephony/mbms/StreamingServiceInfo.aidl +++ b/telephony/java/android/telephony/mbms/StreamingServiceInfo.aidl diff --git a/telephony/java/android/telephony/mbms/UriPathPair.aidl b/telephony/java/android/telephony/mbms/UriPathPair.aidl index 8bf768246da9..8bf768246da9 100755..100644 --- a/telephony/java/android/telephony/mbms/UriPathPair.aidl +++ b/telephony/java/android/telephony/mbms/UriPathPair.aidl diff --git a/telephony/java/android/telephony/mbms/vendor/IMbmsDownloadService.aidl b/telephony/java/android/telephony/mbms/vendor/IMbmsDownloadService.aidl index 04efd53eb743..04efd53eb743 100755..100644 --- a/telephony/java/android/telephony/mbms/vendor/IMbmsDownloadService.aidl +++ b/telephony/java/android/telephony/mbms/vendor/IMbmsDownloadService.aidl diff --git a/telephony/java/android/telephony/mbms/vendor/IMbmsGroupCallService.aidl b/telephony/java/android/telephony/mbms/vendor/IMbmsGroupCallService.aidl index 6e139ac5c462..6e139ac5c462 100755..100644 --- a/telephony/java/android/telephony/mbms/vendor/IMbmsGroupCallService.aidl +++ b/telephony/java/android/telephony/mbms/vendor/IMbmsGroupCallService.aidl diff --git a/telephony/java/android/telephony/mbms/vendor/IMbmsStreamingService.aidl b/telephony/java/android/telephony/mbms/vendor/IMbmsStreamingService.aidl index a4abfac74177..a4abfac74177 100755..100644 --- a/telephony/java/android/telephony/mbms/vendor/IMbmsStreamingService.aidl +++ b/telephony/java/android/telephony/mbms/vendor/IMbmsStreamingService.aidl diff --git a/telephony/java/android/telephony/satellite/SatelliteManager.java b/telephony/java/android/telephony/satellite/SatelliteManager.java index 47f53f372d33..2a359cd56d1b 100644 --- a/telephony/java/android/telephony/satellite/SatelliteManager.java +++ b/telephony/java/android/telephony/satellite/SatelliteManager.java @@ -193,6 +193,14 @@ public final class SatelliteManager { /** * Bundle key to get the response from + * {@link #requestSessionStats(Executor, OutcomeReceiver)}. + * @hide + */ + + public static final String KEY_SESSION_STATS = "session_stats"; + + /** + * Bundle key to get the response from * {@link #requestIsProvisioned(Executor, OutcomeReceiver)}. * @hide */ @@ -2493,6 +2501,65 @@ public final class SatelliteManager { } } + /** + * Request to get the {@link SatelliteSessionStats} of the satellite service. + * + * @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 the {@link SatelliteSessionStats} of the satellite service. + * 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(allOf = {Manifest.permission.PACKAGE_USAGE_STATS, + Manifest.permission.MODIFY_PHONE_STATE}) + @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG) + public void requestSessionStats(@NonNull @CallbackExecutor Executor executor, + @NonNull OutcomeReceiver<SatelliteSessionStats, 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_SESSION_STATS)) { + SatelliteSessionStats stats = + resultData.getParcelable(KEY_SESSION_STATS, + SatelliteSessionStats.class); + executor.execute(() -> Binder.withCleanCallingIdentity(() -> + callback.onResult(stats))); + } else { + loge("KEY_SESSION_STATS 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.requestSatelliteSessionStats(mSubId, receiver); + } else { + loge("requestSessionStats() invalid telephony"); + executor.execute(() -> Binder.withCleanCallingIdentity(() -> callback.onError( + new SatelliteException(SATELLITE_RESULT_ILLEGAL_STATE)))); + } + } catch (RemoteException ex) { + loge("requestSessionStats() RemoteException: " + ex); + executor.execute(() -> Binder.withCleanCallingIdentity(() -> callback.onError( + new SatelliteException(SATELLITE_RESULT_ILLEGAL_STATE)))); + } + } + @Nullable private static ITelephony getITelephony() { ITelephony binder = ITelephony.Stub.asInterface(TelephonyFrameworkInitializer diff --git a/telephony/java/android/telephony/satellite/SatelliteSessionStats.aidl b/telephony/java/android/telephony/satellite/SatelliteSessionStats.aidl new file mode 100644 index 000000000000..417512504969 --- /dev/null +++ b/telephony/java/android/telephony/satellite/SatelliteSessionStats.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 SatelliteSessionStats;
\ No newline at end of file diff --git a/telephony/java/android/telephony/satellite/SatelliteSessionStats.java b/telephony/java/android/telephony/satellite/SatelliteSessionStats.java new file mode 100644 index 000000000000..aabb058691d8 --- /dev/null +++ b/telephony/java/android/telephony/satellite/SatelliteSessionStats.java @@ -0,0 +1,224 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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.NonNull; +import android.os.Parcel; +import android.os.Parcelable; + +import java.util.Objects; + +/** + * SatelliteSessionStats is used to represent the usage stats of the satellite service. + * @hide + */ +public class SatelliteSessionStats implements Parcelable { + private int mCountOfSuccessfulUserMessages; + private int mCountOfUnsuccessfulUserMessages; + private int mCountOfTimedOutUserMessagesWaitingForConnection; + private int mCountOfTimedOutUserMessagesWaitingForAck; + private int mCountOfUserMessagesInQueueToBeSent; + + /** + * SatelliteSessionStats constructor + * @param builder Builder to create SatelliteSessionStats object/ + */ + public SatelliteSessionStats(@NonNull Builder builder) { + mCountOfSuccessfulUserMessages = builder.mCountOfSuccessfulUserMessages; + mCountOfUnsuccessfulUserMessages = builder.mCountOfUnsuccessfulUserMessages; + mCountOfTimedOutUserMessagesWaitingForConnection = + builder.mCountOfTimedOutUserMessagesWaitingForConnection; + mCountOfTimedOutUserMessagesWaitingForAck = + builder.mCountOfTimedOutUserMessagesWaitingForAck; + mCountOfUserMessagesInQueueToBeSent = builder.mCountOfUserMessagesInQueueToBeSent; + } + + private SatelliteSessionStats(Parcel in) { + readFromParcel(in); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(@NonNull Parcel out, int flags) { + out.writeInt(mCountOfSuccessfulUserMessages); + out.writeInt(mCountOfUnsuccessfulUserMessages); + out.writeInt(mCountOfTimedOutUserMessagesWaitingForConnection); + out.writeInt(mCountOfTimedOutUserMessagesWaitingForAck); + out.writeInt(mCountOfUserMessagesInQueueToBeSent); + } + + @NonNull + public static final Creator<SatelliteSessionStats> CREATOR = new Parcelable.Creator<>() { + + @Override + public SatelliteSessionStats createFromParcel(Parcel in) { + return new SatelliteSessionStats(in); + } + + @Override + public SatelliteSessionStats[] newArray(int size) { + return new SatelliteSessionStats[size]; + } + }; + + @Override + @NonNull + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("countOfSuccessfulUserMessages:"); + sb.append(mCountOfSuccessfulUserMessages); + sb.append(","); + + sb.append("countOfUnsuccessfulUserMessages:"); + sb.append(mCountOfUnsuccessfulUserMessages); + sb.append(","); + + sb.append("countOfTimedOutUserMessagesWaitingForConnection:"); + sb.append(mCountOfTimedOutUserMessagesWaitingForConnection); + sb.append(","); + + sb.append("countOfTimedOutUserMessagesWaitingForAck:"); + sb.append(mCountOfTimedOutUserMessagesWaitingForAck); + sb.append(","); + + sb.append("countOfUserMessagesInQueueToBeSent:"); + sb.append(mCountOfUserMessagesInQueueToBeSent); + return sb.toString(); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + SatelliteSessionStats that = (SatelliteSessionStats) o; + return mCountOfSuccessfulUserMessages == that.mCountOfSuccessfulUserMessages + && mCountOfUnsuccessfulUserMessages == that.mCountOfUnsuccessfulUserMessages + && mCountOfTimedOutUserMessagesWaitingForConnection + == that.mCountOfTimedOutUserMessagesWaitingForConnection + && mCountOfTimedOutUserMessagesWaitingForAck + == that.mCountOfTimedOutUserMessagesWaitingForAck + && mCountOfUserMessagesInQueueToBeSent + == that.mCountOfUserMessagesInQueueToBeSent; + } + + @Override + public int hashCode() { + return Objects.hash(mCountOfSuccessfulUserMessages, mCountOfUnsuccessfulUserMessages, + mCountOfTimedOutUserMessagesWaitingForConnection, + mCountOfTimedOutUserMessagesWaitingForAck, + mCountOfUserMessagesInQueueToBeSent); + } + + public int getCountOfSuccessfulUserMessages() { + return mCountOfSuccessfulUserMessages; + } + + public int getCountOfUnsuccessfulUserMessages() { + return mCountOfUnsuccessfulUserMessages; + } + + public int getCountOfTimedOutUserMessagesWaitingForConnection() { + return mCountOfTimedOutUserMessagesWaitingForConnection; + } + + public int getCountOfTimedOutUserMessagesWaitingForAck() { + return mCountOfTimedOutUserMessagesWaitingForAck; + } + + public int getCountOfUserMessagesInQueueToBeSent() { + return mCountOfUserMessagesInQueueToBeSent; + } + + private void readFromParcel(Parcel in) { + mCountOfSuccessfulUserMessages = in.readInt(); + mCountOfUnsuccessfulUserMessages = in.readInt(); + mCountOfTimedOutUserMessagesWaitingForConnection = in.readInt(); + mCountOfTimedOutUserMessagesWaitingForAck = in.readInt(); + mCountOfUserMessagesInQueueToBeSent = in.readInt(); + } + + /** + * A builder class to create {@link SatelliteSessionStats} data object. + */ + public static final class Builder { + private int mCountOfSuccessfulUserMessages; + private int mCountOfUnsuccessfulUserMessages; + private int mCountOfTimedOutUserMessagesWaitingForConnection; + private int mCountOfTimedOutUserMessagesWaitingForAck; + private int mCountOfUserMessagesInQueueToBeSent; + + /** + * Sets countOfSuccessfulUserMessages value of {@link SatelliteSessionStats} + * and then returns the Builder class. + */ + @NonNull + public Builder setCountOfSuccessfulUserMessages(int count) { + mCountOfSuccessfulUserMessages = count; + return this; + } + + /** + * Sets countOfUnsuccessfulUserMessages value of {@link SatelliteSessionStats} + * and then returns the Builder class. + */ + @NonNull + public Builder setCountOfUnsuccessfulUserMessages(int count) { + mCountOfUnsuccessfulUserMessages = count; + return this; + } + + /** + * Sets countOfTimedOutUserMessagesWaitingForConnection value of + * {@link SatelliteSessionStats} and then returns the Builder class. + */ + @NonNull + public Builder setCountOfTimedOutUserMessagesWaitingForConnection(int count) { + mCountOfTimedOutUserMessagesWaitingForConnection = count; + return this; + } + + /** + * Sets countOfTimedOutUserMessagesWaitingForAck value of {@link SatelliteSessionStats} + * and then returns the Builder class. + */ + @NonNull + public Builder setCountOfTimedOutUserMessagesWaitingForAck(int count) { + mCountOfTimedOutUserMessagesWaitingForAck = count; + return this; + } + + /** + * Sets countOfUserMessagesInQueueToBeSent value of {@link SatelliteSessionStats} + * and then returns the Builder class. + */ + @NonNull + public Builder setCountOfUserMessagesInQueueToBeSent(int count) { + mCountOfUserMessagesInQueueToBeSent = count; + return this; + } + + /** Returns SatelliteSessionStats object. */ + @NonNull + public SatelliteSessionStats build() { + return new SatelliteSessionStats(this); + } + } +} diff --git a/telephony/java/android/telephony/satellite/stub/ISatelliteListener.aidl b/telephony/java/android/telephony/satellite/stub/ISatelliteListener.aidl index 5b9dfc67c68c..b4eb15fde632 100644 --- a/telephony/java/android/telephony/satellite/stub/ISatelliteListener.aidl +++ b/telephony/java/android/telephony/satellite/stub/ISatelliteListener.aidl @@ -81,4 +81,12 @@ oneway interface ISatelliteListener { * @param supported True means satellite service is supported and false means it is not. */ void onSatelliteSupportedStateChanged(in boolean supported); + + /** + * Indicates that the satellite registration failed with following failure code + * + * @param causeCode the primary failure cause code of the procedure. + * For LTE (EMM), cause codes are TS 24.301 Sec 9.9.3.9 + */ + void onRegistrationFailure(in int causeCode); } diff --git a/telephony/java/com/android/internal/telephony/IOns.aidl b/telephony/java/com/android/internal/telephony/IOns.aidl index 76b6951f213d..76b6951f213d 100755..100644 --- a/telephony/java/com/android/internal/telephony/IOns.aidl +++ b/telephony/java/com/android/internal/telephony/IOns.aidl diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl index 7d845a3c086c..9b01b71fc596 100644 --- a/telephony/java/com/android/internal/telephony/ITelephony.aidl +++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl @@ -3381,4 +3381,23 @@ interface ITelephony { * @return {@code true} if the boolean config is set successfully, {@code false} otherwise. */ boolean setDatagramControllerBooleanConfig(boolean reset, int booleanType, boolean enable); + + /** + * This API can be used by only CTS to set the cache whether satellite communication is allowed. + * + * @param state a state indicates whether satellite access allowed state should be cached and + * the allowed state. + * @return {@code true} if the setting is successful, {@code false} otherwise. + */ + boolean setIsSatelliteCommunicationAllowedForCurrentLocationCache(in String state); + + /** + * Request to get the session stats of the satellite service. + * + * @param subId The subId of the subscription to get the session stats for. + * @param receiver Result receiver to get the error code of the request and the requested + * session stats of the satellite service. + * @hide + */ + void requestSatelliteSessionStats(int subId, in ResultReceiver receiver); } diff --git a/telephony/java/com/android/internal/telephony/PhoneConstants.java b/telephony/java/com/android/internal/telephony/PhoneConstants.java index a9ebd5c2d7cd..2158f3dbc08f 100644 --- a/telephony/java/com/android/internal/telephony/PhoneConstants.java +++ b/telephony/java/com/android/internal/telephony/PhoneConstants.java @@ -252,4 +252,10 @@ public class PhoneConstants { /** The key to specify the emergency service category */ public static final String EXTRA_EMERGENCY_SERVICE_CATEGORY = "emergency_service_category"; + + /** The key to specify the alternate emergency URNs */ + public static final String EXTRA_EMERGENCY_URNS = "emergency_urns"; + + /** The key to specify whether or not to use emergency routing */ + public static final String EXTRA_USE_EMERGENCY_ROUTING = "use_emergency_routing"; } diff --git a/test-mock/api/system-current.txt b/test-mock/api/system-current.txt index f35095743738..7d891c8150fd 100644 --- a/test-mock/api/system-current.txt +++ b/test-mock/api/system-current.txt @@ -28,7 +28,7 @@ package android.test.mock { method @Deprecated public void removeOnPermissionsChangeListener(android.content.pm.PackageManager.OnPermissionsChangedListener); method @Deprecated public void revokeRuntimePermission(String, String, android.os.UserHandle); method @Deprecated public boolean setDefaultBrowserPackageNameAsUser(String, int); - method public String[] setPackagesSuspended(String[], boolean, android.os.PersistableBundle, android.os.PersistableBundle, String); + method @Deprecated public String[] setPackagesSuspended(String[], boolean, android.os.PersistableBundle, android.os.PersistableBundle, String); method @Deprecated public void setUpdateAvailable(String, boolean); method @Deprecated public boolean updateIntentVerificationStatusAsUser(String, int, int); method @Deprecated public void updatePermissionFlags(String, String, int, int, android.os.UserHandle); diff --git a/tests/AccessoryDisplay/sink/res/drawable-hdpi/ic_app.png b/tests/AccessoryDisplay/sink/res/drawable-hdpi/ic_app.png Binary files differindex 66a198496cfb..66a198496cfb 100755..100644 --- a/tests/AccessoryDisplay/sink/res/drawable-hdpi/ic_app.png +++ b/tests/AccessoryDisplay/sink/res/drawable-hdpi/ic_app.png diff --git a/tests/AccessoryDisplay/source/res/drawable-hdpi/ic_app.png b/tests/AccessoryDisplay/source/res/drawable-hdpi/ic_app.png Binary files differindex 66a198496cfb..66a198496cfb 100755..100644 --- a/tests/AccessoryDisplay/source/res/drawable-hdpi/ic_app.png +++ b/tests/AccessoryDisplay/source/res/drawable-hdpi/ic_app.png diff --git a/tests/AttestationVerificationTest/AndroidManifest.xml b/tests/AttestationVerificationTest/AndroidManifest.xml index 37321ad80b0f..37321ad80b0f 100755..100644 --- a/tests/AttestationVerificationTest/AndroidManifest.xml +++ b/tests/AttestationVerificationTest/AndroidManifest.xml diff --git a/tests/DozeTest/res/drawable-hdpi/ic_app.png b/tests/DozeTest/res/drawable-hdpi/ic_app.png Binary files differindex 66a198496cfb..66a198496cfb 100755..100644 --- a/tests/DozeTest/res/drawable-hdpi/ic_app.png +++ b/tests/DozeTest/res/drawable-hdpi/ic_app.png diff --git a/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/splitscreen/EnterSystemSplitTest.kt b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/splitscreen/EnterSystemSplitTest.kt index a23f211ea1d2..379b45cdf08e 100644 --- a/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/splitscreen/EnterSystemSplitTest.kt +++ b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/splitscreen/EnterSystemSplitTest.kt @@ -24,7 +24,6 @@ import android.tools.flicker.legacy.FlickerBuilder import android.tools.flicker.legacy.LegacyFlickerTest import android.tools.flicker.legacy.LegacyFlickerTestFactory import android.tools.traces.parsers.toFlickerComponent -import androidx.test.filters.FlakyTest import com.android.server.wm.flicker.activityembedding.ActivityEmbeddingTestBase import com.android.server.wm.flicker.helpers.ActivityEmbeddingAppHelper import com.android.server.wm.flicker.testapp.ActivityOptions @@ -175,12 +174,6 @@ class EnterSystemSplitTest(flicker: LegacyFlickerTest) : ActivityEmbeddingTestBa } } - @FlakyTest(bugId = 342596801) - @Test - override fun visibleWindowsShownMoreThanOneConsecutiveEntry() { - super.visibleWindowsShownMoreThanOneConsecutiveEntry() - } - @Ignore("Not applicable to this CUJ.") override fun visibleLayersShownMoreThanOneConsecutiveEntry() {} diff --git a/tests/FlickerTests/Rotation/src/com/android/server/wm/flicker/rotation/RotationTransition.kt b/tests/FlickerTests/Rotation/src/com/android/server/wm/flicker/rotation/RotationTransition.kt index c7da778b752b..c49b509a9db3 100644 --- a/tests/FlickerTests/Rotation/src/com/android/server/wm/flicker/rotation/RotationTransition.kt +++ b/tests/FlickerTests/Rotation/src/com/android/server/wm/flicker/rotation/RotationTransition.kt @@ -21,6 +21,7 @@ import android.tools.device.apphelpers.StandardAppHelper import android.tools.flicker.legacy.FlickerBuilder import android.tools.flicker.legacy.LegacyFlickerTest import android.tools.flicker.subject.layers.LayerTraceEntrySubject +import android.tools.flicker.subject.layers.LayersTraceSubject import android.tools.traces.component.ComponentNameMatcher import android.tools.traces.component.IComponentMatcher import android.tools.traces.surfaceflinger.Display @@ -46,6 +47,7 @@ abstract class RotationTransition(flicker: LegacyFlickerTest) : BaseTest(flicker flicker.assertLayers { this.visibleLayersShownMoreThanOneConsecutiveEntry( ignoreLayers = + LayersTraceSubject.VISIBLE_FOR_MORE_THAN_ONE_ENTRY_IGNORE_LAYERS + listOf( ComponentNameMatcher.SPLASH_SCREEN, ComponentNameMatcher.SNAPSHOT, diff --git a/tests/Input/Android.bp b/tests/Input/Android.bp index c0cbdc3f96f8..f367c38b06e9 100644 --- a/tests/Input/Android.bp +++ b/tests/Input/Android.bp @@ -18,26 +18,31 @@ android_test { "src/**/*.java", "src/**/*.kt", ], + asset_dirs: ["assets"], kotlincflags: [ "-Werror", ], platform_apis: true, certificate: "platform", static_libs: [ + "android.view.flags-aconfig-java", "androidx.test.core", "androidx.test.ext.junit", "androidx.test.ext.truth", "androidx.test.rules", "androidx.test.runner", "androidx.test.uiautomator_uiautomator", + "collector-device-lib", "compatibility-device-util-axt", "cts-input-lib", + "cts-wm-util", "flag-junit", "frameworks-base-testutils", "hamcrest-library", "kotlin-test", "mockito-target-minus-junit4", "platform-test-annotations", + "platform-screenshot-diff-core", "services.core.unboosted", "servicestests-utils", "testables", diff --git a/tests/Input/AndroidManifest.xml b/tests/Input/AndroidManifest.xml index 3b723ddf811f..a05d08ccceba 100644 --- a/tests/Input/AndroidManifest.xml +++ b/tests/Input/AndroidManifest.xml @@ -22,6 +22,8 @@ <uses-permission android:name="android.permission.MONITOR_INPUT"/> <uses-permission android:name="android.permission.READ_DEVICE_CONFIG"/> <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS"/> + <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> + <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"/> <application android:label="InputTest" android:debuggable="true"> diff --git a/tests/Input/AndroidTest.xml b/tests/Input/AndroidTest.xml index f602c5124e77..8db37058af2b 100644 --- a/tests/Input/AndroidTest.xml +++ b/tests/Input/AndroidTest.xml @@ -28,4 +28,10 @@ <!-- Take screenshot upon test failure --> <option name="screenshot-on-failure" value="true" /> </object> + <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector"> + <option name="pull-pattern-keys" value="input_.*" /> + <!-- Pull files created by tests, like the output of screenshot tests --> + <option name="directory-keys" value="/storage/emulated/0/InputTests" /> + <option name="collect-on-run-ended-only" value="false" /> + </metrics_collector> </configuration> diff --git a/tests/Input/assets/testPointerFillStyle.png b/tests/Input/assets/testPointerFillStyle.png Binary files differnew file mode 100644 index 000000000000..b2354f8f4799 --- /dev/null +++ b/tests/Input/assets/testPointerFillStyle.png diff --git a/tests/Input/assets/testPointerScale.png b/tests/Input/assets/testPointerScale.png Binary files differnew file mode 100644 index 000000000000..54d37c24afc6 --- /dev/null +++ b/tests/Input/assets/testPointerScale.png diff --git a/tests/Input/src/com/android/test/input/AnrTest.kt b/tests/Input/src/com/android/test/input/AnrTest.kt index 6b95f5c10a1e..8d1fc508ffe7 100644 --- a/tests/Input/src/com/android/test/input/AnrTest.kt +++ b/tests/Input/src/com/android/test/input/AnrTest.kt @@ -29,6 +29,7 @@ import android.os.IInputConstants.UNMULTIPLIED_DEFAULT_DISPATCHING_TIMEOUT_MILLI import android.os.SystemClock import android.provider.Settings import android.provider.Settings.Global.HIDE_ERROR_DIALOGS +import android.server.wm.CtsWindowInfoUtils.waitForStableWindowGeometry import android.testing.PollingCheck import androidx.test.uiautomator.By @@ -36,13 +37,17 @@ import androidx.test.uiautomator.UiDevice import androidx.test.uiautomator.UiObject2 import androidx.test.uiautomator.Until +import com.android.cts.input.DebugInputRule import com.android.cts.input.UinputTouchScreen +import java.util.concurrent.TimeUnit + import org.junit.After import org.junit.Assert.assertEquals import org.junit.Assert.assertTrue import org.junit.Assert.fail import org.junit.Before +import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith @@ -71,6 +76,9 @@ class AnrTest { private val DISPATCHING_TIMEOUT = (UNMULTIPLIED_DEFAULT_DISPATCHING_TIMEOUT_MILLIS * Build.HW_TIMEOUT_MULTIPLIER) + @get:Rule + val debugInputRule = DebugInputRule() + @Before fun setUp() { val contentResolver = instrumentation.targetContext.contentResolver @@ -86,12 +94,14 @@ class AnrTest { } @Test + @DebugInputRule.DebugInput(bug = 339924248) fun testGestureMonitorAnr_Close() { triggerAnr() clickCloseAppOnAnrDialog() } @Test + @DebugInputRule.DebugInput(bug = 339924248) fun testGestureMonitorAnr_Wait() { triggerAnr() clickWaitOnAnrDialog() @@ -107,7 +117,7 @@ class AnrTest { val closeAppButton: UiObject2? = uiDevice.wait(Until.findObject(By.res("android:id/aerr_close")), 20000) if (closeAppButton == null) { - fail("Could not find anr dialog") + fail("Could not find anr dialog/close button") return } closeAppButton.click() @@ -183,5 +193,6 @@ class AnrTest { val flags = " -W -n " val startCmd = "am start $flags $PACKAGE_NAME/.UnresponsiveGestureMonitorActivity" instrumentation.uiAutomation.executeShellCommand(startCmd) + waitForStableWindowGeometry(5L, TimeUnit.SECONDS) } } diff --git a/tests/Input/src/com/android/test/input/PointerIconLoadingTest.kt b/tests/Input/src/com/android/test/input/PointerIconLoadingTest.kt new file mode 100644 index 000000000000..d196b85a7466 --- /dev/null +++ b/tests/Input/src/com/android/test/input/PointerIconLoadingTest.kt @@ -0,0 +1,136 @@ +/* + * 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.test.input + +import android.content.Context +import android.content.res.Configuration +import android.content.res.Resources +import android.os.Environment +import android.view.ContextThemeWrapper +import android.view.PointerIcon +import android.view.flags.Flags.enableVectorCursorA11ySettings +import android.view.flags.Flags.enableVectorCursors +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import androidx.test.platform.app.InstrumentationRegistry +import org.junit.Assume.assumeTrue +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.rules.TestName +import org.junit.runner.RunWith +import platform.test.screenshot.GoldenPathManager +import platform.test.screenshot.PathConfig +import platform.test.screenshot.ScreenshotTestRule +import platform.test.screenshot.assertAgainstGolden +import platform.test.screenshot.matchers.BitmapMatcher +import platform.test.screenshot.matchers.PixelPerfectMatcher + +/** + * Unit tests for PointerIcon. + * + * Run with: + * atest InputTests:com.android.test.input.PointerIconLoadingTest + */ +@SmallTest +@RunWith(AndroidJUnit4::class) +class PointerIconLoadingTest { + private lateinit var context: Context + private lateinit var exactScreenshotMatcher: BitmapMatcher + + @get:Rule + val testName = TestName() + + @get:Rule + val screenshotRule = ScreenshotTestRule(GoldenPathManager( + InstrumentationRegistry.getInstrumentation().getContext(), + ASSETS_PATH, + TEST_OUTPUT_PATH, + PathConfig() + ), disableIconPool = false) + + @Before + fun setUp() { + context = InstrumentationRegistry.getInstrumentation().targetContext + val config = + Configuration(context.resources.configuration).apply { + densityDpi = DENSITY_DPI + screenWidthDp = SCREEN_WIDTH_DP + screenHeightDp = SCREEN_HEIGHT_DP + smallestScreenWidthDp = SCREEN_WIDTH_DP + } + context = context.createConfigurationContext(config) + + exactScreenshotMatcher = PixelPerfectMatcher() + } + + @Test + fun testPointerFillStyle() { + assumeTrue(enableVectorCursors()) + assumeTrue(enableVectorCursorA11ySettings()) + + val theme: Resources.Theme = context.getResources().newTheme() + theme.setTo(context.getTheme()) + theme.applyStyle( + PointerIcon.vectorFillStyleToResource(PointerIcon.POINTER_ICON_VECTOR_STYLE_FILL_GREEN), + /* force= */ true) + + val pointerIcon = + PointerIcon.getLoadedSystemIcon( + ContextThemeWrapper(context, theme), + PointerIcon.TYPE_ARROW, + /* useLargeIcons= */ false, + /* pointerScale= */ 1f) + + pointerIcon.getBitmap().assertAgainstGolden( + screenshotRule, + testName.methodName, + exactScreenshotMatcher + ) + } + + @Test + fun testPointerScale() { + assumeTrue(enableVectorCursors()) + assumeTrue(enableVectorCursorA11ySettings()) + + val pointerScale = 2f + + val pointerIcon = + PointerIcon.getLoadedSystemIcon( + context, + PointerIcon.TYPE_ARROW, + /* useLargeIcons= */ false, + pointerScale) + + pointerIcon.getBitmap().assertAgainstGolden( + screenshotRule, + testName.methodName, + exactScreenshotMatcher + ) + } + + companion object { + const val DENSITY_DPI = 160 + const val SCREEN_WIDTH_DP = 480 + const val SCREEN_HEIGHT_DP = 800 + const val ASSETS_PATH = "tests/input/assets" + val TEST_OUTPUT_PATH = Environment.getExternalStorageDirectory().absolutePath + + "/InputTests/" + + PointerIconLoadingTest::class.java.simpleName + } +} diff --git a/tests/Internal/TEST_MAPPING b/tests/Internal/TEST_MAPPING new file mode 100644 index 000000000000..20af0287da5a --- /dev/null +++ b/tests/Internal/TEST_MAPPING @@ -0,0 +1,7 @@ +{ + "postsubmit": [ + { + "name": "InternalTests" + } + ] +}
\ No newline at end of file diff --git a/tests/Internal/src/com/android/internal/protolog/LegacyProtoLogImplTest.java b/tests/Internal/src/com/android/internal/protolog/LegacyProtoLogImplTest.java index 5cdfb2858b33..5a27593c7a36 100644 --- a/tests/Internal/src/com/android/internal/protolog/LegacyProtoLogImplTest.java +++ b/tests/Internal/src/com/android/internal/protolog/LegacyProtoLogImplTest.java @@ -393,5 +393,10 @@ public class LegacyProtoLogImplTest { this.mLogToLogcat = logToLogcat; } + @Override + public int getId() { + return ordinal(); + } + } } diff --git a/tests/Internal/src/com/android/internal/protolog/PerfettoProtoLogImplTest.java b/tests/Internal/src/com/android/internal/protolog/PerfettoProtoLogImplTest.java index f6ac080ebf73..1d7b6b348e10 100644 --- a/tests/Internal/src/com/android/internal/protolog/PerfettoProtoLogImplTest.java +++ b/tests/Internal/src/com/android/internal/protolog/PerfettoProtoLogImplTest.java @@ -725,5 +725,10 @@ public class PerfettoProtoLogImplTest { this.mLogToLogcat = logToLogcat; } + @Override + public int getId() { + return ordinal(); + } + } } diff --git a/tests/Internal/src/com/android/internal/protolog/ProtoLogImplTest.java b/tests/Internal/src/com/android/internal/protolog/ProtoLogImplTest.java index 4267c2c127ae..60456f9ea10f 100644 --- a/tests/Internal/src/com/android/internal/protolog/ProtoLogImplTest.java +++ b/tests/Internal/src/com/android/internal/protolog/ProtoLogImplTest.java @@ -174,5 +174,10 @@ public class ProtoLogImplTest { this.mLogToLogcat = logToLogcat; } + @Override + public int getId() { + return ordinal(); + } + } } diff --git a/tests/OdmApps/app/AndroidManifest.xml b/tests/OdmApps/app/AndroidManifest.xml index 84a9ea84b522..84a9ea84b522 100755..100644 --- a/tests/OdmApps/app/AndroidManifest.xml +++ b/tests/OdmApps/app/AndroidManifest.xml diff --git a/tests/OdmApps/priv-app/AndroidManifest.xml b/tests/OdmApps/priv-app/AndroidManifest.xml index 031cf64ea7b1..031cf64ea7b1 100755..100644 --- a/tests/OdmApps/priv-app/AndroidManifest.xml +++ b/tests/OdmApps/priv-app/AndroidManifest.xml diff --git a/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java b/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java index 093923f3ed53..a8b383cd4274 100644 --- a/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java +++ b/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java @@ -101,8 +101,8 @@ public class PackageWatchdogTest { private static final String OBSERVER_NAME_2 = "observer2"; private static final String OBSERVER_NAME_3 = "observer3"; private static final String OBSERVER_NAME_4 = "observer4"; - private static final long SHORT_DURATION = TimeUnit.SECONDS.toMillis(1); - private static final long LONG_DURATION = TimeUnit.SECONDS.toMillis(5); + private static final long SHORT_DURATION = TimeUnit.SECONDS.toMillis(10); + private static final long LONG_DURATION = TimeUnit.SECONDS.toMillis(50); @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); @@ -1453,7 +1453,8 @@ public class PackageWatchdogTest { raiseFatalFailureAndDispatch(watchdog, Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)), PackageWatchdog.FAILURE_REASON_UNKNOWN); - moveTimeForwardAndDispatch(PackageWatchdog.DEFAULT_DEESCALATION_WINDOW_MS); + moveTimeForwardAndDispatch(PackageWatchdog.DEFAULT_DEESCALATION_WINDOW_MS + - TimeUnit.MINUTES.toMillis(1)); // The first failure will be outside the threshold. raiseFatalFailureAndDispatch(watchdog, Arrays.asList(new VersionedPackage(APP_A, @@ -1712,6 +1713,9 @@ public class PackageWatchdogTest { watchdog.onPackageFailure(packages, failureReason); } mTestLooper.dispatchAll(); + if (Flags.recoverabilityDetection()) { + moveTimeForwardAndDispatch(watchdog.DEFAULT_MITIGATION_WINDOW_MS); + } } private PackageWatchdog createWatchdog() { diff --git a/tests/RemoteDisplayProvider/res/drawable-hdpi/ic_app.png b/tests/RemoteDisplayProvider/res/drawable-hdpi/ic_app.png Binary files differindex 66a198496cfb..66a198496cfb 100755..100644 --- a/tests/RemoteDisplayProvider/res/drawable-hdpi/ic_app.png +++ b/tests/RemoteDisplayProvider/res/drawable-hdpi/ic_app.png diff --git a/tests/inputmethod/ConcurrentMultiSessionImeTest/src/com/android/server/inputmethod/multisessiontest/ConcurrentMultiUserTest.java b/tests/inputmethod/ConcurrentMultiSessionImeTest/src/com/android/server/inputmethod/multisessiontest/ConcurrentMultiUserTest.java index 56dbde0e80ce..fff1dd1a7cb1 100644 --- a/tests/inputmethod/ConcurrentMultiSessionImeTest/src/com/android/server/inputmethod/multisessiontest/ConcurrentMultiUserTest.java +++ b/tests/inputmethod/ConcurrentMultiSessionImeTest/src/com/android/server/inputmethod/multisessiontest/ConcurrentMultiUserTest.java @@ -39,11 +39,13 @@ import com.android.bedstead.harrier.DeviceState; import org.junit.After; import org.junit.Before; import org.junit.ClassRule; +import org.junit.Ignore; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; @RunWith(BedsteadJUnit4.class) +@Ignore("b/345557347") public final class ConcurrentMultiUserTest { @ClassRule diff --git a/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/DataType.java b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/DataType.java index 97304cb36081..d2326d10e176 100644 --- a/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/DataType.java +++ b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/DataType.java @@ -21,7 +21,9 @@ import com.android.asllib.util.XmlUtils; import org.w3c.dom.Document; import org.w3c.dom.Element; +import java.util.HashSet; import java.util.List; +import java.util.Objects; import java.util.Set; /** @@ -76,6 +78,43 @@ public class DataType implements AslMarshallable { } } + @Override + public boolean equals(Object obj) { + if (obj == null || obj.getClass() != this.getClass()) { + return false; + } + DataType objAsDataType = (DataType) obj; + return Objects.equals(this.mDataTypeName, objAsDataType.mDataTypeName) + && Objects.equals( + new HashSet<>(this.mPurposes), new HashSet<>(objAsDataType.mPurposes)) + && Objects.equals(this.mIsCollectionOptional, objAsDataType.mIsCollectionOptional) + && Objects.equals(this.mIsSharingOptional, objAsDataType.mIsSharingOptional) + && Objects.equals(this.mEphemeral, objAsDataType.mEphemeral); + } + + @Override + public int hashCode() { + int result = 1; + int prime = 31; + result = + (prime * result) + (this.mDataTypeName != null ? this.mDataTypeName.hashCode() : 0); + result = + (prime * result) + + (this.mPurposes != null ? new HashSet<>(this.mPurposes).hashCode() : 0); + result = + (prime * result) + + (this.mIsCollectionOptional != null + ? this.mIsCollectionOptional.hashCode() + : 0); + result = + (prime * result) + + (this.mIsSharingOptional != null + ? this.mIsSharingOptional.hashCode() + : 0); + result = (prime * result) + (this.mEphemeral != null ? this.mEphemeral.hashCode() : 0); + return result; + } + private final String mDataTypeName; private final List<Purpose> mPurposes; diff --git a/tools/app_metadata_bundles/src/test/java/com/android/asllib/AllTests.java b/tools/app_metadata_bundles/src/test/java/com/android/asllib/AllTests.java index 54c80f6f7345..f156484485ec 100644 --- a/tools/app_metadata_bundles/src/test/java/com/android/asllib/AllTests.java +++ b/tools/app_metadata_bundles/src/test/java/com/android/asllib/AllTests.java @@ -20,6 +20,7 @@ import com.android.asllib.marshallable.AndroidSafetyLabelTest; import com.android.asllib.marshallable.AppInfoTest; import com.android.asllib.marshallable.DataCategoryTest; import com.android.asllib.marshallable.DataLabelsTest; +import com.android.asllib.marshallable.DataTypeEqualityTest; import com.android.asllib.marshallable.DeveloperInfoTest; import com.android.asllib.marshallable.SafetyLabelsTest; import com.android.asllib.marshallable.SecurityLabelsTest; @@ -37,6 +38,7 @@ import org.junit.runners.Suite; AppInfoTest.class, DataCategoryTest.class, DataLabelsTest.class, + DataTypeEqualityTest.class, DeveloperInfoTest.class, SafetyLabelsTest.class, SecurityLabelsTest.class, diff --git a/tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/DataTypeEqualityTest.java b/tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/DataTypeEqualityTest.java new file mode 100644 index 000000000000..da7f2879720e --- /dev/null +++ b/tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/DataTypeEqualityTest.java @@ -0,0 +1,170 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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.asllib.marshallable; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertTrue; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +@RunWith(JUnit4.class) +public class DataTypeEqualityTest { + + public static final List<String> OPTIONAL_FIELD_NAMES = + List.of("isDataDeletable", "isDataEncrypted"); + public static final List<String> OPTIONAL_FIELD_NAMES_OD = + List.of("is_data_deletable", "is_data_encrypted"); + + /** Logic for setting up tests (empty if not yet needed). */ + public static void main(String[] params) throws Exception {} + + @Before + public void setUp() throws Exception { + System.out.println("set up."); + } + + /** Test for equality different order. */ + @Test + public void testEqualityDifferentOrder() throws Exception { + System.out.println("starting testEqualityDifferentOrder."); + DataType dataType1 = + new DataType( + "datatype1", + Arrays.asList( + DataType.Purpose.ADVERTISING, DataType.Purpose.PERSONALIZATION), + true, + false, + null); + DataType dataType2 = + new DataType( + "datatype1", + Arrays.asList( + DataType.Purpose.PERSONALIZATION, DataType.Purpose.ADVERTISING), + true, + false, + null); + assertEquals(dataType1, dataType2); + assertEquals(dataType2, dataType1); + } + + /** Test for contains different order. */ + @Test + public void testContainsDifferentOrder() throws Exception { + System.out.println("starting testContainsDifferentOrder."); + DataType dataType1 = + new DataType( + "datatype1", + Arrays.asList( + DataType.Purpose.ADVERTISING, DataType.Purpose.PERSONALIZATION), + true, + false, + null); + DataType dataType2 = + new DataType( + "datatype1", + Arrays.asList( + DataType.Purpose.PERSONALIZATION, DataType.Purpose.ADVERTISING), + true, + false, + null); + Set<DataType> set = new HashSet<>(); + set.add(dataType1); + assertTrue(set.contains(dataType2)); + } + + /** Test for inequality. */ + @Test + public void testInequality() throws Exception { + System.out.println("starting testInequality."); + DataType dataType1 = + new DataType( + "datatype1", + Arrays.asList( + DataType.Purpose.ADVERTISING, DataType.Purpose.PERSONALIZATION), + true, + false, + null); + DataType dataType2 = + new DataType( + "datatype1", + Arrays.asList(DataType.Purpose.PERSONALIZATION), + true, + false, + null); + assertNotEquals(dataType1, dataType2); + assertNotEquals(dataType2, dataType1); + } + + /** Test for inequality bool. */ + @Test + public void testInequalityBool() throws Exception { + System.out.println("starting testInequalityBool."); + DataType dataType1 = + new DataType( + "datatype1", + Arrays.asList( + DataType.Purpose.ADVERTISING, DataType.Purpose.PERSONALIZATION), + true, + false, + null); + DataType dataType2 = + new DataType( + "datatype1", + Arrays.asList( + DataType.Purpose.ADVERTISING, DataType.Purpose.PERSONALIZATION), + true, + false, + true); + assertNotEquals(dataType1, dataType2); + assertNotEquals(dataType2, dataType1); + } + + /** Test for does not contain. */ + @Test + public void testDoesNotContain() throws Exception { + System.out.println("starting testDoesNotContain."); + System.out.println("starting testContainsDifferentOrder."); + DataType dataType1 = + new DataType( + "datatype1", + Arrays.asList( + DataType.Purpose.ADVERTISING, DataType.Purpose.PERSONALIZATION), + true, + false, + null); + DataType dataType2 = + new DataType( + "datatype1", + Arrays.asList(DataType.Purpose.PERSONALIZATION), + true, + false, + null); + Set<DataType> set = new HashSet<>(); + set.add(dataType1); + assertFalse(set.contains(dataType2)); + } +} diff --git a/tools/codegen/src/com/android/codegen/Main.kt b/tools/codegen/src/com/android/codegen/Main.kt index bcc6230fd53f..bcc6230fd53f 100755..100644 --- a/tools/codegen/src/com/android/codegen/Main.kt +++ b/tools/codegen/src/com/android/codegen/Main.kt diff --git a/tools/localedata/extract_icu_data.py b/tools/localedata/extract_icu_data.py index 81ac897deae0..8f67fa87adb5 100755 --- a/tools/localedata/extract_icu_data.py +++ b/tools/localedata/extract_icu_data.py @@ -22,6 +22,8 @@ import glob import os.path import sys +import xml.etree.ElementTree as ElementTree + def get_locale_parts(locale): """Split a locale into three parts, for langauge, script, and region.""" @@ -40,42 +42,43 @@ def get_locale_parts(locale): def read_likely_subtags(input_file_name): """Read and parse ICU's likelySubtags.txt.""" - with open(input_file_name) as input_file: - likely_script_dict = { - # Android's additions for pseudo-locales. These internal codes make - # sure that the pseudo-locales would not match other English or - # Arabic locales. (We can't use private-use ISO 15924 codes, since - # they may be used by apps for other purposes.) - "en_XA": "~~~A", - "ar_XB": "~~~B", - # Removed data from later versions of ICU - "ji": "Hebr", # Old code for Yiddish, still used in Java and Android - } - representative_locales = { - # Android's additions - "en_Latn_GB", # representative for en_Latn_001 - "es_Latn_MX", # representative for es_Latn_419 - "es_Latn_US", # representative for es_Latn_419 (not the best idea, - # but Android has been shipping with it for quite a - # while. Fortunately, MX < US, so if both exist, MX - # would be chosen.) - } - for line in input_file: - line = line.strip(u' \n\uFEFF') - if line.startswith('//'): - continue - if '{' in line and '}' in line: - from_locale = line[:line.index('{')] - to_locale = line[line.index('"')+1:line.rindex('"')] - from_lang, from_scr, from_region = get_locale_parts(from_locale) - _, to_scr, to_region = get_locale_parts(to_locale) - if from_lang == 'und': - continue # not very useful for our purposes - if from_region is None and to_region not in ['001', 'ZZ']: - representative_locales.add(to_locale) - if from_scr is None: - likely_script_dict[from_locale] = to_scr - return likely_script_dict, frozenset(representative_locales) + likely_script_dict = { + # Android's additions for pseudo-locales. These internal codes make + # sure that the pseudo-locales would not match other English or + # Arabic locales. (We can't use private-use ISO 15924 codes, since + # they may be used by apps for other purposes.) + "en_XA": "~~~A", + "ar_XB": "~~~B", + # Removed data from later versions of ICU + "ji": "Hebr", # Old code for Yiddish, still used in Java and Android + } + representative_locales = { + # Android's additions + "en_Latn_GB", # representative for en_Latn_001 + "es_Latn_MX", # representative for es_Latn_419 + "es_Latn_US", # representative for es_Latn_419 (not the best idea, + # but Android has been shipping with it for quite a + # while. Fortunately, MX < US, so if both exist, MX + # would be chosen.) + } + xml_tree = ElementTree.parse(input_file_name) + likely_subtags = xml_tree.find('likelySubtags') + for child in likely_subtags: + from_locale = child.get('from') + to_locale = child.get('to') + # print(f'from: {from_locale} to: {to_locale}') + from_lang, from_scr, from_region = get_locale_parts(from_locale) + _, to_scr, to_region = get_locale_parts(to_locale) + if to_locale == "FAIL": + continue # "FAIL" cases are not useful here. + if from_lang == 'und': + continue # not very useful for our purposes + if from_region is None and to_region not in ['001', 'ZZ']: + representative_locales.add(to_locale) + if from_scr is None: + likely_script_dict[from_locale] = to_scr + + return likely_script_dict, frozenset(representative_locales) # From packLanguageOrRegion() in ResourceTypes.cpp @@ -86,7 +89,7 @@ def pack_language_or_region(inp, base): elif len(inp) == 2: return ord(inp[0]), ord(inp[1]) else: - assert len(inp) == 3 + assert len(inp) == 3, f'Expects a 3-character string, but "{inp}" ' base = ord(base) first = ord(inp[0]) - base second = ord(inp[1]) - base @@ -161,9 +164,10 @@ def dump_representative_locales(representative_locales): print('});') -def read_and_dump_likely_data(icu_data_dir): +def read_and_dump_likely_data(cldr_source_dir): """Read and dump the likely-script data.""" - likely_subtags_txt = os.path.join(icu_data_dir, 'misc', 'likelySubtags.txt') + likely_subtags_txt = os.path.join(cldr_source_dir, + 'common', 'supplemental', 'likelySubtags.xml') likely_script_dict, representative_locales = read_likely_subtags( likely_subtags_txt) @@ -280,10 +284,11 @@ def main(): icu_data_dir = os.path.join( source_root, 'external', 'icu', 'icu4c', 'source', 'data') + cldr_source_dir = os.path.join(source_root, 'external', 'cldr') print('// Auto-generated by %s' % sys.argv[0]) print() - likely_script_dict = read_and_dump_likely_data(icu_data_dir) + likely_script_dict = read_and_dump_likely_data(cldr_source_dir) read_and_dump_parent_data(icu_data_dir, likely_script_dict) diff --git a/wifi/java/src/android/net/wifi/SoftApConfToXmlMigrationUtil.java b/wifi/java/src/android/net/wifi/SoftApConfToXmlMigrationUtil.java index c5472ce34478..c5472ce34478 100755..100644 --- a/wifi/java/src/android/net/wifi/SoftApConfToXmlMigrationUtil.java +++ b/wifi/java/src/android/net/wifi/SoftApConfToXmlMigrationUtil.java diff --git a/wifi/java/src/android/net/wifi/WifiMigration.java b/wifi/java/src/android/net/wifi/WifiMigration.java index 4fabc0b0babc..4fabc0b0babc 100755..100644 --- a/wifi/java/src/android/net/wifi/WifiMigration.java +++ b/wifi/java/src/android/net/wifi/WifiMigration.java diff --git a/wifi/java/src/android/net/wifi/WifiNetworkScoreCache.java b/wifi/java/src/android/net/wifi/WifiNetworkScoreCache.java index 39036580e2ef..39036580e2ef 100755..100644 --- a/wifi/java/src/android/net/wifi/WifiNetworkScoreCache.java +++ b/wifi/java/src/android/net/wifi/WifiNetworkScoreCache.java |