diff options
918 files changed, 18527 insertions, 8805 deletions
diff --git a/AconfigFlags.bp b/AconfigFlags.bp index 3c5686bd6d13..6f8a189c42b8 100644 --- a/AconfigFlags.bp +++ b/AconfigFlags.bp @@ -100,6 +100,7 @@ aconfig_declarations_group { "framework-jobscheduler-job.flags-aconfig-java", "framework_graphics_flags_java_lib", "hwui_flags_java_lib", + "interaction_jank_monitor_flags_lib", "libcore_exported_aconfig_flags_lib", "libgui_flags_java_lib", "power_flags_lib", @@ -437,10 +438,23 @@ aconfig_declarations { name: "android.companion.virtualdevice.flags-aconfig", package: "android.companion.virtualdevice.flags", container: "system", + exportable: true, srcs: ["core/java/android/companion/virtual/flags/*.aconfig"], } java_aconfig_library { + name: "android.companion.virtualdevice.flags-aconfig-java-export", + aconfig_declarations: "android.companion.virtualdevice.flags-aconfig", + defaults: ["framework-minus-apex-aconfig-java-defaults"], + mode: "exported", + min_sdk_version: "30", + apex_available: [ + "//apex_available:platform", + "com.android.permission", + ], +} + +java_aconfig_library { name: "android.companion.virtual.flags-aconfig-java", aconfig_declarations: "android.companion.virtual.flags-aconfig", defaults: ["framework-minus-apex-aconfig-java-defaults"], @@ -1565,3 +1579,17 @@ java_aconfig_library { aconfig_declarations: "dropbox_flags", defaults: ["framework-minus-apex-aconfig-java-defaults"], } + +// Zero Jank +aconfig_declarations { + name: "interaction_jank_monitor_flags", + package: "com.android.internal.jank", + container: "system", + srcs: ["core/java/com/android/internal/jank/flags.aconfig"], +} + +java_aconfig_library { + name: "interaction_jank_monitor_flags_lib", + aconfig_declarations: "interaction_jank_monitor_flags", + defaults: ["framework-minus-apex-aconfig-java-defaults"], +} diff --git a/apct-tests/perftests/protolog/src/com/android/internal/protolog/ProtologPerfTest.java b/apct-tests/perftests/protolog/src/com/android/internal/protolog/ProtoLogPerfTest.java index e1edb3712ff0..f4759b8bd35c 100644 --- a/apct-tests/perftests/protolog/src/com/android/internal/protolog/ProtologPerfTest.java +++ b/apct-tests/perftests/protolog/src/com/android/internal/protolog/ProtoLogPerfTest.java @@ -15,43 +15,54 @@ */ package com.android.internal.protolog; -import android.perftests.utils.BenchmarkState; -import android.perftests.utils.PerfStatusReporter; +import android.app.Activity; +import android.os.Bundle; +import android.perftests.utils.Stats; + +import androidx.test.InstrumentationRegistry; import com.android.internal.protolog.common.IProtoLogGroup; import com.android.internal.protolog.common.LogLevel; import org.junit.Before; import org.junit.BeforeClass; -import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; import org.junit.runners.Parameterized.Parameters; -import java.util.Arrays; +import java.util.ArrayList; import java.util.Collection; @RunWith(Parameterized.class) -public class ProtologPerfTest { - @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter(); +public class ProtoLogPerfTest { + private final boolean mLogToProto; + private final boolean mLogToLogcat; - @Parameters(name="logToProto_{0}_logToLogcat_{1}") + /** + * Generates the parameters used for this test class + */ + @Parameters(name = "LOG_TO_{0}") public static Collection<Object[]> params() { - return Arrays.asList(new Object[][] { - { true, true }, - { true, false }, - { false, true }, - { false, false } - }); + var params = new ArrayList<Object[]>(); + + for (LogTo logTo : LogTo.values()) { + params.add(new Object[] { logTo }); + } + + return params; } - private final boolean mLogToProto; - private final boolean mLogToLogcat; + public ProtoLogPerfTest(LogTo logTo) { + mLogToProto = switch (logTo) { + case ALL, PROTO_ONLY -> true; + case LOGCAT_ONLY, NONE -> false; + }; - public ProtologPerfTest(boolean logToProto, boolean logToLogcat) { - mLogToProto = logToProto; - mLogToLogcat = logToLogcat; + mLogToLogcat = switch (logTo) { + case ALL, LOGCAT_ONLY -> true; + case PROTO_ONLY, NONE -> false; + }; } @BeforeClass @@ -66,11 +77,11 @@ public class ProtologPerfTest { } @Test - public void logProcessedProtoLogMessageWithoutArgs() { + public void log_Processed_NoArgs() { final var protoLog = ProtoLog.getSingleInstance(); + final var perfMonitor = new PerfMonitor(); - BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); - while (state.keepRunning()) { + while (perfMonitor.keepRunning()) { protoLog.log( LogLevel.INFO, TestProtoLogGroup.TEST_GROUP, 123, 0, (Object[]) null); @@ -78,11 +89,11 @@ public class ProtologPerfTest { } @Test - public void logProcessedProtoLogMessageWithArgs() { + public void log_Processed_WithArgs() { final var protoLog = ProtoLog.getSingleInstance(); + final var perfMonitor = new PerfMonitor(); - BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); - while (state.keepRunning()) { + while (perfMonitor.keepRunning()) { protoLog.log( LogLevel.INFO, TestProtoLogGroup.TEST_GROUP, 123, 0b1110101001010100, @@ -91,18 +102,58 @@ public class ProtologPerfTest { } @Test - public void logNonProcessedProtoLogMessageWithNoArgs() { - BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); - while (state.keepRunning()) { + public void log_Unprocessed_NoArgs() { + final var perfMonitor = new PerfMonitor(); + + while (perfMonitor.keepRunning()) { ProtoLog.d(TestProtoLogGroup.TEST_GROUP, "Test message"); } } @Test - public void logNonProcessedProtoLogMessageWithArgs() { - BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); - while (state.keepRunning()) { - ProtoLog.d(TestProtoLogGroup.TEST_GROUP, "Test messag %s, %d, %b", "arg1", 2, true); + public void log_Unprocessed_WithArgs() { + final var perfMonitor = new PerfMonitor(); + + while (perfMonitor.keepRunning()) { + ProtoLog.d(TestProtoLogGroup.TEST_GROUP, "Test message %s, %d, %b", "arg1", 2, true); + } + } + + private class PerfMonitor { + private int mIteration = 0; + + private static final int WARM_UP_ITERATIONS = 10; + private static final int ITERATIONS = 1000; + + private final ArrayList<Long> mResults = new ArrayList<>(); + + private long mStartTimeNs; + + public boolean keepRunning() { + final long currentTime = System.nanoTime(); + + mIteration++; + + if (mIteration > ITERATIONS) { + reportResults(); + return false; + } + + if (mIteration > WARM_UP_ITERATIONS) { + mResults.add(currentTime - mStartTimeNs); + } + + mStartTimeNs = System.nanoTime(); + return true; + } + + public void reportResults() { + final var stats = new Stats(mResults); + + Bundle status = new Bundle(); + status.putLong("protologging_time_mean_ns", (long) stats.getMean()); + status.putLong("protologging_time_median_ns", (long) stats.getMedian()); + InstrumentationRegistry.getInstrumentation().sendStatus(Activity.RESULT_OK, status); } } @@ -168,4 +219,11 @@ public class ProtologPerfTest { return ordinal(); } } + + private enum LogTo { + PROTO_ONLY, + LOGCAT_ONLY, + ALL, + NONE, + } } diff --git a/apex/jobscheduler/service/aconfig/job.aconfig b/apex/jobscheduler/service/aconfig/job.aconfig index 613b7842ae3a..e5389b4f96fb 100644 --- a/apex/jobscheduler/service/aconfig/job.aconfig +++ b/apex/jobscheduler/service/aconfig/job.aconfig @@ -65,3 +65,13 @@ flag { description: "Remove started user if user will be stopped due to user switch" bug: "321598070" } + +flag { + name: "use_correct_process_state_for_logging" + namespace: "backstage_power" + description: "Use correct process state for statsd logging" + bug: "361308212" + metadata { + purpose: PURPOSE_BUGFIX + } +} diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java index d65a66c83fcc..be8e304a8101 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java +++ b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java @@ -473,6 +473,14 @@ public final class JobServiceContext implements ServiceConnection { mInitialDownloadedBytesFromCalling = TrafficStats.getUidRxBytes(job.getUid()); mInitialUploadedBytesFromCalling = TrafficStats.getUidTxBytes(job.getUid()); + int procState = mService.getUidProcState(job.getUid()); + if (Flags.useCorrectProcessStateForLogging() + && procState > ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND) { + // Try to get the latest proc state from AMS, there might be some delay + // for the proc states worse than TRANSIENT_BACKGROUND. + procState = mActivityManagerInternal.getUidProcessState(job.getUid()); + } + FrameworkStatsLog.write(FrameworkStatsLog.SCHEDULED_JOB_STATE_CHANGED, job.isProxyJob() ? new int[]{sourceUid, job.getUid()} : new int[]{sourceUid}, // Given that the source tag is set by the calling app, it should be connected @@ -517,7 +525,7 @@ public final class JobServiceContext implements ServiceConnection { job.getEstimatedNetworkDownloadBytes(), job.getEstimatedNetworkUploadBytes(), job.getWorkCount(), - ActivityManager.processStateAmToProto(mService.getUidProcState(job.getUid())), + ActivityManager.processStateAmToProto(procState), job.getNamespaceHash(), /* system_measured_source_download_bytes */ 0, /* system_measured_source_upload_bytes */ 0, @@ -1528,6 +1536,13 @@ public final class JobServiceContext implements ServiceConnection { mJobPackageTracker.noteInactive(completedJob, loggingInternalStopReason, loggingDebugReason); final int sourceUid = completedJob.getSourceUid(); + int procState = mService.getUidProcState(completedJob.getUid()); + if (Flags.useCorrectProcessStateForLogging() + && procState > ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND) { + // Try to get the latest proc state from AMS, there might be some delay + // for the proc states worse than TRANSIENT_BACKGROUND. + procState = mActivityManagerInternal.getUidProcessState(completedJob.getUid()); + } FrameworkStatsLog.write(FrameworkStatsLog.SCHEDULED_JOB_STATE_CHANGED, completedJob.isProxyJob() ? new int[]{sourceUid, completedJob.getUid()} : new int[]{sourceUid}, @@ -1573,7 +1588,7 @@ public final class JobServiceContext implements ServiceConnection { completedJob.getEstimatedNetworkUploadBytes(), completedJob.getWorkCount(), ActivityManager - .processStateAmToProto(mService.getUidProcState(completedJob.getUid())), + .processStateAmToProto(procState), completedJob.getNamespaceHash(), TrafficStats.getUidRxBytes(completedJob.getSourceUid()) - mInitialDownloadedBytesFromSource, diff --git a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java index d92351de3aa1..c9d340757c6b 100644 --- a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java +++ b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java @@ -1989,10 +1989,8 @@ public class AppStandbyController mAdminProtectedPackages.put(userId, packageNames); } } - if (android.app.admin.flags.Flags.disallowUserControlBgUsageFix()) { - if (!Flags.avoidIdleCheck() || mInjector.getBootPhase() >= PHASE_BOOT_COMPLETED) { - postCheckIdleStates(userId); - } + if (!Flags.avoidIdleCheck() || mInjector.getBootPhase() >= PHASE_BOOT_COMPLETED) { + postCheckIdleStates(userId); } } diff --git a/core/api/current.txt b/core/api/current.txt index 861be4079acc..ed8ab06810f9 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -141,7 +141,7 @@ package android { field public static final String MANAGE_DEVICE_POLICY_APPS_CONTROL = "android.permission.MANAGE_DEVICE_POLICY_APPS_CONTROL"; field public static final String MANAGE_DEVICE_POLICY_APP_RESTRICTIONS = "android.permission.MANAGE_DEVICE_POLICY_APP_RESTRICTIONS"; field public static final String MANAGE_DEVICE_POLICY_APP_USER_DATA = "android.permission.MANAGE_DEVICE_POLICY_APP_USER_DATA"; - field @FlaggedApi("android.app.admin.flags.assist_content_user_restriction_enabled") public static final String MANAGE_DEVICE_POLICY_ASSIST_CONTENT = "android.permission.MANAGE_DEVICE_POLICY_ASSIST_CONTENT"; + field public static final String MANAGE_DEVICE_POLICY_ASSIST_CONTENT = "android.permission.MANAGE_DEVICE_POLICY_ASSIST_CONTENT"; field public static final String MANAGE_DEVICE_POLICY_AUDIO_OUTPUT = "android.permission.MANAGE_DEVICE_POLICY_AUDIO_OUTPUT"; field public static final String MANAGE_DEVICE_POLICY_AUTOFILL = "android.permission.MANAGE_DEVICE_POLICY_AUTOFILL"; field public static final String MANAGE_DEVICE_POLICY_BACKUP_SERVICE = "android.permission.MANAGE_DEVICE_POLICY_BACKUP_SERVICE"; @@ -169,7 +169,7 @@ package android { field public static final String MANAGE_DEVICE_POLICY_LOCK = "android.permission.MANAGE_DEVICE_POLICY_LOCK"; field public static final String MANAGE_DEVICE_POLICY_LOCK_CREDENTIALS = "android.permission.MANAGE_DEVICE_POLICY_LOCK_CREDENTIALS"; field public static final String MANAGE_DEVICE_POLICY_LOCK_TASK = "android.permission.MANAGE_DEVICE_POLICY_LOCK_TASK"; - field @FlaggedApi("android.app.admin.flags.esim_management_enabled") public static final String MANAGE_DEVICE_POLICY_MANAGED_SUBSCRIPTIONS = "android.permission.MANAGE_DEVICE_POLICY_MANAGED_SUBSCRIPTIONS"; + field public static final String MANAGE_DEVICE_POLICY_MANAGED_SUBSCRIPTIONS = "android.permission.MANAGE_DEVICE_POLICY_MANAGED_SUBSCRIPTIONS"; field public static final String MANAGE_DEVICE_POLICY_METERED_DATA = "android.permission.MANAGE_DEVICE_POLICY_METERED_DATA"; field public static final String MANAGE_DEVICE_POLICY_MICROPHONE = "android.permission.MANAGE_DEVICE_POLICY_MICROPHONE"; field @FlaggedApi("android.app.admin.flags.dedicated_device_control_api_enabled") public static final String MANAGE_DEVICE_POLICY_MICROPHONE_TOGGLE = "android.permission.MANAGE_DEVICE_POLICY_MICROPHONE_TOGGLE"; @@ -7875,7 +7875,7 @@ package android.app.admin { method public void writeToParcel(android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator<android.app.admin.DeviceAdminInfo> CREATOR; field public static final int HEADLESS_DEVICE_OWNER_MODE_AFFILIATED = 1; // 0x1 - field @FlaggedApi("android.app.admin.flags.headless_device_owner_single_user_enabled") public static final int HEADLESS_DEVICE_OWNER_MODE_SINGLE_USER = 2; // 0x2 + field public static final int HEADLESS_DEVICE_OWNER_MODE_SINGLE_USER = 2; // 0x2 field public static final int HEADLESS_DEVICE_OWNER_MODE_UNSUPPORTED = 0; // 0x0 field public static final int USES_ENCRYPTED_STORAGE = 7; // 0x7 field public static final int USES_POLICY_DISABLE_CAMERA = 8; // 0x8 @@ -8081,7 +8081,7 @@ package android.app.admin { method public CharSequence getStartUserSessionMessage(@NonNull android.content.ComponentName); method @Deprecated public boolean getStorageEncryption(@Nullable android.content.ComponentName); method public int getStorageEncryptionStatus(); - method @FlaggedApi("android.app.admin.flags.esim_management_enabled") @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_DEVICE_POLICY_MANAGED_SUBSCRIPTIONS) public java.util.Set<java.lang.Integer> getSubscriptionIds(); + method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_DEVICE_POLICY_MANAGED_SUBSCRIPTIONS) public java.util.Set<java.lang.Integer> getSubscriptionIds(); method @Nullable public android.app.admin.SystemUpdatePolicy getSystemUpdatePolicy(); method @Nullable public android.os.PersistableBundle getTransferOwnershipBundle(); method @Nullable public java.util.List<android.os.PersistableBundle> getTrustAgentConfiguration(@Nullable android.content.ComponentName, @NonNull android.content.ComponentName); @@ -34116,7 +34116,7 @@ package android.os { field public static final String DISALLOW_AIRPLANE_MODE = "no_airplane_mode"; field public static final String DISALLOW_AMBIENT_DISPLAY = "no_ambient_display"; field public static final String DISALLOW_APPS_CONTROL = "no_control_apps"; - field @FlaggedApi("android.app.admin.flags.assist_content_user_restriction_enabled") public static final String DISALLOW_ASSIST_CONTENT = "no_assist_content"; + field public static final String DISALLOW_ASSIST_CONTENT = "no_assist_content"; field public static final String DISALLOW_AUTOFILL = "no_autofill"; field public static final String DISALLOW_BLUETOOTH = "no_bluetooth"; field public static final String DISALLOW_BLUETOOTH_SHARING = "no_bluetooth_sharing"; @@ -34166,7 +34166,7 @@ package android.os { field public static final String DISALLOW_SHARE_INTO_MANAGED_PROFILE = "no_sharing_into_profile"; field public static final String DISALLOW_SHARE_LOCATION = "no_share_location"; field public static final String DISALLOW_SHARING_ADMIN_CONFIGURED_WIFI = "no_sharing_admin_configured_wifi"; - field @FlaggedApi("android.app.admin.flags.esim_management_enabled") public static final String DISALLOW_SIM_GLOBALLY = "no_sim_globally"; + field public static final String DISALLOW_SIM_GLOBALLY = "no_sim_globally"; field public static final String DISALLOW_SMS = "no_sms"; field public static final String DISALLOW_SYSTEM_ERROR_DIALOGS = "no_system_error_dialogs"; field @FlaggedApi("com.android.net.thread.platform.flags.thread_user_restriction_enabled") public static final String DISALLOW_THREAD_NETWORK = "no_thread_network"; @@ -43968,8 +43968,11 @@ package android.telephony { field @FlaggedApi("com.android.internal.telephony.flags.carrier_enabled_satellite_flag") public static final String KEY_SATELLITE_CONNECTION_HYSTERESIS_SEC_INT = "satellite_connection_hysteresis_sec_int"; field @FlaggedApi("com.android.internal.telephony.flags.carrier_enabled_satellite_flag") public static final String KEY_SATELLITE_ENTITLEMENT_STATUS_REFRESH_DAYS_INT = "satellite_entitlement_status_refresh_days_int"; field @FlaggedApi("com.android.internal.telephony.flags.carrier_enabled_satellite_flag") public static final String KEY_SATELLITE_ENTITLEMENT_SUPPORTED_BOOL = "satellite_entitlement_supported_bool"; + field @FlaggedApi("com.android.internal.telephony.flags.carrier_roaming_nb_iot_ntn") public static final String KEY_SATELLITE_ESOS_INACTIVITY_TIMEOUT_SEC_INT = "satellite_esos_inactivity_timeout_sec_int"; field @FlaggedApi("com.android.internal.telephony.flags.carrier_roaming_nb_iot_ntn") public static final String KEY_SATELLITE_ESOS_SUPPORTED_BOOL = "satellite_esos_supported_bool"; - field @FlaggedApi("com.android.internal.telephony.flags.carrier_roaming_nb_iot_ntn") public static final String KEY_SATELLITE_SCREEN_OFF_INACTIVITY_TIMEOUT_SEC_INT = "satellite_screen_off_inactivity_timeout_duration_sec_int"; + field @FlaggedApi("com.android.internal.telephony.flags.carrier_roaming_nb_iot_ntn") public static final String KEY_SATELLITE_P2P_SMS_INACTIVITY_TIMEOUT_SEC_INT = "satellite_p2p_sms_inactivity_timeout_sec_int"; + field @FlaggedApi("com.android.internal.telephony.flags.carrier_roaming_nb_iot_ntn") public static final String KEY_SATELLITE_ROAMING_P2P_SMS_SUPPORTED_BOOL = "satellite_roaming_p2p_sms_supported_bool"; + field @FlaggedApi("com.android.internal.telephony.flags.carrier_roaming_nb_iot_ntn") public static final String KEY_SATELLITE_SCREEN_OFF_INACTIVITY_TIMEOUT_SEC_INT = "satellite_screen_off_inactivity_timeout_sec_int"; field public static final String KEY_SHOW_4G_FOR_3G_DATA_ICON_BOOL = "show_4g_for_3g_data_icon_bool"; field public static final String KEY_SHOW_4G_FOR_LTE_DATA_ICON_BOOL = "show_4g_for_lte_data_icon_bool"; field public static final String KEY_SHOW_APN_SETTING_CDMA_BOOL = "show_apn_setting_cdma_bool"; diff --git a/core/api/system-current.txt b/core/api/system-current.txt index 5413c6606bcb..f26522b9b919 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -1422,7 +1422,7 @@ package android.app.admin { field public static final int STATUS_DEVICE_ADMIN_NOT_SUPPORTED = 13; // 0xd field public static final int STATUS_HAS_DEVICE_OWNER = 1; // 0x1 field public static final int STATUS_HAS_PAIRED = 8; // 0x8 - field @FlaggedApi("android.app.admin.flags.headless_device_owner_single_user_enabled") public static final int STATUS_HEADLESS_ONLY_SYSTEM_USER = 17; // 0x11 + field public static final int STATUS_HEADLESS_ONLY_SYSTEM_USER = 17; // 0x11 field public static final int STATUS_HEADLESS_SYSTEM_USER_MODE_NOT_SUPPORTED = 16; // 0x10 field public static final int STATUS_MANAGED_USERS_NOT_SUPPORTED = 9; // 0x9 field public static final int STATUS_NONSYSTEM_USER_EXISTS = 5; // 0x5 @@ -11160,6 +11160,7 @@ package android.os { method @RequiresPermission(android.Manifest.permission.RECOVERY) public static void processPackage(android.content.Context, java.io.File, android.os.RecoverySystem.ProgressListener) throws java.io.IOException; method @Deprecated @RequiresPermission(android.Manifest.permission.RECOVERY) public static void rebootAndApply(@NonNull android.content.Context, @NonNull String, @NonNull String) throws java.io.IOException; method @RequiresPermission(anyOf={android.Manifest.permission.RECOVERY, android.Manifest.permission.REBOOT}) public static int rebootAndApply(@NonNull android.content.Context, @NonNull String, boolean) throws java.io.IOException; + method @FlaggedApi("android.crashrecovery.flags.enable_crashrecovery") @RequiresPermission(android.Manifest.permission.RECOVERY) public static void rebootPromptAndWipeUserData(@NonNull android.content.Context, @NonNull String) throws java.io.IOException; method @RequiresPermission(allOf={android.Manifest.permission.RECOVERY, android.Manifest.permission.REBOOT}) public static void rebootWipeAb(android.content.Context, java.io.File, String) throws java.io.IOException; method @RequiresPermission(android.Manifest.permission.RECOVERY) public static void scheduleUpdateOnBoot(android.content.Context, java.io.File) throws java.io.IOException; method @Deprecated public static boolean verifyPackageCompatibility(java.io.File) throws java.io.IOException; diff --git a/core/api/test-current.txt b/core/api/test-current.txt index ec23cfe7c6bb..009d08245da2 100644 --- a/core/api/test-current.txt +++ b/core/api/test-current.txt @@ -1542,6 +1542,10 @@ package android.hardware { method @Deprecated public final void setPreviewSurface(android.view.Surface) throws java.io.IOException; } + public final class Sensor { + method public int getHandle(); + } + public final class SensorPrivacyManager { method @FlaggedApi("com.android.internal.camera.flags.camera_privacy_allowlist") @RequiresPermission(android.Manifest.permission.MANAGE_SENSOR_PRIVACY) public void setCameraPrivacyAllowlist(@NonNull java.util.List<java.lang.String>); method @RequiresPermission(android.Manifest.permission.MANAGE_SENSOR_PRIVACY) public void setSensorPrivacy(int, int, boolean); @@ -3134,6 +3138,7 @@ package android.service.dreams { public abstract class DreamOverlayService extends android.app.Service { ctor public DreamOverlayService(); + method @FlaggedApi("android.service.dreams.publish_preview_state_to_overlay") public final boolean isDreamInPreviewMode(); method @Nullable public final android.os.IBinder onBind(@NonNull android.content.Intent); method public void onEndDream(); method public abstract void onStartDream(@NonNull android.view.WindowManager.LayoutParams); @@ -3666,7 +3671,11 @@ package android.view { method @Nullable public android.view.Display.Mode getUserPreferredDisplayMode(); method public boolean hasAccess(int); method @RequiresPermission(android.Manifest.permission.MODIFY_USER_PREFERRED_DISPLAY_MODE) public void setUserPreferredDisplayMode(@NonNull android.view.Display.Mode); + field public static final int FLAG_ALWAYS_UNLOCKED = 512; // 0x200 + field public static final int FLAG_OWN_FOCUS = 2048; // 0x800 + field public static final int FLAG_TOUCH_FEEDBACK_DISABLED = 1024; // 0x400 field public static final int FLAG_TRUSTED = 128; // 0x80 + field public static final int REMOVE_MODE_DESTROY_CONTENT = 1; // 0x1 field public static final int TYPE_EXTERNAL = 2; // 0x2 field public static final int TYPE_INTERNAL = 1; // 0x1 field public static final int TYPE_OVERLAY = 4; // 0x4 diff --git a/core/java/android/app/StatusBarManager.java b/core/java/android/app/StatusBarManager.java index 14195c473c6d..63e039143917 100644 --- a/core/java/android/app/StatusBarManager.java +++ b/core/java/android/app/StatusBarManager.java @@ -1326,6 +1326,7 @@ public class StatusBarManager { private boolean mClock; private boolean mNotificationIcons; private boolean mRotationSuggestion; + private boolean mQuickSettings; /** @hide */ public DisableInfo(int flags1, int flags2) { @@ -1338,6 +1339,7 @@ public class StatusBarManager { mClock = (flags1 & DISABLE_CLOCK) != 0; mNotificationIcons = (flags1 & DISABLE_NOTIFICATION_ICONS) != 0; mRotationSuggestion = (flags2 & DISABLE2_ROTATE_SUGGESTIONS) != 0; + mQuickSettings = (flags2 & DISABLE2_QUICK_SETTINGS) != 0; } /** @hide */ @@ -1471,6 +1473,20 @@ public class StatusBarManager { } /** + * @hide + */ + public void setQuickSettingsDisabled(boolean disabled) { + mQuickSettings = disabled; + } + + /** + * @hide + */ + public boolean isQuickSettingsDisabled() { + return mQuickSettings; + } + + /** * @return {@code true} if no components are disabled (default state) * @hide */ @@ -1478,7 +1494,7 @@ public class StatusBarManager { public boolean areAllComponentsEnabled() { return !mStatusBarExpansion && !mNavigateHome && !mNotificationPeeking && !mRecents && !mSearch && !mSystemIcons && !mClock && !mNotificationIcons - && !mRotationSuggestion; + && !mRotationSuggestion && !mQuickSettings; } /** @hide */ @@ -1492,6 +1508,7 @@ public class StatusBarManager { mClock = false; mNotificationIcons = false; mRotationSuggestion = false; + mQuickSettings = false; } /** @@ -1502,7 +1519,7 @@ public class StatusBarManager { public boolean areAllComponentsDisabled() { return mStatusBarExpansion && mNavigateHome && mNotificationPeeking && mRecents && mSearch && mSystemIcons && mClock && mNotificationIcons - && mRotationSuggestion; + && mRotationSuggestion && mQuickSettings; } /** @hide */ @@ -1516,6 +1533,7 @@ public class StatusBarManager { mClock = true; mNotificationIcons = true; mRotationSuggestion = true; + mQuickSettings = true; } @NonNull @@ -1533,6 +1551,7 @@ public class StatusBarManager { sb.append(" mClock=").append(mClock ? "disabled" : "enabled"); sb.append(" mNotificationIcons=").append(mNotificationIcons ? "disabled" : "enabled"); sb.append(" mRotationSuggestion=").append(mRotationSuggestion ? "disabled" : "enabled"); + sb.append(" mQuickSettings=").append(mQuickSettings ? "disabled" : "enabled"); return sb.toString(); @@ -1557,6 +1576,7 @@ public class StatusBarManager { if (mClock) disable1 |= DISABLE_CLOCK; if (mNotificationIcons) disable1 |= DISABLE_NOTIFICATION_ICONS; if (mRotationSuggestion) disable2 |= DISABLE2_ROTATE_SUGGESTIONS; + if (mQuickSettings) disable2 |= DISABLE2_QUICK_SETTINGS; return new Pair<Integer, Integer>(disable1, disable2); } diff --git a/core/java/android/app/admin/DeviceAdminInfo.java b/core/java/android/app/admin/DeviceAdminInfo.java index 46c9e781bed1..4f2efa493a1b 100644 --- a/core/java/android/app/admin/DeviceAdminInfo.java +++ b/core/java/android/app/admin/DeviceAdminInfo.java @@ -16,9 +16,6 @@ package android.app.admin; -import static android.app.admin.flags.Flags.FLAG_HEADLESS_DEVICE_OWNER_SINGLE_USER_ENABLED; - -import android.annotation.FlaggedApi; import android.annotation.IntDef; import android.annotation.NonNull; import android.app.admin.flags.Flags; @@ -195,7 +192,6 @@ public final class DeviceAdminInfo implements Parcelable { * DPCs should set the value of attribute "headless-device-owner-mode" inside the * "headless-system-user" tag as "single_user". */ - @FlaggedApi(FLAG_HEADLESS_DEVICE_OWNER_SINGLE_USER_ENABLED) public static final int HEADLESS_DEVICE_OWNER_MODE_SINGLE_USER = 2; /** diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index 5088ea6b603c..d31d8f27844a 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -56,10 +56,8 @@ import static android.Manifest.permission.SET_TIME_ZONE; import static android.app.admin.DeviceAdminInfo.HEADLESS_DEVICE_OWNER_MODE_UNSUPPORTED; import static android.app.admin.flags.Flags.FLAG_DEVICE_POLICY_SIZE_TRACKING_INTERNAL_BUG_FIX_ENABLED; import static android.app.admin.flags.Flags.FLAG_DEVICE_THEFT_API_ENABLED; -import static android.app.admin.flags.Flags.FLAG_ESIM_MANAGEMENT_ENABLED; import static android.app.admin.flags.Flags.FLAG_DEVICE_POLICY_SIZE_TRACKING_ENABLED; import static android.app.admin.flags.Flags.FLAG_HEADLESS_DEVICE_OWNER_PROVISIONING_FIX_ENABLED; -import static android.app.admin.flags.Flags.FLAG_HEADLESS_DEVICE_OWNER_SINGLE_USER_ENABLED; import static android.app.admin.flags.Flags.onboardingBugreportV2Enabled; import static android.app.admin.flags.Flags.onboardingConsentlessBugreports; import static android.app.admin.flags.Flags.FLAG_IS_MTE_POLICY_ENFORCED; @@ -2988,7 +2986,6 @@ public class DevicePolicyManager { * @hide */ @SystemApi - @FlaggedApi(FLAG_HEADLESS_DEVICE_OWNER_SINGLE_USER_ENABLED) public static final int STATUS_HEADLESS_ONLY_SYSTEM_USER = 17; /** @@ -17744,7 +17741,6 @@ public class DevicePolicyManager { * @throws SecurityException if the caller is not authorized to call this method. * @return ids of all managed subscriptions currently downloaded by an admin on the device. */ - @FlaggedApi(FLAG_ESIM_MANAGEMENT_ENABLED) @RequiresPermission(android.Manifest.permission.MANAGE_DEVICE_POLICY_MANAGED_SUBSCRIPTIONS) @NonNull public Set<Integer> getSubscriptionIds() { diff --git a/core/java/android/app/admin/flags/flags.aconfig b/core/java/android/app/admin/flags/flags.aconfig index 56f47922b078..29a5048daae6 100644 --- a/core/java/android/app/admin/flags/flags.aconfig +++ b/core/java/android/app/admin/flags/flags.aconfig @@ -125,13 +125,6 @@ flag { } flag { - name: "dumpsys_policy_engine_migration_enabled" - namespace: "enterprise" - description: "Update DumpSys to include information about migrated APIs in DPE" - bug: "304999634" -} - -flag { name: "allow_querying_profile_type" is_exported: true namespace: "enterprise" @@ -146,6 +139,7 @@ flag { bug: "293441361" } +# Fully rolled out and must not be used. flag { name: "assist_content_user_restriction_enabled" is_exported: true @@ -172,6 +166,7 @@ flag { bug: "304999634" } +# Fully rolled out and must not be used. flag { name: "esim_management_enabled" is_exported: true @@ -180,6 +175,7 @@ flag { bug: "295301164" } +# Fully rolled out and must not be used. flag { name: "headless_device_owner_single_user_enabled" is_exported: true @@ -207,26 +203,6 @@ flag { } flag { - name: "power_exemption_bg_usage_fix" - namespace: "enterprise" - description: "Ensure aps with EXEMPT_FROM_POWER_RESTRICTIONS can execute in the background" - bug: "333379020" - metadata { - purpose: PURPOSE_BUGFIX - } -} - -flag { - name: "disallow_user_control_bg_usage_fix" - namespace: "enterprise" - description: "Make DPM.setUserControlDisabledPackages() ensure background usage is allowed" - bug: "326031059" - metadata { - purpose: PURPOSE_BUGFIX - } -} - -flag { name: "disallow_user_control_stopped_state_fix" namespace: "enterprise" description: "Ensure DPM.setUserControlDisabledPackages() clears FLAG_STOPPED for the app" diff --git a/core/java/android/app/appfunctions/AppFunctionManagerHelper.java b/core/java/android/app/appfunctions/AppFunctionManagerHelper.java new file mode 100644 index 000000000000..3169f0e48877 --- /dev/null +++ b/core/java/android/app/appfunctions/AppFunctionManagerHelper.java @@ -0,0 +1,210 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.app.appfunctions; + +import static android.app.appfunctions.AppFunctionRuntimeMetadata.PROPERTY_APP_FUNCTION_STATIC_METADATA_QUALIFIED_ID; +import static android.app.appfunctions.AppFunctionRuntimeMetadata.PROPERTY_ENABLED; +import static android.app.appfunctions.AppFunctionStaticMetadataHelper.APP_FUNCTION_INDEXER_PACKAGE; +import static android.app.appfunctions.AppFunctionStaticMetadataHelper.STATIC_PROPERTY_ENABLED_BY_DEFAULT; +import static android.app.appfunctions.flags.Flags.FLAG_ENABLE_APP_FUNCTION_MANAGER; + +import android.annotation.CallbackExecutor; +import android.annotation.FlaggedApi; +import android.annotation.NonNull; +import android.app.appsearch.AppSearchManager; +import android.app.appsearch.AppSearchResult; +import android.app.appsearch.GlobalSearchSession; +import android.app.appsearch.JoinSpec; +import android.app.appsearch.SearchResult; +import android.app.appsearch.SearchResults; +import android.app.appsearch.SearchSpec; +import android.os.OutcomeReceiver; + +import java.io.IOException; +import java.util.List; +import java.util.Objects; +import java.util.concurrent.Executor; + +/** + * Helper class containing utilities for {@link AppFunctionManager}. + * + * @hide + */ +@FlaggedApi(FLAG_ENABLE_APP_FUNCTION_MANAGER) +public class AppFunctionManagerHelper { + + /** + * Returns (through a callback) a boolean indicating whether the app function is enabled. + * <p> + * This method can only check app functions that are owned by the caller owned by packages + * visible to the caller. + * <p> + * If operation fails, the callback's {@link OutcomeReceiver#onError} is called with errors: + * <ul> + * <li>{@link IllegalArgumentException}, if the function is not found</li> + * <li>{@link SecurityException}, if the caller does not have permission to query the + * target package + * </li> + * </ul> + * + * @param functionIdentifier the identifier of the app function to check (unique within the + * target package) and in most cases, these are automatically + * generated by the AppFunctions SDK + * @param targetPackage the package name of the app function's owner + * @param appSearchExecutor the executor to run the metadata search mechanism through AppSearch + * @param callbackExecutor the executor to run the callback + * @param callback the callback to receive the function enabled check result + * @hide + */ + public static void isAppFunctionEnabled( + @NonNull String functionIdentifier, + @NonNull String targetPackage, + @NonNull AppSearchManager appSearchManager, + @NonNull Executor appSearchExecutor, + @NonNull @CallbackExecutor Executor callbackExecutor, + @NonNull OutcomeReceiver<Boolean, Exception> callback + ) { + Objects.requireNonNull(functionIdentifier); + Objects.requireNonNull(targetPackage); + Objects.requireNonNull(appSearchManager); + Objects.requireNonNull(appSearchExecutor); + Objects.requireNonNull(callbackExecutor); + Objects.requireNonNull(callback); + + appSearchManager.createGlobalSearchSession(appSearchExecutor, + (searchSessionResult) -> { + if (!searchSessionResult.isSuccess()) { + callbackExecutor.execute(() -> + callback.onError(failedResultToException(searchSessionResult))); + return; + } + try (GlobalSearchSession searchSession = searchSessionResult.getResultValue()) { + SearchResults results = searchJoinedStaticWithRuntimeAppFunctions( + searchSession, targetPackage, functionIdentifier); + results.getNextPage(appSearchExecutor, + listAppSearchResult -> callbackExecutor.execute(() -> { + if (listAppSearchResult.isSuccess()) { + callback.onResult(getEnabledStateFromSearchResults( + Objects.requireNonNull( + listAppSearchResult.getResultValue()))); + } else { + callback.onError( + failedResultToException(listAppSearchResult)); + } + })); + } catch (Exception e) { + callbackExecutor.execute(() -> callback.onError(e)); + } + }); + } + + /** + * Searches joined app function static and runtime metadata using the function Id and the + * package. + * + * @hide + */ + private static @NonNull SearchResults searchJoinedStaticWithRuntimeAppFunctions( + @NonNull GlobalSearchSession session, + @NonNull String targetPackage, + @NonNull String functionIdentifier) { + SearchSpec runtimeSearchSpec = getAppFunctionRuntimeMetadataSearchSpecByFunctionId( + targetPackage); + JoinSpec joinSpec = new JoinSpec.Builder( + PROPERTY_APP_FUNCTION_STATIC_METADATA_QUALIFIED_ID) + .setNestedSearch( + functionIdentifier, + runtimeSearchSpec).build(); + SearchSpec joinedStaticWithRuntimeSearchSpec = new SearchSpec.Builder() + .setJoinSpec(joinSpec) + .addFilterPackageNames(APP_FUNCTION_INDEXER_PACKAGE) + .addFilterSchemas( + AppFunctionStaticMetadataHelper.getStaticSchemaNameForPackage( + targetPackage)) + .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY) + .build(); + return session.search(functionIdentifier, joinedStaticWithRuntimeSearchSpec); + } + + /** + * Finds whether the function is enabled or not from the search results returned by + * {@link #searchJoinedStaticWithRuntimeAppFunctions}. + * + * @throws IllegalArgumentException if the function is not found in the results + * @hide + */ + private static boolean getEnabledStateFromSearchResults( + @NonNull List<SearchResult> joinedStaticRuntimeResults) { + if (joinedStaticRuntimeResults.isEmpty()) { + // Function not found. + throw new IllegalArgumentException("App function not found."); + } else { + List<SearchResult> runtimeMetadataResults = + joinedStaticRuntimeResults.getFirst().getJoinedResults(); + if (!runtimeMetadataResults.isEmpty()) { + Boolean result = (Boolean) runtimeMetadataResults + .getFirst().getGenericDocument() + .getProperty(PROPERTY_ENABLED); + if (result != null) { + return result; + } + } + // Runtime metadata not found. Using the default value in the static metadata. + return joinedStaticRuntimeResults.getFirst().getGenericDocument() + .getPropertyBoolean(STATIC_PROPERTY_ENABLED_BY_DEFAULT); + } + } + + /** + * Returns search spec that queries app function metadata for a specific package name by its + * function identifier. + * + * @hide + */ + public static @NonNull SearchSpec getAppFunctionRuntimeMetadataSearchSpecByFunctionId( + @NonNull String targetPackage) { + return new SearchSpec.Builder() + .addFilterPackageNames(APP_FUNCTION_INDEXER_PACKAGE) + .addFilterSchemas( + AppFunctionRuntimeMetadata.getRuntimeSchemaNameForPackage( + targetPackage)) + .addFilterProperties( + AppFunctionRuntimeMetadata.getRuntimeSchemaNameForPackage( + targetPackage), + List.of(AppFunctionRuntimeMetadata.PROPERTY_FUNCTION_ID)) + .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY) + .build(); + } + + /** + * Converts a failed app search result codes into an exception. + * + * @hide + */ + public static @NonNull Exception failedResultToException( + @NonNull AppSearchResult appSearchResult) { + return switch (appSearchResult.getResultCode()) { + case AppSearchResult.RESULT_INVALID_ARGUMENT -> new IllegalArgumentException( + appSearchResult.getErrorMessage()); + case AppSearchResult.RESULT_IO_ERROR -> new IOException( + appSearchResult.getErrorMessage()); + case AppSearchResult.RESULT_SECURITY_ERROR -> new SecurityException( + appSearchResult.getErrorMessage()); + default -> new IllegalStateException(appSearchResult.getErrorMessage()); + }; + } +} diff --git a/core/java/android/app/appfunctions/AppFunctionRuntimeMetadata.java b/core/java/android/app/appfunctions/AppFunctionRuntimeMetadata.java new file mode 100644 index 000000000000..fdd12b0b49d3 --- /dev/null +++ b/core/java/android/app/appfunctions/AppFunctionRuntimeMetadata.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 android.app.appfunctions; + +import static android.app.appfunctions.flags.Flags.FLAG_ENABLE_APP_FUNCTION_MANAGER; + +import android.annotation.FlaggedApi; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.app.appsearch.AppSearchSchema; +import android.app.appsearch.GenericDocument; + +import com.android.internal.annotations.VisibleForTesting; + +import java.util.Objects; + +/** + * Represents runtime function metadata of an app function. + * + * <p>This is a temporary solution for app function indexing, as later we would like to index the + * actual function signature entity class shape instead of just the schema info. + * + * @hide + */ +// TODO(b/357551503): Link to canonical docs rather than duplicating once they +// are available. +@FlaggedApi(FLAG_ENABLE_APP_FUNCTION_MANAGER) +public class AppFunctionRuntimeMetadata extends GenericDocument { + public static final String RUNTIME_SCHEMA_TYPE = "AppFunctionRuntimeMetadata"; + public static final String APP_FUNCTION_INDEXER_PACKAGE = "android"; + public static final String APP_FUNCTION_RUNTIME_METADATA_DB = "appfunctions-db"; + public static final String APP_FUNCTION_RUNTIME_NAMESPACE = "app_functions_runtime"; + public static final String PROPERTY_FUNCTION_ID = "functionId"; + public static final String PROPERTY_PACKAGE_NAME = "packageName"; + public static final String PROPERTY_ENABLED = "enabled"; + public static final String PROPERTY_APP_FUNCTION_STATIC_METADATA_QUALIFIED_ID = + "appFunctionStaticMetadataQualifiedId"; + private static final String TAG = "AppSearchAppFunction"; + private static final String RUNTIME_SCHEMA_TYPE_SEPARATOR = "-"; + + public AppFunctionRuntimeMetadata(@NonNull GenericDocument genericDocument) { + super(genericDocument); + } + + /** + * Returns a per-app runtime metadata schema name, to store all functions for that package. + */ + public static String getRuntimeSchemaNameForPackage(@NonNull String pkg) { + return RUNTIME_SCHEMA_TYPE + RUNTIME_SCHEMA_TYPE_SEPARATOR + Objects.requireNonNull(pkg); + } + + /** + * Returns the document id for an app function's runtime metadata. + */ + public static String getDocumentIdForAppFunction( + @NonNull String pkg, @NonNull String functionId) { + return pkg + "/" + functionId; + } + + /** + * Different packages have different visibility requirements. To allow for different visibility, + * we need to have per-package app function schemas. + * <p>This schema should be set visible to callers from the package owner itself and for callers + * with {@link android.permission.EXECUTE_APP_FUNCTIONS_TRUSTED} or {@link + * android.permission.EXECUTE_APP_FUNCTIONS} permissions. + * + * @param packageName The package name to create a schema for. + */ + @NonNull + public static AppSearchSchema createAppFunctionRuntimeSchema(@NonNull String packageName) { + return new AppSearchSchema.Builder(getRuntimeSchemaNameForPackage(packageName)) + .addProperty( + new AppSearchSchema.StringPropertyConfig.Builder(PROPERTY_FUNCTION_ID) + .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL) + .setIndexingType( + AppSearchSchema.StringPropertyConfig + .INDEXING_TYPE_EXACT_TERMS) + .setTokenizerType( + AppSearchSchema.StringPropertyConfig + .TOKENIZER_TYPE_VERBATIM) + .build()) + .addProperty( + new AppSearchSchema.StringPropertyConfig.Builder(PROPERTY_PACKAGE_NAME) + .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL) + .setIndexingType( + AppSearchSchema.StringPropertyConfig + .INDEXING_TYPE_EXACT_TERMS) + .setTokenizerType( + AppSearchSchema.StringPropertyConfig + .TOKENIZER_TYPE_VERBATIM) + .build()) + .addProperty( + new AppSearchSchema.BooleanPropertyConfig.Builder(PROPERTY_ENABLED) + .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL) + .build()) + .addProperty( + new AppSearchSchema.StringPropertyConfig.Builder( + PROPERTY_APP_FUNCTION_STATIC_METADATA_QUALIFIED_ID) + .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL) + .setJoinableValueType( + AppSearchSchema.StringPropertyConfig + .JOINABLE_VALUE_TYPE_QUALIFIED_ID) + .build()) + .addParentType(RUNTIME_SCHEMA_TYPE) + .build(); + } + + /** + * Returns the function id. This might look like "com.example.message#send_message". + */ + @NonNull + public String getFunctionId() { + return Objects.requireNonNull(getPropertyString(PROPERTY_FUNCTION_ID)); + } + + /** + * Returns the package name of the package that owns this function. + */ + @NonNull + public String getPackageName() { + return Objects.requireNonNull(getPropertyString(PROPERTY_PACKAGE_NAME)); + } + + /** + * Returns if the function is set to be enabled or not. If not set, the {@link + * AppFunctionStaticMetadataHelper#STATIC_PROPERTY_ENABLED_BY_DEFAULT} value would be used. + */ + @Nullable + public Boolean getEnabled() { + return (Boolean) getProperty(PROPERTY_ENABLED); + } + + /** + * Returns the qualified id linking to the static metadata of the app function. + */ + @Nullable + @VisibleForTesting + public String getAppFunctionStaticMetadataQualifiedId() { + return getPropertyString(PROPERTY_APP_FUNCTION_STATIC_METADATA_QUALIFIED_ID); + } + + public static final class Builder extends GenericDocument.Builder<Builder> { + /** + * Creates a Builder for a {@link AppFunctionRuntimeMetadata}. + * + * @param packageName the name of the package that owns the function. + * @param functionId the id of the function. + * @param staticMetadataQualifiedId the qualified static metadata id that this runtime + * metadata refers to. + */ + public Builder( + @NonNull String packageName, + @NonNull String functionId, + @NonNull String staticMetadataQualifiedId) { + super( + APP_FUNCTION_RUNTIME_NAMESPACE, + getDocumentIdForAppFunction( + Objects.requireNonNull(packageName), + Objects.requireNonNull(functionId)), + getRuntimeSchemaNameForPackage(packageName)); + setPropertyString(PROPERTY_PACKAGE_NAME, packageName); + setPropertyString(PROPERTY_FUNCTION_ID, functionId); + + // Set qualified id automatically + setPropertyString( + PROPERTY_APP_FUNCTION_STATIC_METADATA_QUALIFIED_ID, + staticMetadataQualifiedId); + } + + + /** + * Sets an indicator specifying if the function is enabled or not. This would override the + * default enabled state in the static metadata ({@link + * AppFunctionStaticMetadataHelper#STATIC_PROPERTY_ENABLED_BY_DEFAULT}). + */ + @NonNull + public Builder setEnabled(boolean enabled) { + setPropertyBoolean(PROPERTY_ENABLED, enabled); + return this; + } + + /** + * Creates the {@link AppFunctionRuntimeMetadata} GenericDocument. + */ + @NonNull + public AppFunctionRuntimeMetadata build() { + return new AppFunctionRuntimeMetadata(super.build()); + } + } +} diff --git a/core/java/android/app/appfunctions/AppFunctionStaticMetadataHelper.java b/core/java/android/app/appfunctions/AppFunctionStaticMetadataHelper.java new file mode 100644 index 000000000000..6d4172aff8b3 --- /dev/null +++ b/core/java/android/app/appfunctions/AppFunctionStaticMetadataHelper.java @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.app.appfunctions; + +import static android.app.appfunctions.flags.Flags.FLAG_ENABLE_APP_FUNCTION_MANAGER; + +import android.annotation.FlaggedApi; +import android.annotation.NonNull; +import android.app.appsearch.util.DocumentIdUtil; + +import java.util.Objects; + +/** + * Contains constants and helper related to static metadata represented with {@code + * com.android.server.appsearch.appsindexer.appsearchtypes.AppFunctionStaticMetadata}. + * <p> + * The constants listed here **must not change** and be kept consistent with the canonical + * static metadata class. + * + * @hide + */ +@FlaggedApi(FLAG_ENABLE_APP_FUNCTION_MANAGER) +public class AppFunctionStaticMetadataHelper { + public static final String STATIC_SCHEMA_TYPE = "AppFunctionStaticMetadata"; + public static final String STATIC_PROPERTY_ENABLED_BY_DEFAULT = "enabledByDefault"; + + public static final String APP_FUNCTION_STATIC_NAMESPACE = "app_functions"; + + // These are constants that has to be kept the same with {@code + // com.android.server.appsearch.appsindexer.appsearchtypes.AppSearchHelper}. + public static final String APP_FUNCTION_STATIC_METADATA_DB = "apps-db"; + public static final String APP_FUNCTION_INDEXER_PACKAGE = "android"; + + /** + * Returns a per-app static metadata schema name, to store all functions for that package. + */ + public static String getStaticSchemaNameForPackage(@NonNull String pkg) { + return STATIC_SCHEMA_TYPE + "-" + Objects.requireNonNull(pkg); + } + + /** Returns the document id for an app function's static metadata. */ + public static String getDocumentIdForAppFunction( + @NonNull String pkg, @NonNull String functionId) { + return pkg + "/" + functionId; + } + + /** + * Returns the fully qualified Id used in AppSearch for the given package and function id + * app function static metadata. + */ + public static String getStaticMetadataQualifiedId(String packageName, String functionId) { + return DocumentIdUtil.createQualifiedId( + APP_FUNCTION_INDEXER_PACKAGE, + APP_FUNCTION_STATIC_METADATA_DB, + APP_FUNCTION_STATIC_NAMESPACE, + getDocumentIdForAppFunction(packageName, functionId)); + } +} diff --git a/core/java/android/companion/flags.aconfig b/core/java/android/companion/flags.aconfig index ee9114f74092..93d62cfeb537 100644 --- a/core/java/android/companion/flags.aconfig +++ b/core/java/android/companion/flags.aconfig @@ -10,13 +10,6 @@ flag { } flag { - name: "companion_transport_apis" - namespace: "companion" - description: "Grants access to the companion transport apis." - bug: "288297505" -} - -flag { name: "association_tag" is_exported: true namespace: "companion" diff --git a/core/java/android/companion/virtual/flags/flags.aconfig b/core/java/android/companion/virtual/flags/flags.aconfig index b4c36e1bc513..22a9ccf425c2 100644 --- a/core/java/android/companion/virtual/flags/flags.aconfig +++ b/core/java/android/companion/virtual/flags/flags.aconfig @@ -50,6 +50,7 @@ flag { name: "activity_control_api" description: "Enable APIs for fine grained activity policy, fallback and callbacks" bug: "333443509" + is_exported: true } flag { diff --git a/core/java/android/companion/virtual/sensor/VirtualSensorConfig.java b/core/java/android/companion/virtual/sensor/VirtualSensorConfig.java index 82f183fd1d62..68bc9bce28d2 100644 --- a/core/java/android/companion/virtual/sensor/VirtualSensorConfig.java +++ b/core/java/android/companion/virtual/sensor/VirtualSensorConfig.java @@ -17,7 +17,13 @@ package android.companion.virtual.sensor; +import static android.hardware.Sensor.REPORTING_MODE_CONTINUOUS; +import static android.hardware.Sensor.REPORTING_MODE_ONE_SHOT; +import static android.hardware.Sensor.REPORTING_MODE_ON_CHANGE; +import static android.hardware.Sensor.REPORTING_MODE_SPECIAL_TRIGGER; + import android.annotation.FlaggedApi; +import android.annotation.IntDef; import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; @@ -30,6 +36,8 @@ import android.hardware.SensorDirectChannel; import android.os.Parcel; import android.os.Parcelable; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.util.Objects; @@ -71,6 +79,17 @@ public final class VirtualSensorConfig implements Parcelable { private final int mFlags; + /** @hide */ + @IntDef(prefix = "REPORTING_MODE_", value = { + REPORTING_MODE_CONTINUOUS, + REPORTING_MODE_ON_CHANGE, + REPORTING_MODE_ONE_SHOT, + REPORTING_MODE_SPECIAL_TRIGGER + }) + + @Retention(RetentionPolicy.SOURCE) + public @interface ReportingMode {} + private VirtualSensorConfig(int type, @NonNull String name, @Nullable String vendor, float maximumRange, float resolution, float power, int minDelay, int maxDelay, int flags) { @@ -240,7 +259,7 @@ public final class VirtualSensorConfig implements Parcelable { * @see Sensor#getReportingMode() */ @FlaggedApi(Flags.FLAG_DEVICE_AWARE_DISPLAY_POWER) - public int getReportingMode() { + public @ReportingMode int getReportingMode() { return ((mFlags & REPORTING_MODE_MASK) >> REPORTING_MODE_SHIFT); } @@ -442,11 +461,11 @@ public final class VirtualSensorConfig implements Parcelable { */ @FlaggedApi(Flags.FLAG_DEVICE_AWARE_DISPLAY_POWER) @NonNull - public VirtualSensorConfig.Builder setReportingMode(int reportingMode) { - if (reportingMode != Sensor.REPORTING_MODE_CONTINUOUS - && reportingMode != Sensor.REPORTING_MODE_ON_CHANGE - && reportingMode != Sensor.REPORTING_MODE_ONE_SHOT - && reportingMode != Sensor.REPORTING_MODE_SPECIAL_TRIGGER) { + public VirtualSensorConfig.Builder setReportingMode(@ReportingMode int reportingMode) { + if (reportingMode != REPORTING_MODE_CONTINUOUS + && reportingMode != REPORTING_MODE_ON_CHANGE + && reportingMode != REPORTING_MODE_ONE_SHOT + && reportingMode != REPORTING_MODE_SPECIAL_TRIGGER) { throw new IllegalArgumentException("Invalid reporting mode: " + reportingMode); } mFlags |= reportingMode << REPORTING_MODE_SHIFT; diff --git a/core/java/android/content/AttributionSource.java b/core/java/android/content/AttributionSource.java index 37f419d717c2..ffed5366d31b 100644 --- a/core/java/android/content/AttributionSource.java +++ b/core/java/android/content/AttributionSource.java @@ -629,11 +629,10 @@ public final class AttributionSource implements Parcelable { * A builder for {@link AttributionSource} */ public static final class Builder { + private boolean mHasBeenUsed; private @NonNull final AttributionSourceState mAttributionSourceState = new AttributionSourceState(); - private long mBuilderFieldsSet = 0L; - /** * Creates a new Builder. * @@ -642,8 +641,17 @@ public final class AttributionSource implements Parcelable { */ public Builder(int uid) { mAttributionSourceState.uid = uid; + mAttributionSourceState.pid = Process.INVALID_PID; + mAttributionSourceState.deviceId = Context.DEVICE_ID_DEFAULT; + mAttributionSourceState.token = sDefaultToken; } + /** + * Creates a builder that is ready to build a new {@link AttributionSource} where + * all fields (primitive, immutable data, pointers) are copied from the given + * {@link AttributionSource}. Builder methods can still be used to mutate fields further. + * @param current The source to copy fields from. + */ public Builder(@NonNull AttributionSource current) { if (current == null) { throw new IllegalArgumentException("current AttributionSource can not be null"); @@ -652,9 +660,11 @@ public final class AttributionSource implements Parcelable { mAttributionSourceState.pid = current.getPid(); mAttributionSourceState.packageName = current.getPackageName(); mAttributionSourceState.attributionTag = current.getAttributionTag(); - mAttributionSourceState.token = current.getToken(); mAttributionSourceState.renouncedPermissions = current.mAttributionSourceState.renouncedPermissions; + mAttributionSourceState.deviceId = current.getDeviceId(); + mAttributionSourceState.next = current.mAttributionSourceState.next; + mAttributionSourceState.token = current.getToken(); } /** @@ -666,7 +676,6 @@ public final class AttributionSource implements Parcelable { */ public @NonNull Builder setPid(int value) { checkNotUsed(); - mBuilderFieldsSet |= 0x2; mAttributionSourceState.pid = value; return this; } @@ -676,7 +685,6 @@ public final class AttributionSource implements Parcelable { */ public @NonNull Builder setPackageName(@Nullable String value) { checkNotUsed(); - mBuilderFieldsSet |= 0x4; mAttributionSourceState.packageName = value; return this; } @@ -686,7 +694,6 @@ public final class AttributionSource implements Parcelable { */ public @NonNull Builder setAttributionTag(@Nullable String value) { checkNotUsed(); - mBuilderFieldsSet |= 0x8; mAttributionSourceState.attributionTag = value; return this; } @@ -717,11 +724,11 @@ public final class AttributionSource implements Parcelable { */ @SystemApi @RequiresPermission(android.Manifest.permission.RENOUNCE_PERMISSIONS) - public @NonNull Builder setRenouncedPermissions(@Nullable Set<String> value) { + public @NonNull Builder setRenouncedPermissions( + @Nullable Set<String> renouncedPermissions) { checkNotUsed(); - mBuilderFieldsSet |= 0x10; - mAttributionSourceState.renouncedPermissions = (value != null) - ? value.toArray(new String[0]) : null; + mAttributionSourceState.renouncedPermissions = (renouncedPermissions != null) + ? renouncedPermissions.toArray(new String[0]) : null; return this; } @@ -734,7 +741,6 @@ public final class AttributionSource implements Parcelable { @FlaggedApi(Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS_ENABLED) public @NonNull Builder setDeviceId(int deviceId) { checkNotUsed(); - mBuilderFieldsSet |= 0x12; mAttributionSourceState.deviceId = deviceId; return this; } @@ -744,7 +750,6 @@ public final class AttributionSource implements Parcelable { */ public @NonNull Builder setNext(@Nullable AttributionSource value) { checkNotUsed(); - mBuilderFieldsSet |= 0x20; mAttributionSourceState.next = (value != null) ? new AttributionSourceState[] {value.mAttributionSourceState} : mAttributionSourceState.next; return this; @@ -759,7 +764,6 @@ public final class AttributionSource implements Parcelable { if (value == null) { throw new IllegalArgumentException("Null AttributionSource not permitted."); } - mBuilderFieldsSet |= 0x20; mAttributionSourceState.next = new AttributionSourceState[]{value.mAttributionSourceState}; return this; @@ -768,28 +772,7 @@ public final class AttributionSource implements Parcelable { /** Builds the instance. This builder should not be touched after calling this! */ public @NonNull AttributionSource build() { checkNotUsed(); - mBuilderFieldsSet |= 0x40; // Mark builder used - - if ((mBuilderFieldsSet & 0x2) == 0) { - mAttributionSourceState.pid = Process.INVALID_PID; - } - if ((mBuilderFieldsSet & 0x4) == 0) { - mAttributionSourceState.packageName = null; - } - if ((mBuilderFieldsSet & 0x8) == 0) { - mAttributionSourceState.attributionTag = null; - } - if ((mBuilderFieldsSet & 0x10) == 0) { - mAttributionSourceState.renouncedPermissions = null; - } - if ((mBuilderFieldsSet & 0x12) == 0) { - mAttributionSourceState.deviceId = Context.DEVICE_ID_DEFAULT; - } - if ((mBuilderFieldsSet & 0x20) == 0) { - mAttributionSourceState.next = null; - } - - mAttributionSourceState.token = sDefaultToken; + mHasBeenUsed = true; if (mAttributionSourceState.next == null) { // The NDK aidl backend doesn't support null parcelable arrays. @@ -799,7 +782,7 @@ public final class AttributionSource implements Parcelable { } private void checkNotUsed() { - if ((mBuilderFieldsSet & 0x40) != 0) { + if (mHasBeenUsed) { throw new IllegalStateException( "This Builder should not be reused. Use a new Builder instance instead"); } diff --git a/core/java/android/hardware/Sensor.java b/core/java/android/hardware/Sensor.java index 10c37301b0b0..e0b9f60f812c 100644 --- a/core/java/android/hardware/Sensor.java +++ b/core/java/android/hardware/Sensor.java @@ -17,7 +17,9 @@ package android.hardware; +import android.annotation.SuppressLint; import android.annotation.SystemApi; +import android.annotation.TestApi; import android.compat.annotation.UnsupportedAppUsage; import android.hardware.input.InputSensorInfo; import android.os.Build; @@ -1182,6 +1184,8 @@ public final class Sensor { /** @hide */ @UnsupportedAppUsage + @SuppressLint("UnflaggedApi") // Promotion to TestApi + @TestApi public int getHandle() { return mHandle; } diff --git a/core/java/android/hardware/biometrics/AuthenticateOptions.java b/core/java/android/hardware/biometrics/AuthenticateOptions.java index 77660713275f..4dc6ea19a35d 100644 --- a/core/java/android/hardware/biometrics/AuthenticateOptions.java +++ b/core/java/android/hardware/biometrics/AuthenticateOptions.java @@ -74,4 +74,7 @@ public interface AuthenticateOptions { /** The attribution tag, if any. */ @Nullable String getAttributionTag(); + + /** If the authentication is requested due to mandatory biometrics being active. */ + boolean isMandatoryBiometrics(); } diff --git a/core/java/android/hardware/biometrics/IBiometricAuthenticator.aidl b/core/java/android/hardware/biometrics/IBiometricAuthenticator.aidl index 17cd18cc4182..b195225420b9 100644 --- a/core/java/android/hardware/biometrics/IBiometricAuthenticator.aidl +++ b/core/java/android/hardware/biometrics/IBiometricAuthenticator.aidl @@ -49,7 +49,7 @@ interface IBiometricAuthenticator { void prepareForAuthentication(boolean requireConfirmation, IBinder token, long operationId, int userId, IBiometricSensorReceiver sensorReceiver, String opPackageName, long requestId, int cookie, boolean allowBackgroundAuthentication, - boolean isForLegacyFingerprintManager); + boolean isForLegacyFingerprintManager, boolean isMandatoryBiometrics); // Starts authentication with the previously prepared client. void startPreparedClient(int cookie); diff --git a/core/java/android/hardware/face/FaceAuthenticateOptions.java b/core/java/android/hardware/face/FaceAuthenticateOptions.java index 518f902acdcb..8babbfa7169d 100644 --- a/core/java/android/hardware/face/FaceAuthenticateOptions.java +++ b/core/java/android/hardware/face/FaceAuthenticateOptions.java @@ -120,6 +120,8 @@ public class FaceAuthenticateOptions implements AuthenticateOptions, Parcelable } + /** If the authentication is requested due to mandatory biometrics being active. */ + private boolean mIsMandatoryBiometrics; // Code below generated by codegen v1.0.23. // @@ -188,7 +190,8 @@ public class FaceAuthenticateOptions implements AuthenticateOptions, Parcelable @AuthenticateReason int authenticateReason, @PowerManager.WakeReason int wakeReason, @NonNull String opPackageName, - @Nullable String attributionTag) { + @Nullable String attributionTag, + boolean isMandatoryBiometrics) { this.mUserId = userId; this.mSensorId = sensorId; this.mDisplayState = displayState; @@ -229,6 +232,7 @@ public class FaceAuthenticateOptions implements AuthenticateOptions, Parcelable com.android.internal.util.AnnotationValidations.validate( NonNull.class, null, mOpPackageName); this.mAttributionTag = attributionTag; + this.mIsMandatoryBiometrics = isMandatoryBiometrics; // onConstructed(); // You can define this method to get a callback } @@ -261,7 +265,7 @@ public class FaceAuthenticateOptions implements AuthenticateOptions, Parcelable * The reason for this operation when requested by the system (sysui), * otherwise AUTHENTICATE_REASON_UNKNOWN. * - * See packages/SystemUI/src/com/android/systemui/deviceentry/shared/FaceAuthReason.kt + * See frameworks/base/packages/SystemUI/src/com/android/keyguard/FaceAuthReason.kt * for more details about each reason. */ @DataClass.Generated.Member @@ -299,6 +303,14 @@ public class FaceAuthenticateOptions implements AuthenticateOptions, Parcelable } /** + * If the authentication is requested due to mandatory biometrics being active. + */ + @DataClass.Generated.Member + public boolean isMandatoryBiometrics() { + return mIsMandatoryBiometrics; + } + + /** * The sensor id for this operation. */ @DataClass.Generated.Member @@ -332,6 +344,15 @@ public class FaceAuthenticateOptions implements AuthenticateOptions, Parcelable return this; } + /** + * If the authentication is requested due to mandatory biometrics being active. + */ + @DataClass.Generated.Member + public @NonNull FaceAuthenticateOptions setIsMandatoryBiometrics( boolean value) { + mIsMandatoryBiometrics = value; + return this; + } + @Override @DataClass.Generated.Member public boolean equals(@Nullable Object o) { @@ -351,7 +372,8 @@ public class FaceAuthenticateOptions implements AuthenticateOptions, Parcelable && mAuthenticateReason == that.mAuthenticateReason && mWakeReason == that.mWakeReason && java.util.Objects.equals(mOpPackageName, that.mOpPackageName) - && java.util.Objects.equals(mAttributionTag, that.mAttributionTag); + && java.util.Objects.equals(mAttributionTag, that.mAttributionTag) + && mIsMandatoryBiometrics == that.mIsMandatoryBiometrics; } @Override @@ -368,6 +390,7 @@ public class FaceAuthenticateOptions implements AuthenticateOptions, Parcelable _hash = 31 * _hash + mWakeReason; _hash = 31 * _hash + java.util.Objects.hashCode(mOpPackageName); _hash = 31 * _hash + java.util.Objects.hashCode(mAttributionTag); + _hash = 31 * _hash + Boolean.hashCode(mIsMandatoryBiometrics); return _hash; } @@ -377,9 +400,10 @@ public class FaceAuthenticateOptions implements AuthenticateOptions, Parcelable // You can override field parcelling by defining methods like: // void parcelFieldName(Parcel dest, int flags) { ... } - byte flg = 0; + int flg = 0; + if (mIsMandatoryBiometrics) flg |= 0x80; if (mAttributionTag != null) flg |= 0x40; - dest.writeByte(flg); + dest.writeInt(flg); dest.writeInt(mUserId); dest.writeInt(mSensorId); dest.writeInt(mDisplayState); @@ -400,7 +424,8 @@ public class FaceAuthenticateOptions implements AuthenticateOptions, Parcelable // You can override field unparcelling by defining methods like: // static FieldType unparcelFieldName(Parcel in) { ... } - byte flg = in.readByte(); + int flg = in.readInt(); + boolean isMandatoryBiometrics = (flg & 0x80) != 0; int userId = in.readInt(); int sensorId = in.readInt(); int displayState = in.readInt(); @@ -449,6 +474,7 @@ public class FaceAuthenticateOptions implements AuthenticateOptions, Parcelable com.android.internal.util.AnnotationValidations.validate( NonNull.class, null, mOpPackageName); this.mAttributionTag = attributionTag; + this.mIsMandatoryBiometrics = isMandatoryBiometrics; // onConstructed(); // You can define this method to get a callback } @@ -481,6 +507,7 @@ public class FaceAuthenticateOptions implements AuthenticateOptions, Parcelable private @PowerManager.WakeReason int mWakeReason; private @NonNull String mOpPackageName; private @Nullable String mAttributionTag; + private boolean mIsMandatoryBiometrics; private long mBuilderFieldsSet = 0L; @@ -524,7 +551,7 @@ public class FaceAuthenticateOptions implements AuthenticateOptions, Parcelable * The reason for this operation when requested by the system (sysui), * otherwise AUTHENTICATE_REASON_UNKNOWN. * - * See packages/SystemUI/src/com/android/systemui/deviceentry/shared/FaceAuthReason.kt + * See frameworks/base/packages/SystemUI/src/com/android/keyguard/FaceAuthReason.kt * for more details about each reason. */ @DataClass.Generated.Member @@ -573,10 +600,21 @@ public class FaceAuthenticateOptions implements AuthenticateOptions, Parcelable return this; } + /** + * If the authentication is requested due to mandatory biometrics being active. + */ + @DataClass.Generated.Member + public @NonNull Builder setIsMandatoryBiometrics(boolean value) { + checkNotUsed(); + mBuilderFieldsSet |= 0x80; + mIsMandatoryBiometrics = value; + return this; + } + /** Builds the instance. This builder should not be touched after calling this! */ public @NonNull FaceAuthenticateOptions build() { checkNotUsed(); - mBuilderFieldsSet |= 0x80; // Mark builder used + mBuilderFieldsSet |= 0x100; // Mark builder used if ((mBuilderFieldsSet & 0x1) == 0) { mUserId = defaultUserId(); @@ -606,12 +644,13 @@ public class FaceAuthenticateOptions implements AuthenticateOptions, Parcelable mAuthenticateReason, mWakeReason, mOpPackageName, - mAttributionTag); + mAttributionTag, + mIsMandatoryBiometrics); return o; } private void checkNotUsed() { - if ((mBuilderFieldsSet & 0x80) != 0) { + if ((mBuilderFieldsSet & 0x100) != 0) { throw new IllegalStateException( "This Builder should not be reused. Use a new Builder instance instead"); } @@ -619,10 +658,10 @@ public class FaceAuthenticateOptions implements AuthenticateOptions, Parcelable } @DataClass.Generated( - time = 1677119626034L, + time = 1723436679828L, codegenVersion = "1.0.23", sourceFile = "frameworks/base/core/java/android/hardware/face/FaceAuthenticateOptions.java", - inputSignatures = "private final int mUserId\nprivate int mSensorId\nprivate final @android.hardware.biometrics.AuthenticateOptions.DisplayState int mDisplayState\npublic static final int AUTHENTICATE_REASON_UNKNOWN\npublic static final int AUTHENTICATE_REASON_STARTED_WAKING_UP\npublic static final int AUTHENTICATE_REASON_PRIMARY_BOUNCER_SHOWN\npublic static final int AUTHENTICATE_REASON_ASSISTANT_VISIBLE\npublic static final int AUTHENTICATE_REASON_ALTERNATE_BIOMETRIC_BOUNCER_SHOWN\npublic static final int AUTHENTICATE_REASON_NOTIFICATION_PANEL_CLICKED\npublic static final int AUTHENTICATE_REASON_OCCLUDING_APP_REQUESTED\npublic static final int AUTHENTICATE_REASON_PICK_UP_GESTURE_TRIGGERED\npublic static final int AUTHENTICATE_REASON_QS_EXPANDED\npublic static final int AUTHENTICATE_REASON_SWIPE_UP_ON_BOUNCER\npublic static final int AUTHENTICATE_REASON_UDFPS_POINTER_DOWN\nprivate final @android.hardware.face.FaceAuthenticateOptions.AuthenticateReason int mAuthenticateReason\nprivate final @android.os.PowerManager.WakeReason int mWakeReason\nprivate @android.annotation.NonNull java.lang.String mOpPackageName\nprivate @android.annotation.Nullable java.lang.String mAttributionTag\nprivate static int defaultUserId()\nprivate static int defaultSensorId()\nprivate static int defaultDisplayState()\nprivate static int defaultAuthenticateReason()\nprivate static int defaultWakeReason()\nprivate static java.lang.String defaultOpPackageName()\nprivate static java.lang.String defaultAttributionTag()\nclass FaceAuthenticateOptions extends java.lang.Object implements [android.hardware.biometrics.AuthenticateOptions, android.os.Parcelable]\n@com.android.internal.util.DataClass(genParcelable=true, genAidl=true, genBuilder=true, genSetters=true, genEqualsHashCode=true)") + inputSignatures = "private final int mUserId\nprivate int mSensorId\nprivate final @android.hardware.biometrics.AuthenticateOptions.DisplayState int mDisplayState\npublic static final int AUTHENTICATE_REASON_UNKNOWN\npublic static final int AUTHENTICATE_REASON_STARTED_WAKING_UP\npublic static final int AUTHENTICATE_REASON_PRIMARY_BOUNCER_SHOWN\npublic static final int AUTHENTICATE_REASON_ASSISTANT_VISIBLE\npublic static final int AUTHENTICATE_REASON_ALTERNATE_BIOMETRIC_BOUNCER_SHOWN\npublic static final int AUTHENTICATE_REASON_NOTIFICATION_PANEL_CLICKED\npublic static final int AUTHENTICATE_REASON_OCCLUDING_APP_REQUESTED\npublic static final int AUTHENTICATE_REASON_PICK_UP_GESTURE_TRIGGERED\npublic static final int AUTHENTICATE_REASON_QS_EXPANDED\npublic static final int AUTHENTICATE_REASON_SWIPE_UP_ON_BOUNCER\npublic static final int AUTHENTICATE_REASON_UDFPS_POINTER_DOWN\nprivate final @android.hardware.face.FaceAuthenticateOptions.AuthenticateReason int mAuthenticateReason\nprivate final @android.os.PowerManager.WakeReason int mWakeReason\nprivate @android.annotation.NonNull java.lang.String mOpPackageName\nprivate @android.annotation.Nullable java.lang.String mAttributionTag\nprivate boolean mIsMandatoryBiometrics\nprivate static int defaultUserId()\nprivate static int defaultSensorId()\nprivate static int defaultDisplayState()\nprivate static int defaultAuthenticateReason()\nprivate static int defaultWakeReason()\nprivate static java.lang.String defaultOpPackageName()\nprivate static java.lang.String defaultAttributionTag()\nclass FaceAuthenticateOptions extends java.lang.Object implements [android.hardware.biometrics.AuthenticateOptions, android.os.Parcelable]\n@com.android.internal.util.DataClass(genParcelable=true, genAidl=true, genBuilder=true, genSetters=true, genEqualsHashCode=true)") @Deprecated private void __metadata() {} diff --git a/core/java/android/hardware/fingerprint/FingerprintAuthenticateOptions.java b/core/java/android/hardware/fingerprint/FingerprintAuthenticateOptions.java index dc66542f4f37..ddf1e5b9dd17 100644 --- a/core/java/android/hardware/fingerprint/FingerprintAuthenticateOptions.java +++ b/core/java/android/hardware/fingerprint/FingerprintAuthenticateOptions.java @@ -97,6 +97,11 @@ public final class FingerprintAuthenticateOptions implements AuthenticateOptions return null; } + /** + * If the authentication is requested due to mandatory biometrics being active. + */ + private boolean mIsMandatoryBiometrics; + // Code below generated by codegen v1.0.23. // // DO NOT MODIFY! @@ -118,7 +123,8 @@ public final class FingerprintAuthenticateOptions implements AuthenticateOptions @AuthenticateOptions.DisplayState int displayState, @NonNull String opPackageName, @Nullable String attributionTag, - @Nullable AuthenticateReason.Vendor vendorReason) { + @Nullable AuthenticateReason.Vendor vendorReason, + boolean isMandatoryBiometrics) { this.mUserId = userId; this.mSensorId = sensorId; this.mIgnoreEnrollmentState = ignoreEnrollmentState; @@ -130,6 +136,7 @@ public final class FingerprintAuthenticateOptions implements AuthenticateOptions NonNull.class, null, mOpPackageName); this.mAttributionTag = attributionTag; this.mVendorReason = vendorReason; + this.mIsMandatoryBiometrics = isMandatoryBiometrics; // onConstructed(); // You can define this method to get a callback } @@ -199,6 +206,14 @@ public final class FingerprintAuthenticateOptions implements AuthenticateOptions } /** + * If the authentication is requested due to mandatory biometrics being active. + */ + @DataClass.Generated.Member + public boolean isMandatoryBiometrics() { + return mIsMandatoryBiometrics; + } + + /** * The sensor id for this operation. */ @DataClass.Generated.Member @@ -244,6 +259,15 @@ public final class FingerprintAuthenticateOptions implements AuthenticateOptions return this; } + /** + * If the authentication is requested due to mandatory biometrics being active. + */ + @DataClass.Generated.Member + public @NonNull FingerprintAuthenticateOptions setIsMandatoryBiometrics( boolean value) { + mIsMandatoryBiometrics = value; + return this; + } + @Override @DataClass.Generated.Member public boolean equals(@Nullable Object o) { @@ -263,7 +287,8 @@ public final class FingerprintAuthenticateOptions implements AuthenticateOptions && mDisplayState == that.mDisplayState && java.util.Objects.equals(mOpPackageName, that.mOpPackageName) && java.util.Objects.equals(mAttributionTag, that.mAttributionTag) - && java.util.Objects.equals(mVendorReason, that.mVendorReason); + && java.util.Objects.equals(mVendorReason, that.mVendorReason) + && mIsMandatoryBiometrics == that.mIsMandatoryBiometrics; } @Override @@ -280,6 +305,7 @@ public final class FingerprintAuthenticateOptions implements AuthenticateOptions _hash = 31 * _hash + java.util.Objects.hashCode(mOpPackageName); _hash = 31 * _hash + java.util.Objects.hashCode(mAttributionTag); _hash = 31 * _hash + java.util.Objects.hashCode(mVendorReason); + _hash = 31 * _hash + Boolean.hashCode(mIsMandatoryBiometrics); return _hash; } @@ -289,11 +315,12 @@ public final class FingerprintAuthenticateOptions implements AuthenticateOptions // You can override field parcelling by defining methods like: // void parcelFieldName(Parcel dest, int flags) { ... } - byte flg = 0; + int flg = 0; if (mIgnoreEnrollmentState) flg |= 0x4; + if (mIsMandatoryBiometrics) flg |= 0x80; if (mAttributionTag != null) flg |= 0x20; if (mVendorReason != null) flg |= 0x40; - dest.writeByte(flg); + dest.writeInt(flg); dest.writeInt(mUserId); dest.writeInt(mSensorId); dest.writeInt(mDisplayState); @@ -313,8 +340,9 @@ public final class FingerprintAuthenticateOptions implements AuthenticateOptions // You can override field unparcelling by defining methods like: // static FieldType unparcelFieldName(Parcel in) { ... } - byte flg = in.readByte(); + int flg = in.readInt(); boolean ignoreEnrollmentState = (flg & 0x4) != 0; + boolean isMandatoryBiometrics = (flg & 0x80) != 0; int userId = in.readInt(); int sensorId = in.readInt(); int displayState = in.readInt(); @@ -333,6 +361,7 @@ public final class FingerprintAuthenticateOptions implements AuthenticateOptions NonNull.class, null, mOpPackageName); this.mAttributionTag = attributionTag; this.mVendorReason = vendorReason; + this.mIsMandatoryBiometrics = isMandatoryBiometrics; // onConstructed(); // You can define this method to get a callback } @@ -365,6 +394,7 @@ public final class FingerprintAuthenticateOptions implements AuthenticateOptions private @NonNull String mOpPackageName; private @Nullable String mAttributionTag; private @Nullable AuthenticateReason.Vendor mVendorReason; + private boolean mIsMandatoryBiometrics; private long mBuilderFieldsSet = 0L; @@ -456,10 +486,21 @@ public final class FingerprintAuthenticateOptions implements AuthenticateOptions return this; } + /** + * If the authentication is requested due to mandatory biometrics being active. + */ + @DataClass.Generated.Member + public @NonNull Builder setIsMandatoryBiometrics(boolean value) { + checkNotUsed(); + mBuilderFieldsSet |= 0x80; + mIsMandatoryBiometrics = value; + return this; + } + /** Builds the instance. This builder should not be touched after calling this! */ public @NonNull FingerprintAuthenticateOptions build() { checkNotUsed(); - mBuilderFieldsSet |= 0x80; // Mark builder used + mBuilderFieldsSet |= 0x100; // Mark builder used if ((mBuilderFieldsSet & 0x1) == 0) { mUserId = defaultUserId(); @@ -489,12 +530,13 @@ public final class FingerprintAuthenticateOptions implements AuthenticateOptions mDisplayState, mOpPackageName, mAttributionTag, - mVendorReason); + mVendorReason, + mIsMandatoryBiometrics); return o; } private void checkNotUsed() { - if ((mBuilderFieldsSet & 0x80) != 0) { + if ((mBuilderFieldsSet & 0x100) != 0) { throw new IllegalStateException( "This Builder should not be reused. Use a new Builder instance instead"); } @@ -502,10 +544,10 @@ public final class FingerprintAuthenticateOptions implements AuthenticateOptions } @DataClass.Generated( - time = 1689703591032L, + time = 1723436831455L, codegenVersion = "1.0.23", sourceFile = "frameworks/base/core/java/android/hardware/fingerprint/FingerprintAuthenticateOptions.java", - inputSignatures = "private final int mUserId\nprivate int mSensorId\nprivate final boolean mIgnoreEnrollmentState\nprivate final @android.hardware.biometrics.AuthenticateOptions.DisplayState int mDisplayState\nprivate @android.annotation.NonNull java.lang.String mOpPackageName\nprivate @android.annotation.Nullable java.lang.String mAttributionTag\nprivate @android.annotation.Nullable android.hardware.biometrics.common.AuthenticateReason.Vendor mVendorReason\nprivate static int defaultUserId()\nprivate static int defaultSensorId()\nprivate static boolean defaultIgnoreEnrollmentState()\nprivate static int defaultDisplayState()\nprivate static java.lang.String defaultOpPackageName()\nprivate static java.lang.String defaultAttributionTag()\nprivate static android.hardware.biometrics.common.AuthenticateReason.Vendor defaultVendorReason()\nclass FingerprintAuthenticateOptions extends java.lang.Object implements [android.hardware.biometrics.AuthenticateOptions, android.os.Parcelable]\n@com.android.internal.util.DataClass(genParcelable=true, genAidl=true, genBuilder=true, genSetters=true, genEqualsHashCode=true)") + inputSignatures = "private final int mUserId\nprivate int mSensorId\nprivate final boolean mIgnoreEnrollmentState\nprivate final @android.hardware.biometrics.AuthenticateOptions.DisplayState int mDisplayState\nprivate @android.annotation.NonNull java.lang.String mOpPackageName\nprivate @android.annotation.Nullable java.lang.String mAttributionTag\nprivate @android.annotation.Nullable android.hardware.biometrics.common.AuthenticateReason.Vendor mVendorReason\nprivate boolean mIsMandatoryBiometrics\nprivate static int defaultUserId()\nprivate static int defaultSensorId()\nprivate static boolean defaultIgnoreEnrollmentState()\nprivate static int defaultDisplayState()\nprivate static java.lang.String defaultOpPackageName()\nprivate static java.lang.String defaultAttributionTag()\nprivate static android.hardware.biometrics.common.AuthenticateReason.Vendor defaultVendorReason()\nclass FingerprintAuthenticateOptions extends java.lang.Object implements [android.hardware.biometrics.AuthenticateOptions, android.os.Parcelable]\n@com.android.internal.util.DataClass(genParcelable=true, genAidl=true, genBuilder=true, genSetters=true, genEqualsHashCode=true)") @Deprecated private void __metadata() {} diff --git a/core/java/android/hardware/input/InputManagerGlobal.java b/core/java/android/hardware/input/InputManagerGlobal.java index 03cf7c5f926c..2a362381a003 100644 --- a/core/java/android/hardware/input/InputManagerGlobal.java +++ b/core/java/android/hardware/input/InputManagerGlobal.java @@ -567,7 +567,7 @@ public final class InputManagerGlobal { Objects.requireNonNull(listener, "listener must not be null"); synchronized (mOnTabletModeChangedListeners) { - if (mOnTabletModeChangedListeners == null) { + if (mOnTabletModeChangedListeners.isEmpty()) { initializeTabletModeListenerLocked(); } int idx = findOnTabletModeChangedListenerLocked(listener); diff --git a/core/java/android/os/Handler.java b/core/java/android/os/Handler.java index 92b630f3063a..80f39bfbdc21 100644 --- a/core/java/android/os/Handler.java +++ b/core/java/android/os/Handler.java @@ -46,13 +46,13 @@ import java.lang.reflect.Modifier; * a {@link Message} object containing a bundle of data that will be * processed by the Handler's {@link #handleMessage} method (requiring that * you implement a subclass of Handler). - * + * * <p>When posting or sending to a Handler, you can either * allow the item to be processed as soon as the message queue is ready * to do so, or specify a delay before it gets processed or absolute time for * it to be processed. The latter two allow you to implement timeouts, * ticks, and other timing-based behavior. - * + * * <p>When a * process is created for your application, its main thread is dedicated to * running a message queue that takes care of managing the top-level @@ -85,13 +85,13 @@ public class Handler { */ boolean handleMessage(@NonNull Message msg); } - + /** * Subclasses must implement this to receive messages. */ public void handleMessage(@NonNull Message msg) { } - + /** * Handle system messages here. */ @@ -343,8 +343,8 @@ public class Handler { * The default implementation will either return the class name of the * message callback if any, or the hexadecimal representation of the * message "what" field. - * - * @param message The message whose name is being queried + * + * @param message The message whose name is being queried */ @NonNull public String getMessageName(@NonNull Message message) { @@ -367,7 +367,7 @@ public class Handler { /** * Same as {@link #obtainMessage()}, except that it also sets the what member of the returned Message. - * + * * @param what Value to assign to the returned Message.what field. * @return A Message from the global message pool. */ @@ -376,12 +376,12 @@ public class Handler { { return Message.obtain(this, what); } - + /** - * - * Same as {@link #obtainMessage()}, except that it also sets the what and obj members + * + * Same as {@link #obtainMessage()}, except that it also sets the what and obj members * of the returned Message. - * + * * @param what Value to assign to the returned Message.what field. * @param obj Value to assign to the returned Message.obj field. * @return A Message from the global message pool. @@ -392,7 +392,7 @@ public class Handler { } /** - * + * * Same as {@link #obtainMessage()}, except that it also sets the what, arg1 and arg2 members of the returned * Message. * @param what Value to assign to the returned Message.what field. @@ -405,10 +405,10 @@ public class Handler { { return Message.obtain(this, what, arg1, arg2); } - + /** - * - * Same as {@link #obtainMessage()}, except that it also sets the what, obj, arg1,and arg2 values on the + * + * Same as {@link #obtainMessage()}, except that it also sets the what, obj, arg1,and arg2 values on the * returned Message. * @param what Value to assign to the returned Message.what field. * @param arg1 Value to assign to the returned Message.arg1 field. @@ -423,19 +423,19 @@ public class Handler { /** * Causes the Runnable r to be added to the message queue. - * The runnable will be run on the thread to which this handler is - * attached. - * + * The runnable will be run on the thread to which this handler is + * attached. + * * @param r The Runnable that will be executed. - * - * @return Returns true if the Runnable was successfully placed in to the + * + * @return Returns true if the Runnable was successfully placed in to the * message queue. Returns false on failure, usually because the * looper processing the message queue is exiting. */ public final boolean post(@NonNull Runnable r) { return sendMessageDelayed(getPostMessage(r), 0); } - + /** * Causes the Runnable r to be added to the message queue, to be run * at a specific time given by <var>uptimeMillis</var>. @@ -446,8 +446,8 @@ public class Handler { * @param r The Runnable that will be executed. * @param uptimeMillis The absolute time at which the callback should run, * using the {@link android.os.SystemClock#uptimeMillis} time-base. - * - * @return Returns true if the Runnable was successfully placed in to the + * + * @return Returns true if the Runnable was successfully placed in to the * message queue. Returns false on failure, usually because the * looper processing the message queue is exiting. Note that a * result of true does not mean the Runnable will be processed -- if @@ -457,7 +457,7 @@ public class Handler { public final boolean postAtTime(@NonNull Runnable r, long uptimeMillis) { return sendMessageAtTime(getPostMessage(r), uptimeMillis); } - + /** * Causes the Runnable r to be added to the message queue, to be run * at a specific time given by <var>uptimeMillis</var>. @@ -470,21 +470,21 @@ public class Handler { * {@link #removeCallbacksAndMessages}. * @param uptimeMillis The absolute time at which the callback should run, * using the {@link android.os.SystemClock#uptimeMillis} time-base. - * - * @return Returns true if the Runnable was successfully placed in to the + * + * @return Returns true if the Runnable was successfully placed in to the * message queue. Returns false on failure, usually because the * looper processing the message queue is exiting. Note that a * result of true does not mean the Runnable will be processed -- if * the looper is quit before the delivery time of the message * occurs then the message will be dropped. - * + * * @see android.os.SystemClock#uptimeMillis */ public final boolean postAtTime( @NonNull Runnable r, @Nullable Object token, long uptimeMillis) { return sendMessageAtTime(getPostMessage(r, token), uptimeMillis); } - + /** * Causes the Runnable r to be added to the message queue, to be run * after the specified amount of time elapses. @@ -492,12 +492,12 @@ public class Handler { * is attached. * <b>The time-base is {@link android.os.SystemClock#uptimeMillis}.</b> * Time spent in deep sleep will add an additional delay to execution. - * + * * @param r The Runnable that will be executed. * @param delayMillis The delay (in milliseconds) until the Runnable * will be executed. - * - * @return Returns true if the Runnable was successfully placed in to the + * + * @return Returns true if the Runnable was successfully placed in to the * message queue. Returns false on failure, usually because the * looper processing the message queue is exiting. Note that a * result of true does not mean the Runnable will be processed -- @@ -507,7 +507,7 @@ public class Handler { public final boolean postDelayed(@NonNull Runnable r, long delayMillis) { return sendMessageDelayed(getPostMessage(r), delayMillis); } - + /** @hide */ public final boolean postDelayed(Runnable r, int what, long delayMillis) { return sendMessageDelayed(getPostMessage(r).setWhat(what), delayMillis); @@ -547,10 +547,10 @@ public class Handler { * <b>This method is only for use in very special circumstances -- it * can easily starve the message queue, cause ordering problems, or have * other unexpected side-effects.</b> - * + * * @param r The Runnable that will be executed. - * - * @return Returns true if the message was successfully placed in to the + * + * @return Returns true if the message was successfully placed in to the * message queue. Returns false on failure, usually because the * looper processing the message queue is exiting. */ @@ -635,8 +635,8 @@ public class Handler { * Pushes a message onto the end of the message queue after all pending messages * before the current time. It will be received in {@link #handleMessage}, * in the thread attached to this handler. - * - * @return Returns true if the message was successfully placed in to the + * + * @return Returns true if the message was successfully placed in to the * message queue. Returns false on failure, usually because the * looper processing the message queue is exiting. */ @@ -646,8 +646,8 @@ public class Handler { /** * Sends a Message containing only the what value. - * - * @return Returns true if the message was successfully placed in to the + * + * @return Returns true if the message was successfully placed in to the * message queue. Returns false on failure, usually because the * looper processing the message queue is exiting. */ @@ -659,9 +659,9 @@ public class Handler { /** * Sends a Message containing only the what value, to be delivered * after the specified amount of time elapses. - * @see #sendMessageDelayed(android.os.Message, long) - * - * @return Returns true if the message was successfully placed in to the + * @see #sendMessageDelayed(android.os.Message, long) + * + * @return Returns true if the message was successfully placed in to the * message queue. Returns false on failure, usually because the * looper processing the message queue is exiting. */ @@ -672,11 +672,11 @@ public class Handler { } /** - * Sends a Message containing only the what value, to be delivered + * Sends a Message containing only the what value, to be delivered * at a specific time. * @see #sendMessageAtTime(android.os.Message, long) - * - * @return Returns true if the message was successfully placed in to the + * + * @return Returns true if the message was successfully placed in to the * message queue. Returns false on failure, usually because the * looper processing the message queue is exiting. */ @@ -691,8 +691,8 @@ public class Handler { * Enqueue a message into the message queue after all pending messages * before (current time + delayMillis). You will receive it in * {@link #handleMessage}, in the thread attached to this handler. - * - * @return Returns true if the message was successfully placed in to the + * + * @return Returns true if the message was successfully placed in to the * message queue. Returns false on failure, usually because the * looper processing the message queue is exiting. Note that a * result of true does not mean the message will be processed -- if @@ -713,12 +713,12 @@ public class Handler { * Time spent in deep sleep will add an additional delay to execution. * You will receive it in {@link #handleMessage}, in the thread attached * to this handler. - * + * * @param uptimeMillis The absolute time at which the message should be * delivered, using the * {@link android.os.SystemClock#uptimeMillis} time-base. - * - * @return Returns true if the message was successfully placed in to the + * + * @return Returns true if the message was successfully placed in to the * message queue. Returns false on failure, usually because the * looper processing the message queue is exiting. Note that a * result of true does not mean the message will be processed -- if @@ -743,8 +743,8 @@ public class Handler { * <b>This method is only for use in very special circumstances -- it * can easily starve the message queue, cause ordering problems, or have * other unexpected side-effects.</b> - * - * @return Returns true if the message was successfully placed in to the + * + * @return Returns true if the message was successfully placed in to the * message queue. Returns false on failure, usually because the * looper processing the message queue is exiting. */ @@ -798,6 +798,12 @@ public class Handler { /** * Remove any pending posts of messages with code 'what' that are in the * message queue. + * + * Note that `Message#what` is 0 unless otherwise set. + * When calling `postMessage(Runnable)` or `postAtTime(Runnable, long)`, + * the `Runnable` is internally wrapped with a `Message` whose `what` is 0. + * Calling `removeMessages(0)` will remove all messages without a `what`, + * including posted `Runnable`s. */ public final void removeMessages(int what) { mQueue.removeMessages(this, what, null); @@ -889,7 +895,7 @@ public class Handler { } // if we can get rid of this method, the handler need not remember its loop - // we could instead export a getMessageQueue() method... + // we could instead export a getMessageQueue() method... @NonNull public final Looper getLooper() { return mLooper; diff --git a/core/java/android/os/Message.java b/core/java/android/os/Message.java index a1db9be0b693..702fdc2bbaa6 100644 --- a/core/java/android/os/Message.java +++ b/core/java/android/os/Message.java @@ -41,6 +41,9 @@ public final class Message implements Parcelable { * what this message is about. Each {@link Handler} has its own name-space * for message codes, so you do not need to worry about yours conflicting * with other handlers. + * + * If not specified, this value is 0. + * Use values other than 0 to indicate custom message codes. */ public int what; diff --git a/core/java/android/os/RecoverySystem.java b/core/java/android/os/RecoverySystem.java index bb74a3e7f896..398140dafc33 100644 --- a/core/java/android/os/RecoverySystem.java +++ b/core/java/android/os/RecoverySystem.java @@ -18,6 +18,7 @@ package android.os; import static android.view.Display.DEFAULT_DISPLAY; +import android.annotation.FlaggedApi; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; @@ -1170,11 +1171,27 @@ public class RecoverySystem { return removedSubsCount.get() == subscriptionInfos.size(); } - /** {@hide} */ - public static void rebootPromptAndWipeUserData(Context context, String reason) + /** + * Reboot into recovery and prompt for wiping the device. + * + * This is used as last resort in case the device is not recoverable using + * other recovery steps. This first checks if fs-checkpoint is available, in + * which case we commit the checkpoint, otherwise it performs the reboot in + * recovery mode and shows user prompt for wiping the device. + * + * @param context the context to use. + * @param reason the reason to wipe. + * + * @throws IOException if something goes wrong. + * + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.RECOVERY) + @FlaggedApi(android.crashrecovery.flags.Flags.FLAG_ENABLE_CRASHRECOVERY) + public static void rebootPromptAndWipeUserData(@NonNull Context context, @NonNull String reason) throws IOException { boolean checkpointing = false; - boolean needReboot = false; IVold vold = null; try { vold = IVold.Stub.asInterface(ServiceManager.checkService("vold")); diff --git a/core/java/android/os/SharedMemory.java b/core/java/android/os/SharedMemory.java index c801fabf9427..46ae9d8682ee 100644 --- a/core/java/android/os/SharedMemory.java +++ b/core/java/android/os/SharedMemory.java @@ -379,6 +379,14 @@ public final class SharedMemory implements Parcelable, Closeable { private int mReferenceCount; private MemoryRegistration(int size) { + // Round up to the nearest page size + final int PAGE_SIZE = OsConstants._SC_PAGE_SIZE; + if (PAGE_SIZE > 0) { + final int remainder = size % PAGE_SIZE; + if (remainder != 0) { + size += PAGE_SIZE - remainder; + } + } mSize = size; mReferenceCount = 1; VMRuntime.getRuntime().registerNativeAllocation(mSize); diff --git a/core/java/android/os/SystemClock.java b/core/java/android/os/SystemClock.java index 23bd30a21c4c..0ed1ab6c8d72 100644 --- a/core/java/android/os/SystemClock.java +++ b/core/java/android/os/SystemClock.java @@ -314,6 +314,15 @@ public final class SystemClock { } /** + * @see #currentNetworkTimeMillis(ITimeDetectorService) + * @hide + */ + public static long currentNetworkTimeMillis() { + return currentNetworkTimeMillis(ITimeDetectorService.Stub + .asInterface(ServiceManager.getService(Context.TIME_DETECTOR_SERVICE))); + } + + /** * Returns milliseconds since January 1, 1970 00:00:00.0 UTC, synchronized * using a remote network source outside the device. * <p> @@ -331,14 +340,14 @@ public final class SystemClock { * at any time. Due to network delays, variations between servers, or local * (client side) clock drift, the accuracy of the returned times cannot be * guaranteed. In extreme cases, consecutive calls to {@link - * #currentNetworkTimeMillis()} could return times that are out of order. + * #currentNetworkTimeMillis(ITimeDetectorService)} could return times that + * are out of order. * * @throws DateTimeException when no network time can be provided. * @hide */ - public static long currentNetworkTimeMillis() { - ITimeDetectorService timeDetectorService = ITimeDetectorService.Stub - .asInterface(ServiceManager.getService(Context.TIME_DETECTOR_SERVICE)); + public static long currentNetworkTimeMillis( + ITimeDetectorService timeDetectorService) { if (timeDetectorService != null) { UnixEpochTime time; try { @@ -380,16 +389,21 @@ public final class SystemClock { * at any time. Due to network delays, variations between servers, or local * (client side) clock drift, the accuracy of the returned times cannot be * guaranteed. In extreme cases, consecutive calls to {@link - * Clock#millis()} on the returned {@link Clock}could return times that are + * Clock#millis()} on the returned {@link Clock} could return times that are * out of order. * * @throws DateTimeException when no network time can be provided. */ public static @NonNull Clock currentNetworkTimeClock() { return new SimpleClock(ZoneOffset.UTC) { + private ITimeDetectorService mSvc; @Override public long millis() { - return SystemClock.currentNetworkTimeMillis(); + if (mSvc == null) { + mSvc = ITimeDetectorService.Stub + .asInterface(ServiceManager.getService(Context.TIME_DETECTOR_SERVICE)); + } + return SystemClock.currentNetworkTimeMillis(mSvc); } }; } diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java index 536eca6f9a0f..f1ec0e4e9bdc 100644 --- a/core/java/android/os/UserManager.java +++ b/core/java/android/os/UserManager.java @@ -1978,7 +1978,6 @@ public class UserManager { * @see DevicePolicyManager#clearUserRestriction(ComponentName, String) * @see #getUserRestrictions() */ - @FlaggedApi(android.app.admin.flags.Flags.FLAG_ESIM_MANAGEMENT_ENABLED) public static final String DISALLOW_SIM_GLOBALLY = "no_sim_globally"; @@ -1999,7 +1998,6 @@ public class UserManager { * @see DevicePolicyManager#clearUserRestriction(ComponentName, String) * @see #getUserRestrictions() */ - @FlaggedApi(android.app.admin.flags.Flags.FLAG_ASSIST_CONTENT_USER_RESTRICTION_ENABLED) public static final String DISALLOW_ASSIST_CONTENT = "no_assist_content"; /** diff --git a/core/java/android/permission/TEST_MAPPING b/core/java/android/permission/TEST_MAPPING index a15d9bc1b485..b317b80d5677 100644 --- a/core/java/android/permission/TEST_MAPPING +++ b/core/java/android/permission/TEST_MAPPING @@ -1,15 +1,7 @@ { "presubmit": [ { - "name": "CtsPermissionTestCases", - "options": [ - { - "include-filter": "android.permission.cts.PermissionControllerTest" - }, - { - "include-filter": "android.permission.cts.RuntimePermissionPresentationInfoTest" - } - ] + "name": "CtsPermissionTestCases_Platform" } ], "postsubmit": [ @@ -36,4 +28,4 @@ ] } ] -}
\ No newline at end of file +} diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 85d23259ffbb..24f52d0151bb 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -19666,6 +19666,8 @@ public final class Settings { public static final int PAIRED_DEVICE_OS_TYPE_ANDROID = 1; /** @hide */ public static final int PAIRED_DEVICE_OS_TYPE_IOS = 2; + /** @hide */ + public static final int PAIRED_DEVICE_OS_TYPE_NONE = 3; /** * The bluetooth settings selected BLE role for the companion. diff --git a/core/java/android/service/dreams/DreamOverlayService.java b/core/java/android/service/dreams/DreamOverlayService.java index 244257cb61c8..711c41498929 100644 --- a/core/java/android/service/dreams/DreamOverlayService.java +++ b/core/java/android/service/dreams/DreamOverlayService.java @@ -51,13 +51,14 @@ public abstract class DreamOverlayService extends Service { */ private Executor mExecutor; - private boolean mCurrentRedirectToWake; + private Boolean mCurrentRedirectToWake; // An {@link IDreamOverlayClient} implementation that identifies itself when forwarding // requests to the {@link DreamOverlayService} private static class OverlayClient extends IDreamOverlayClient.Stub { private final WeakReference<DreamOverlayService> mService; private boolean mShowComplications; + private boolean mIsPreview; private ComponentName mDreamComponent; IDreamOverlayCallback mDreamOverlayCallback; @@ -75,9 +76,11 @@ public abstract class DreamOverlayService extends Service { @Override public void startDream(WindowManager.LayoutParams params, IDreamOverlayCallback callback, - String dreamComponent, boolean shouldShowComplications) throws RemoteException { + String dreamComponent, boolean isPreview, boolean shouldShowComplications) + throws RemoteException { mDreamComponent = ComponentName.unflattenFromString(dreamComponent); mShowComplications = shouldShowComplications; + mIsPreview = isPreview; mDreamOverlayCallback = callback; applyToDream(dreamOverlayService -> dreamOverlayService.startDream(this, params)); } @@ -124,6 +127,10 @@ public abstract class DreamOverlayService extends Service { return mShowComplications; } + private boolean isDreamInPreviewMode() { + return mIsPreview; + } + private ComponentName getComponent() { return mDreamComponent; } @@ -134,7 +141,7 @@ public abstract class DreamOverlayService extends Service { mExecutor.execute(() -> { endDreamInternal(mCurrentClient); mCurrentClient = client; - if (Flags.dreamWakeRedirect()) { + if (Flags.dreamWakeRedirect() && mCurrentRedirectToWake != null) { mCurrentClient.redirectWake(mCurrentRedirectToWake); } @@ -303,7 +310,6 @@ public abstract class DreamOverlayService extends Service { * * @hide */ - @FlaggedApi(Flags.FLAG_DREAM_WAKE_REDIRECT) public void onWakeRequested() { } @@ -320,6 +326,19 @@ public abstract class DreamOverlayService extends Service { } /** + * Returns whether dream is in preview mode. + */ + @FlaggedApi(Flags.FLAG_PUBLISH_PREVIEW_STATE_TO_OVERLAY) + public final boolean isDreamInPreviewMode() { + if (mCurrentClient == null) { + throw new IllegalStateException( + "requested if preview when no dream active"); + } + + return mCurrentClient.isDreamInPreviewMode(); + } + + /** * Returns the active dream component. * @hide */ diff --git a/core/java/android/service/dreams/DreamService.java b/core/java/android/service/dreams/DreamService.java index c3585e3c5288..ce31e1ea7e38 100644 --- a/core/java/android/service/dreams/DreamService.java +++ b/core/java/android/service/dreams/DreamService.java @@ -1701,6 +1701,7 @@ public class DreamService extends Service implements Window.Callback { try { overlay.startDream(mWindow.getAttributes(), mOverlayCallback, mDreamComponent.flattenToString(), + mPreviewMode, mShouldShowComplications); } catch (RemoteException e) { Log.e(mTag, "could not send window attributes:" + e); diff --git a/core/java/android/service/dreams/IDreamOverlayClient.aidl b/core/java/android/service/dreams/IDreamOverlayClient.aidl index 0eb15a07edf7..e9df402c6d07 100644 --- a/core/java/android/service/dreams/IDreamOverlayClient.aidl +++ b/core/java/android/service/dreams/IDreamOverlayClient.aidl @@ -31,11 +31,12 @@ interface IDreamOverlayClient { * @param callback The {@link IDreamOverlayCallback} for requesting actions such as exiting the * dream. * @param dreamComponent The component name of the dream service requesting overlay. + * @param isPreview Whether the dream is in preview mode. * @param shouldShowComplications Whether the dream overlay should show complications, e.g. clock * and weather. */ void startDream(in LayoutParams params, in IDreamOverlayCallback callback, - in String dreamComponent, in boolean shouldShowComplications); + in String dreamComponent, in boolean isPreview, in boolean shouldShowComplications); /** Called when the dream is waking, to do any exit animations */ void wakeUp(); diff --git a/core/java/android/service/dreams/flags.aconfig b/core/java/android/service/dreams/flags.aconfig index 83e0adfca3be..72f2de805474 100644 --- a/core/java/android/service/dreams/flags.aconfig +++ b/core/java/android/service/dreams/flags.aconfig @@ -57,3 +57,13 @@ flag { purpose: PURPOSE_BUGFIX } } + +flag { + name: "publish_preview_state_to_overlay" + namespace: "systemui" + description: "send preview information from dream to overlay" + bug: "333734282" + metadata { + purpose: PURPOSE_BUGFIX + } +} diff --git a/core/java/android/util/Half.java b/core/java/android/util/Half.java index fe536a6e4e68..22583acb75ce 100644 --- a/core/java/android/util/Half.java +++ b/core/java/android/util/Half.java @@ -19,6 +19,7 @@ package android.util; import android.annotation.HalfFloat; import android.annotation.NonNull; import android.annotation.Nullable; +import android.ravenwood.annotation.RavenwoodKeepWholeClass; import libcore.util.FP16; @@ -92,6 +93,7 @@ import libcore.util.FP16; * <p>This table shows that numbers higher than 1024 lose all fractional precision.</p> */ @SuppressWarnings("SimplifiableIfStatement") +@RavenwoodKeepWholeClass public final class Half extends Number implements Comparable<Half> { /** * The number of bits used to represent a half-precision float value. diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java index 1f7ed8be357c..82c52a6e8931 100644 --- a/core/java/android/view/Display.java +++ b/core/java/android/view/Display.java @@ -314,6 +314,8 @@ public final class Display { * @hide * @see #getFlags() */ + @SuppressLint("UnflaggedApi") // Promotion to TestApi + @TestApi public static final int FLAG_ALWAYS_UNLOCKED = 1 << 9; /** @@ -323,6 +325,8 @@ public final class Display { * @hide * @see #getFlags() */ + @SuppressLint("UnflaggedApi") // Promotion to TestApi + @TestApi public static final int FLAG_TOUCH_FEEDBACK_DISABLED = 1 << 10; /** @@ -336,6 +340,8 @@ public final class Display { * @see #FLAG_TRUSTED * @hide */ + @SuppressLint("UnflaggedApi") // Promotion to TestApi + @TestApi public static final int FLAG_OWN_FOCUS = 1 << 11; /** @@ -642,6 +648,8 @@ public final class Display { * @hide */ // TODO (b/114338689): Remove the flag and use WindowManager#REMOVE_CONTENT_MODE_DESTROY + @SuppressLint("UnflaggedApi") // Promotion to TestApi + @TestApi public static final int REMOVE_MODE_DESTROY_CONTENT = 1; /** @hide */ diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java index b1df51f7affa..7c8cd932f737 100644 --- a/core/java/android/view/InsetsController.java +++ b/core/java/android/view/InsetsController.java @@ -488,7 +488,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation @Override public Interpolator getInsetsInterpolator(boolean hasZeroInsetsIme) { if ((mRequestedTypes & ime()) != 0) { - if (mHasAnimationCallbacks && hasZeroInsetsIme) { + if (mHasAnimationCallbacks && !hasZeroInsetsIme) { return SYNC_IME_INTERPOLATOR; } else if (mShow) { return LINEAR_OUT_SLOW_IN_INTERPOLATOR; @@ -536,7 +536,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation @Override public long getDurationMs(boolean hasZeroInsetsIme) { if ((mRequestedTypes & ime()) != 0) { - if (mHasAnimationCallbacks && hasZeroInsetsIme) { + if (mHasAnimationCallbacks && !hasZeroInsetsIme) { return ANIMATION_DURATION_SYNC_IME_MS; } else { return ANIMATION_DURATION_UNSYNC_IME_MS; diff --git a/core/java/android/view/InsetsSourceConsumer.java b/core/java/android/view/InsetsSourceConsumer.java index 477e35b6e655..391d757365e6 100644 --- a/core/java/android/view/InsetsSourceConsumer.java +++ b/core/java/android/view/InsetsSourceConsumer.java @@ -310,21 +310,22 @@ public class InsetsSourceConsumer { } final boolean requestedVisible = (mController.getRequestedVisibleTypes() & mType) != 0; + // 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; + } 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) { + // TODO(b/323136120) add a flag to the control, to define whether a leash is + // needed and make it generic for all types + if (mId == InsetsSource.ID_IME && mSourceControl.getLeash() == null) { if (DEBUG) { Log.d(TAG, TextUtils.formatSimple( "applyLocalVisibilityOverride: Set the source visibility to false, as" @@ -338,16 +339,6 @@ public class InsetsSourceConsumer { // 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; diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java index 9e4b27d3fa55..2dda835436bc 100644 --- a/core/java/android/view/SurfaceControl.java +++ b/core/java/android/view/SurfaceControl.java @@ -445,16 +445,20 @@ public final class SurfaceControl implements Parcelable { // Jank due to unknown reasons. public static final int UNKNOWN = 0x80; - public JankData(long frameVsyncId, @JankType int jankType, long frameIntervalNs) { + public JankData(long frameVsyncId, @JankType int jankType, long frameIntervalNs, + long scheduledAppFrameTimeNs, long actualAppFrameTimeNs) { this.frameVsyncId = frameVsyncId; this.jankType = jankType; this.frameIntervalNs = frameIntervalNs; - + this.scheduledAppFrameTimeNs = scheduledAppFrameTimeNs; + this.actualAppFrameTimeNs = actualAppFrameTimeNs; } public final long frameVsyncId; public final @JankType int jankType; public final long frameIntervalNs; + public final long scheduledAppFrameTimeNs; + public final long actualAppFrameTimeNs; } /** diff --git a/core/java/android/view/accessibility/AccessibilityManager.java b/core/java/android/view/accessibility/AccessibilityManager.java index a4cea3364998..ab29df357268 100644 --- a/core/java/android/view/accessibility/AccessibilityManager.java +++ b/core/java/android/view/accessibility/AccessibilityManager.java @@ -1051,6 +1051,52 @@ public final class AccessibilityManager { } /** + * Registers callback for when user initialization has completed. + * Does nothing if the same callback is already registered. + * + * @param callback The callback to be registered + * @hide + */ + public void registerUserInitializationCompleteCallback( + @NonNull IUserInitializationCompleteCallback callback) { + IAccessibilityManager service; + synchronized (mLock) { + service = getServiceLocked(); + if (service == null) { + return; + } + } + try { + service.registerUserInitializationCompleteCallback(callback); + } catch (RemoteException re) { + Log.e(LOG_TAG, "Error while registering userInitializationCompleteCallback. ", re); + } + } + + /** + * Unregisters callback for when user initialization has completed. + * + * @param callback The callback to be unregistered + * @hide + */ + public void unregisterUserInitializationCompleteCallback( + @NonNull IUserInitializationCompleteCallback callback) { + IAccessibilityManager service; + synchronized (mLock) { + service = getServiceLocked(); + if (service == null) { + return; + } + } + try { + service.unregisterUserInitializationCompleteCallback(callback); + } catch (RemoteException re) { + Log.e(LOG_TAG, + "Error while unregistering userInitializationCompleteCallback. ", re); + } + } + + /** * Whether the current accessibility request comes from an * {@link AccessibilityService} with the {@link AccessibilityServiceInfo#isAccessibilityTool} * property set to true. diff --git a/core/java/android/view/accessibility/IAccessibilityManager.aidl b/core/java/android/view/accessibility/IAccessibilityManager.aidl index 72a1fe424906..bf79a2c6c6ea 100644 --- a/core/java/android/view/accessibility/IAccessibilityManager.aidl +++ b/core/java/android/view/accessibility/IAccessibilityManager.aidl @@ -29,6 +29,7 @@ import android.view.accessibility.IAccessibilityInteractionConnection; import android.view.accessibility.IAccessibilityManagerClient; import android.view.accessibility.AccessibilityWindowAttributes; import android.view.accessibility.IMagnificationConnection; +import android.view.accessibility.IUserInitializationCompleteCallback; import android.view.InputEvent; import android.view.IWindow; import android.view.MagnificationSpec; @@ -192,4 +193,10 @@ interface IAccessibilityManager { @EnforcePermission("MANAGE_ACCESSIBILITY") Bundle getA11yFeatureToTileMap(int userId); + + @RequiresNoPermission + void registerUserInitializationCompleteCallback(IUserInitializationCompleteCallback callback); + + @RequiresNoPermission + void unregisterUserInitializationCompleteCallback(IUserInitializationCompleteCallback callback); } diff --git a/core/java/android/view/accessibility/IUserInitializationCompleteCallback.aidl b/core/java/android/view/accessibility/IUserInitializationCompleteCallback.aidl new file mode 100644 index 000000000000..fe6c8e25dd00 --- /dev/null +++ b/core/java/android/view/accessibility/IUserInitializationCompleteCallback.aidl @@ -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 android.view.accessibility; + +/** + * A callback for when a new user finishes initializing + * NOTE: Must remain a oneway interface, as it is called from system_server while holding a lock. + * oneway allows it to return immediately and not hold the lock for longer than is necessary. + * @hide + */ + +oneway interface IUserInitializationCompleteCallback { + + /** + * Called when a user initialization completes. + * + * @param userId the id of the initialized user + */ + @RequiresNoPermission + void onUserInitializationComplete(int userId); +} diff --git a/core/java/android/widget/Chronometer.java b/core/java/android/widget/Chronometer.java index 9931aea41913..ac5656dd0201 100644 --- a/core/java/android/widget/Chronometer.java +++ b/core/java/android/widget/Chronometer.java @@ -289,8 +289,7 @@ public class Chronometer extends TextView { private synchronized void updateText(long now) { mNow = now; - long seconds = mCountDown ? mBase - now : now - mBase; - seconds /= 1000; + long seconds = Math.round((mCountDown ? mBase - now - 499 : now - mBase) / 1000f); boolean negative = false; if (seconds < 0) { seconds = -seconds; @@ -348,9 +347,19 @@ public class Chronometer extends TextView { }; private void postTickOnNextSecond() { - long nowMillis = SystemClock.elapsedRealtime(); - int millis = (int) ((nowMillis - mBase) % 1000); - postDelayed(mTickRunnable, 1000 - millis); + long nowMillis = mNow; + long delayMillis; + if (mCountDown) { + delayMillis = (mBase - nowMillis) % 1000; + if (delayMillis <= 0) { + delayMillis += 1000; + } + } else { + delayMillis = 1000 - (Math.abs(nowMillis - mBase) % 1000); + } + // Aim for 1 millisecond into the next second so we don't update exactly on the second + delayMillis++; + postDelayed(mTickRunnable, delayMillis); } void dispatchChronometerTick() { diff --git a/core/java/android/window/SnapshotDrawerUtils.java b/core/java/android/window/SnapshotDrawerUtils.java index 205f1defccd1..2f595d107a6c 100644 --- a/core/java/android/window/SnapshotDrawerUtils.java +++ b/core/java/android/window/SnapshotDrawerUtils.java @@ -59,7 +59,6 @@ import android.os.IBinder; import android.util.Log; import android.view.InsetsState; import android.view.SurfaceControl; -import android.view.SurfaceSession; import android.view.ViewGroup; import android.view.WindowInsets; import android.view.WindowManager; @@ -185,7 +184,6 @@ public class SnapshotDrawerUtils { private void drawSizeMismatchSnapshot() { final HardwareBuffer buffer = mSnapshot.getHardwareBuffer(); - final SurfaceSession session = new SurfaceSession(); // We consider nearly matched dimensions as there can be rounding errors and the user // won't notice very minute differences from scaling one dimension more than the other @@ -193,7 +191,7 @@ public class SnapshotDrawerUtils { && !Flags.drawSnapshotAspectRatioMatch(); // Keep a reference to it such that it doesn't get destroyed when finalized. - SurfaceControl childSurfaceControl = new SurfaceControl.Builder(session) + SurfaceControl childSurfaceControl = new SurfaceControl.Builder() .setName(mTitle + " - task-snapshot-surface") .setBLASTLayer() .setFormat(buffer.getFormat()) diff --git a/core/java/android/window/TransitionInfo.java b/core/java/android/window/TransitionInfo.java index 1083f64513b1..ec79f94a6dd3 100644 --- a/core/java/android/window/TransitionInfo.java +++ b/core/java/android/window/TransitionInfo.java @@ -1141,6 +1141,7 @@ public final class TransitionInfo implements Parcelable { // Customize activity transition animation private CustomActivityTransition mCustomActivityOpenTransition; private CustomActivityTransition mCustomActivityCloseTransition; + private int mUserId; private AnimationOptions(int type) { mType = type; @@ -1159,6 +1160,7 @@ public final class TransitionInfo implements Parcelable { mAnimations = in.readInt(); mCustomActivityOpenTransition = in.readTypedObject(CustomActivityTransition.CREATOR); mCustomActivityCloseTransition = in.readTypedObject(CustomActivityTransition.CREATOR); + mUserId = in.readInt(); } /** Make basic customized animation for a package */ @@ -1283,6 +1285,14 @@ public final class TransitionInfo implements Parcelable { return options; } + public void setUserId(int userId) { + mUserId = userId; + } + + public int getUserId() { + return mUserId; + } + public int getType() { return mType; } @@ -1349,6 +1359,7 @@ public final class TransitionInfo implements Parcelable { dest.writeInt(mAnimations); dest.writeTypedObject(mCustomActivityOpenTransition, flags); dest.writeTypedObject(mCustomActivityCloseTransition, flags); + dest.writeInt(mUserId); } @NonNull @@ -1406,6 +1417,7 @@ public final class TransitionInfo implements Parcelable { if (mExitResId != DEFAULT_ANIMATION_RESOURCES_ID) { sb.append(" exitResId=").append(mExitResId); } + sb.append(" mUserId=").append(mUserId); sb.append('}'); return sb.toString(); } diff --git a/core/java/android/window/flags/lse_desktop_experience.aconfig b/core/java/android/window/flags/lse_desktop_experience.aconfig index 217bca77af0c..ebf87f1d1dba 100644 --- a/core/java/android/window/flags/lse_desktop_experience.aconfig +++ b/core/java/android/window/flags/lse_desktop_experience.aconfig @@ -115,6 +115,16 @@ flag { } flag { + name: "respect_orientation_change_for_unresizeable" + namespace: "lse_desktop_experience" + description: "Whether to resize task to respect requested orientation change of unresizeable activity" + bug: "353338503" + metadata { + purpose: PURPOSE_BUGFIX + } +} + +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" diff --git a/core/java/android/window/flags/wallpaper_manager.aconfig b/core/java/android/window/flags/wallpaper_manager.aconfig index 8c6721a7e96a..efacc346ac0a 100644 --- a/core/java/android/window/flags/wallpaper_manager.aconfig +++ b/core/java/android/window/flags/wallpaper_manager.aconfig @@ -49,3 +49,13 @@ flag { purpose: PURPOSE_BUGFIX } } + +flag { + name: "avoid_rebinding_intentionally_disconnected_wallpaper" + namespace: "systemui" + description: "Prevents rebinding with intentionally disconnected wallpaper services." + bug: "332871851" + metadata { + purpose: PURPOSE_BUGFIX + } +} diff --git a/core/java/android/window/flags/windowing_frontend.aconfig b/core/java/android/window/flags/windowing_frontend.aconfig index 67fc27042ff8..a786fc24d9a7 100644 --- a/core/java/android/window/flags/windowing_frontend.aconfig +++ b/core/java/android/window/flags/windowing_frontend.aconfig @@ -246,3 +246,14 @@ flag { purpose: PURPOSE_BUGFIX } } + +flag { + name: "always_capture_activity_snapshot" + namespace: "windowing_frontend" + description: "Always capture activity snapshot regardless predictive back status" + bug: "362183912" + is_fixed_read_only: true + metadata { + purpose: PURPOSE_BUGFIX + } +}
\ No newline at end of file diff --git a/core/java/com/android/internal/infra/TEST_MAPPING b/core/java/com/android/internal/infra/TEST_MAPPING index e4550c0db135..35f0553d41d7 100644 --- a/core/java/com/android/internal/infra/TEST_MAPPING +++ b/core/java/com/android/internal/infra/TEST_MAPPING @@ -9,15 +9,7 @@ ] }, { - "name": "CtsPermissionTestCases", - "options": [ - { - "include-filter": "android.permission.cts.PermissionControllerTest" - }, - { - "exclude-annotation": "androidx.test.filters.FlakyTest" - } - ] + "name": "CtsPermissionTestCases_Platform" }, { "name": "FrameworksCoreTests_internal_infra" diff --git a/core/java/com/android/internal/jank/FrameTracker.java b/core/java/com/android/internal/jank/FrameTracker.java index 53ef49bd3f65..d474c6db4f02 100644 --- a/core/java/com/android/internal/jank/FrameTracker.java +++ b/core/java/com/android/internal/jank/FrameTracker.java @@ -127,7 +127,7 @@ public class FrameTracker implements HardwareRendererObserver.OnFrameMetricsAvai private Runnable mWaitForFinishTimedOut; private static class JankInfo { - long frameVsyncId; + final long frameVsyncId; long totalDurationNanos; boolean isFirstFrame; boolean hwuiCallbackFired; @@ -135,29 +135,42 @@ public class FrameTracker implements HardwareRendererObserver.OnFrameMetricsAvai @JankType int jankType; @RefreshRate int refreshRate; - static JankInfo createFromHwuiCallback(long frameVsyncId, long totalDurationNanos, - boolean isFirstFrame) { - return new JankInfo(frameVsyncId, true, false, JANK_NONE, UNKNOWN_REFRESH_RATE, - totalDurationNanos, isFirstFrame); + static JankInfo createFromHwuiCallback( + long frameVsyncId, long totalDurationNanos, boolean isFirstFrame) { + return new JankInfo(frameVsyncId).update(totalDurationNanos, isFirstFrame); } - static JankInfo createFromSurfaceControlCallback(long frameVsyncId, - @JankType int jankType, @RefreshRate int refreshRate) { - return new JankInfo( - frameVsyncId, false, true, jankType, refreshRate, 0, false /* isFirstFrame */); + static JankInfo createFromSurfaceControlCallback(SurfaceControl.JankData jankStat) { + return new JankInfo(jankStat.frameVsyncId).update(jankStat); } - private JankInfo(long frameVsyncId, boolean hwuiCallbackFired, - boolean surfaceControlCallbackFired, @JankType int jankType, - @RefreshRate int refreshRate, - long totalDurationNanos, boolean isFirstFrame) { + private JankInfo(long frameVsyncId) { this.frameVsyncId = frameVsyncId; - this.hwuiCallbackFired = hwuiCallbackFired; - this.surfaceControlCallbackFired = surfaceControlCallbackFired; - this.jankType = jankType; - this.refreshRate = refreshRate; - this.totalDurationNanos = totalDurationNanos; + this.hwuiCallbackFired = false; + this.surfaceControlCallbackFired = false; + this.jankType = JANK_NONE; + this.refreshRate = UNKNOWN_REFRESH_RATE; + this.totalDurationNanos = 0; + this.isFirstFrame = false; + } + + private JankInfo update(SurfaceControl.JankData jankStat) { + this.surfaceControlCallbackFired = true; + this.jankType = jankStat.jankType; + this.refreshRate = DisplayRefreshRate.getRefreshRate(jankStat.frameIntervalNs); + if (Flags.useSfFrameDuration()) { + this.totalDurationNanos = jankStat.actualAppFrameTimeNs; + } + return this; + } + + private JankInfo update(long totalDurationNanos, boolean isFirstFrame) { + this.hwuiCallbackFired = true; + if (!Flags.useSfFrameDuration()) { + this.totalDurationNanos = totalDurationNanos; + } this.isFirstFrame = isFirstFrame; + return this; } @Override @@ -457,16 +470,12 @@ public class FrameTracker implements HardwareRendererObserver.OnFrameMetricsAvai if (!isInRange(jankStat.frameVsyncId)) { continue; } - int refreshRate = DisplayRefreshRate.getRefreshRate(jankStat.frameIntervalNs); JankInfo info = findJankInfo(jankStat.frameVsyncId); if (info != null) { - info.surfaceControlCallbackFired = true; - info.jankType = jankStat.jankType; - info.refreshRate = refreshRate; + info.update(jankStat); } else { mJankInfos.put((int) jankStat.frameVsyncId, - JankInfo.createFromSurfaceControlCallback( - jankStat.frameVsyncId, jankStat.jankType, refreshRate)); + JankInfo.createFromSurfaceControlCallback(jankStat)); } } processJankInfos(); @@ -513,9 +522,7 @@ public class FrameTracker implements HardwareRendererObserver.OnFrameMetricsAvai } JankInfo info = findJankInfo(frameVsyncId); if (info != null) { - info.hwuiCallbackFired = true; - info.totalDurationNanos = totalDurationNanos; - info.isFirstFrame = isFirstFrame; + info.update(totalDurationNanos, isFirstFrame); } else { mJankInfos.put((int) frameVsyncId, JankInfo.createFromHwuiCallback( frameVsyncId, totalDurationNanos, isFirstFrame)); diff --git a/core/java/com/android/internal/jank/flags.aconfig b/core/java/com/android/internal/jank/flags.aconfig new file mode 100644 index 000000000000..676bb70e7797 --- /dev/null +++ b/core/java/com/android/internal/jank/flags.aconfig @@ -0,0 +1,9 @@ +package: "com.android.internal.jank" +container: "system" + +flag { + name: "use_sf_frame_duration" + namespace: "android_platform_window_surfaces" + description: "Whether to get the frame duration from SurfaceFlinger, or HWUI" + bug: "354763298" +} diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java index e14249cc028b..e0529b339d4a 100644 --- a/core/java/com/android/internal/policy/PhoneWindow.java +++ b/core/java/com/android/internal/policy/PhoneWindow.java @@ -3333,6 +3333,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { Bundle args = new Bundle(); args.putInt(Intent.EXTRA_ASSIST_INPUT_DEVICE_ID, event.getDeviceId()); args.putLong(Intent.EXTRA_TIME, event.getEventTime()); + args.putBoolean(Intent.EXTRA_ASSIST_INPUT_HINT_KEYBOARD, true); ((SearchManager) getContext().getSystemService(Context.SEARCH_SERVICE)) .launchAssist(args); return true; diff --git a/core/java/com/android/internal/policy/TransitionAnimation.java b/core/java/com/android/internal/policy/TransitionAnimation.java index 238e6f56153b..201f26760956 100644 --- a/core/java/com/android/internal/policy/TransitionAnimation.java +++ b/core/java/com/android/internal/policy/TransitionAnimation.java @@ -49,7 +49,7 @@ import android.hardware.HardwareBuffer; import android.media.Image; import android.media.ImageReader; import android.os.Handler; -import android.os.SystemProperties; +import android.os.UserHandle; import android.util.Slog; import android.view.InflateException; import android.view.SurfaceControl; @@ -187,23 +187,44 @@ public class TransitionAnimation { return createHiddenByKeyguardExit(mContext, mInterpolator, onWallpaper, toShade, subtle); } + /** Load keyguard unocclude animation for user. */ + @Nullable + public Animation loadKeyguardUnoccludeAnimation(int userId) { + return loadDefaultAnimationRes(com.android.internal.R.anim.wallpaper_open_exit, userId); + } + + /** Same as {@code loadKeyguardUnoccludeAnimation} for current user. */ @Nullable public Animation loadKeyguardUnoccludeAnimation() { - return loadDefaultAnimationRes(com.android.internal.R.anim.wallpaper_open_exit); + return loadKeyguardUnoccludeAnimation(UserHandle.USER_CURRENT); } + /** Load voice activity open animation for user. */ @Nullable - public Animation loadVoiceActivityOpenAnimation(boolean enter) { + public Animation loadVoiceActivityOpenAnimation(boolean enter, int userId) { return loadDefaultAnimationRes(enter ? com.android.internal.R.anim.voice_activity_open_enter - : com.android.internal.R.anim.voice_activity_open_exit); + : com.android.internal.R.anim.voice_activity_open_exit, userId); } + /** Same as {@code loadVoiceActivityOpenAnimation} for current user. */ @Nullable - public Animation loadVoiceActivityExitAnimation(boolean enter) { + public Animation loadVoiceActivityOpenAnimation(boolean enter) { + return loadVoiceActivityOpenAnimation(enter, UserHandle.USER_CURRENT); + } + + /** Load voice activity exit animation for user. */ + @Nullable + public Animation loadVoiceActivityExitAnimation(boolean enter, int userId) { return loadDefaultAnimationRes(enter ? com.android.internal.R.anim.voice_activity_close_enter - : com.android.internal.R.anim.voice_activity_close_exit); + : com.android.internal.R.anim.voice_activity_close_exit, userId); + } + + /** Same as {@code loadVoiceActivityExitAnimation} for current user. */ + @Nullable + public Animation loadVoiceActivityExitAnimation(boolean enter) { + return loadVoiceActivityExitAnimation(enter, UserHandle.USER_CURRENT); } @Nullable @@ -211,10 +232,17 @@ public class TransitionAnimation { return loadAnimationRes(packageName, resId); } + /** Load cross profile app enter animation for user. */ @Nullable - public Animation loadCrossProfileAppEnterAnimation() { + public Animation loadCrossProfileAppEnterAnimation(int userId) { return loadAnimationRes(DEFAULT_PACKAGE, - com.android.internal.R.anim.task_open_enter_cross_profile_apps); + com.android.internal.R.anim.task_open_enter_cross_profile_apps, userId); + } + + /** Same as {@code loadCrossProfileAppEnterAnimation} for current user. */ + @Nullable + public Animation loadCrossProfileAppEnterAnimation() { + return loadCrossProfileAppEnterAnimation(UserHandle.USER_CURRENT); } @Nullable @@ -230,11 +258,11 @@ public class TransitionAnimation { appRect.height(), 0, null); } - /** Load animation by resource Id from specific package. */ + /** Load animation by resource Id from specific package for user. */ @Nullable - public Animation loadAnimationRes(String packageName, int resId) { + public Animation loadAnimationRes(String packageName, int resId, int userId) { if (ResourceId.isValid(resId)) { - AttributeCache.Entry ent = getCachedAnimations(packageName, resId); + AttributeCache.Entry ent = getCachedAnimations(packageName, resId, userId); if (ent != null) { return loadAnimationSafely(ent.context, resId, mTag); } @@ -242,10 +270,22 @@ public class TransitionAnimation { return null; } - /** Load animation by resource Id from android package. */ + /** Same as {@code loadAnimationRes} for current user. */ + @Nullable + public Animation loadAnimationRes(String packageName, int resId) { + return loadAnimationRes(packageName, resId, UserHandle.USER_CURRENT); + } + + /** Load animation by resource Id from android package for user. */ + @Nullable + public Animation loadDefaultAnimationRes(int resId, int userId) { + return loadAnimationRes(DEFAULT_PACKAGE, resId, userId); + } + + /** Same as {@code loadDefaultAnimationRes} for current user. */ @Nullable public Animation loadDefaultAnimationRes(int resId) { - return loadAnimationRes(DEFAULT_PACKAGE, resId); + return loadAnimationRes(DEFAULT_PACKAGE, resId, UserHandle.USER_CURRENT); } /** Load animation by attribute Id from specific LayoutParams */ @@ -378,10 +418,10 @@ public class TransitionAnimation { } @Nullable - private AttributeCache.Entry getCachedAnimations(String packageName, int resId) { + private AttributeCache.Entry getCachedAnimations(String packageName, int resId, int userId) { if (mDebug) { - Slog.v(mTag, "Loading animations: package=" - + packageName + " resId=0x" + Integer.toHexString(resId)); + Slog.v(mTag, "Loading animations: package=" + packageName + " resId=0x" + + Integer.toHexString(resId) + " for user=" + userId); } if (packageName != null) { if ((resId & 0xFF000000) == 0x01000000) { @@ -392,11 +432,16 @@ public class TransitionAnimation { + packageName); } return AttributeCache.instance().get(packageName, resId, - com.android.internal.R.styleable.WindowAnimation); + com.android.internal.R.styleable.WindowAnimation, userId); } return null; } + @Nullable + private AttributeCache.Entry getCachedAnimations(String packageName, int resId) { + return getCachedAnimations(packageName, resId, UserHandle.USER_CURRENT); + } + /** Returns window animation style ID from {@link LayoutParams} or from system in some cases */ public int getAnimationStyleResId(@NonNull LayoutParams lp) { int resId = lp.windowAnimations; diff --git a/core/java/com/android/internal/protolog/PerfettoProtoLogImpl.java b/core/java/com/android/internal/protolog/PerfettoProtoLogImpl.java index b2fdf17f8564..2ff8c8c4db37 100644 --- a/core/java/com/android/internal/protolog/PerfettoProtoLogImpl.java +++ b/core/java/com/android/internal/protolog/PerfettoProtoLogImpl.java @@ -190,7 +190,7 @@ public class PerfettoProtoLogImpl extends IProtoLogClient.Stub implements IProto IProtoLogConfigurationService.Stub.asInterface(ServiceManager.getService( PROTOLOG_CONFIGURATION_SERVICE)); Objects.requireNonNull(mProtoLogConfigurationService, - "ServiceManager returned a null ProtoLog service"); + "ServiceManager returned a null ProtoLog Configuration Service"); try { var args = new ProtoLogConfigurationService.RegisterClientArgs(); diff --git a/core/java/com/android/internal/protolog/ProtoLogDataSource.java b/core/java/com/android/internal/protolog/ProtoLogDataSource.java index 5c06b87f78ac..ef6bece0cc0c 100644 --- a/core/java/com/android/internal/protolog/ProtoLogDataSource.java +++ b/core/java/com/android/internal/protolog/ProtoLogDataSource.java @@ -195,7 +195,7 @@ public class ProtoLogDataSource extends DataSource<ProtoLogDataSource.Instance, int defaultLogFromLevelInt = configStream.readInt(DEFAULT_LOG_FROM_LEVEL); if (defaultLogFromLevelInt < defaultLogFromLevel.ordinal()) { defaultLogFromLevel = - logLevelFromInt(configStream.readInt(DEFAULT_LOG_FROM_LEVEL)); + logLevelFromInt(defaultLogFromLevelInt); } break; case (int) TRACING_MODE: diff --git a/core/java/com/android/internal/statusbar/StatusBarIcon.java b/core/java/com/android/internal/statusbar/StatusBarIcon.java index f8a143693c83..1938cdb0ba84 100644 --- a/core/java/com/android/internal/statusbar/StatusBarIcon.java +++ b/core/java/com/android/internal/statusbar/StatusBarIcon.java @@ -51,6 +51,19 @@ public class StatusBarIcon implements Parcelable { ResourceIcon } + public enum Shape { + /** + * Icon view should use WRAP_CONTENT -- so that the horizontal space occupied depends on the + * icon's shape (skinny/fat icons take less/more). Most icons will want to use this option + * for a nicer-looking overall spacing in the status bar, as long as the icon is "known" + * (i.e. not coming from a 3P package). + */ + WRAP_CONTENT, + + /** Icon should always be displayed in a space as wide as the status bar is tall. */ + FIXED_SPACE, + } + public UserHandle user; public String pkg; public Icon icon; @@ -59,6 +72,7 @@ public class StatusBarIcon implements Parcelable { public int number; public CharSequence contentDescription; public Type type; + public Shape shape; /** * Optional {@link Drawable} corresponding to {@link #icon}. This field is not parcelable, so @@ -68,7 +82,7 @@ public class StatusBarIcon implements Parcelable { @Nullable public Drawable preloadedIcon; public StatusBarIcon(UserHandle user, String resPackage, Icon icon, int iconLevel, int number, - CharSequence contentDescription, Type type) { + CharSequence contentDescription, Type type, Shape shape) { 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 @@ -83,6 +97,13 @@ public class StatusBarIcon implements Parcelable { this.number = number; this.contentDescription = contentDescription; this.type = type; + this.shape = shape; + } + + public StatusBarIcon(UserHandle user, String resPackage, Icon icon, int iconLevel, int number, + CharSequence contentDescription, Type type) { + this(user, resPackage, icon, iconLevel, number, contentDescription, type, + Shape.WRAP_CONTENT); } public StatusBarIcon(String iconPackage, UserHandle user, @@ -107,7 +128,7 @@ public class StatusBarIcon implements Parcelable { @Override public StatusBarIcon clone() { StatusBarIcon that = new StatusBarIcon(this.user, this.pkg, this.icon, - this.iconLevel, this.number, this.contentDescription, this.type); + this.iconLevel, this.number, this.contentDescription, this.type, this.shape); that.visible = this.visible; that.preloadedIcon = this.preloadedIcon; return that; @@ -129,6 +150,7 @@ public class StatusBarIcon implements Parcelable { this.number = in.readInt(); this.contentDescription = in.readCharSequence(); this.type = Type.valueOf(in.readString()); + this.shape = Shape.valueOf(in.readString()); } public void writeToParcel(Parcel out, int flags) { @@ -140,6 +162,7 @@ public class StatusBarIcon implements Parcelable { out.writeInt(this.number); out.writeCharSequence(this.contentDescription); out.writeString(this.type.name()); + out.writeString(this.shape.name()); } public int describeContents() { diff --git a/core/jni/android_util_Binder.cpp b/core/jni/android_util_Binder.cpp index 2068bd7bc8ea..46b4695a9cec 100644 --- a/core/jni/android_util_Binder.cpp +++ b/core/jni/android_util_Binder.cpp @@ -466,10 +466,25 @@ class JavaBBinderHolder public: sp<JavaBBinder> get(JNIEnv* env, jobject obj) { - AutoMutex _l(mLock); - sp<JavaBBinder> b = mBinder.promote(); - if (b == NULL) { - b = new JavaBBinder(env, obj); + sp<JavaBBinder> b; + { + AutoMutex _l(mLock); + // must take lock to promote because we set the same wp<> + // on another thread. + b = mBinder.promote(); + } + + if (b) return b; + + // b/360067751: constructor may trigger GC, so call outside lock + b = new JavaBBinder(env, obj); + + { + AutoMutex _l(mLock); + // if it was constructed on another thread in the meantime, + // return that. 'b' will just get destructed. + if (sp<JavaBBinder> b2 = mBinder.promote(); b2) return b2; + if (mVintf) { ::android::internal::Stability::markVintf(b.get()); } diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp index 0f531641903a..17c89f88b441 100644 --- a/core/jni/android_view_SurfaceControl.cpp +++ b/core/jni/android_view_SurfaceControl.cpp @@ -2089,9 +2089,11 @@ public: jobjectArray jJankDataArray = env->NewObjectArray(jankData.size(), gJankDataClassInfo.clazz, nullptr); for (size_t i = 0; i < jankData.size(); i++) { - jobject jJankData = env->NewObject(gJankDataClassInfo.clazz, gJankDataClassInfo.ctor, - jankData[i].frameVsyncId, jankData[i].jankType, - jankData[i].frameIntervalNs); + jobject jJankData = + env->NewObject(gJankDataClassInfo.clazz, gJankDataClassInfo.ctor, + jankData[i].frameVsyncId, jankData[i].jankType, + jankData[i].frameIntervalNs, jankData[i].scheduledAppFrameTimeNs, + jankData[i].actualAppFrameTimeNs); env->SetObjectArrayElement(jJankDataArray, i, jJankData); env->DeleteLocalRef(jJankData); } @@ -2727,7 +2729,7 @@ int register_android_view_SurfaceControl(JNIEnv* env) jclass jankDataClazz = FindClassOrDie(env, "android/view/SurfaceControl$JankData"); gJankDataClassInfo.clazz = MakeGlobalRefOrDie(env, jankDataClazz); - gJankDataClassInfo.ctor = GetMethodIDOrDie(env, gJankDataClassInfo.clazz, "<init>", "(JIJ)V"); + gJankDataClassInfo.ctor = GetMethodIDOrDie(env, gJankDataClassInfo.clazz, "<init>", "(JIJJJ)V"); jclass onJankDataListenerClazz = FindClassOrDie(env, "android/view/SurfaceControl$OnJankDataListener"); gJankDataListenerClassInfo.clazz = MakeGlobalRefOrDie(env, onJankDataListenerClazz); diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 91c33702d3e3..17ff2ebc4f20 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -843,6 +843,7 @@ <protected-broadcast android:name="android.app.action.CONSOLIDATED_NOTIFICATION_POLICY_CHANGED" /> <protected-broadcast android:name="android.intent.action.MAIN_USER_LOCKSCREEN_KNOWLEDGE_FACTOR_CHANGED" /> <protected-broadcast android:name="com.android.uwb.uwbcountrycode.GEOCODE_RETRY" /> + <protected-broadcast android:name="android.telephony.action.ACTION_SATELLITE_SUBSCRIBER_ID_LIST_CHANGED" /> <!-- ====================================================================== --> <!-- RUNTIME PERMISSIONS --> @@ -3765,7 +3766,6 @@ 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" android:protectionLevel="internal|role" /> @@ -4026,7 +4026,6 @@ 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" /> diff --git a/core/res/res/drawable/ic_zen_priority_modes.xml b/core/res/res/drawable/ic_zen_priority_modes.xml new file mode 100644 index 000000000000..98de27bb349f --- /dev/null +++ b/core/res/res/drawable/ic_zen_priority_modes.xml @@ -0,0 +1,25 @@ +<!-- +Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="960" + android:viewportHeight="960" + android:tint="?android:attr/colorControlNormal"> + <path + android:pathData="M160,480v-80h320v80L160,480ZM480,880q-80,0 -153.5,-29.5T196,764l56,-56q47,44 106,68t122,24q133,0 226.5,-93.5T800,480q0,-133 -93.5,-226.5T480,160v-80q83,0 155.5,31.5t127,86q54.5,54.5 86,127T880,480q0,82 -31.5,155t-86,127.5q-54.5,54.5 -127,86T480,880Z" + android:fillColor="@android:color/white"/> +</vector> diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml index bf5884bd4dff..381111cf518a 100644 --- a/core/res/res/values-af/strings.xml +++ b/core/res/res/values-af/strings.xml @@ -1195,8 +1195,7 @@ <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"Terug"</string> <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"Wissel invoermetode"</string> <string name="input_method_ime_switch_long_click_action_desc" msgid="3161942124116646998">"Maak invoermetodekieser oop"</string> - <!-- no translation found for input_method_switcher_settings_button (5609835654697108485) --> - <skip /> + <string name="input_method_switcher_settings_button" msgid="5609835654697108485">"Instellings"</string> <string name="low_internal_storage_view_title" msgid="9024241779284783414">"Bergingspasie word min"</string> <string name="low_internal_storage_view_text" msgid="8172166728369697835">"Sommige stelselfunksies werk moontlik nie"</string> <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Nie genoeg berging vir die stelsel nie. Maak seker jy het 250 MB spasie beskikbaar en herbegin."</string> diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml index d97c903ce134..aa7dcecc0b11 100644 --- a/core/res/res/values-am/strings.xml +++ b/core/res/res/values-am/strings.xml @@ -1195,8 +1195,7 @@ <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"ተመለስ"</string> <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"የግቤት ስልትን ቀይር"</string> <string name="input_method_ime_switch_long_click_action_desc" msgid="3161942124116646998">"የግቤት ስልት መራጭን ክፈት"</string> - <!-- no translation found for input_method_switcher_settings_button (5609835654697108485) --> - <skip /> + <string name="input_method_switcher_settings_button" msgid="5609835654697108485">"ቅንብሮች"</string> <string name="low_internal_storage_view_title" msgid="9024241779284783414">"የማከማቻ ቦታ እያለቀ ነው"</string> <string name="low_internal_storage_view_text" msgid="8172166728369697835">"አንዳንድ የስርዓት ተግባራት ላይሰሩ ይችላሉ"</string> <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"ለስርዓቱ የሚሆን በቂ ቦታ የለም። 250 ሜባ ነፃ ቦታ እንዳለዎት ያረጋግጡና ዳግም ያስጀምሩ።"</string> diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml index c7983411fcc1..4f03ff3d2812 100644 --- a/core/res/res/values-ar/strings.xml +++ b/core/res/res/values-ar/strings.xml @@ -1199,8 +1199,7 @@ <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"رجوع"</string> <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"تبديل أسلوب الإدخال"</string> <string name="input_method_ime_switch_long_click_action_desc" msgid="3161942124116646998">"فتح أداة اختيار أسلوب الإدخال"</string> - <!-- no translation found for input_method_switcher_settings_button (5609835654697108485) --> - <skip /> + <string name="input_method_switcher_settings_button" msgid="5609835654697108485">"الإعدادات"</string> <string name="low_internal_storage_view_title" msgid="9024241779284783414">"مساحة التخزين منخفضة"</string> <string name="low_internal_storage_view_text" msgid="8172166728369697835">"قد لا تعمل بعض وظائف النظام"</string> <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"ليست هناك مساحة تخزين كافية للنظام. تأكد من أنه لديك مساحة خالية تبلغ ٢٥٠ ميغابايت وأعد التشغيل."</string> diff --git a/core/res/res/values-as/strings.xml b/core/res/res/values-as/strings.xml index af97d4363105..8c9cda9652dd 100644 --- a/core/res/res/values-as/strings.xml +++ b/core/res/res/values-as/strings.xml @@ -1195,8 +1195,7 @@ <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"উভতি যাওক"</string> <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"ইনপুটৰ পদ্ধতি সলনি কৰক"</string> <string name="input_method_ime_switch_long_click_action_desc" msgid="3161942124116646998">"ইনপুট পদ্ধতি বাছনিকর্তা খোলক"</string> - <!-- no translation found for input_method_switcher_settings_button (5609835654697108485) --> - <skip /> + <string name="input_method_switcher_settings_button" msgid="5609835654697108485">"ছেটিং"</string> <string name="low_internal_storage_view_title" msgid="9024241779284783414">"ষ্ট’ৰেজৰ খালী ঠাই শেষ হৈ আছে"</string> <string name="low_internal_storage_view_text" msgid="8172166728369697835">"ছিষ্টেমৰ কিছুমান কাৰ্যকলাপে কাম নকৰিবও পাৰে"</string> <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"ছিষ্টেমৰ বাবে পৰ্যাপ্ত খালী ঠাই নাই। আপোনাৰ ২৫০এমবি খালী ঠাই থকাটো নিশ্চিত কৰক আৰু ৰিষ্টাৰ্ট কৰক।"</string> diff --git a/core/res/res/values-az/strings.xml b/core/res/res/values-az/strings.xml index 72daeaffd777..afbe7153aaff 100644 --- a/core/res/res/values-az/strings.xml +++ b/core/res/res/values-az/strings.xml @@ -1195,8 +1195,7 @@ <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"Geriyə"</string> <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"Daxiletmə metodunu dəyişdirin"</string> <string name="input_method_ime_switch_long_click_action_desc" msgid="3161942124116646998">"Daxiletmə metodu seçicisini açın"</string> - <!-- no translation found for input_method_switcher_settings_button (5609835654697108485) --> - <skip /> + <string name="input_method_switcher_settings_button" msgid="5609835654697108485">"Ayarlar"</string> <string name="low_internal_storage_view_title" msgid="9024241779284783414">"Yaddaş yeri bitir"</string> <string name="low_internal_storage_view_text" msgid="8172166728369697835">"Bəzi sistem funksiyaları işləməyə bilər"</string> <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Sistem üçün yetərincə yaddaş ehtiyatı yoxdur. 250 MB yaddaş ehtiyatının olmasına əmin olun və yenidən başladın."</string> diff --git a/core/res/res/values-b+sr+Latn/strings.xml b/core/res/res/values-b+sr+Latn/strings.xml index 917df575c1a2..a20775d1ccdc 100644 --- a/core/res/res/values-b+sr+Latn/strings.xml +++ b/core/res/res/values-b+sr+Latn/strings.xml @@ -1196,8 +1196,7 @@ <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"Nazad"</string> <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"Promenite metod unosa"</string> <string name="input_method_ime_switch_long_click_action_desc" msgid="3161942124116646998">"Otvori birač metoda unosa"</string> - <!-- no translation found for input_method_switcher_settings_button (5609835654697108485) --> - <skip /> + <string name="input_method_switcher_settings_button" msgid="5609835654697108485">"Podešavanja"</string> <string name="low_internal_storage_view_title" msgid="9024241779284783414">"Memorijski prostor je na izmaku"</string> <string name="low_internal_storage_view_text" msgid="8172166728369697835">"Neke sistemske funkcije možda ne funkcionišu"</string> <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Nema dovoljno memorijskog prostora za sistem. Uverite se da imate 250 MB slobodnog prostora i ponovo pokrenite."</string> diff --git a/core/res/res/values-be/strings.xml b/core/res/res/values-be/strings.xml index 86e81d1d9a03..871f8d428374 100644 --- a/core/res/res/values-be/strings.xml +++ b/core/res/res/values-be/strings.xml @@ -1197,8 +1197,7 @@ <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"Назад"</string> <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"Пераключэнне рэжыму ўводу"</string> <string name="input_method_ime_switch_long_click_action_desc" msgid="3161942124116646998">"Выбраць спосаб уводу"</string> - <!-- no translation found for input_method_switcher_settings_button (5609835654697108485) --> - <skip /> + <string name="input_method_switcher_settings_button" msgid="5609835654697108485">"Налады"</string> <string name="low_internal_storage_view_title" msgid="9024241779284783414">"Месца для захавання на зыходзе"</string> <string name="low_internal_storage_view_text" msgid="8172166728369697835">"Некаторыя сістэмныя функцыі могуць не працаваць"</string> <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Не хапае сховішча для сістэмы. Пераканайцеся, што ў вас ёсць 250 МБ свабоднага месца, і перазапусціце."</string> diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml index 9f869469d9bc..c22325c54ed2 100644 --- a/core/res/res/values-bg/strings.xml +++ b/core/res/res/values-bg/strings.xml @@ -1195,8 +1195,7 @@ <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"Назад"</string> <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"Превключване на метода на въвеждане"</string> <string name="input_method_ime_switch_long_click_action_desc" msgid="3161942124116646998">"Отваряне на инструмента за избор на метод на въвеждане"</string> - <!-- no translation found for input_method_switcher_settings_button (5609835654697108485) --> - <skip /> + <string name="input_method_switcher_settings_button" msgid="5609835654697108485">"Настройки"</string> <string name="low_internal_storage_view_title" msgid="9024241779284783414">"Мястото в хранилището е на изчерпване"</string> <string name="low_internal_storage_view_text" msgid="8172166728369697835">"Възможно е някои функции на системата да не работят"</string> <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"За системата няма достатъчно място в хранилището. Уверете се, че имате свободни 250 МБ, и рестартирайте."</string> diff --git a/core/res/res/values-bn/strings.xml b/core/res/res/values-bn/strings.xml index 9bcefa7c067e..776714cddbca 100644 --- a/core/res/res/values-bn/strings.xml +++ b/core/res/res/values-bn/strings.xml @@ -1195,8 +1195,7 @@ <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"ফিরে যান"</string> <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"ইনপুট পদ্ধতি পাল্টান"</string> <string name="input_method_ime_switch_long_click_action_desc" msgid="3161942124116646998">"ইনপুট পদ্ধতির পিকার খুলুন"</string> - <!-- no translation found for input_method_switcher_settings_button (5609835654697108485) --> - <skip /> + <string name="input_method_switcher_settings_button" msgid="5609835654697108485">"সেটিংস"</string> <string name="low_internal_storage_view_title" msgid="9024241779284783414">"স্টোরেজ পূর্ণ হতে চলেছে"</string> <string name="low_internal_storage_view_text" msgid="8172166728369697835">"কিছু কিছু সিস্টেম ক্রিয়াকলাপ কাজ নাও করতে পারে"</string> <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"সিস্টেমের জন্য যথেষ্ট স্টোরেজ নেই৷ আপনার কাছে ২৫০এমবি ফাঁকা স্থান রয়েছে কিনা সে বিষয়ে নিশ্চিত হন এবং সিস্টেম চালু করুন৷"</string> diff --git a/core/res/res/values-bs/strings.xml b/core/res/res/values-bs/strings.xml index 017e8b4116a9..9306cfd660de 100644 --- a/core/res/res/values-bs/strings.xml +++ b/core/res/res/values-bs/strings.xml @@ -1196,8 +1196,7 @@ <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"Nazad"</string> <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"Promjena načina unosa"</string> <string name="input_method_ime_switch_long_click_action_desc" msgid="3161942124116646998">"Otvaranje birača načina unosa"</string> - <!-- no translation found for input_method_switcher_settings_button (5609835654697108485) --> - <skip /> + <string name="input_method_switcher_settings_button" msgid="5609835654697108485">"Postavke"</string> <string name="low_internal_storage_view_title" msgid="9024241779284783414">"Ponestaje prostora za pohranu"</string> <string name="low_internal_storage_view_text" msgid="8172166728369697835">"Neke funkcije sistema možda neće raditi"</string> <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Nema dovoljno prostora za sistem. Obezbijedite 250MB slobodnog prostora i ponovo pokrenite uređaj."</string> diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml index e3b373415d91..80420dd80641 100644 --- a/core/res/res/values-ca/strings.xml +++ b/core/res/res/values-ca/strings.xml @@ -1196,8 +1196,7 @@ <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"Enrere"</string> <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"Canvia el mètode d\'introducció de text"</string> <string name="input_method_ime_switch_long_click_action_desc" msgid="3161942124116646998">"Obre el selector de mètode d\'introducció"</string> - <!-- no translation found for input_method_switcher_settings_button (5609835654697108485) --> - <skip /> + <string name="input_method_switcher_settings_button" msgid="5609835654697108485">"Configuració"</string> <string name="low_internal_storage_view_title" msgid="9024241779284783414">"L\'espai d\'emmagatzematge s\'està esgotant"</string> <string name="low_internal_storage_view_text" msgid="8172166728369697835">"És possible que algunes funcions del sistema no funcionin"</string> <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"No hi ha prou espai d\'emmagatzematge per al sistema. Comprova que tinguis 250 MB d\'espai lliure i reinicia."</string> diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml index 5dde261ac56e..bc5cdfedc658 100644 --- a/core/res/res/values-cs/strings.xml +++ b/core/res/res/values-cs/strings.xml @@ -1197,8 +1197,7 @@ <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"Zpět"</string> <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"Přepnout metodu zadávání"</string> <string name="input_method_ime_switch_long_click_action_desc" msgid="3161942124116646998">"Otevřít výběr metody zadávání"</string> - <!-- no translation found for input_method_switcher_settings_button (5609835654697108485) --> - <skip /> + <string name="input_method_switcher_settings_button" msgid="5609835654697108485">"Nastavení"</string> <string name="low_internal_storage_view_title" msgid="9024241779284783414">"V úložišti je málo místa"</string> <string name="low_internal_storage_view_text" msgid="8172166728369697835">"Některé systémové funkce nemusí fungovat"</string> <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Pro systém není dostatek místa v úložišti. Uvolněte alespoň 250 MB místa a restartujte zařízení."</string> diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml index 00fc1eb684d9..6cbe6739d134 100644 --- a/core/res/res/values-da/strings.xml +++ b/core/res/res/values-da/strings.xml @@ -1195,8 +1195,7 @@ <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"Tilbage"</string> <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"Skift indtastningsmetode"</string> <string name="input_method_ime_switch_long_click_action_desc" msgid="3161942124116646998">"Åbn indtastningsmetodevælgeren"</string> - <!-- no translation found for input_method_switcher_settings_button (5609835654697108485) --> - <skip /> + <string name="input_method_switcher_settings_button" msgid="5609835654697108485">"Indstillinger"</string> <string name="low_internal_storage_view_title" msgid="9024241779284783414">"Der er snart ikke mere lagerplads"</string> <string name="low_internal_storage_view_text" msgid="8172166728369697835">"Nogle systemfunktioner virker måske ikke"</string> <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Der er ikke nok ledig lagerplads til systemet. Sørg for, at du har 250 MB ledig plads, og genstart."</string> diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml index bc35788159de..dece83f95620 100644 --- a/core/res/res/values-de/strings.xml +++ b/core/res/res/values-de/strings.xml @@ -1195,8 +1195,7 @@ <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"Zurück"</string> <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"Eingabemethode wechseln"</string> <string name="input_method_ime_switch_long_click_action_desc" msgid="3161942124116646998">"Auswahl für die Eingabemethode öffnen"</string> - <!-- no translation found for input_method_switcher_settings_button (5609835654697108485) --> - <skip /> + <string name="input_method_switcher_settings_button" msgid="5609835654697108485">"Einstellungen"</string> <string name="low_internal_storage_view_title" msgid="9024241779284783414">"Der Speicherplatz wird knapp"</string> <string name="low_internal_storage_view_text" msgid="8172166728369697835">"Einige Systemfunktionen funktionieren eventuell nicht."</string> <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Der Speicherplatz reicht nicht für das System aus. Stelle sicher, dass 250 MB freier Speicherplatz vorhanden sind, und starte das Gerät dann neu."</string> diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml index b9f3d54c501e..cc179a406a54 100644 --- a/core/res/res/values-el/strings.xml +++ b/core/res/res/values-el/strings.xml @@ -1029,7 +1029,7 @@ <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="3118353451602377380">"Έχετε πληκτρολογήσει τον κωδικό πρόσβασης εσφαλμένα <xliff:g id="NUMBER_0">%1$d</xliff:g> φορές. \n\nΠροσπαθήστε ξανά σε <xliff:g id="NUMBER_1">%2$d</xliff:g> δευτερόλεπτα."</string> <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="2874278239714821984">"Έχετε πληκτρολογήσει τον αριθμό σας PIN εσφαλμένα <xliff:g id="NUMBER_0">%1$d</xliff:g> φορές. \n\nΠροσπαθήστε ξανά σε <xliff:g id="NUMBER_1">%2$d</xliff:g> δευτερόλεπτα."</string> <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="3069635524964070596">"Σχεδιάσατε το μοτίβο ξεκλειδώματος εσφαλμένα <xliff:g id="NUMBER_0">%1$d</xliff:g> φορές. Μετά από <xliff:g id="NUMBER_1">%2$d</xliff:g> ανεπιτυχείς προσπάθειες ακόμη, θα σας ζητηθεί να ξεκλειδώσετε το tablet σας με τη χρήση της σύνδεσής σας Google.\n\n Προσπαθήστε ξανά σε <xliff:g id="NUMBER_2">%3$d</xliff:g> δευτερόλεπτα."</string> - <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="6399092175942158529">"Σχεδιάσατε εσφαλμένα το μοτίβο ξεκλειδώματος <xliff:g id="NUMBER_0">%1$d</xliff:g> φορές. Μετά από ακόμα <xliff:g id="NUMBER_1">%2$d</xliff:g> ανεπιτυχείς προσπάθειες, θα σας ζητηθεί να ξεκλειδώσετε τη συσκευή σας Android TV χρησιμοποιώντας τα στοιχεία σύνδεσής σας στο Google.\n\nΠροσπαθήστε ξανά σε <xliff:g id="NUMBER_2">%3$d</xliff:g> δευτερόλεπτα."</string> + <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="6399092175942158529">"Σχεδιάσατε εσφαλμένα το μοτίβο ξεκλειδώματος <xliff:g id="NUMBER_0">%1$d</xliff:g> φορές. Μετά από ακόμα <xliff:g id="NUMBER_1">%2$d</xliff:g> ανεπιτυχείς προσπάθειες, θα σας ζητηθεί να ξεκλειδώσετε τη συσκευή σας Android TV χρησιμοποιώντας τα στοιχεία σύνδεσής σας στην Google.\n\nΠροσπαθήστε ξανά σε <xliff:g id="NUMBER_2">%3$d</xliff:g> δευτερόλεπτα."</string> <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="5691623136957148335">"Σχεδιάσατε το μοτίβο ξεκλειδώματος εσφαλμένα <xliff:g id="NUMBER_0">%1$d</xliff:g> φορές. Μετά από <xliff:g id="NUMBER_1">%2$d</xliff:g> ανεπιτυχείς προσπάθειες ακόμη, θα σας ζητηθεί να ξεκλειδώσετε το τηλέφωνό σας με τη χρήση της σύνδεσής σας Google.\n\n Προσπαθήστε ξανά σε <xliff:g id="NUMBER_2">%3$d</xliff:g> δευτερόλεπτα."</string> <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="7914445759242151426">"Προσπαθήσατε να ξεκλειδώσετε εσφαλμένα το tablet <xliff:g id="NUMBER_0">%1$d</xliff:g> φορές. Μετά από <xliff:g id="NUMBER_1">%2$d</xliff:g> προσπάθειες, το tablet θα επαναφερθεί στις εργοστασιακές ρυθμίσεις και όλα τα δεδομένα χρήστη θα χαθούν."</string> <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="4275591249631864248">"Δοκιμάσατε να ξεκλειδώσετε τη συσκευή Android TV <xliff:g id="NUMBER_0">%1$d</xliff:g> φορές χωρίς επιτυχία. Μετά από ακόμα <xliff:g id="NUMBER_1">%2$d</xliff:g> ανεπιτυχείς προσπάθειες, θα γίνει επαναφορά των προεπιλεγμένων εργοστασιακών ρυθμίσεων στη συσκευή σας Android TV και όλα τα δεδομένα χρήστη θα χαθούν."</string> @@ -1195,8 +1195,7 @@ <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"Πίσω"</string> <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"Εναλλαγή μεθόδου εισαγωγής"</string> <string name="input_method_ime_switch_long_click_action_desc" msgid="3161942124116646998">"Άνοιγμα εργαλείου επιλογής μεθόδου εισαγωγής"</string> - <!-- no translation found for input_method_switcher_settings_button (5609835654697108485) --> - <skip /> + <string name="input_method_switcher_settings_button" msgid="5609835654697108485">"Ρυθμίσεις"</string> <string name="low_internal_storage_view_title" msgid="9024241779284783414">"Ο αποθηκευτικός χώρος εξαντλείται"</string> <string name="low_internal_storage_view_text" msgid="8172166728369697835">"Ορισμένες λειτουργίες συστήματος ενδέχεται να μην λειτουργούν"</string> <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Δεν υπάρχει αρκετός αποθηκευτικός χώρος για το σύστημα. Βεβαιωθείτε ότι διαθέτετε 250 MB ελεύθερου χώρου και κάντε επανεκκίνηση."</string> diff --git a/core/res/res/values-en-rAU/strings.xml b/core/res/res/values-en-rAU/strings.xml index 6f265dfcd1a4..caa52c4097ec 100644 --- a/core/res/res/values-en-rAU/strings.xml +++ b/core/res/res/values-en-rAU/strings.xml @@ -1195,8 +1195,7 @@ <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"Back"</string> <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"Switch input method"</string> <string name="input_method_ime_switch_long_click_action_desc" msgid="3161942124116646998">"Open input method picker"</string> - <!-- no translation found for input_method_switcher_settings_button (5609835654697108485) --> - <skip /> + <string name="input_method_switcher_settings_button" msgid="5609835654697108485">"Settings"</string> <string name="low_internal_storage_view_title" msgid="9024241779284783414">"Storage space running out"</string> <string name="low_internal_storage_view_text" msgid="8172166728369697835">"Some system functions may not work"</string> <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Not enough storage for the system. Make sure that you have 250 MB of free space and restart."</string> diff --git a/core/res/res/values-en-rCA/strings.xml b/core/res/res/values-en-rCA/strings.xml index 6a4bc1a4b47f..3850b007ad07 100644 --- a/core/res/res/values-en-rCA/strings.xml +++ b/core/res/res/values-en-rCA/strings.xml @@ -1195,8 +1195,7 @@ <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"Back"</string> <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"Switch input method"</string> <string name="input_method_ime_switch_long_click_action_desc" msgid="3161942124116646998">"Open input method picker"</string> - <!-- no translation found for input_method_switcher_settings_button (5609835654697108485) --> - <skip /> + <string name="input_method_switcher_settings_button" msgid="5609835654697108485">"Settings"</string> <string name="low_internal_storage_view_title" msgid="9024241779284783414">"Storage space running out"</string> <string name="low_internal_storage_view_text" msgid="8172166728369697835">"Some system functions may not work"</string> <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Not enough storage for the system. Make sure you have 250MB of free space and restart."</string> diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml index 0bf754c1d1e4..bf5c61c1e8f3 100644 --- a/core/res/res/values-en-rGB/strings.xml +++ b/core/res/res/values-en-rGB/strings.xml @@ -1195,8 +1195,7 @@ <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"Back"</string> <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"Switch input method"</string> <string name="input_method_ime_switch_long_click_action_desc" msgid="3161942124116646998">"Open input method picker"</string> - <!-- no translation found for input_method_switcher_settings_button (5609835654697108485) --> - <skip /> + <string name="input_method_switcher_settings_button" msgid="5609835654697108485">"Settings"</string> <string name="low_internal_storage_view_title" msgid="9024241779284783414">"Storage space running out"</string> <string name="low_internal_storage_view_text" msgid="8172166728369697835">"Some system functions may not work"</string> <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Not enough storage for the system. Make sure that you have 250 MB of free space and restart."</string> diff --git a/core/res/res/values-en-rIN/strings.xml b/core/res/res/values-en-rIN/strings.xml index 04f5d5cf9a4b..e829fa3dad12 100644 --- a/core/res/res/values-en-rIN/strings.xml +++ b/core/res/res/values-en-rIN/strings.xml @@ -1195,8 +1195,7 @@ <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"Back"</string> <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"Switch input method"</string> <string name="input_method_ime_switch_long_click_action_desc" msgid="3161942124116646998">"Open input method picker"</string> - <!-- no translation found for input_method_switcher_settings_button (5609835654697108485) --> - <skip /> + <string name="input_method_switcher_settings_button" msgid="5609835654697108485">"Settings"</string> <string name="low_internal_storage_view_title" msgid="9024241779284783414">"Storage space running out"</string> <string name="low_internal_storage_view_text" msgid="8172166728369697835">"Some system functions may not work"</string> <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Not enough storage for the system. Make sure that you have 250 MB of free space and restart."</string> diff --git a/core/res/res/values-en-rXC/strings.xml b/core/res/res/values-en-rXC/strings.xml index 74d2fbeb8825..0b9be3b3da7b 100644 --- a/core/res/res/values-en-rXC/strings.xml +++ b/core/res/res/values-en-rXC/strings.xml @@ -1195,8 +1195,7 @@ <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"Back"</string> <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"Switch input method"</string> <string name="input_method_ime_switch_long_click_action_desc" msgid="3161942124116646998">"Open input method picker"</string> - <!-- no translation found for input_method_switcher_settings_button (5609835654697108485) --> - <skip /> + <string name="input_method_switcher_settings_button" msgid="5609835654697108485">"Settings"</string> <string name="low_internal_storage_view_title" msgid="9024241779284783414">"Storage space running out"</string> <string name="low_internal_storage_view_text" msgid="8172166728369697835">"Some system functions may not work"</string> <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Not enough storage for the system. Make sure you have 250MB of free space and restart."</string> diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml index b94201a27b4e..3814944c6d8c 100644 --- a/core/res/res/values-es-rUS/strings.xml +++ b/core/res/res/values-es-rUS/strings.xml @@ -1196,8 +1196,7 @@ <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"Atrás"</string> <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"Cambiar método de entrada"</string> <string name="input_method_ime_switch_long_click_action_desc" msgid="3161942124116646998">"Abrir selector de método de entrada"</string> - <!-- no translation found for input_method_switcher_settings_button (5609835654697108485) --> - <skip /> + <string name="input_method_switcher_settings_button" msgid="5609835654697108485">"Configuración"</string> <string name="low_internal_storage_view_title" msgid="9024241779284783414">"Queda poco espacio de almacenamiento"</string> <string name="low_internal_storage_view_text" msgid="8172166728369697835">"Es posible que algunas funciones del sistema no estén disponibles."</string> <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"No hay espacio suficiente para el sistema. Asegúrate de que haya 250 MB libres y reinicia el dispositivo."</string> diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml index 77f10f52d93e..215cf39c44b4 100644 --- a/core/res/res/values-es/strings.xml +++ b/core/res/res/values-es/strings.xml @@ -1196,8 +1196,7 @@ <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"Atrás"</string> <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"Cambiar método de introducción de texto"</string> <string name="input_method_ime_switch_long_click_action_desc" msgid="3161942124116646998">"Abrir selector de método de introducción"</string> - <!-- no translation found for input_method_switcher_settings_button (5609835654697108485) --> - <skip /> + <string name="input_method_switcher_settings_button" msgid="5609835654697108485">"Ajustes"</string> <string name="low_internal_storage_view_title" msgid="9024241779284783414">"Queda poco espacio"</string> <string name="low_internal_storage_view_text" msgid="8172166728369697835">"Es posible que algunas funciones del sistema no funcionen."</string> <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"No hay espacio suficiente para el sistema. Comprueba que haya 250 MB libres y reinicia el dispositivo."</string> @@ -2393,13 +2392,13 @@ <string name="connected_display_unavailable_notification_title" msgid="5265409360902073556">"No se puede proyectar a la pantalla"</string> <string name="connected_display_unavailable_notification_content" msgid="3845903313751217516">"Usa otro cable y vuelve a intentarlo"</string> <string name="connected_display_thermally_unavailable_notification_content" msgid="9205758199439955949">"Tu dispositivo está demasiado caliente y no puede proyectar a la pantalla hasta que se enfríe"</string> - <string name="concurrent_display_notification_name" msgid="1526911253558311131">"Dual Screen"</string> - <string name="concurrent_display_notification_active_title" msgid="4892473462327943673">"La función Dual Screen está activada"</string> + <string name="concurrent_display_notification_name" msgid="1526911253558311131">"Pantalla dual"</string> + <string name="concurrent_display_notification_active_title" msgid="4892473462327943673">"La función Pantalla dual está activada"</string> <string name="concurrent_display_notification_active_content" msgid="5889355473710601270">"<xliff:g id="APP_NAME">%1$s</xliff:g> está usando ambas pantallas para mostrar contenido"</string> <string name="concurrent_display_notification_thermal_title" msgid="5921609404644739229">"El dispositivo está demasiado caliente"</string> - <string name="concurrent_display_notification_thermal_content" msgid="2075484836527609319">"Dual Screen no está disponible porque el teléfono se está calentando demasiado"</string> - <string name="concurrent_display_notification_power_save_title" msgid="1794569070730736281">"Dual Screen no está disponible"</string> - <string name="concurrent_display_notification_power_save_content" msgid="2198116070583851493">"Dual Screen no está disponible porque la función Ahorro de batería está activada. Puedes desactivarla en Ajustes."</string> + <string name="concurrent_display_notification_thermal_content" msgid="2075484836527609319">"Pantalla dual no está disponible porque el teléfono se está calentando demasiado"</string> + <string name="concurrent_display_notification_power_save_title" msgid="1794569070730736281">"Pantalla dual no está disponible"</string> + <string name="concurrent_display_notification_power_save_content" msgid="2198116070583851493">"Pantalla dual no está disponible porque la función Ahorro de batería está activada. Puedes desactivarla en Ajustes."</string> <string name="device_state_notification_settings_button" msgid="691937505741872749">"Ir a Ajustes"</string> <string name="device_state_notification_turn_off_button" msgid="6327161707661689232">"Desactivar"</string> <string name="keyboard_layout_notification_selected_title" msgid="1202560174252421219">"<xliff:g id="DEVICE_NAME">%s</xliff:g> configurado"</string> diff --git a/core/res/res/values-et/strings.xml b/core/res/res/values-et/strings.xml index 1db2c5d02eea..4694efa44570 100644 --- a/core/res/res/values-et/strings.xml +++ b/core/res/res/values-et/strings.xml @@ -1195,8 +1195,7 @@ <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"Tagasi"</string> <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"Sisestusmeetodi vahetamine"</string> <string name="input_method_ime_switch_long_click_action_desc" msgid="3161942124116646998">"Sisestusmeetodi valija avamine"</string> - <!-- no translation found for input_method_switcher_settings_button (5609835654697108485) --> - <skip /> + <string name="input_method_switcher_settings_button" msgid="5609835654697108485">"Seaded"</string> <string name="low_internal_storage_view_title" msgid="9024241779284783414">"Talletusruum saab täis"</string> <string name="low_internal_storage_view_text" msgid="8172166728369697835">"Mõned süsteemifunktsioonid ei pruugi töötada"</string> <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Süsteemis pole piisavalt talletusruumi. Veenduge, et seadmes oleks 250 MB vaba ruumi, ja käivitage seade uuesti."</string> diff --git a/core/res/res/values-eu/strings.xml b/core/res/res/values-eu/strings.xml index 6f927453613c..2cae18a2893b 100644 --- a/core/res/res/values-eu/strings.xml +++ b/core/res/res/values-eu/strings.xml @@ -1195,8 +1195,7 @@ <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"Atzera"</string> <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"Aldatu idazketa-metodoa"</string> <string name="input_method_ime_switch_long_click_action_desc" msgid="3161942124116646998">"Ireki idazketa-metodoaren hautatzailea"</string> - <!-- no translation found for input_method_switcher_settings_button (5609835654697108485) --> - <skip /> + <string name="input_method_switcher_settings_button" msgid="5609835654697108485">"Ezarpenak"</string> <string name="low_internal_storage_view_title" msgid="9024241779284783414">"Memoria betetzen ari da"</string> <string name="low_internal_storage_view_text" msgid="8172166728369697835">"Sistemaren funtzio batzuek ez dute agian funtzionatuko"</string> <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Sisteman ez dago behar adina memoria. Ziurtatu gutxienez 250 MB erabilgarri dituzula eta, ondoren, berrabiarazi gailua."</string> @@ -1335,7 +1334,7 @@ <string name="ringtone_picker_title_alarm" msgid="7438934548339024767">"Alarma-soinuak"</string> <string name="ringtone_picker_title_notification" msgid="6387191794719608122">"Jakinarazpen-soinuak"</string> <string name="ringtone_unknown" msgid="5059495249862816475">"Ezezaguna"</string> - <string name="wifi_available_sign_in" msgid="381054692557675237">"Hasi saioa Wi-Fi sarean"</string> + <string name="wifi_available_sign_in" msgid="381054692557675237">"Hasi saioa wifi-sarean"</string> <string name="network_available_sign_in" msgid="1520342291829283114">"Hasi saioa sarean"</string> <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) --> <skip /> diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml index 728d87d43fbf..9a75d3a61dec 100644 --- a/core/res/res/values-fa/strings.xml +++ b/core/res/res/values-fa/strings.xml @@ -1195,8 +1195,7 @@ <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"برگشت"</string> <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"تغییر روش ورودی"</string> <string name="input_method_ime_switch_long_click_action_desc" msgid="3161942124116646998">"باز کردن انتخابگر روش ورودی"</string> - <!-- no translation found for input_method_switcher_settings_button (5609835654697108485) --> - <skip /> + <string name="input_method_switcher_settings_button" msgid="5609835654697108485">"تنظیمات"</string> <string name="low_internal_storage_view_title" msgid="9024241779284783414">"فضای ذخیرهسازی درحال پر شدن است"</string> <string name="low_internal_storage_view_text" msgid="8172166728369697835">"برخی از عملکردهای سیستم ممکن است کار نکنند"</string> <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"فضای ذخیرهسازی سیستم کافی نیست. اطمینان حاصل کنید که دارای ۲۵۰ مگابایت فضای خالی هستید و سیستم را راهاندازی مجدد کنید."</string> @@ -2392,13 +2391,13 @@ <string name="connected_display_unavailable_notification_title" msgid="5265409360902073556">"قرینهسازی روی نمایشگر ممکن نبود"</string> <string name="connected_display_unavailable_notification_content" msgid="3845903313751217516">"از کابل دیگری استفاده کنید و دوباره امتحان کنید"</string> <string name="connected_display_thermally_unavailable_notification_content" msgid="9205758199439955949">"دستگاه بسیار گرم است و تا زمانیکه خنک نشود نمیتواند محتوا را روی نمایشگر قرینهسازی کند."</string> - <string name="concurrent_display_notification_name" msgid="1526911253558311131">"Dual screen"</string> - <string name="concurrent_display_notification_active_title" msgid="4892473462327943673">"Dual Screen روشن است"</string> + <string name="concurrent_display_notification_name" msgid="1526911253558311131">"صفحهنمایش دوگانه"</string> + <string name="concurrent_display_notification_active_title" msgid="4892473462327943673">"«صفحهنمایش دوگانه» روشن است"</string> <string name="concurrent_display_notification_active_content" msgid="5889355473710601270">"<xliff:g id="APP_NAME">%1$s</xliff:g> از هر دو نمایشگر برای نمایش محتوا استفاده میکند"</string> <string name="concurrent_display_notification_thermal_title" msgid="5921609404644739229">"دستگاه بیشازحد گرم شده است"</string> - <string name="concurrent_display_notification_thermal_content" msgid="2075484836527609319">"Dual Screen دردسترس نیست زیرا تلفن بیشازحد گرم شده است"</string> - <string name="concurrent_display_notification_power_save_title" msgid="1794569070730736281">"Dual Screen دردسترس نیست"</string> - <string name="concurrent_display_notification_power_save_content" msgid="2198116070583851493">"Dual Screen دردسترس نیست چون «بهینهسازی باتری» روشن است. میتوانید این ویژگی را در «تنظیمات» خاموش کنید."</string> + <string name="concurrent_display_notification_thermal_content" msgid="2075484836527609319">"«صفحهنمایش دوگانه» دردسترس نیست زیرا تلفن بیشازحد گرم شده است"</string> + <string name="concurrent_display_notification_power_save_title" msgid="1794569070730736281">"«صفحهنمایش دوگانه» دردسترس نیست"</string> + <string name="concurrent_display_notification_power_save_content" msgid="2198116070583851493">"«صفحهنمایش دوگانه» دردسترس نیست چون «بهینهسازی باتری» روشن است. میتوانید این ویژگی را در «تنظیمات» خاموش کنید."</string> <string name="device_state_notification_settings_button" msgid="691937505741872749">"رفتن به تنظیمات"</string> <string name="device_state_notification_turn_off_button" msgid="6327161707661689232">"خاموش کردن"</string> <string name="keyboard_layout_notification_selected_title" msgid="1202560174252421219">"<xliff:g id="DEVICE_NAME">%s</xliff:g> پیکربندی شد"</string> diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml index 776b2dbcc804..1b2ddb0e38c7 100644 --- a/core/res/res/values-fi/strings.xml +++ b/core/res/res/values-fi/strings.xml @@ -1195,8 +1195,7 @@ <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"Takaisin"</string> <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"Vaihda syöttötapaa"</string> <string name="input_method_ime_switch_long_click_action_desc" msgid="3161942124116646998">"Avaa syöttötavan valinta"</string> - <!-- no translation found for input_method_switcher_settings_button (5609835654697108485) --> - <skip /> + <string name="input_method_switcher_settings_button" msgid="5609835654697108485">"Asetukset"</string> <string name="low_internal_storage_view_title" msgid="9024241779284783414">"Tallennustila loppumassa"</string> <string name="low_internal_storage_view_text" msgid="8172166728369697835">"Kaikki järjestelmätoiminnot eivät välttämättä toimi"</string> <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Tallennustila ei riitä. Varmista, että vapaata tilaa on 250 Mt, ja käynnistä uudelleen."</string> diff --git a/core/res/res/values-fr-rCA/strings.xml b/core/res/res/values-fr-rCA/strings.xml index 325615139f95..d3ebe5a3621d 100644 --- a/core/res/res/values-fr-rCA/strings.xml +++ b/core/res/res/values-fr-rCA/strings.xml @@ -1196,8 +1196,7 @@ <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"Retour"</string> <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"Changer de méthode d\'entrée"</string> <string name="input_method_ime_switch_long_click_action_desc" msgid="3161942124116646998">"Ouvrir le sélecteur de méthode d\'entrée"</string> - <!-- no translation found for input_method_switcher_settings_button (5609835654697108485) --> - <skip /> + <string name="input_method_switcher_settings_button" msgid="5609835654697108485">"Paramètres"</string> <string name="low_internal_storage_view_title" msgid="9024241779284783414">"Espace de stockage bientôt saturé"</string> <string name="low_internal_storage_view_text" msgid="8172166728369697835">"Il est possible que certaines fonctionnalités du système ne soient pas opérationnelles."</string> <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Espace de stockage insuffisant pour le système. Assurez-vous de disposer de 250 Mo d\'espace libre, puis redémarrez."</string> diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml index a6aee4cb517b..d617143de134 100644 --- a/core/res/res/values-fr/strings.xml +++ b/core/res/res/values-fr/strings.xml @@ -1196,8 +1196,7 @@ <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"Retour"</string> <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"Changer le mode de saisie"</string> <string name="input_method_ime_switch_long_click_action_desc" msgid="3161942124116646998">"Ouvrir l\'outil de sélection du mode de saisie"</string> - <!-- no translation found for input_method_switcher_settings_button (5609835654697108485) --> - <skip /> + <string name="input_method_switcher_settings_button" msgid="5609835654697108485">"Paramètres"</string> <string name="low_internal_storage_view_title" msgid="9024241779284783414">"Espace de stockage bientôt saturé"</string> <string name="low_internal_storage_view_text" msgid="8172166728369697835">"Il est possible que certaines fonctionnalités du système ne soient pas opérationnelles."</string> <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Espace de stockage insuffisant pour le système. Assurez-vous de disposer de 250 Mo d\'espace libre, puis redémarrez."</string> diff --git a/core/res/res/values-gl/strings.xml b/core/res/res/values-gl/strings.xml index 85d3eed2db66..9366f4e1fac2 100644 --- a/core/res/res/values-gl/strings.xml +++ b/core/res/res/values-gl/strings.xml @@ -1195,8 +1195,7 @@ <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"Atrás"</string> <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"Cambia o método de introdución"</string> <string name="input_method_ime_switch_long_click_action_desc" msgid="3161942124116646998">"Abrir o selector do método de introdución de texto"</string> - <!-- no translation found for input_method_switcher_settings_button (5609835654697108485) --> - <skip /> + <string name="input_method_switcher_settings_button" msgid="5609835654697108485">"Configuración"</string> <string name="low_internal_storage_view_title" msgid="9024241779284783414">"Estase esgotando o espazo de almacenamento"</string> <string name="low_internal_storage_view_text" msgid="8172166728369697835">"É posible que algunhas funcións do sistema non funcionen"</string> <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Non hai almacenamento suficiente para o sistema. Asegúrate de ter un espazo libre de 250 MB e reinicia o dispositivo."</string> diff --git a/core/res/res/values-gu/strings.xml b/core/res/res/values-gu/strings.xml index 6c028edcd0e2..631270476bb2 100644 --- a/core/res/res/values-gu/strings.xml +++ b/core/res/res/values-gu/strings.xml @@ -90,7 +90,7 @@ <string name="notification_channel_emergency_callback" msgid="54074839059123159">"કટોકટી કૉલબૅક મોડ"</string> <string name="notification_channel_mobile_data_status" msgid="1941911162076442474">"મોબાઇલ ડેટાની સ્થિતિ"</string> <string name="notification_channel_sms" msgid="1243384981025535724">"SMS મેસેજ"</string> - <string name="notification_channel_voice_mail" msgid="8457433203106654172">"વૉઇસમેઇલ સંદેશા"</string> + <string name="notification_channel_voice_mail" msgid="8457433203106654172">"વૉઇસમેઇલ મેસેજ"</string> <string name="notification_channel_wfc" msgid="9048240466765169038">"વાઇ-ફાઇ કૉલિંગ"</string> <string name="notification_channel_sim" msgid="5098802350325677490">"સિમનું સ્ટેટસ"</string> <string name="notification_channel_sim_high_prio" msgid="642361929452850928">"સિમ કાર્ડનું ઉચ્ચ પ્રાધાન્યતાનું સ્ટેટસ"</string> @@ -122,7 +122,7 @@ <string name="roamingTextSearching" msgid="5323235489657753486">"સેવા શોધી રહ્યું છે"</string> <string name="wfcRegErrorTitle" msgid="3193072971584858020">"વાઇ-ફાઇ કૉલિંગ સેટ કરી શકાયું નથી"</string> <string-array name="wfcOperatorErrorAlertMessages"> - <item msgid="468830943567116703">"વાઇ-ફાઇ પરથી કૉલ કરવા અને સંદેશા મોકલવા માટે પહેલાં તમારા કૅરિઅરને આ સેવા સેટ કરવા માટે કહો. પછી સેટિંગમાંથી વાઇ-ફાઇ કૉલિંગ ફરીથી ચાલુ કરો. (ભૂલ કોડ: <xliff:g id="CODE">%1$s</xliff:g>)"</item> + <item msgid="468830943567116703">"વાઇ-ફાઇ પરથી કૉલ કરવા અને મેસેજ મોકલવા માટે પહેલાં તમારા મોબાઇલ ઑપરેટરને આ સેવા સેટ કરવા માટે કહો. પછી સેટિંગમાંથી વાઇ-ફાઇ કૉલિંગ ફરીથી ચાલુ કરો. (ભૂલ કોડ: <xliff:g id="CODE">%1$s</xliff:g>)"</item> </string-array> <string-array name="wfcOperatorErrorNotificationMessages"> <item msgid="4795145070505729156">"તમારા કૅરિઅરમાં વાઇ-ફાઇ કૉલિંગ રજિસ્ટર કરવામાં સમસ્યા આવી: <xliff:g id="CODE">%1$s</xliff:g>"</item> @@ -292,7 +292,7 @@ <string name="notification_channel_car_mode" msgid="2123919247040988436">"કાર મોડ"</string> <string name="notification_channel_account" msgid="6436294521740148173">"એકાઉન્ટ સ્થિતિ"</string> <string name="notification_channel_developer" msgid="1691059964407549150">"વિકાસકર્તા માટેના સંદેશા"</string> - <string name="notification_channel_developer_important" msgid="7197281908918789589">"ડેવલપર માટેના મહત્ત્વપૂર્ણ સંદેશા"</string> + <string name="notification_channel_developer_important" msgid="7197281908918789589">"ડેવલપર માટેના મહત્ત્વપૂર્ણ મેસેજ"</string> <string name="notification_channel_updates" msgid="7907863984825495278">"અપડેટ્સ"</string> <string name="notification_channel_network_status" msgid="2127687368725272809">"નેટવર્ક સ્થિતિ"</string> <string name="notification_channel_network_alerts" msgid="6312366315654526528">"નેટવર્ક ચેતવણીઓ"</string> @@ -324,7 +324,7 @@ <string name="permgrouplab_calendar" msgid="6426860926123033230">"કૅલેન્ડર"</string> <string name="permgroupdesc_calendar" msgid="6762751063361489379">"તમારા કેલેન્ડરને ઍક્સેસ કરવાની"</string> <string name="permgrouplab_sms" msgid="795737735126084874">"SMS"</string> - <string name="permgroupdesc_sms" msgid="5726462398070064542">"SMS સંદેશા મોકલવાની અને જોવાની"</string> + <string name="permgroupdesc_sms" msgid="5726462398070064542">"SMS મેસેજ મોકલવાની અને જોવાની"</string> <string name="permgrouplab_storage" msgid="17339216290379241">"ફાઇલો"</string> <string name="permgroupdesc_storage" msgid="5378659041354582769">"તમારા ડિવાઇસ પરની ફાઇલો ઍક્સેસ કરો"</string> <string name="permgrouplab_readMediaAural" msgid="1858331312624942053">"મ્યુઝિક અને ઑડિયો"</string> @@ -379,27 +379,27 @@ <string name="permdesc_processOutgoingCalls" msgid="7833149750590606334">"એપ્લિકેશનને આઉટગોઇંગ કૉલ દરમિયાન કૉલને એક અલગ નંબર પર રીડાયરેક્ટ કરવા અથવા કૉલને સંપૂર્ણપણે છોડી દેવાનાં વિકલ્પ સાથે ડાયલ થઈ રહેલા નંબરને જોવાની મંજૂરી આપે છે."</string> <string name="permlab_answerPhoneCalls" msgid="4131324833663725855">"ફોન કૉલને જવાબ આપો"</string> <string name="permdesc_answerPhoneCalls" msgid="894386681983116838">"ઍપ્લિકેશનને ઇનકમિંગ ફોન કૉલને જવાબ આપવાની મંજૂરી આપે છે."</string> - <string name="permlab_receiveSms" msgid="505961632050451881">"ટેક્સ્ટ સંદેશા (SMS) પ્રાપ્ત કરો"</string> - <string name="permdesc_receiveSms" msgid="1797345626687832285">"ઍપ્લિકેશનને SMS સંદેશા પ્રાપ્ત કરવાની અને તેના પર પ્રક્રિયા કરવાની મંજૂરી આપે છે. આનો અર્થ એ કે ઍપ્લિકેશન તમને દર્શાવ્યા વિના તમારા ઉપકરણ પર મોકલેલ સંદેશાઓનું નિરીક્ષણ કરી શકે છે અથવા કાઢી નાખી શકે છે."</string> - <string name="permlab_receiveMms" msgid="4000650116674380275">"ટેક્સ્ટ સંદેશા (MMS) પ્રાપ્ત કરો"</string> - <string name="permdesc_receiveMms" msgid="958102423732219710">"ઍપ્લિકેશનને MMS સંદેશા પ્રાપ્ત કરવાની અને તેના પર પ્રક્રિયા કરવાની મંજૂરી આપે છે. આનો અર્થ એ કે ઍપ્લિકેશન તમને દર્શાવ્યા વિના તમારા ઉપકરણ પર મોકલેલ સંદેશાઓનું નિરીક્ષણ કરી શકે છે અથવા કાઢી નાખી શકે છે."</string> - <string name="permlab_bindCellBroadcastService" msgid="586746677002040651">"સેલ બ્રોડકાસ્ટ સંદેશા ફૉરવર્ડ કરો"</string> - <string name="permdesc_bindCellBroadcastService" msgid="6540910200973641606">"સેલ બ્રોડકાસ્ટ સંદેશા પ્રાપ્ત થાય કે તરત ફૉરવર્ડ કરવા માટે સેલ બ્રોડકાસ્ટ મૉડ્યૂલ સાથે પ્રતિબદ્ધ થવા બાબતે ઍપને મંજૂરી આપે છે. તમને કટોકટીની પરિસ્થિતિની ચેતવણી આપવા માટે સેલ બ્રોડકાસ્ટ અલર્ટ અમુક સ્થાનોમાં ડિલિવર કરવામાં આવે છે. કટોકટી અંગેનો સેલ બ્રોડકાસ્ટ પ્રાપ્ત થાય, ત્યારે દુર્ભાવનાપૂર્ણ ઍપ તમારા ડિવાઇસના કાર્યપ્રદર્શન અથવા ઑપરેશનમાં વિક્ષેપ પાડે તેમ બની શકે છે."</string> + <string name="permlab_receiveSms" msgid="505961632050451881">"ટેક્સ્ટ મેસેજ (SMS) મેળવો"</string> + <string name="permdesc_receiveSms" msgid="1797345626687832285">"ઍપને SMS મેસેજ મેળવવાની અને તેના પર પ્રક્રિયા કરવાની મંજૂરી આપે છે. આનો અર્થ એ કે ઍપ તમને દર્શાવ્યા વિના તમારા ડિવાઇસ પર મોકલેલા મેસેજનું નિરીક્ષણ કરી શકે છે અથવા કાઢી નાખી શકે છે."</string> + <string name="permlab_receiveMms" msgid="4000650116674380275">"ટેક્સ્ટ મેસેજ (MMS) મેળવો"</string> + <string name="permdesc_receiveMms" msgid="958102423732219710">"ઍપને MMS મેસેજ પ્રાપ્ત કરવાની અને તેના પર પ્રક્રિયા કરવાની મંજૂરી આપે છે. આનો અર્થ એ કે ઍપ તમને દર્શાવ્યા વિના તમારા ડિવાઇસ પર મોકલેલા મેસેજને મૉનિટર કરી શકે છે અથવા ડિલીટ કરી શકે છે."</string> + <string name="permlab_bindCellBroadcastService" msgid="586746677002040651">"સેલ બ્રોડકાસ્ટ મેસેજ ફૉરવર્ડ કરો"</string> + <string name="permdesc_bindCellBroadcastService" msgid="6540910200973641606">"સેલ બ્રોડકાસ્ટ મેસેજ પ્રાપ્ત થાય કે તરત ફૉરવર્ડ કરવા માટે સેલ બ્રોડકાસ્ટ મૉડ્યૂલ સાથે પ્રતિબદ્ધ થવા બાબતે ઍપને મંજૂરી આપે છે. તમને કટોકટીની પરિસ્થિતિની ચેતવણી આપવા માટે સેલ બ્રોડકાસ્ટ અલર્ટ અમુક સ્થાનોમાં ડિલિવર કરવામાં આવે છે. કટોકટી અંગેનો સેલ બ્રોડકાસ્ટ પ્રાપ્ત થાય, ત્યારે દુર્ભાવનાપૂર્ણ ઍપ તમારા ડિવાઇસના કાર્યપ્રદર્શન અથવા ઑપરેશનમાં વિક્ષેપ પાડે તેમ બની શકે છે."</string> <string name="permlab_manageOngoingCalls" msgid="281244770664231782">"ચાલી રહેલા કૉલ મેનેજ કરો"</string> <string name="permdesc_manageOngoingCalls" msgid="7003138133829915265">"ઍપને તમારા ડિવાઇસ પર ચાલુ કૉલ વિશેની વિગતો જોવાની અને આ કૉલને નિયંત્રિત કરવાની મંજૂરી આપે છે."</string> <string name="permlab_accessLastKnownCellId" msgid="7638226620825665130">"છેલ્લી જ્ઞાત સેલ ઓળખને ઍક્સેસ કરો."</string> <string name="permdesc_accessLastKnownCellId" msgid="6664621339249308857">"ઍપને ટેલિફોન દ્વારા પ્રદાન કરવામાં આવેલી છેલ્લી જ્ઞાત સેલ ઓળખને ઍક્સેસ કરવાની મંજૂરી આપે છે."</string> - <string name="permlab_readCellBroadcasts" msgid="5869884450872137693">"સેલ બ્રોડકાસ્ટ સંદેશા વાંચો"</string> + <string name="permlab_readCellBroadcasts" msgid="5869884450872137693">"સેલ બ્રોડકાસ્ટ મેસેજ વાંચો"</string> <string name="permdesc_readCellBroadcasts" msgid="672513437331980168">"ઍપ તમારા ડિવાઇસ દ્વારા પ્રાપ્ત થયેલ સેલ બ્રોડકાસ્ટ સંદેશાને વાંચવાની મંજૂરી આપે છે. સેલ બ્રોડકાસ્ટ ચેતવણીઓ તમને ઇમર્જન્સીની સ્થિતિઓ અંગે ચેતવવા માટે કેટલાક સ્થાનોમાં વિતરિત થાય છે. જ્યારે ઇમર્જન્સીનો સેલ બ્રોડકાસ્ટ પ્રાપ્ત થાય ત્યારે દુર્ભાવનાપૂર્ણ ઍપ તમારા ડિવાઇસના કાર્યપ્રદર્શન અથવા ઓપરેશનમાં હસ્તક્ષેપ કરી શકે છે."</string> <string name="permlab_subscribedFeedsRead" msgid="217624769238425461">"સબ્સ્ક્રાઇબ કરેલ ફીડ્સ વાંચો"</string> <string name="permdesc_subscribedFeedsRead" msgid="6911349196661811865">"એપ્લિકેશનને હાલમાં સમન્વયિત ફીડ્સ વિશે વિગતો મેળવવાની મંજૂરી આપે છે."</string> - <string name="permlab_sendSms" msgid="7757368721742014252">"SMS સંદેશા મોકલો અને જુઓ"</string> - <string name="permdesc_sendSms" msgid="6757089798435130769">"એપ્લિકેશનને SMS સંદેશા મોકલવાની મંજૂરી આપે છે. આના પરિણામે અનપેક્ષિત શુલ્ક લાગી શકે છે. દુર્ભાવનાપૂર્ણ ઍપ્લિકેશનો તમારી પુષ્ટિ વિના સંદેશા મોકલીને તમારા નાણા ખર્ચાવી શકે છે."</string> - <string name="permlab_readSms" msgid="5164176626258800297">"તમારા ટેક્સ્ટ સંદેશા (SMS અથવા MMS) વાંચો"</string> - <string name="permdesc_readSms" product="tablet" msgid="7912990447198112829">"આ ઍપ્લિકેશન, તમારા ટેબ્લેટ પર સંગ્રહિત તમામ SMS (ટેક્સ્ટ) સંદેશા વાંચી શકે છે."</string> - <string name="permdesc_readSms" product="tv" msgid="3054753345758011986">"આ ઍપ, તમારા Android TV ડિવાઇસ પર સંગ્રહિત તમામ SMS (ટેક્સ્ટ) સંદેશા વાંચી શકે છે."</string> - <string name="permdesc_readSms" product="default" msgid="774753371111699782">"આ ઍપ્લિકેશન, તમારા ફોન પર સંગ્રહિત તમામ SMS (ટેક્સ્ટ) સંદેશા વાંચી શકે છે."</string> - <string name="permlab_receiveWapPush" msgid="4223747702856929056">"ટેક્સ્ટ સંદેશા (WAP) પ્રાપ્ત કરો"</string> + <string name="permlab_sendSms" msgid="7757368721742014252">"SMS મેસેજ મોકલો અને જુઓ"</string> + <string name="permdesc_sendSms" msgid="6757089798435130769">"ઍપને SMS મેસેજ મોકલવાની મંજૂરી આપે છે. આના પરિણામે અનપેક્ષિત શુલ્ક લાગી શકે છે. દુર્ભાવનાપૂર્ણ ઍપ તમારા કન્ફર્મેશન વિના મેસેજ મોકલીને તમારા નાણા ખર્ચાવી શકે છે."</string> + <string name="permlab_readSms" msgid="5164176626258800297">"તમારા ટેક્સ્ટ મેસેજ (SMS અથવા MMS) વાંચો"</string> + <string name="permdesc_readSms" product="tablet" msgid="7912990447198112829">"આ ઍપ, તમારા ટેબ્લેટ પર સંગ્રહિત તમામ SMS (ટેક્સ્ટ) મેસેજ વાંચી શકે છે."</string> + <string name="permdesc_readSms" product="tv" msgid="3054753345758011986">"આ ઍપ, તમારા Android TV ડિવાઇસ પર સંગ્રહિત તમામ SMS (ટેક્સ્ટ) મેસેજ વાંચી શકે છે."</string> + <string name="permdesc_readSms" product="default" msgid="774753371111699782">"આ ઍપ, તમારા ફોન પર સંગ્રહિત તમામ SMS (ટેક્સ્ટ) મેસેજ વાંચી શકે છે."</string> + <string name="permlab_receiveWapPush" msgid="4223747702856929056">"ટેક્સ્ટ મેસેજ (WAP) મેળવો"</string> <string name="permdesc_receiveWapPush" msgid="1638677888301778457">"એપ્લિકેશનને WAP સંદેશા પ્રાપ્ત કરવાની અને તેના પર પ્રક્રિયા કરવાની મંજૂરી આપે છે. આ પરવાનગીમાં તમને દર્શાવ્યા વિના તમને મોકલેલ સંદેશાઓનું નિરીક્ષણ કરવાની અને કાઢી નાખવાની ક્ષમતાનો સમાવેશ થાય છે."</string> <string name="permlab_getTasks" msgid="7460048811831750262">"ચાલુ ઍપ્લિકેશનો પુનઃપ્રાપ્ત કરો"</string> <string name="permdesc_getTasks" msgid="7388138607018233726">"એપ્લિકેશનને વર્તમાનમાં અને તાજેતરમાં ચાલી રહેલ Tasks વિશેની વિગતવાર માહિતી પુનઃપ્રાપ્ત કરવાની મંજૂરી આપે છે. આ એપ્લિકેશનને ઉપકરણ પર કઈ એપ્લિકેશન્સનો ઉપયોગ થાય છે તેના વિશેની માહિતી શોધવાની મંજૂરી આપી શકે છે."</string> @@ -492,9 +492,9 @@ <string name="permdesc_readCalendar" product="tv" msgid="5811726712981647628">"આ ઍપ, તમારા Android TV ડિવાઇસ પર સંગ્રહિત બધા કૅલેન્ડર ઇવેન્ટને વાંચી શકે છે અને તમારા કૅલેન્ડર ડેટાને શેર કરી અથવા સાચવી શકે છે."</string> <string name="permdesc_readCalendar" product="default" msgid="9118823807655829957">"આ ઍપ્લિકેશન, તમારા ફોન પર સંગ્રહિત તમામ કૅલેન્ડર ઇવેન્ટ્સને વાંચી શકે છે અને તમારા કૅલેન્ડર ડેટાને શેર કરી અથવા સાચવી શકે છે."</string> <string name="permlab_writeCalendar" msgid="6422137308329578076">"કૅલેન્ડર ઇવેન્ટ્સ ઉમેરો અથવા સંશોધિત કરો અને માલિકની જાણ બહાર અતિથિઓને ઇમેઇલ મોકલો"</string> - <string name="permdesc_writeCalendar" product="tablet" msgid="8722230940717092850">"આ ઍપ્લિકેશન, તમારા ટેબ્લેટ પર કૅલેન્ડર ઇવેન્ટ્સ ઉમેરી, દૂર કરી અથવા બદલી શકે છે. આ ઍપ્લિકેશન, કૅલેન્ડર માલિકો તરફથી આવતાં હોય તેવા લાગતાં સંદેશા મોકલી શકે છે અથવા તેમના માલિકોને સૂચિત કર્યા વિના ઇવેન્ટ્સ બદલી શકે છે."</string> - <string name="permdesc_writeCalendar" product="tv" msgid="951246749004952706">"આ ઍપ, તમારા Android TV ડિવાઇસ પર કૅલેન્ડર ઇવેન્ટ ઉમેરી, કાઢી નાખી અથવા બદલી શકે છે. આ ઍપ, કૅલેન્ડર માલિકો તરફથી આવતાં હોય તેવા લાગતાં સંદેશા મોકલી શકે છે અથવા તેમના માલિકોને સૂચિત કર્યા વિના ઇવેન્ટ બદલી શકે છે."</string> - <string name="permdesc_writeCalendar" product="default" msgid="5416380074475634233">"આ ઍપ્લિકેશન, તમારા ફોન પર કૅલેન્ડર ઇવેન્ટ્સ ઉમેરી, દૂર કરી અથવા બદલી શકે છે. આ ઍપ્લિકેશન, કૅલેન્ડર માલિકો તરફથી આવતાં હોય તેવા લાગતાં સંદેશા મોકલી શકે છે અથવા તેમના માલિકોને સૂચિત કર્યા વિના ઇવેન્ટ્સ બદલી શકે છે."</string> + <string name="permdesc_writeCalendar" product="tablet" msgid="8722230940717092850">"આ ઍપ, તમારા ટેબ્લેટ પર કૅલેન્ડર ઇવેન્ટ ઉમેરી, દૂર કરી અથવા બદલી શકે છે. આ ઍપ, કૅલેન્ડર માલિકો તરફથી આવતાં હોય તેવા લાગતાં મેસેજ મોકલી શકે છે અથવા તેમના માલિકોને સૂચિત કર્યા વિના કૅલેન્ડર બદલી શકે છે."</string> + <string name="permdesc_writeCalendar" product="tv" msgid="951246749004952706">"આ ઍપ, તમારા Android TV ડિવાઇસ પર કૅલેન્ડર ઇવેન્ટ ઉમેરી, કાઢી નાખી અથવા બદલી શકે છે. આ ઍપ, કૅલેન્ડર માલિકો તરફથી આવતાં હોય તેવા લાગતાં મેસેજ મોકલી શકે છે અથવા તેમના માલિકોને સૂચિત કર્યા વિના ઇવેન્ટ બદલી શકે છે."</string> + <string name="permdesc_writeCalendar" product="default" msgid="5416380074475634233">"આ ઍપ, તમારા ફોન પર કૅલેન્ડર ઇવેન્ટ ઉમેરી, દૂર કરી અથવા બદલી શકે છે. આ ઍપ, કૅલેન્ડર માલિકો તરફથી આવતાં હોય તેવા લાગતાં મેસેજ મોકલી શકે છે અથવા તેમના માલિકોને સૂચિત કર્યા વિના ઇવેન્ટ બદલી શકે છે."</string> <string name="permlab_accessLocationExtraCommands" msgid="5162339812057983988">"વધારાના સ્થાન પ્રદાતા આદેશોને ઍક્સેસ કરો"</string> <string name="permdesc_accessLocationExtraCommands" msgid="355369611979907967">"એપ્લિકેશનને વધારાના સ્થાન પ્રદાતા આદેશોને ઍક્સેસ કરવાની મંજૂરી આપે છે. આ એપ્લિકેશનને GPS અથવા અન્ય સ્થાન સ્રોતોના ઓપરેશનમાં દખલ કરવાની મંજૂરી આપી શકે છે."</string> <string name="permlab_accessFineLocation" msgid="6426318438195622966">"ફૉરગ્રાઉન્ડમાં ફક્ત ચોક્કસ સ્થાન ઍક્સેસ કરો"</string> @@ -1101,7 +1101,7 @@ <string name="permlab_setAlarm" msgid="1158001610254173567">"એલાર્મ સેટ કરો"</string> <string name="permdesc_setAlarm" msgid="2185033720060109640">"એપ્લિકેશનને ઇન્સ્ટોલ કરેલ અલાર્મ ઘડિયાળ એપ્લિકેશનમાં અલાર્મ સેટ કરવાની મંજૂરી આપે છે. કેટલીક અલાર્મ ઘડિયાળ ઍપ્લિકેશનો, આ સુવિધા લાગુ કરી શકતી નથી."</string> <string name="permlab_addVoicemail" msgid="4770245808840814471">"વૉઇસમેઇલ ઉમેરો"</string> - <string name="permdesc_addVoicemail" msgid="5470312139820074324">"એપ્લિકેશનને તમારા વૉઇસમેઇલ ઇનબોક્સ પર સંદેશા ઉમેરવાની મંજૂરી આપે છે."</string> + <string name="permdesc_addVoicemail" msgid="5470312139820074324">"એપને તમારા વૉઇસમેઇલ ઇનબોક્સ પર મેસેજ ઉમેરવાની મંજૂરી આપે છે."</string> <string name="pasted_from_clipboard" msgid="7355790625710831847">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> દ્વારા તમારા ક્લિપબોર્ડ પરથી પેસ્ટ કરવામાં આવ્યું"</string> <string name="more_item_label" msgid="7419249600215749115">"વધુ"</string> <string name="prepend_shortcut_label" msgid="1743716737502867951">"મેનૂ+"</string> @@ -1195,8 +1195,7 @@ <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"પાછળ"</string> <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"ઇનપુટ પદ્ધતિ સ્વિચ કરો"</string> <string name="input_method_ime_switch_long_click_action_desc" msgid="3161942124116646998">"ઇનપુટ પદ્ધતિ પિકર ખોલો"</string> - <!-- no translation found for input_method_switcher_settings_button (5609835654697108485) --> - <skip /> + <string name="input_method_switcher_settings_button" msgid="5609835654697108485">"સેટિંગ"</string> <string name="low_internal_storage_view_title" msgid="9024241779284783414">"સ્ટોરેજ સ્થાન સમાપ્ત થયું"</string> <string name="low_internal_storage_view_text" msgid="8172166728369697835">"કેટલાક સિસ્ટમ Tasks કામ કરી શકશે નહીં"</string> <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"સિસ્ટમ માટે પર્યાપ્ત સ્ટોરેજ નથી. ખાતરી કરો કે તમારી પાસે 250MB ખાલી સ્થાન છે અને ફરીથી પ્રારંભ કરો."</string> @@ -1360,8 +1359,8 @@ <string name="accept" msgid="5447154347815825107">"સ્વીકારો"</string> <string name="decline" msgid="6490507610282145874">"નકારો"</string> <string name="select_character" msgid="3352797107930786979">"અક્ષર શામેલ કરો"</string> - <string name="sms_control_title" msgid="4748684259903148341">"SMS સંદેશા મોકલી રહ્યું છે"</string> - <string name="sms_control_message" msgid="6574313876316388239">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> મોટા પ્રમાણમાં SMS સંદેશા મોકલી રહ્યું છે. શું તમે સંદેશા મોકલવાનું ચાલુ રાખવા માટે આ એપ્લિકેશનને મંજૂરી આપવા માગો છો?"</string> + <string name="sms_control_title" msgid="4748684259903148341">"SMS મેસેજ મોકલી રહ્યાં છે"</string> + <string name="sms_control_message" msgid="6574313876316388239">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> મોટા પ્રમાણમાં SMS મેસેજ મોકલી રહ્યું છે. શું તમે મેસેજ મોકલવાનું ચાલુ રાખવા માટે આ એપને મંજૂરી આપવા માગો છો?"</string> <string name="sms_control_yes" msgid="4858845109269524622">"મંજૂરી આપો"</string> <string name="sms_control_no" msgid="4845717880040355570">"નકારો"</string> <string name="sms_short_code_confirm_message" msgid="1385416688897538724">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> તમને <b><xliff:g id="DEST_ADDRESS">%2$s</xliff:g></b> પર સંદેશ મોકલવા માગે છે."</string> @@ -2037,7 +2036,7 @@ <string name="deprecated_target_sdk_message" msgid="5246906284426844596">"Androidના કોઈ જૂના વર્ઝન માટે આ ઍપ બનાવવામાં આવી હતી. તે કદાચ યોગ્ય રીતે કામ કરતી નથી અને તેમાં નવીનતમ સુરક્ષા અને પ્રાઇવસી સંબંધિત સંરક્ષણો શામેલ નથી. કોઈ અપડેટ ચેક કરો અથવા ઍપના ડેવલપરનો સંપર્ક કરો."</string> <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"અપડેટ માટે તપાસો"</string> <string name="deprecated_abi_message" msgid="6820548011196218091">"આ ઍપ Androidના નવીનતમ વર્ઝન સાથે સુસંગત નથી. કોઈ અપડેટ ચેક કરો અથવા ઍપના ડેવલપરનો સંપર્ક કરો."</string> - <string name="new_sms_notification_title" msgid="6528758221319927107">"તમારી પાસે નવા સંદેશા છે"</string> + <string name="new_sms_notification_title" msgid="6528758221319927107">"તમારી પાસે નવા મેસેજ છે"</string> <string name="new_sms_notification_content" msgid="3197949934153460639">"જોવા માટે SMS ઍપ્લિકેશન ખોલો"</string> <string name="profile_encrypted_title" msgid="9001208667521266472">"કેટલીક કાર્યક્ષમતા મર્યાદિત હોઈ શકે છે"</string> <string name="profile_encrypted_detail" msgid="5279730442756849055">"કાર્યાલયની પ્રોફાઇલ લૉક કરી"</string> @@ -2153,7 +2152,7 @@ <string name="nas_upgrade_notification_enable_action" msgid="3046406808378726874">"ઓકે"</string> <string name="nas_upgrade_notification_disable_action" msgid="3794833210043497982">"બંધ કરો"</string> <string name="nas_upgrade_notification_learn_more_action" msgid="7011130656195423947">"વધુ જાણો"</string> - <string name="nas_upgrade_notification_learn_more_content" msgid="3735480566983530650">"Android 12માં Android માટે અનુકૂળ નોટિફિકેશનને બદલે વધુ સારા નોટિફિકેશન છે. આ સુવિધા સૂચિત ક્રિયાઓ અને જવાબો બતાવે છે તેમજ તમારા નોટિફિકેશનની યોગ્ય ગોઠવણી કરે છે.\n\nવધુ સારા નોટિફિકેશન સંપર્કોના નામ અને સંદેશા જેવી વ્યક્તિગત માહિતી સહિત નોટિફિકેશનનું બધું કન્ટેન્ટ ઍક્સેસ કરી શકે છે. આ સુવિધા ફોન કૉલના જવાબ આપવા કે \'ખલેલ પાડશો નહીં\'નું નિયંત્રણ કરવા જેવા નોટિફિકેશન છોડવાની કે તેનો જવાબ આપવાની ક્રિયા પણ કરી શકે છે."</string> + <string name="nas_upgrade_notification_learn_more_content" msgid="3735480566983530650">"Android 12માં Android માટે અનુકૂળ નોટિફિકેશનને બદલે વધુ સારા નોટિફિકેશન છે. આ સુવિધા સૂચિત ક્રિયાઓ અને જવાબો બતાવે છે તેમજ તમારા નોટિફિકેશનની યોગ્ય ગોઠવણી કરે છે.\n\nવધુ સારા નોટિફિકેશન સંપર્કોના નામ અને મેસેજ જેવી વ્યક્તિગત માહિતી સહિત નોટિફિકેશનનું બધું કન્ટેન્ટ ઍક્સેસ કરી શકે છે. આ સુવિધા ફોન કૉલના જવાબ આપવા કે \'ખલેલ પાડશો નહીં\'નું નિયંત્રણ કરવા જેવા નોટિફિકેશન છોડવાની કે તેનો જવાબ આપવાની ક્રિયા પણ કરી શકે છે."</string> <string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"રૂટિન મોડની માહિતીનું નોટિફિકેશન"</string> <string name="dynamic_mode_notification_title" msgid="1388718452788985481">"બૅટરી સેવરની સુવિધા ચાલુ કરી છે"</string> <string name="dynamic_mode_notification_summary" msgid="1639031262484979689">"બૅટરીની આવરદા વધારવા માટે બૅટરીનો વપરાશ ઘટાડી રહ્યાં છીએ"</string> diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml index 7bd182bb91dc..02369f96df30 100644 --- a/core/res/res/values-hi/strings.xml +++ b/core/res/res/values-hi/strings.xml @@ -1195,8 +1195,7 @@ <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"वापस जाएं"</string> <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"इनपुट का तरीका बदलें"</string> <string name="input_method_ime_switch_long_click_action_desc" msgid="3161942124116646998">"\'इनपुट का तरीका\' पिकर को खोलें"</string> - <!-- no translation found for input_method_switcher_settings_button (5609835654697108485) --> - <skip /> + <string name="input_method_switcher_settings_button" msgid="5609835654697108485">"सेटिंग"</string> <string name="low_internal_storage_view_title" msgid="9024241779284783414">"मेमोरी में जगह नहीं बची है"</string> <string name="low_internal_storage_view_text" msgid="8172166728369697835">"हो सकता है कुछ सिस्टम फ़ंक्शन काम नहीं करें"</string> <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"सिस्टम के लिए ज़रूरी मेमोरी नहीं है. पक्का करें कि आपके पास 250एमबी की खाली जगह है और फिर से शुरू करें."</string> diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml index e71e1511b2e6..487d9e77134e 100644 --- a/core/res/res/values-hr/strings.xml +++ b/core/res/res/values-hr/strings.xml @@ -1196,8 +1196,7 @@ <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"Natrag"</string> <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"Promjena načina unosa"</string> <string name="input_method_ime_switch_long_click_action_desc" msgid="3161942124116646998">"Otvori alat za odabir načina unosa"</string> - <!-- no translation found for input_method_switcher_settings_button (5609835654697108485) --> - <skip /> + <string name="input_method_switcher_settings_button" msgid="5609835654697108485">"Postavke"</string> <string name="low_internal_storage_view_title" msgid="9024241779284783414">"Ponestaje prostora za pohranu"</string> <string name="low_internal_storage_view_text" msgid="8172166728369697835">"Neke sistemske funkcije možda neće raditi"</string> <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Nema dovoljno pohrane za sustav. Oslobodite 250 MB prostora i pokrenite uređaj ponovo."</string> diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml index 01d78a2268ca..ca8787da0225 100644 --- a/core/res/res/values-hu/strings.xml +++ b/core/res/res/values-hu/strings.xml @@ -1195,8 +1195,7 @@ <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"Vissza"</string> <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"Beviteli módszer váltása"</string> <string name="input_method_ime_switch_long_click_action_desc" msgid="3161942124116646998">"A bevitelimód-választó megnyitása"</string> - <!-- no translation found for input_method_switcher_settings_button (5609835654697108485) --> - <skip /> + <string name="input_method_switcher_settings_button" msgid="5609835654697108485">"Beállítások"</string> <string name="low_internal_storage_view_title" msgid="9024241779284783414">"Kevés a szabad terület"</string> <string name="low_internal_storage_view_text" msgid="8172166728369697835">"Előfordulhat, hogy néhány rendszerfunkció nem működik."</string> <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Nincs elegendő tárhely a rendszerhez. Győződjön meg arról, hogy rendelkezik 250 MB szabad területtel, majd kezdje elölről."</string> diff --git a/core/res/res/values-hy/strings.xml b/core/res/res/values-hy/strings.xml index 9293a8384ac2..273e7745cbe4 100644 --- a/core/res/res/values-hy/strings.xml +++ b/core/res/res/values-hy/strings.xml @@ -525,7 +525,7 @@ <string name="permdesc_cameraOpenCloseListener" msgid="2002636131008772908">"Այս հավելվածը կարող է հետզանգեր ստանալ՝ ցանկացած տեսախցիկի բացվելու (կնշվի բացող հավելվածը) և փակվելու դեպքում։"</string> <string name="permlab_cameraHeadlessSystemUser" msgid="680194666834500050">"Թույլատրել հավելվածին կամ ծառայությանը օգտագործել որպես միջերեսի համակարգային օգտատեր։"</string> <string name="permdesc_cameraHeadlessSystemUser" msgid="6963163319710996412">"Այս հավելվածին ձեր տեսախցիկը հասանելի է որպես առանց միջերեսի համակարգային օգտատեր։"</string> - <string name="permlab_vibrate" msgid="8596800035791962017">"կառավարել թրթռումը"</string> + <string name="permlab_vibrate" msgid="8596800035791962017">"կառավարել թրթռոցը"</string> <string name="permdesc_vibrate" msgid="8733343234582083721">"Թույլ է տալիս հավելվածին կառավարել թրթռոցը:"</string> <string name="permdesc_vibrator_state" msgid="7050024956594170724">"Հավելվածին թույլ է տալիս օգտագործել սարքի թրթռալու ռեժիմը։"</string> <string name="permlab_callPhone" msgid="1798582257194643320">"ուղղակիորեն զանգել հեռախոսահամարներին"</string> @@ -1195,8 +1195,7 @@ <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"Հետ"</string> <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"Փոխել ներածման եղանակը"</string> <string name="input_method_ime_switch_long_click_action_desc" msgid="3161942124116646998">"Բացել ներածման եղանակի ընտրիչը"</string> - <!-- no translation found for input_method_switcher_settings_button (5609835654697108485) --> - <skip /> + <string name="input_method_switcher_settings_button" msgid="5609835654697108485">"Կարգավորումներ"</string> <string name="low_internal_storage_view_title" msgid="9024241779284783414">"Հիշողությունը սպառվում է"</string> <string name="low_internal_storage_view_text" msgid="8172166728369697835">"Որոշ գործառույթներ կարող են չաշխատել"</string> <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Համակարգի համար բավարար հիշողություն չկա: Համոզվեք, որ ունեք 250ՄԲ ազատ տարածություն և վերագործարկեք:"</string> @@ -2424,7 +2423,7 @@ <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Անվտանգության նկատառումներով՝ բովանդակությունը թաքցվել է ցուցադրումից"</string> <string name="satellite_notification_title" msgid="4026338973463121526">"Ավտոմատ միացել է արբանյակին"</string> - <string name="satellite_notification_summary" msgid="5207364139430767162">"Դուք կարող եք ուղարկել և ստանալ հաղորդագրություններ՝ առանց բջջային կամ Wi-Fi կապի"</string> + <string name="satellite_notification_summary" msgid="5207364139430767162">"Դուք կարող եք հաղորդագրություններ ուղարկել և ստանալ առանց բջջային կամ Wi-Fi կապի"</string> <string name="satellite_notification_manual_title" msgid="1097504441849466279">"Օգտագործե՞լ արբանյակային հաղորդագրումը"</string> <string name="satellite_notification_manual_summary" msgid="901206289846283445">"Ուղարկեք և ստացեք հաղորդագրություններ առանց բջջային կամ Wi-Fi ցանցի"</string> <string name="satellite_notification_open_message" msgid="4149234979688273729">"Բացել Messages-ը"</string> diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml index 3b1e037a0a47..47588ffb18c1 100644 --- a/core/res/res/values-in/strings.xml +++ b/core/res/res/values-in/strings.xml @@ -1195,8 +1195,7 @@ <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"Kembali"</string> <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"Beralih metode input"</string> <string name="input_method_ime_switch_long_click_action_desc" msgid="3161942124116646998">"Buka pemilih metode input"</string> - <!-- no translation found for input_method_switcher_settings_button (5609835654697108485) --> - <skip /> + <string name="input_method_switcher_settings_button" msgid="5609835654697108485">"Setelan"</string> <string name="low_internal_storage_view_title" msgid="9024241779284783414">"Ruang penyimpanan hampir habis"</string> <string name="low_internal_storage_view_text" msgid="8172166728369697835">"Beberapa fungsi sistem mungkin tidak dapat bekerja"</string> <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Penyimpanan tidak cukup untuk sistem. Pastikan Anda memiliki 250 MB ruang kosong, lalu mulai ulang."</string> diff --git a/core/res/res/values-is/strings.xml b/core/res/res/values-is/strings.xml index 7bc4ddffee25..a833c8d2d6c9 100644 --- a/core/res/res/values-is/strings.xml +++ b/core/res/res/values-is/strings.xml @@ -1195,8 +1195,7 @@ <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"Til baka"</string> <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"Skipta um innfærsluaðferð"</string> <string name="input_method_ime_switch_long_click_action_desc" msgid="3161942124116646998">"Opna val á innfærsluaðferð"</string> - <!-- no translation found for input_method_switcher_settings_button (5609835654697108485) --> - <skip /> + <string name="input_method_switcher_settings_button" msgid="5609835654697108485">"Stillingar"</string> <string name="low_internal_storage_view_title" msgid="9024241779284783414">"Geymslurýmið er senn á þrotum"</string> <string name="low_internal_storage_view_text" msgid="8172166728369697835">"Sumir kerfiseiginleikar kunna að vera óvirkir"</string> <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Ekki nægt geymslurými fyrir kerfið. Gakktu úr skugga um að 250 MB séu laus og endurræstu."</string> diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml index cb89354413b3..662a9c07680a 100644 --- a/core/res/res/values-it/strings.xml +++ b/core/res/res/values-it/strings.xml @@ -1196,8 +1196,7 @@ <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"Indietro"</string> <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"Cambia metodo di immissione"</string> <string name="input_method_ime_switch_long_click_action_desc" msgid="3161942124116646998">"Apri selettore metodo di immissione"</string> - <!-- no translation found for input_method_switcher_settings_button (5609835654697108485) --> - <skip /> + <string name="input_method_switcher_settings_button" msgid="5609835654697108485">"Impostazioni"</string> <string name="low_internal_storage_view_title" msgid="9024241779284783414">"Spazio di archiviazione in esaurimento"</string> <string name="low_internal_storage_view_text" msgid="8172166728369697835">"Alcune funzioni di sistema potrebbero non funzionare"</string> <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Memoria insufficiente per il sistema. Assicurati di avere 250 MB di spazio libero e riavvia."</string> diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml index 41829df0e2ba..37bffa40ed14 100644 --- a/core/res/res/values-iw/strings.xml +++ b/core/res/res/values-iw/strings.xml @@ -1196,8 +1196,7 @@ <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"חזרה"</string> <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"החלפה של שיטת הקלט"</string> <string name="input_method_ime_switch_long_click_action_desc" msgid="3161942124116646998">"פתיחה של בוחר שיטות הקלט"</string> - <!-- no translation found for input_method_switcher_settings_button (5609835654697108485) --> - <skip /> + <string name="input_method_switcher_settings_button" msgid="5609835654697108485">"הגדרות"</string> <string name="low_internal_storage_view_title" msgid="9024241779284783414">"מקום האחסון עומד להיגמר"</string> <string name="low_internal_storage_view_text" msgid="8172166728369697835">"ייתכן שפונקציות מערכת מסוימות לא יפעלו"</string> <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"אין מספיק מקום אחסון עבור המערכת. עליך לוודא שיש לך מקום פנוי בנפח של 250MB ולהתחיל שוב."</string> diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml index 437a5e6f1342..cce4abe62e56 100644 --- a/core/res/res/values-ja/strings.xml +++ b/core/res/res/values-ja/strings.xml @@ -1195,8 +1195,7 @@ <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"戻る"</string> <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"入力方法の切り替え"</string> <string name="input_method_ime_switch_long_click_action_desc" msgid="3161942124116646998">"入力方法の選択ツールを開く"</string> - <!-- no translation found for input_method_switcher_settings_button (5609835654697108485) --> - <skip /> + <string name="input_method_switcher_settings_button" msgid="5609835654697108485">"設定"</string> <string name="low_internal_storage_view_title" msgid="9024241779284783414">"空き容量わずか"</string> <string name="low_internal_storage_view_text" msgid="8172166728369697835">"一部のシステム機能が動作しない可能性があります"</string> <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"システムに十分な容量がありません。250MBの空き容量を確保して再起動してください。"</string> diff --git a/core/res/res/values-ka/strings.xml b/core/res/res/values-ka/strings.xml index bc0ab8afc675..0d8b047a5b23 100644 --- a/core/res/res/values-ka/strings.xml +++ b/core/res/res/values-ka/strings.xml @@ -1195,8 +1195,7 @@ <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"უკან"</string> <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"შეყვანის მეთოდის გადართვა"</string> <string name="input_method_ime_switch_long_click_action_desc" msgid="3161942124116646998">"შეყვანის მეთოდის ამომრჩევის გახსნა"</string> - <!-- no translation found for input_method_switcher_settings_button (5609835654697108485) --> - <skip /> + <string name="input_method_switcher_settings_button" msgid="5609835654697108485">"პარამეტრები"</string> <string name="low_internal_storage_view_title" msgid="9024241779284783414">"თავისუფალი ადგილი იწურება"</string> <string name="low_internal_storage_view_text" msgid="8172166728369697835">"სისტემის ზოგიერთმა ფუნქციამ შესაძლოა არ იმუშავოს"</string> <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"სისტემისათვის საკმარისი საცავი არ არის. დარწმუნდით, რომ იქონიოთ სულ მცირე 250 მბაიტი თავისუფალი სივრცე და დაიწყეთ ხელახლა."</string> diff --git a/core/res/res/values-kk/strings.xml b/core/res/res/values-kk/strings.xml index 1574a78144c9..72a2a3bdf67a 100644 --- a/core/res/res/values-kk/strings.xml +++ b/core/res/res/values-kk/strings.xml @@ -1195,8 +1195,7 @@ <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"Артқа"</string> <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"Енгізу әдісін ауыстыру"</string> <string name="input_method_ime_switch_long_click_action_desc" msgid="3161942124116646998">"Енгізу әдісін таңдау құралын ашу"</string> - <!-- no translation found for input_method_switcher_settings_button (5609835654697108485) --> - <skip /> + <string name="input_method_switcher_settings_button" msgid="5609835654697108485">"Параметрлер"</string> <string name="low_internal_storage_view_title" msgid="9024241779284783414">"Жадта орын азайып барады"</string> <string name="low_internal_storage_view_text" msgid="8172166728369697835">"Жүйенің кейбір функциялары жұмыс істемеуі мүмкін"</string> <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Жүйе үшін жад жеткіліксіз. 250 МБ бос орын бар екенін тексеріп, қайта іске қосыңыз."</string> diff --git a/core/res/res/values-km/strings.xml b/core/res/res/values-km/strings.xml index 4374bb67db46..2e5589edd9aa 100644 --- a/core/res/res/values-km/strings.xml +++ b/core/res/res/values-km/strings.xml @@ -1195,8 +1195,7 @@ <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"ថយក្រោយ"</string> <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"ប្ដូរវិធីសាស្ត្របញ្ចូល"</string> <string name="input_method_ime_switch_long_click_action_desc" msgid="3161942124116646998">"បើកផ្ទាំងជ្រើសរើសវិធីបញ្ចូល"</string> - <!-- no translation found for input_method_switcher_settings_button (5609835654697108485) --> - <skip /> + <string name="input_method_switcher_settings_button" msgid="5609835654697108485">"ការកំណត់"</string> <string name="low_internal_storage_view_title" msgid="9024241779284783414">"អស់ទំហំផ្ទុក"</string> <string name="low_internal_storage_view_text" msgid="8172166728369697835">"មុខងារប្រព័ន្ធមួយចំនួនអាចមិនដំណើរការ"</string> <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"មិនមានទំហំផ្ទុកគ្រប់គ្រាន់សម្រាប់ប្រព័ន្ធ។ សូមប្រាកដថាអ្នកមានទំហំទំនេរ 250MB ហើយចាប់ផ្ដើមឡើងវិញ។"</string> diff --git a/core/res/res/values-kn/strings.xml b/core/res/res/values-kn/strings.xml index 00f22af456a0..e5a509227c80 100644 --- a/core/res/res/values-kn/strings.xml +++ b/core/res/res/values-kn/strings.xml @@ -1195,8 +1195,7 @@ <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"ಹಿಂದಕ್ಕೆ"</string> <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"ಇನ್ಪುಟ್ ವಿಧಾನವನ್ನು ಬದಲಿಸಿ"</string> <string name="input_method_ime_switch_long_click_action_desc" msgid="3161942124116646998">"ಇನ್ಪುಟ್ ವಿಧಾನದ ಪಿಕರ್ ಅನ್ನು ತೆರೆಯಿರಿ"</string> - <!-- no translation found for input_method_switcher_settings_button (5609835654697108485) --> - <skip /> + <string name="input_method_switcher_settings_button" msgid="5609835654697108485">"ಸೆಟ್ಟಿಂಗ್ಗಳು"</string> <string name="low_internal_storage_view_title" msgid="9024241779284783414">"ಸಂಗ್ರಹಣೆ ಸ್ಥಳವು ತುಂಬಿದೆ"</string> <string name="low_internal_storage_view_text" msgid="8172166728369697835">"ಕೆಲವು ಸಿಸ್ಟಂ ಕಾರ್ಯವಿಧಾನಗಳು ಕಾರ್ಯನಿರ್ವಹಿಸದೇ ಇರಬಹುದು"</string> <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"ಸಿಸ್ಟಂನಲ್ಲಿ ಸಾಕಷ್ಟು ಸಂಗ್ರಹಣೆಯಿಲ್ಲ. ನೀವು 250MB ನಷ್ಟು ಖಾಲಿ ಸ್ಥಳವನ್ನು ಹೊಂದಿರುವಿರಾ ಎಂಬುದನ್ನು ಖಚಿತಪಡಿಸಿಕೊಳ್ಳಿ ಹಾಗೂ ಮರುಪ್ರಾರಂಭಿಸಿ."</string> diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml index 3c38d5fb4957..1413170c36cc 100644 --- a/core/res/res/values-ko/strings.xml +++ b/core/res/res/values-ko/strings.xml @@ -1195,8 +1195,7 @@ <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"뒤로"</string> <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"입력 방법 전환"</string> <string name="input_method_ime_switch_long_click_action_desc" msgid="3161942124116646998">"입력 방법 선택 도구 열기"</string> - <!-- no translation found for input_method_switcher_settings_button (5609835654697108485) --> - <skip /> + <string name="input_method_switcher_settings_button" msgid="5609835654697108485">"설정"</string> <string name="low_internal_storage_view_title" msgid="9024241779284783414">"저장 공간이 부족함"</string> <string name="low_internal_storage_view_text" msgid="8172166728369697835">"일부 시스템 기능이 작동하지 않을 수 있습니다."</string> <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"시스템의 저장 공간이 부족합니다. 250MB의 여유 공간이 확보한 후 다시 시작하세요."</string> diff --git a/core/res/res/values-ky/strings.xml b/core/res/res/values-ky/strings.xml index 1f29fa884e4c..112bf0a0d08e 100644 --- a/core/res/res/values-ky/strings.xml +++ b/core/res/res/values-ky/strings.xml @@ -1195,8 +1195,7 @@ <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"Артка"</string> <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"Киргизүү ыкмасын өзгөртүү"</string> <string name="input_method_ime_switch_long_click_action_desc" msgid="3161942124116646998">"Киргизүү ыкмасын тандоо"</string> - <!-- no translation found for input_method_switcher_settings_button (5609835654697108485) --> - <skip /> + <string name="input_method_switcher_settings_button" msgid="5609835654697108485">"Параметрлер"</string> <string name="low_internal_storage_view_title" msgid="9024241779284783414">"Сактагычта орун калбай баратат"</string> <string name="low_internal_storage_view_text" msgid="8172166728369697835">"Айрым функциялар иштебеши мүмкүн"</string> <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Системада сактагыч жетишсиз. 250МБ бош орун бар экенин текшерип туруп, өчүрүп күйгүзүңүз."</string> diff --git a/core/res/res/values-lo/strings.xml b/core/res/res/values-lo/strings.xml index 3ea5e53a9ab1..ba43481d89eb 100644 --- a/core/res/res/values-lo/strings.xml +++ b/core/res/res/values-lo/strings.xml @@ -1195,8 +1195,7 @@ <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"ກັບຄືນ"</string> <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"ສະຫຼັບວິທີການປ້ອນຂໍ້ມູນ"</string> <string name="input_method_ime_switch_long_click_action_desc" msgid="3161942124116646998">"ເປີດຕົວເລືອກວິທີການປ້ອນຂໍ້ມູນ"</string> - <!-- no translation found for input_method_switcher_settings_button (5609835654697108485) --> - <skip /> + <string name="input_method_switcher_settings_button" msgid="5609835654697108485">"ການຕັ້ງຄ່າ"</string> <string name="low_internal_storage_view_title" msgid="9024241779284783414">"ພື້ນທີ່ຈັດເກັບຂໍ້ມູນກຳລັງຈະເຕັມ"</string> <string name="low_internal_storage_view_text" msgid="8172166728369697835">"ການເຮັດວຽກບາງຢ່າງຂອງລະບົບບາງອາດຈະໃຊ້ບໍ່ໄດ້"</string> <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"ບໍ່ມີບ່ອນເກັບຂໍ້ມູນພຽງພໍສຳລັບລະບົບ. ກວດສອບໃຫ້ແນ່ໃຈວ່າທ່ານມີພື້ນທີ່ຫວ່າງຢ່າງໜ້ອຍ 250MB ແລ້ວລອງໃໝ່."</string> diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml index 78159b436f7d..ad30d8030d7c 100644 --- a/core/res/res/values-lt/strings.xml +++ b/core/res/res/values-lt/strings.xml @@ -1197,8 +1197,7 @@ <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"Atgal"</string> <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"Perjungti įvesties metodą"</string> <string name="input_method_ime_switch_long_click_action_desc" msgid="3161942124116646998">"Atidaryti įvesties metodo rinkiklį"</string> - <!-- no translation found for input_method_switcher_settings_button (5609835654697108485) --> - <skip /> + <string name="input_method_switcher_settings_button" msgid="5609835654697108485">"Nustatymai"</string> <string name="low_internal_storage_view_title" msgid="9024241779284783414">"Mažėja laisvos saugyklos vietos"</string> <string name="low_internal_storage_view_text" msgid="8172166728369697835">"Kai kurios sistemos funkcijos gali neveikti"</string> <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Sistemos saugykloje nepakanka vietos. Įsitikinkite, kad yra 250 MB laisvos vietos, ir paleiskite iš naujo."</string> diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml index 84c69f12ab6e..329bbc3b31f9 100644 --- a/core/res/res/values-lv/strings.xml +++ b/core/res/res/values-lv/strings.xml @@ -1196,8 +1196,7 @@ <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"Atpakaļ"</string> <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"Pārslēgt ievades metodi"</string> <string name="input_method_ime_switch_long_click_action_desc" msgid="3161942124116646998">"Atvērt ievades metodes atlasītāju"</string> - <!-- no translation found for input_method_switcher_settings_button (5609835654697108485) --> - <skip /> + <string name="input_method_switcher_settings_button" msgid="5609835654697108485">"Iestatījumi"</string> <string name="low_internal_storage_view_title" msgid="9024241779284783414">"Paliek maz brīvas vietas"</string> <string name="low_internal_storage_view_text" msgid="8172166728369697835">"Dažas sistēmas funkcijas var nedarboties."</string> <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Sistēmai pietrūkst vietas. Atbrīvojiet vismaz 250 MB vietas un restartējiet ierīci."</string> diff --git a/core/res/res/values-mk/strings.xml b/core/res/res/values-mk/strings.xml index e3e97a8b85b6..4bb03406462b 100644 --- a/core/res/res/values-mk/strings.xml +++ b/core/res/res/values-mk/strings.xml @@ -1195,8 +1195,7 @@ <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"Назад"</string> <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"Префрлете го методот за внесување"</string> <string name="input_method_ime_switch_long_click_action_desc" msgid="3161942124116646998">"Отворете го избирачот на метод за внесување"</string> - <!-- no translation found for input_method_switcher_settings_button (5609835654697108485) --> - <skip /> + <string name="input_method_switcher_settings_button" msgid="5609835654697108485">"Поставки"</string> <string name="low_internal_storage_view_title" msgid="9024241779284783414">"Капацитетот е речиси полн"</string> <string name="low_internal_storage_view_text" msgid="8172166728369697835">"Некои системски функции може да не работат"</string> <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Нема доволно меморија во системот. Проверете дали има слободен простор од 250 MB и рестартирајте."</string> diff --git a/core/res/res/values-ml/strings.xml b/core/res/res/values-ml/strings.xml index cc15d1e4d3f5..72e522d00016 100644 --- a/core/res/res/values-ml/strings.xml +++ b/core/res/res/values-ml/strings.xml @@ -1195,8 +1195,7 @@ <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"മടങ്ങുക"</string> <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"ഇൻപുട്ട് രീതി മാറുക"</string> <string name="input_method_ime_switch_long_click_action_desc" msgid="3161942124116646998">"ഇൻപുട്ട് രീതി പിക്കർ തുറക്കുക"</string> - <!-- no translation found for input_method_switcher_settings_button (5609835654697108485) --> - <skip /> + <string name="input_method_switcher_settings_button" msgid="5609835654697108485">"ക്രമീകരണം"</string> <string name="low_internal_storage_view_title" msgid="9024241779284783414">"സംഭരണയിടം കഴിഞ്ഞു"</string> <string name="low_internal_storage_view_text" msgid="8172166728369697835">"ചില സിസ്റ്റം പ്രവർത്തനങ്ങൾ പ്രവർത്തിക്കണമെന്നില്ല."</string> <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"സിസ്റ്റത്തിനായി മതിയായ സംഭരണമില്ല. 250MB സൗജന്യ സംഭരണമുണ്ടെന്ന് ഉറപ്പുവരുത്തി പുനരാരംഭിക്കുക."</string> diff --git a/core/res/res/values-mn/strings.xml b/core/res/res/values-mn/strings.xml index a523fce8108f..439897559be2 100644 --- a/core/res/res/values-mn/strings.xml +++ b/core/res/res/values-mn/strings.xml @@ -1195,8 +1195,7 @@ <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"Буцах"</string> <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"Оруулах аргыг сэлгэх"</string> <string name="input_method_ime_switch_long_click_action_desc" msgid="3161942124116646998">"Оруулах арга сонгогчийг нээх"</string> - <!-- no translation found for input_method_switcher_settings_button (5609835654697108485) --> - <skip /> + <string name="input_method_switcher_settings_button" msgid="5609835654697108485">"Тохиргоо"</string> <string name="low_internal_storage_view_title" msgid="9024241779284783414">"Сангийн хэмжээ дутагдаж байна"</string> <string name="low_internal_storage_view_text" msgid="8172166728369697835">"Зарим систем функц ажиллахгүй байна"</string> <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Системд хангалттай сан байхгүй байна. 250MБ чөлөөтэй зай байгаа эсэхийг шалгаад дахин эхлүүлнэ үү."</string> diff --git a/core/res/res/values-mr/strings.xml b/core/res/res/values-mr/strings.xml index 37378b4a34e6..ac39d557412c 100644 --- a/core/res/res/values-mr/strings.xml +++ b/core/res/res/values-mr/strings.xml @@ -1195,8 +1195,7 @@ <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"मागे जा"</string> <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"इनपुट पद्धत स्विच करा"</string> <string name="input_method_ime_switch_long_click_action_desc" msgid="3161942124116646998">"इनपुट पद्धत पिकर उघडा"</string> - <!-- no translation found for input_method_switcher_settings_button (5609835654697108485) --> - <skip /> + <string name="input_method_switcher_settings_button" msgid="5609835654697108485">"सेटिंग्ज"</string> <string name="low_internal_storage_view_title" msgid="9024241779284783414">"संचयन स्थान संपत आहे"</string> <string name="low_internal_storage_view_text" msgid="8172166728369697835">"काही सिस्टम कार्ये कार्य करू शकत नाहीत"</string> <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"सिस्टीमसाठी पुरेसे संचयन नाही. आपल्याकडे 250MB मोकळे स्थान असल्याचे सुनिश्चित करा आणि रीस्टार्ट करा."</string> diff --git a/core/res/res/values-ms/strings.xml b/core/res/res/values-ms/strings.xml index 41a64a0b5c48..88eef461b8ae 100644 --- a/core/res/res/values-ms/strings.xml +++ b/core/res/res/values-ms/strings.xml @@ -1195,8 +1195,7 @@ <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"Kembali"</string> <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"Tukar kaedah masukan"</string> <string name="input_method_ime_switch_long_click_action_desc" msgid="3161942124116646998">"Buka pemilih kaedah input"</string> - <!-- no translation found for input_method_switcher_settings_button (5609835654697108485) --> - <skip /> + <string name="input_method_switcher_settings_button" msgid="5609835654697108485">"Tetapan"</string> <string name="low_internal_storage_view_title" msgid="9024241779284783414">"Ruang storan semakin berkurangan"</string> <string name="low_internal_storage_view_text" msgid="8172166728369697835">"Beberapa fungsi sistem mungkin tidak berfungsi"</string> <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Tidak cukup storan untuk sistem. Pastikan anda mempunyai 250MB ruang kosong dan mulakan semula."</string> diff --git a/core/res/res/values-my/strings.xml b/core/res/res/values-my/strings.xml index 229f1a156007..d5c1fbd82049 100644 --- a/core/res/res/values-my/strings.xml +++ b/core/res/res/values-my/strings.xml @@ -1195,8 +1195,7 @@ <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"နောက်သို့"</string> <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"လက်ကွက်ပြောင်းရန်"</string> <string name="input_method_ime_switch_long_click_action_desc" msgid="3161942124116646998">"လက်ကွက်ရွေးစနစ် ဖွင့်ရန်"</string> - <!-- no translation found for input_method_switcher_settings_button (5609835654697108485) --> - <skip /> + <string name="input_method_switcher_settings_button" msgid="5609835654697108485">"ဆက်တင်များ"</string> <string name="low_internal_storage_view_title" msgid="9024241779284783414">"သိမ်းဆည်သော နေရာ နည်းနေပါသည်"</string> <string name="low_internal_storage_view_text" msgid="8172166728369697835">"တချို့ စနစ်လုပ်ငန်းများ အလုပ် မလုပ်ခြင်း ဖြစ်နိုင်ပါသည်"</string> <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"စနစ်အတွက် သိုလှောင်ခန်း မလုံလောက်ပါ။ သင့်ဆီမှာ နေရာလွတ် ၂၅၀ MB ရှိတာ စစ်ကြည့်ပြီး စတင်ပါ။"</string> diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml index 5e39528ecccb..53b6ad512f3e 100644 --- a/core/res/res/values-nb/strings.xml +++ b/core/res/res/values-nb/strings.xml @@ -1195,8 +1195,7 @@ <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"Tilbake"</string> <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"Bytt inndatametode"</string> <string name="input_method_ime_switch_long_click_action_desc" msgid="3161942124116646998">"Åpne valg av inndatametode"</string> - <!-- no translation found for input_method_switcher_settings_button (5609835654697108485) --> - <skip /> + <string name="input_method_switcher_settings_button" msgid="5609835654697108485">"Innstillinger"</string> <string name="low_internal_storage_view_title" msgid="9024241779284783414">"Lite ledig lagringsplass"</string> <string name="low_internal_storage_view_text" msgid="8172166728369697835">"Enkelte systemfunksjoner fungerer muligens ikke slik de skal"</string> <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Det er ikke nok lagringsplass for systemet. Kontroller at du har 250 MB ledig plass, og start på nytt."</string> diff --git a/core/res/res/values-ne/strings.xml b/core/res/res/values-ne/strings.xml index 056e2537f036..2a23e877efd5 100644 --- a/core/res/res/values-ne/strings.xml +++ b/core/res/res/values-ne/strings.xml @@ -1195,8 +1195,7 @@ <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"पछाडि"</string> <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"इनपुट विधि बदल्नुहोस्"</string> <string name="input_method_ime_switch_long_click_action_desc" msgid="3161942124116646998">"इनपुट विधि पिकर खोल्नुहोस्"</string> - <!-- no translation found for input_method_switcher_settings_button (5609835654697108485) --> - <skip /> + <string name="input_method_switcher_settings_button" msgid="5609835654697108485">"सेटिङ"</string> <string name="low_internal_storage_view_title" msgid="9024241779284783414">"भण्डारण ठाउँ सकिँदै छ"</string> <string name="low_internal_storage_view_text" msgid="8172166728369697835">"सायद केही प्रणाली कार्यक्रमहरूले काम गर्दैनन्"</string> <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"प्रणालीको लागि पर्याप्त भण्डारण छैन। तपाईँसँग २५० मेगा बाइट ठाउँ खाली भएको निश्चित गर्नुहोस् र फेरि सुरु गर्नुहोस्।"</string> @@ -1443,7 +1442,7 @@ <string name="alert_windows_notification_channel_name" msgid="3437528564303192620">"<xliff:g id="NAME">%s</xliff:g> अन्य एपहरूमा देखिँदैछ"</string> <string name="alert_windows_notification_title" msgid="6331662751095228536">"<xliff:g id="NAME">%s</xliff:g> अन्य एपहरूमा देखिँदैछ"</string> <string name="alert_windows_notification_message" msgid="6538171456970725333">"तपाईं <xliff:g id="NAME">%s</xliff:g> ले यो विशेषता प्रयोग नगरेको चाहनुहुन्न भने सेटिङहरू खोली यसलाई निष्क्रिय पार्न ट्याप गर्नुहोस्।"</string> - <string name="alert_windows_notification_turn_off_action" msgid="7805857234839123780">"निष्क्रिय पार्नुहोस्"</string> + <string name="alert_windows_notification_turn_off_action" msgid="7805857234839123780">"अफ गर्नुहोस्"</string> <string name="ext_media_checking_notification_title" msgid="8299199995416510094">"जाँच गर्दै <xliff:g id="NAME">%s</xliff:g>…"</string> <string name="ext_media_checking_notification_message" msgid="2231566971425375542">"हालको सामग्री समीक्षा गर्दै"</string> <string name="ext_media_checking_notification_message" product="tv" msgid="7986154434946021415">"मिडिया भण्डारणको जाँच गरिँदै छ"</string> diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml index dc2675f7abf9..5106f7c58ea7 100644 --- a/core/res/res/values-nl/strings.xml +++ b/core/res/res/values-nl/strings.xml @@ -1195,8 +1195,7 @@ <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"Terug"</string> <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"Invoermethode wijzigen"</string> <string name="input_method_ime_switch_long_click_action_desc" msgid="3161942124116646998">"Kiezer voor invoermethoden openen"</string> - <!-- no translation found for input_method_switcher_settings_button (5609835654697108485) --> - <skip /> + <string name="input_method_switcher_settings_button" msgid="5609835654697108485">"Instellingen"</string> <string name="low_internal_storage_view_title" msgid="9024241779284783414">"Opslagruimte is bijna vol"</string> <string name="low_internal_storage_view_text" msgid="8172166728369697835">"Bepaalde systeemfuncties werken mogelijk niet"</string> <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Onvoldoende opslagruimte voor het systeem. Zorg ervoor dat je 250 MB vrije ruimte hebt en start opnieuw."</string> diff --git a/core/res/res/values-or/strings.xml b/core/res/res/values-or/strings.xml index 0ddb32901c47..f9e9374d38b7 100644 --- a/core/res/res/values-or/strings.xml +++ b/core/res/res/values-or/strings.xml @@ -1195,8 +1195,7 @@ <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"ପଛକୁ ଫେରନ୍ତୁ"</string> <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"ଇନପୁଟ ପଦ୍ଧତି ସ୍ୱିଚ କରନ୍ତୁ"</string> <string name="input_method_ime_switch_long_click_action_desc" msgid="3161942124116646998">"ଇନପୁଟ ପଦ୍ଧତି ପିକରକୁ ଖୋଲନ୍ତୁ"</string> - <!-- no translation found for input_method_switcher_settings_button (5609835654697108485) --> - <skip /> + <string name="input_method_switcher_settings_button" msgid="5609835654697108485">"ସେଟିଂସ"</string> <string name="low_internal_storage_view_title" msgid="9024241779284783414">"ଷ୍ଟୋରେଜ୍ ସ୍ପେସ୍ ଶେଷ ହେବାରେ ଲାଗିଛି"</string> <string name="low_internal_storage_view_text" msgid="8172166728369697835">"କିଛି ସିଷ୍ଟମ ପ୍ରକାର୍ଯ୍ୟ କାମ କରିନପାରେ"</string> <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"ସିଷ୍ଟମ୍ ପାଇଁ ପ୍ରର୍ଯ୍ୟାପ୍ତ ଷ୍ଟୋରେଜ୍ ନାହିଁ। ସୁନିଶ୍ଚିତ କରନ୍ତୁ ଯେ, ଆପଣଙ୍କ ପାଖରେ 250MB ଖାଲି ଜାଗା ଅଛି ଏବଂ ପୁନଃ ଆରମ୍ଭ କରନ୍ତୁ।"</string> diff --git a/core/res/res/values-pa/strings.xml b/core/res/res/values-pa/strings.xml index ea616f672dff..e30983236b25 100644 --- a/core/res/res/values-pa/strings.xml +++ b/core/res/res/values-pa/strings.xml @@ -1195,8 +1195,7 @@ <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"ਪਿੱਛੇ"</string> <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"ਇਨਪੁੱਟ ਵਿਧੀ ਨੂੰ ਸਵਿੱਚ ਕਰੋ"</string> <string name="input_method_ime_switch_long_click_action_desc" msgid="3161942124116646998">"ਇਨਪੁੱਟ ਵਿਧੀ ਚੋਣਕਾਰ ਨੂੰ ਖੋਲ੍ਹੋ"</string> - <!-- no translation found for input_method_switcher_settings_button (5609835654697108485) --> - <skip /> + <string name="input_method_switcher_settings_button" msgid="5609835654697108485">"ਸੈਟਿੰਗਾਂ"</string> <string name="low_internal_storage_view_title" msgid="9024241779284783414">"ਸਟੋਰੇਜ ਦੀ ਜਗ੍ਹਾ ਖਤਮ ਹੋ ਰਹੀ ਹੈ"</string> <string name="low_internal_storage_view_text" msgid="8172166728369697835">"ਕੁਝ ਸਿਸਟਮ ਫੰਕਸ਼ਨ ਕੰਮ ਨਹੀਂ ਵੀ ਕਰ ਸਕਦੇ"</string> <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"ਸਿਸਟਮ ਲਈ ਲੋੜੀਂਦੀ ਸਟੋਰੇਜ ਨਹੀਂ ਹੈ। ਯਕੀਨੀ ਬਣਾਓ ਕਿ ਤੁਹਾਡੇ ਕੋਲ 250MB ਖਾਲੀ ਜਗ੍ਹਾ ਹੈ ਅਤੇ ਮੁੜ-ਚਾਲੂ ਕਰੋ।"</string> diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml index b302a2c93d4e..b6f70d626903 100644 --- a/core/res/res/values-pl/strings.xml +++ b/core/res/res/values-pl/strings.xml @@ -1197,8 +1197,7 @@ <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"Wstecz"</string> <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"Przełącz metodę wprowadzania"</string> <string name="input_method_ime_switch_long_click_action_desc" msgid="3161942124116646998">"Otwórz selektor metody wprowadzania"</string> - <!-- no translation found for input_method_switcher_settings_button (5609835654697108485) --> - <skip /> + <string name="input_method_switcher_settings_button" msgid="5609835654697108485">"Ustawienia"</string> <string name="low_internal_storage_view_title" msgid="9024241779284783414">"Kończy się miejsce"</string> <string name="low_internal_storage_view_text" msgid="8172166728369697835">"Niektóre funkcje systemu mogą nie działać"</string> <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Za mało pamięci w systemie. Upewnij się, że masz 250 MB wolnego miejsca i uruchom urządzenie ponownie."</string> diff --git a/core/res/res/values-pt-rBR/strings.xml b/core/res/res/values-pt-rBR/strings.xml index 334f8c9b32ae..d5035e092c8d 100644 --- a/core/res/res/values-pt-rBR/strings.xml +++ b/core/res/res/values-pt-rBR/strings.xml @@ -1196,8 +1196,7 @@ <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"Voltar"</string> <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"Mudar o método de entrada"</string> <string name="input_method_ime_switch_long_click_action_desc" msgid="3161942124116646998">"Abrir o seletor de método de entrada"</string> - <!-- no translation found for input_method_switcher_settings_button (5609835654697108485) --> - <skip /> + <string name="input_method_switcher_settings_button" msgid="5609835654697108485">"Configurações"</string> <string name="low_internal_storage_view_title" msgid="9024241779284783414">"Pouco espaço de armazenamento"</string> <string name="low_internal_storage_view_text" msgid="8172166728369697835">"Algumas funções do sistema podem não funcionar"</string> <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Não há armazenamento suficiente para o sistema. Certifique-se de ter 250 MB de espaço livre e reinicie."</string> @@ -1403,7 +1402,7 @@ <string name="usb_midi_notification_title" msgid="7404506788950595557">"MIDI via USB ativado"</string> <string name="usb_uvc_notification_title" msgid="2030032862673400008">"Dispositivo conectado como Webcam"</string> <string name="usb_accessory_notification_title" msgid="1385394660861956980">"Acessório USB conectado"</string> - <string name="usb_notification_message" msgid="4715163067192110676">"Toque para conferir mais opções."</string> + <string name="usb_notification_message" msgid="4715163067192110676">"Toque para mais opções."</string> <string name="usb_power_notification_message" msgid="7284765627437897702">"Carregando dispositivo conectado. Toque para ver mais opções."</string> <string name="usb_unsupported_audio_accessory_title" msgid="2335775548086533065">"Acessório de áudio analógico detectado"</string> <string name="usb_unsupported_audio_accessory_message" msgid="1300168007129796621">"O dispositivo anexo não é compatível com esse smartphone. Toque para saber mais."</string> diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml index 70fa9f97d15f..a9ba018f8b90 100644 --- a/core/res/res/values-pt-rPT/strings.xml +++ b/core/res/res/values-pt-rPT/strings.xml @@ -1196,8 +1196,7 @@ <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"Voltar"</string> <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"Alternar o método de introdução"</string> <string name="input_method_ime_switch_long_click_action_desc" msgid="3161942124116646998">"Abrir o selecionador do método de introdução"</string> - <!-- no translation found for input_method_switcher_settings_button (5609835654697108485) --> - <skip /> + <string name="input_method_switcher_settings_button" msgid="5609835654697108485">"Definições"</string> <string name="low_internal_storage_view_title" msgid="9024241779284783414">"Está quase sem espaço de armazenamento"</string> <string name="low_internal_storage_view_text" msgid="8172166728369697835">"Algumas funções do sistema poderão não funcionar"</string> <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Não existe armazenamento suficiente para o sistema. Certifique-se de que tem 250 MB de espaço livre e reinicie."</string> diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml index 334f8c9b32ae..d5035e092c8d 100644 --- a/core/res/res/values-pt/strings.xml +++ b/core/res/res/values-pt/strings.xml @@ -1196,8 +1196,7 @@ <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"Voltar"</string> <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"Mudar o método de entrada"</string> <string name="input_method_ime_switch_long_click_action_desc" msgid="3161942124116646998">"Abrir o seletor de método de entrada"</string> - <!-- no translation found for input_method_switcher_settings_button (5609835654697108485) --> - <skip /> + <string name="input_method_switcher_settings_button" msgid="5609835654697108485">"Configurações"</string> <string name="low_internal_storage_view_title" msgid="9024241779284783414">"Pouco espaço de armazenamento"</string> <string name="low_internal_storage_view_text" msgid="8172166728369697835">"Algumas funções do sistema podem não funcionar"</string> <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Não há armazenamento suficiente para o sistema. Certifique-se de ter 250 MB de espaço livre e reinicie."</string> @@ -1403,7 +1402,7 @@ <string name="usb_midi_notification_title" msgid="7404506788950595557">"MIDI via USB ativado"</string> <string name="usb_uvc_notification_title" msgid="2030032862673400008">"Dispositivo conectado como Webcam"</string> <string name="usb_accessory_notification_title" msgid="1385394660861956980">"Acessório USB conectado"</string> - <string name="usb_notification_message" msgid="4715163067192110676">"Toque para conferir mais opções."</string> + <string name="usb_notification_message" msgid="4715163067192110676">"Toque para mais opções."</string> <string name="usb_power_notification_message" msgid="7284765627437897702">"Carregando dispositivo conectado. Toque para ver mais opções."</string> <string name="usb_unsupported_audio_accessory_title" msgid="2335775548086533065">"Acessório de áudio analógico detectado"</string> <string name="usb_unsupported_audio_accessory_message" msgid="1300168007129796621">"O dispositivo anexo não é compatível com esse smartphone. Toque para saber mais."</string> diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml index 12ad4f331fe8..444dbd4570c9 100644 --- a/core/res/res/values-ro/strings.xml +++ b/core/res/res/values-ro/strings.xml @@ -1196,8 +1196,7 @@ <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"Înapoi"</string> <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"Schimbă metoda de introducere"</string> <string name="input_method_ime_switch_long_click_action_desc" msgid="3161942124116646998">"Deschide selectorul metodei de introducere a textului"</string> - <!-- no translation found for input_method_switcher_settings_button (5609835654697108485) --> - <skip /> + <string name="input_method_switcher_settings_button" msgid="5609835654697108485">"Setări"</string> <string name="low_internal_storage_view_title" msgid="9024241779284783414">"Spațiul de stocare aproape ocupat"</string> <string name="low_internal_storage_view_text" msgid="8172166728369697835">"Este posibil ca unele funcții de sistem să nu funcționeze"</string> <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Spațiu de stocare insuficient pentru sistem. Asigură-te că ai 250 MB de spațiu liber și repornește."</string> diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml index 0ae47781ba1d..46e7b9d0715c 100644 --- a/core/res/res/values-ru/strings.xml +++ b/core/res/res/values-ru/strings.xml @@ -1197,8 +1197,7 @@ <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"Назад"</string> <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"Сменить способ ввода"</string> <string name="input_method_ime_switch_long_click_action_desc" msgid="3161942124116646998">"Выбрать способ ввода"</string> - <!-- no translation found for input_method_switcher_settings_button (5609835654697108485) --> - <skip /> + <string name="input_method_switcher_settings_button" msgid="5609835654697108485">"Настройки"</string> <string name="low_internal_storage_view_title" msgid="9024241779284783414">"Недостаточно памяти"</string> <string name="low_internal_storage_view_text" msgid="8172166728369697835">"Некоторые функции могут не работать"</string> <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Недостаточно свободного места для системы. Освободите не менее 250 МБ дискового пространства и перезапустите устройство."</string> diff --git a/core/res/res/values-si/strings.xml b/core/res/res/values-si/strings.xml index 4544ccb245d6..fb0356913d8c 100644 --- a/core/res/res/values-si/strings.xml +++ b/core/res/res/values-si/strings.xml @@ -1195,8 +1195,7 @@ <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"ආපසු"</string> <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"ආදාන ක්රමය මාරු කිරීම"</string> <string name="input_method_ime_switch_long_click_action_desc" msgid="3161942124116646998">"ආදාන ක්රම තෝරකය විවෘත කරන්න"</string> - <!-- no translation found for input_method_switcher_settings_button (5609835654697108485) --> - <skip /> + <string name="input_method_switcher_settings_button" msgid="5609835654697108485">"සැකසීම්"</string> <string name="low_internal_storage_view_title" msgid="9024241779284783414">"ආචයනය ඉඩ ප්රමාණය අඩු වී ඇත"</string> <string name="low_internal_storage_view_text" msgid="8172166728369697835">"සමහර පද්ධති කාර්යයන් ක්රියා නොකරනු ඇත"</string> <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"පද්ධතිය සඳහා ප්රමාණවත් ඉඩ නොමැත. ඔබට 250MB නිදහස් ඉඩක් තිබෙන ඔබට තිබෙන බව සහතික කරගෙන නැවත උත්සාහ කරන්න."</string> diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml index 359ca8ff69fe..bde470e32e77 100644 --- a/core/res/res/values-sk/strings.xml +++ b/core/res/res/values-sk/strings.xml @@ -1197,8 +1197,7 @@ <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"Späť"</string> <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"Prepnúť metódu vstupu"</string> <string name="input_method_ime_switch_long_click_action_desc" msgid="3161942124116646998">"Otvoriť výber metódy vstupu"</string> - <!-- no translation found for input_method_switcher_settings_button (5609835654697108485) --> - <skip /> + <string name="input_method_switcher_settings_button" msgid="5609835654697108485">"Nastavenia"</string> <string name="low_internal_storage_view_title" msgid="9024241779284783414">"Nedostatok ukladacieho priestoru"</string> <string name="low_internal_storage_view_text" msgid="8172166728369697835">"Niektoré systémové funkcie nemusia fungovať"</string> <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"V úložisku nie je dostatok voľného miesta pre systém. Zaistite, aby ste mali 250 MB voľného miesta a zariadenie reštartujte."</string> diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml index 146a4c250735..b70322b81008 100644 --- a/core/res/res/values-sl/strings.xml +++ b/core/res/res/values-sl/strings.xml @@ -1197,8 +1197,7 @@ <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"Nazaj"</string> <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"Preklop načina vnosa"</string> <string name="input_method_ime_switch_long_click_action_desc" msgid="3161942124116646998">"Odpiranje izbirnika načina vnosa"</string> - <!-- no translation found for input_method_switcher_settings_button (5609835654697108485) --> - <skip /> + <string name="input_method_switcher_settings_button" msgid="5609835654697108485">"Nastavitve"</string> <string name="low_internal_storage_view_title" msgid="9024241779284783414">"Prostor za shranjevanje bo pošel"</string> <string name="low_internal_storage_view_text" msgid="8172166728369697835">"Nekatere sistemske funkcije morda ne delujejo"</string> <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"V shrambi ni dovolj prostora za sistem. Sprostite 250 MB prostora in znova zaženite napravo."</string> diff --git a/core/res/res/values-sq/strings.xml b/core/res/res/values-sq/strings.xml index 896783aa19fa..304059732ad0 100644 --- a/core/res/res/values-sq/strings.xml +++ b/core/res/res/values-sq/strings.xml @@ -1195,8 +1195,7 @@ <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"Pas"</string> <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"Ndërro metodën e hyrjes"</string> <string name="input_method_ime_switch_long_click_action_desc" msgid="3161942124116646998">"Hap zgjedhësin e metodës së hyrjes"</string> - <!-- no translation found for input_method_switcher_settings_button (5609835654697108485) --> - <skip /> + <string name="input_method_switcher_settings_button" msgid="5609835654697108485">"Cilësimet"</string> <string name="low_internal_storage_view_title" msgid="9024241779284783414">"Hapësira ruajtëse po mbaron"</string> <string name="low_internal_storage_view_text" msgid="8172166728369697835">"Disa funksione të sistemit mund të mos punojnë"</string> <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Nuk ka hapësirë të mjaftueshme ruajtjeje për sistemin. Sigurohu që të kesh 250 MB hapësirë të lirë dhe pastaj të rifillosh."</string> diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml index 4bcd8337426f..f8e5a79b5ac2 100644 --- a/core/res/res/values-sr/strings.xml +++ b/core/res/res/values-sr/strings.xml @@ -1196,8 +1196,7 @@ <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"Назад"</string> <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"Промените метод уноса"</string> <string name="input_method_ime_switch_long_click_action_desc" msgid="3161942124116646998">"Отвори бирач метода уноса"</string> - <!-- no translation found for input_method_switcher_settings_button (5609835654697108485) --> - <skip /> + <string name="input_method_switcher_settings_button" msgid="5609835654697108485">"Подешавања"</string> <string name="low_internal_storage_view_title" msgid="9024241779284783414">"Меморијски простор је на измаку"</string> <string name="low_internal_storage_view_text" msgid="8172166728369697835">"Неке системске функције можда не функционишу"</string> <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Нема довољно меморијског простора за систем. Уверите се да имате 250 MB слободног простора и поново покрените."</string> diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml index edd5929453ca..8e08c6bb35a4 100644 --- a/core/res/res/values-sv/strings.xml +++ b/core/res/res/values-sv/strings.xml @@ -1195,8 +1195,7 @@ <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"Tillbaka"</string> <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"Byt inmatningsmetod"</string> <string name="input_method_ime_switch_long_click_action_desc" msgid="3161942124116646998">"Öppna inmatningsmetodsväljaren"</string> - <!-- no translation found for input_method_switcher_settings_button (5609835654697108485) --> - <skip /> + <string name="input_method_switcher_settings_button" msgid="5609835654697108485">"Inställningar"</string> <string name="low_internal_storage_view_title" msgid="9024241779284783414">"Lagringsutrymmet börjar ta slut"</string> <string name="low_internal_storage_view_text" msgid="8172166728369697835">"Det kan hända att vissa systemfunktioner inte fungerar"</string> <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Det finns inte tillräckligt med utrymme för systemet. Kontrollera att du har ett lagringsutrymme på minst 250 MB och starta om."</string> diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml index 0c10e9c9059e..a9385c1f89d7 100644 --- a/core/res/res/values-sw/strings.xml +++ b/core/res/res/values-sw/strings.xml @@ -1195,8 +1195,7 @@ <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"Rudi nyuma"</string> <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"Badilisha mbinu ya kuingiza data"</string> <string name="input_method_ime_switch_long_click_action_desc" msgid="3161942124116646998">"Fungua kiteua mbinu ya kuingiza data"</string> - <!-- no translation found for input_method_switcher_settings_button (5609835654697108485) --> - <skip /> + <string name="input_method_switcher_settings_button" msgid="5609835654697108485">"Mipangilio"</string> <string name="low_internal_storage_view_title" msgid="9024241779284783414">"Nafasi ya kuhifadhi inakaribia kujaa"</string> <string name="low_internal_storage_view_text" msgid="8172166728369697835">"Baadhi ya vipengee vya mfumo huenda visifanye kazi"</string> <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Hifadhi haitoshi kwa ajili ya mfumo. Hakikisha una MB 250 za nafasi ya hifadhi isiyotumika na uanzishe upya."</string> diff --git a/core/res/res/values-ta/strings.xml b/core/res/res/values-ta/strings.xml index 7bbe00a94624..6ba7a541e7fb 100644 --- a/core/res/res/values-ta/strings.xml +++ b/core/res/res/values-ta/strings.xml @@ -1195,8 +1195,7 @@ <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"பின்செல்லும்"</string> <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"உள்ளீட்டு முறையை மாற்றும்"</string> <string name="input_method_ime_switch_long_click_action_desc" msgid="3161942124116646998">"உள்ளீட்டு முறைத் தேர்வுக் கருவியைத் திறக்கும்"</string> - <!-- no translation found for input_method_switcher_settings_button (5609835654697108485) --> - <skip /> + <string name="input_method_switcher_settings_button" msgid="5609835654697108485">"அமைப்புகள்"</string> <string name="low_internal_storage_view_title" msgid="9024241779284783414">"சேமிப்பிடம் குறைகிறது"</string> <string name="low_internal_storage_view_text" msgid="8172166728369697835">"சில அமைப்பு செயல்பாடுகள் வேலை செய்யாமல் போகலாம்"</string> <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"முறைமையில் போதுமான சேமிப்பகம் இல்லை. 250மெ.பை. அளவு காலி இடவசதி இருப்பதை உறுதிசெய்து மீண்டும் தொடங்கவும்."</string> diff --git a/core/res/res/values-te/strings.xml b/core/res/res/values-te/strings.xml index 003d7e9a4fee..ae1a2c3b01ea 100644 --- a/core/res/res/values-te/strings.xml +++ b/core/res/res/values-te/strings.xml @@ -1195,8 +1195,7 @@ <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"వెనుకకు"</string> <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"ఇన్పుట్ విధానాన్ని మార్చండి"</string> <string name="input_method_ime_switch_long_click_action_desc" msgid="3161942124116646998">"ఇన్పుట్ విధాన సెలెక్టర్ను తెరవండి"</string> - <!-- no translation found for input_method_switcher_settings_button (5609835654697108485) --> - <skip /> + <string name="input_method_switcher_settings_button" msgid="5609835654697108485">"సెట్టింగ్లు"</string> <string name="low_internal_storage_view_title" msgid="9024241779284783414">"స్టోరేజ్ ఖాళీ అయిపోతోంది"</string> <string name="low_internal_storage_view_text" msgid="8172166728369697835">"కొన్ని సిస్టమ్ కార్యాచరణలు పని చేయకపోవచ్చు"</string> <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"సిస్టమ్ కోసం తగినంత స్టోరేజ్ లేదు. మీకు 250MB ఖాళీ స్థలం ఉందని నిర్ధారించుకుని, పునఃప్రారంభించండి."</string> diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml index 19bd98bff43f..655cf4c8a0f0 100644 --- a/core/res/res/values-th/strings.xml +++ b/core/res/res/values-th/strings.xml @@ -1195,8 +1195,7 @@ <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"กลับ"</string> <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"สลับวิธีการป้อนข้อมูล"</string> <string name="input_method_ime_switch_long_click_action_desc" msgid="3161942124116646998">"เปิดเครื่องมือเลือกวิธีการป้อนข้อมูล"</string> - <!-- no translation found for input_method_switcher_settings_button (5609835654697108485) --> - <skip /> + <string name="input_method_switcher_settings_button" msgid="5609835654697108485">"การตั้งค่า"</string> <string name="low_internal_storage_view_title" msgid="9024241779284783414">"พื้นที่จัดเก็บเหลือน้อย"</string> <string name="low_internal_storage_view_text" msgid="8172166728369697835">"บางฟังก์ชันระบบอาจไม่ทำงาน"</string> <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"พื้นที่เก็บข้อมูลไม่เพียงพอสำหรับระบบ โปรดตรวจสอบว่าคุณมีพื้นที่ว่าง 250 MB แล้วรีสตาร์ท"</string> diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml index a53ca7836234..885be75702a7 100644 --- a/core/res/res/values-tl/strings.xml +++ b/core/res/res/values-tl/strings.xml @@ -1195,8 +1195,7 @@ <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"Bumalik"</string> <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"Magpalit ng pamamaraan ng pag-input"</string> <string name="input_method_ime_switch_long_click_action_desc" msgid="3161942124116646998">"Buksan ang picker ng pamamaraan ng pag-input"</string> - <!-- no translation found for input_method_switcher_settings_button (5609835654697108485) --> - <skip /> + <string name="input_method_switcher_settings_button" msgid="5609835654697108485">"Mga Setting"</string> <string name="low_internal_storage_view_title" msgid="9024241779284783414">"Nauubusan na ang puwang ng storage"</string> <string name="low_internal_storage_view_text" msgid="8172166728369697835">"Maaaring hindi gumana nang tama ang ilang paggana ng system"</string> <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Walang sapat na storage para sa system. Tiyaking mayroon kang 250MB na libreng espasyo at i-restart."</string> diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml index 4c603e03809d..a5d064e1fdb1 100644 --- a/core/res/res/values-tr/strings.xml +++ b/core/res/res/values-tr/strings.xml @@ -1195,8 +1195,7 @@ <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"Geri"</string> <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"Giriş yöntemini değiştir"</string> <string name="input_method_ime_switch_long_click_action_desc" msgid="3161942124116646998">"Giriş yöntemi seçiciyi aç"</string> - <!-- no translation found for input_method_switcher_settings_button (5609835654697108485) --> - <skip /> + <string name="input_method_switcher_settings_button" msgid="5609835654697108485">"Ayarlar"</string> <string name="low_internal_storage_view_title" msgid="9024241779284783414">"Depolama alanı bitiyor"</string> <string name="low_internal_storage_view_text" msgid="8172166728369697835">"Bazı sistem işlevleri çalışmayabilir"</string> <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Sistem için yeterli depolama alanı yok. 250 MB boş alanınızın bulunduğundan emin olun ve yeniden başlatın."</string> diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml index 1153830b6159..5514ba4c65b1 100644 --- a/core/res/res/values-uk/strings.xml +++ b/core/res/res/values-uk/strings.xml @@ -1197,8 +1197,7 @@ <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"Назад"</string> <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"Змінити метод введення"</string> <string name="input_method_ime_switch_long_click_action_desc" msgid="3161942124116646998">"Відкрити засіб вибору методу введення"</string> - <!-- no translation found for input_method_switcher_settings_button (5609835654697108485) --> - <skip /> + <string name="input_method_switcher_settings_button" msgid="5609835654697108485">"Налаштування"</string> <string name="low_internal_storage_view_title" msgid="9024241779284783414">"Закінчується пам’ять"</string> <string name="low_internal_storage_view_text" msgid="8172166728369697835">"Деякі системні функції можуть не працювати"</string> <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Недостатньо місця для системи. Переконайтесь, що на пристрої є 250 МБ вільного місця, і повторіть спробу."</string> diff --git a/core/res/res/values-ur/strings.xml b/core/res/res/values-ur/strings.xml index 55a917d41a43..7c6317c520d1 100644 --- a/core/res/res/values-ur/strings.xml +++ b/core/res/res/values-ur/strings.xml @@ -1195,8 +1195,7 @@ <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"پیچھے"</string> <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"اندراج کا طریقہ سوئچ کریں"</string> <string name="input_method_ime_switch_long_click_action_desc" msgid="3161942124116646998">"اندراج کے طریقے کا منتخب کنندہ کھولیں"</string> - <!-- no translation found for input_method_switcher_settings_button (5609835654697108485) --> - <skip /> + <string name="input_method_switcher_settings_button" msgid="5609835654697108485">"ترتیبات"</string> <string name="low_internal_storage_view_title" msgid="9024241779284783414">"اسٹوریج کی جگہ ختم ہو رہی ہے"</string> <string name="low_internal_storage_view_text" msgid="8172166728369697835">"ممکن ہے سسٹم کے کچھ فنکشنز کام نہ کریں"</string> <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"سسٹم کیلئے کافی اسٹوریج نہیں ہے۔ اس بات کو یقینی بنائیں کہ آپ کے پاس 250MB خالی جگہ ہے اور دوبارہ شروع کریں۔"</string> diff --git a/core/res/res/values-uz/strings.xml b/core/res/res/values-uz/strings.xml index f2451d54aff7..868a9278b50e 100644 --- a/core/res/res/values-uz/strings.xml +++ b/core/res/res/values-uz/strings.xml @@ -1195,8 +1195,7 @@ <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"Orqaga"</string> <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"Matn kiritish usulini almashtirish"</string> <string name="input_method_ime_switch_long_click_action_desc" msgid="3161942124116646998">"Kiritish usulini tanlash oynasini ochish"</string> - <!-- no translation found for input_method_switcher_settings_button (5609835654697108485) --> - <skip /> + <string name="input_method_switcher_settings_button" msgid="5609835654697108485">"Sozlamalar"</string> <string name="low_internal_storage_view_title" msgid="9024241779284783414">"Xotirada joy yetarli emas"</string> <string name="low_internal_storage_view_text" msgid="8172166728369697835">"Ayrim funksiyalar ishlamasligi mumkin"</string> <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Tizim uchun xotirada joy yetarli emas. Avval 250 megabayt joy bo‘shatib, keyin qurilmani o‘chirib yoqing."</string> diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml index 7c354a4584ca..c7337efe14f7 100644 --- a/core/res/res/values-vi/strings.xml +++ b/core/res/res/values-vi/strings.xml @@ -1195,8 +1195,7 @@ <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"Quay lại"</string> <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"Chuyển phương thức nhập"</string> <string name="input_method_ime_switch_long_click_action_desc" msgid="3161942124116646998">"Mở bộ chọn phương thức nhập"</string> - <!-- no translation found for input_method_switcher_settings_button (5609835654697108485) --> - <skip /> + <string name="input_method_switcher_settings_button" msgid="5609835654697108485">"Cài đặt"</string> <string name="low_internal_storage_view_title" msgid="9024241779284783414">"Sắp hết dung lượng lưu trữ"</string> <string name="low_internal_storage_view_text" msgid="8172166728369697835">"Một số chức năng hệ thống có thể không hoạt động"</string> <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Không đủ bộ nhớ cho hệ thống. Đảm bảo bạn có 250 MB dung lượng trống và khởi động lại."</string> diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml index 1b8dd3a08d99..378e548fdd6b 100644 --- a/core/res/res/values-zh-rCN/strings.xml +++ b/core/res/res/values-zh-rCN/strings.xml @@ -1195,8 +1195,7 @@ <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"返回"</string> <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"切换输入法"</string> <string name="input_method_ime_switch_long_click_action_desc" msgid="3161942124116646998">"打开输入法选择器"</string> - <!-- no translation found for input_method_switcher_settings_button (5609835654697108485) --> - <skip /> + <string name="input_method_switcher_settings_button" msgid="5609835654697108485">"设置"</string> <string name="low_internal_storage_view_title" msgid="9024241779284783414">"存储空间不足"</string> <string name="low_internal_storage_view_text" msgid="8172166728369697835">"某些系统功能可能无法正常使用"</string> <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"系统存储空间不足。请确保您有250MB的可用空间,然后重新启动。"</string> diff --git a/core/res/res/values-zh-rHK/strings.xml b/core/res/res/values-zh-rHK/strings.xml index 91ccc079cad5..5a9db4f088e6 100644 --- a/core/res/res/values-zh-rHK/strings.xml +++ b/core/res/res/values-zh-rHK/strings.xml @@ -1195,8 +1195,7 @@ <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"返回"</string> <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"切換輸入方法"</string> <string name="input_method_ime_switch_long_click_action_desc" msgid="3161942124116646998">"打開輸入方法點選器"</string> - <!-- no translation found for input_method_switcher_settings_button (5609835654697108485) --> - <skip /> + <string name="input_method_switcher_settings_button" msgid="5609835654697108485">"設定"</string> <string name="low_internal_storage_view_title" msgid="9024241779284783414">"儲存空間即將用盡"</string> <string name="low_internal_storage_view_text" msgid="8172166728369697835">"部分系統功能可能無法運作"</string> <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"系統儲存空間不足。請確認裝置有 250 MB 的可用空間,然後重新啟動。"</string> diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml index 8f5ae1943c22..5cfca16a4ce2 100644 --- a/core/res/res/values-zh-rTW/strings.xml +++ b/core/res/res/values-zh-rTW/strings.xml @@ -1195,8 +1195,7 @@ <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"返回"</string> <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"切換輸入法"</string> <string name="input_method_ime_switch_long_click_action_desc" msgid="3161942124116646998">"開啟輸入法挑選器"</string> - <!-- no translation found for input_method_switcher_settings_button (5609835654697108485) --> - <skip /> + <string name="input_method_switcher_settings_button" msgid="5609835654697108485">"設定"</string> <string name="low_internal_storage_view_title" msgid="9024241779284783414">"儲存空間即將用盡"</string> <string name="low_internal_storage_view_text" msgid="8172166728369697835">"部分系統功能可能無法運作"</string> <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"系統儲存空間不足。請確定你已釋出 250MB 的可用空間,然後重新啟動。"</string> diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml index f09e9224b0da..c8e49a1694d0 100644 --- a/core/res/res/values-zu/strings.xml +++ b/core/res/res/values-zu/strings.xml @@ -1195,8 +1195,7 @@ <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"Emuva"</string> <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"Shintsha indlela yokufaka"</string> <string name="input_method_ime_switch_long_click_action_desc" msgid="3161942124116646998">"Vula okokukhetha kwendlela yokufaka"</string> - <!-- no translation found for input_method_switcher_settings_button (5609835654697108485) --> - <skip /> + <string name="input_method_switcher_settings_button" msgid="5609835654697108485">"Amasethingi"</string> <string name="low_internal_storage_view_title" msgid="9024241779284783414">"Isikhala sokulondoloza siyaphela"</string> <string name="low_internal_storage_view_text" msgid="8172166728369697835">"Eminye imisebenzi yohlelo ingahle ingasebenzi"</string> <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Akusona isitoreji esanele sesistimu. Qiniseka ukuthi unesikhala esikhululekile esingu-250MB uphinde uqalise kabusha."</string> diff --git a/core/res/res/values/config_telephony.xml b/core/res/res/values/config_telephony.xml index a7240ffc6012..69437b44fd6e 100644 --- a/core/res/res/values/config_telephony.xml +++ b/core/res/res/values/config_telephony.xml @@ -440,10 +440,6 @@ <string name="config_satellite_carrier_roaming_esos_provisioned_class" translatable="false"></string> <java-symbol type="string" name="config_satellite_carrier_roaming_esos_provisioned_class" /> - <!-- Telephony satellite gateway intent for handling carrier roaming to satellite is using ESOS messaging. --> - <string name="config_satellite_carrier_roaming_esos_provisioned_intent_action" translatable="false"></string> - <java-symbol type="string" name="config_satellite_carrier_roaming_esos_provisioned_intent_action" /> - <!-- The time duration in minutes to wait before retry validating a possible change in satellite allowed region. The default value is 10 minutes. --> <integer name="config_satellite_delay_minutes_before_retry_validating_possible_change_in_allowed_region">10</integer> diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml index c084b4c1e834..dc99634ddabc 100644 --- a/core/res/res/values/styles.xml +++ b/core/res/res/values/styles.xml @@ -1504,26 +1504,26 @@ please see styles_device_defaults.xml. <!-- @hide --> <style name="PointerIconVectorStyleFillGreen"> - <item name="pointerIconVectorFill">#6DD58C</item> - <item name="pointerIconVectorFillInverse">#6DD58C</item> + <item name="pointerIconVectorFill">#1AA64A</item> + <item name="pointerIconVectorFillInverse">#1AA64A</item> </style> <!-- @hide --> <style name="PointerIconVectorStyleFillYellow"> - <item name="pointerIconVectorFill">#FDD663</item> - <item name="pointerIconVectorFillInverse">#FDD663</item> + <item name="pointerIconVectorFill">#F55E57</item> + <item name="pointerIconVectorFillInverse">#F55E57</item> </style> <!-- @hide --> <style name="PointerIconVectorStyleFillPink"> - <item name="pointerIconVectorFill">#F2B8B5</item> - <item name="pointerIconVectorFillInverse">#F2B8B5</item> + <item name="pointerIconVectorFill">#F94AAB</item> + <item name="pointerIconVectorFillInverse">#F94AAB</item> </style> <!-- @hide --> <style name="PointerIconVectorStyleFillBlue"> - <item name="pointerIconVectorFill">#8AB4F8</item> - <item name="pointerIconVectorFillInverse">#8AB4F8</item> + <item name="pointerIconVectorFill">#009DC9</item> + <item name="pointerIconVectorFillInverse">#009DC9</item> </style> <!-- @hide --> @@ -1535,7 +1535,7 @@ please see styles_device_defaults.xml. <!-- @hide --> <style name="PointerIconVectorStyleStrokeBlack"> <item name="pointerIconVectorStroke">@color/black</item> - <item name="pointerIconVectorStrokeInverse">@color/white</item> + <item name="pointerIconVectorStrokeInverse">@color/black</item> </style> <!-- @hide --> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index bbe661e78b1d..74922aca3b02 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -5566,6 +5566,7 @@ <java-symbol type="string" name="recs_notification_channel_label"/> <!-- Priority Modes icons --> + <java-symbol type="drawable" name="ic_zen_priority_modes" /> <java-symbol type="drawable" name="ic_zen_mode_type_bedtime" /> <java-symbol type="drawable" name="ic_zen_mode_type_driving" /> <java-symbol type="drawable" name="ic_zen_mode_type_immersive" /> diff --git a/core/tests/batterystatstests/BatteryStatsViewer/AndroidManifest.xml b/core/tests/batterystatstests/BatteryStatsViewer/AndroidManifest.xml index 94bde68fdf11..127dbfd25b09 100644 --- a/core/tests/batterystatstests/BatteryStatsViewer/AndroidManifest.xml +++ b/core/tests/batterystatstests/BatteryStatsViewer/AndroidManifest.xml @@ -20,6 +20,7 @@ <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/> <uses-permission android:name="android.permission.BATTERY_STATS"/> + <uses-permission android:name="android.permission.CHANGE_COMPONENT_ENABLED_STATE"/> <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS"/> <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES"/> @@ -31,7 +32,8 @@ <activity android:name=".BatteryConsumerPickerActivity" android:label="Battery Stats" android:launchMode="singleTop" - android:exported="true"> + android:exported="true" + android:enabled="false"> <intent-filter> <action android:name="android.intent.action.MAIN"/> <category android:name="android.intent.category.LAUNCHER"/> @@ -41,5 +43,25 @@ <activity android:name=".BatteryStatsViewerActivity" android:label="Battery Stats" android:parentActivityName=".BatteryConsumerPickerActivity"/> + + <activity android:name=".TrampolineActivity" + android:exported="true" + android:theme="@android:style/Theme.NoDisplay"> + <intent-filter> + <action android:name="com.android.settings.action.IA_SETTINGS"/> + <category android:name="android.intent.category.DEFAULT" /> + </intent-filter> + + <meta-data android:name="com.android.settings.category" + android:value="com.android.settings.category.ia.development" /> + <meta-data android:name="com.android.settings.title" + android:resource="@string/settings_title" /> + <meta-data android:name="com.android.settings.summary" + android:resource="@string/settings_summary" /> + <meta-data android:name="com.android.settings.group_key" + android:value="debug_debugging_category" /> + <meta-data android:name="com.android.settings.order" + android:value="2" /> + </activity> </application> </manifest> diff --git a/core/tests/batterystatstests/BatteryStatsViewer/res/values/strings.xml b/core/tests/batterystatstests/BatteryStatsViewer/res/values/strings.xml new file mode 100644 index 000000000000..c23c1484cf59 --- /dev/null +++ b/core/tests/batterystatstests/BatteryStatsViewer/res/values/strings.xml @@ -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. + --> + +<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="settings_title">Launch Battery Stats Viewer</string> + <string name="settings_summary">The Battery Stats Viewer will be visible in the Launcher after it is opened once.</string> +</resources> diff --git a/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/TrampolineActivity.java b/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/TrampolineActivity.java new file mode 100644 index 000000000000..b01648838695 --- /dev/null +++ b/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/TrampolineActivity.java @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.frameworks.core.batterystatsviewer; + +import android.app.Activity; +import android.content.ComponentName; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.os.Bundle; + +import androidx.annotation.Nullable; + +public class TrampolineActivity extends Activity { + + @Override + protected void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + showLauncherIcon(); + launchMainActivity(); + } + + private void showLauncherIcon() { + PackageManager pm = getPackageManager(); + pm.setComponentEnabledSetting(new ComponentName(this, BatteryConsumerPickerActivity.class), + PackageManager.COMPONENT_ENABLED_STATE_ENABLED, + PackageManager.DONT_KILL_APP); + } + + private void launchMainActivity() { + startActivity(new Intent(this, BatteryConsumerPickerActivity.class)); + } +} diff --git a/core/tests/coretests/Android.bp b/core/tests/coretests/Android.bp index edf461a4d5a6..b0e48f1ccd93 100644 --- a/core/tests/coretests/Android.bp +++ b/core/tests/coretests/Android.bp @@ -803,3 +803,11 @@ test_module_config { include_annotations: ["android.platform.test.annotations.PlatinumTest"], exclude_annotations: FLAKY_OR_IGNORED, } + +test_module_config { + name: "FrameworksCoreTests_android_tracing", + base: "FrameworksCoreTests", + team: "trendy_team_windowing_tools", + test_suites: ["device-tests"], + include_filters: ["android.tracing"], +} diff --git a/core/tests/coretests/src/android/tracing/TEST_MAPPING b/core/tests/coretests/src/android/tracing/TEST_MAPPING new file mode 100644 index 000000000000..4b7adf92cc03 --- /dev/null +++ b/core/tests/coretests/src/android/tracing/TEST_MAPPING @@ -0,0 +1,8 @@ +{ + "postsubmit": [ + { + "name": "FrameworksCoreTests_android_tracing", + "file_patterns": [".*\\.java"] + } + ] +} diff --git a/core/tests/coretests/src/android/widget/ChronometerTest.java b/core/tests/coretests/src/android/widget/ChronometerTest.java index 3c738372377a..cb331176aa31 100644 --- a/core/tests/coretests/src/android/widget/ChronometerTest.java +++ b/core/tests/coretests/src/android/widget/ChronometerTest.java @@ -17,6 +17,7 @@ package android.widget; import android.app.Activity; +import android.os.SystemClock; import android.test.ActivityInstrumentationTestCase2; import androidx.test.filters.LargeTest; @@ -28,7 +29,7 @@ import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; /** - * Test {@link DatePicker} focus changes. + * Test {@link Chronometer} counting up and down. */ @SuppressWarnings("deprecation") @LargeTest @@ -50,26 +51,48 @@ public class ChronometerTest extends ActivityInstrumentationTestCase2<Chronomete } public void testChronometerTicksSequentially() throws Throwable { - final CountDownLatch latch = new CountDownLatch(5); + final CountDownLatch latch = new CountDownLatch(6); ArrayList<String> ticks = new ArrayList<>(); runOnUiThread(() -> { mChronometer.setOnChronometerTickListener((chronometer) -> { ticks.add(chronometer.getText().toString()); latch.countDown(); try { - Thread.sleep(500); + Thread.sleep(250); } catch (InterruptedException e) { } }); mChronometer.start(); }); - assertTrue(latch.await(6, TimeUnit.SECONDS)); - assertTrue(ticks.size() >= 5); + assertTrue(latch.await(5500, TimeUnit.MILLISECONDS)); assertEquals("00:00", ticks.get(0)); assertEquals("00:01", ticks.get(1)); assertEquals("00:02", ticks.get(2)); assertEquals("00:03", ticks.get(3)); assertEquals("00:04", ticks.get(4)); + assertEquals("00:05", ticks.get(5)); + } + + public void testChronometerCountDown() throws Throwable { + final CountDownLatch latch = new CountDownLatch(5); + ArrayList<String> ticks = new ArrayList<>(); + runOnUiThread(() -> { + mChronometer.setBase(SystemClock.elapsedRealtime() + 3_000); + mChronometer.setCountDown(true); + mChronometer.post(() -> { + mChronometer.setOnChronometerTickListener((chronometer) -> { + ticks.add(chronometer.getText().toString()); + latch.countDown(); + }); + mChronometer.start(); + }); + }); + assertTrue(latch.await(4500, TimeUnit.MILLISECONDS)); + assertEquals("00:02", ticks.get(0)); + assertEquals("00:01", ticks.get(1)); + assertEquals("00:00", ticks.get(2)); + assertEquals("−00:01", ticks.get(3)); + assertEquals("−00:02", ticks.get(4)); } private void runOnUiThread(Runnable runnable) throws InterruptedException { diff --git a/core/tests/coretests/src/com/android/internal/jank/FrameTrackerTest.java b/core/tests/coretests/src/com/android/internal/jank/FrameTrackerTest.java index 499caf5e12d3..c3a5b19c9442 100644 --- a/core/tests/coretests/src/com/android/internal/jank/FrameTrackerTest.java +++ b/core/tests/coretests/src/com/android/internal/jank/FrameTrackerTest.java @@ -359,7 +359,7 @@ public class FrameTrackerTest { tracker.end(FrameTracker.REASON_END_NORMAL); // Send incomplete callback for 102L - sendSfFrame(tracker, 102L, JANK_NONE); + sendSfFrame(tracker, 4, 102L, JANK_NONE); // Send janky but complete callbck fo 103L sendFrame(tracker, 50, JANK_APP_DEADLINE_MISSED, 103L); @@ -629,7 +629,7 @@ public class FrameTrackerTest { if (!tracker.mSurfaceOnly) { sendHwuiFrame(tracker, durationMillis, vsyncId, firstWindowFrame); } - sendSfFrame(tracker, vsyncId, jankType); + sendSfFrame(tracker, durationMillis, vsyncId, jankType); } private void sendHwuiFrame(FrameTracker tracker, long durationMillis, long vsyncId, @@ -645,11 +645,13 @@ public class FrameTrackerTest { captor.getValue().run(); } - private void sendSfFrame(FrameTracker tracker, long vsyncId, @JankType int jankType) { + private void sendSfFrame( + FrameTracker tracker, long durationMillis, long vsyncId, @JankType int jankType) { final ArgumentCaptor<Runnable> captor = ArgumentCaptor.forClass(Runnable.class); doNothing().when(tracker).postCallback(captor.capture()); mListenerCapture.getValue().onJankDataAvailable(new JankData[] { - new JankData(vsyncId, jankType, FRAME_TIME_60Hz) + new JankData(vsyncId, jankType, FRAME_TIME_60Hz, FRAME_TIME_60Hz, + TimeUnit.MILLISECONDS.toNanos(durationMillis)) }); captor.getValue().run(); } 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 b183ecb50591..149e132a0df4 100644 --- a/core/tests/coretests/src/com/android/internal/statusbar/StatusBarIconTest.java +++ b/core/tests/coretests/src/com/android/internal/statusbar/StatusBarIconTest.java @@ -20,6 +20,7 @@ import static com.google.common.truth.Truth.assertThat; import android.graphics.Color; import android.graphics.drawable.ColorDrawable; +import android.graphics.drawable.Icon; import android.os.Parcel; import android.os.UserHandle; @@ -69,22 +70,22 @@ public class StatusBarIconTest { assertThat(copy.preloadedIcon).isEqualTo(original.preloadedIcon); } - private static StatusBarIcon newStatusBarIcon() { final UserHandle dummyUserHandle = UserHandle.of(100); final String dummyIconPackageName = "com.android.internal.statusbar.test"; - final int dummyIconId = 123; + final Icon dummyIcon = Icon.createWithResource(dummyIconPackageName, 123); final int dummyIconLevel = 1; final int dummyIconNumber = 2; final CharSequence dummyIconContentDescription = "dummyIcon"; return new StatusBarIcon( - dummyIconPackageName, dummyUserHandle, - dummyIconId, + dummyIconPackageName, + dummyIcon, dummyIconLevel, dummyIconNumber, dummyIconContentDescription, - StatusBarIcon.Type.SystemIcon); + StatusBarIcon.Type.SystemIcon, + StatusBarIcon.Shape.FIXED_SPACE); } private static void assertSerializableFieldsEqual(StatusBarIcon copy, StatusBarIcon original) { @@ -96,6 +97,7 @@ public class StatusBarIconTest { assertThat(copy.number).isEqualTo(original.number); assertThat(copy.contentDescription).isEqualTo(original.contentDescription); assertThat(copy.type).isEqualTo(original.type); + assertThat(copy.shape).isEqualTo(original.shape); } private static StatusBarIcon parcelAndUnparcel(StatusBarIcon original) { diff --git a/data/fonts/Android.bp b/data/fonts/Android.bp index cbaac21aa10b..4edf52bae161 100644 --- a/data/fonts/Android.bp +++ b/data/fonts/Android.bp @@ -71,16 +71,9 @@ prebuilt_fonts_xml { }, } -// TODO(nona): Change this to use generate_font_fallback to be able to generate XML from -// per family JSON config -prebuilt_fonts_xml { +prebuilt_etc { name: "font_fallback.xml", - src: "font_fallback.xml", - soong_config_variables: { - use_var_font: { - src: "font_fallback_cjkvf.xml", - }, - }, + src: ":generate_font_fallback", } ///////////////////////////////// diff --git a/data/keyboards/Vendor_0957_Product_0033.idc b/data/keyboards/Vendor_0957_Product_0033.idc new file mode 100644 index 000000000000..7dfbe2cced34 --- /dev/null +++ b/data/keyboards/Vendor_0957_Product_0033.idc @@ -0,0 +1,23 @@ +# 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. +# + +# Input Device Configuration file for Google Reference RCU Remote. +# PID 0033 is for new G20 with start button. + +# Basic Parameters +keyboard.layout = Vendor_0957_Product_0031 +# The reason why we set is follow https://docs.partner.android.com/tv/build/gtv/boot-resume +keyboard.doNotWakeByDefault = 1 +audio.mic = 1 diff --git a/graphics/java/android/graphics/Matrix44.java b/graphics/java/android/graphics/Matrix44.java index a99e20101c3b..683f6149a203 100644 --- a/graphics/java/android/graphics/Matrix44.java +++ b/graphics/java/android/graphics/Matrix44.java @@ -19,6 +19,7 @@ package android.graphics; import android.annotation.FlaggedApi; import android.annotation.IntRange; import android.annotation.NonNull; +import android.ravenwood.annotation.RavenwoodKeepWholeClass; import com.android.graphics.hwui.flags.Flags; @@ -30,6 +31,7 @@ import java.util.Arrays; * in row-major order. The values and operations are treated as column vectors. */ @FlaggedApi(Flags.FLAG_MATRIX_44) +@RavenwoodKeepWholeClass public class Matrix44 { final float[] mBackingArray; /** diff --git a/graphics/java/android/graphics/Outline.java b/graphics/java/android/graphics/Outline.java index 618e6dcc4433..c7b89412cc47 100644 --- a/graphics/java/android/graphics/Outline.java +++ b/graphics/java/android/graphics/Outline.java @@ -21,6 +21,7 @@ import android.annotation.IntDef; import android.annotation.NonNull; import android.compat.annotation.UnsupportedAppUsage; import android.graphics.drawable.Drawable; +import android.ravenwood.annotation.RavenwoodKeepWholeClass; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -35,6 +36,7 @@ import java.lang.annotation.RetentionPolicy; * @see android.view.View#setOutlineProvider(android.view.ViewOutlineProvider) * @see Drawable#getOutline(Outline) */ +@RavenwoodKeepWholeClass public final class Outline { private static final float RADIUS_UNDEFINED = Float.NEGATIVE_INFINITY; diff --git a/graphics/java/android/graphics/ParcelableColorSpace.java b/graphics/java/android/graphics/ParcelableColorSpace.java index 748d66cb5f6c..76c1715475ae 100644 --- a/graphics/java/android/graphics/ParcelableColorSpace.java +++ b/graphics/java/android/graphics/ParcelableColorSpace.java @@ -20,6 +20,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.os.Parcel; import android.os.Parcelable; +import android.ravenwood.annotation.RavenwoodKeepWholeClass; /** * A {@link Parcelable} wrapper for a {@link ColorSpace}. In order to enable parceling, the @@ -27,6 +28,7 @@ import android.os.Parcelable; * {@link ColorSpace.Rgb} instance that has an ICC parametric transfer function as returned by * {@link ColorSpace.Rgb#getTransferParameters()}. */ +@RavenwoodKeepWholeClass public final class ParcelableColorSpace implements Parcelable { private final ColorSpace mColorSpace; diff --git a/graphics/java/android/graphics/PixelFormat.java b/graphics/java/android/graphics/PixelFormat.java index 3ec5b9cc7dae..a872e03db3b5 100644 --- a/graphics/java/android/graphics/PixelFormat.java +++ b/graphics/java/android/graphics/PixelFormat.java @@ -17,10 +17,12 @@ package android.graphics; import android.annotation.IntDef; +import android.ravenwood.annotation.RavenwoodKeepWholeClass; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +@RavenwoodKeepWholeClass public class PixelFormat { /** @hide */ @IntDef({UNKNOWN, TRANSLUCENT, TRANSPARENT, OPAQUE}) diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/BackupHelper.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/BackupHelper.java index 69a68c85a2fe..0726624a05f8 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/BackupHelper.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/BackupHelper.java @@ -24,6 +24,7 @@ import android.util.Log; import androidx.annotation.NonNull; +import java.util.ArrayList; import java.util.List; /** @@ -80,9 +81,14 @@ class BackupHelper { } if (DEBUG) Log.d(TAG, "Start to back up " + taskContainers); + final List<ParcelableTaskContainerData> parcelableTaskContainerDataList = new ArrayList<>( + taskContainers.size()); + for (TaskContainer taskContainer : taskContainers) { + parcelableTaskContainerDataList.add(taskContainer.getParcelableData()); + } final Bundle state = new Bundle(); - state.setClassLoader(TaskContainer.class.getClassLoader()); - state.putParcelableList(KEY_TASK_CONTAINERS, taskContainers); + state.setClassLoader(ParcelableTaskContainerData.class.getClassLoader()); + state.putParcelableList(KEY_TASK_CONTAINERS, parcelableTaskContainerDataList); mController.setSavedState(state); } @@ -91,10 +97,12 @@ class BackupHelper { return; } - final List<TaskContainer> taskContainers = savedState.getParcelableArrayList( - KEY_TASK_CONTAINERS, TaskContainer.class); - for (TaskContainer taskContainer : taskContainers) { - if (DEBUG) Log.d(TAG, "restore task " + taskContainer.getTaskId()); + final List<ParcelableTaskContainerData> parcelableTaskContainerDataList = + savedState.getParcelableArrayList(KEY_TASK_CONTAINERS, + ParcelableTaskContainerData.class); + for (ParcelableTaskContainerData data : parcelableTaskContainerDataList) { + final TaskContainer taskContainer = new TaskContainer(data, mController); + if (DEBUG) Log.d(TAG, "Restoring task " + taskContainer.getTaskId()); // TODO(b/289875940): implement the TaskContainer restoration. } } diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/ParcelableSplitContainerData.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/ParcelableSplitContainerData.java new file mode 100644 index 000000000000..817cfce69b2e --- /dev/null +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/ParcelableSplitContainerData.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 androidx.window.extensions.embedding; + +import android.os.IBinder; +import android.os.Parcel; +import android.os.Parcelable; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +/** + * This class holds the Parcelable data of a {@link SplitContainer}. + */ +class ParcelableSplitContainerData implements Parcelable { + + /** + * A reference to the target {@link SplitContainer} that owns the data. This will not be + * parcelled and will be {@code null} when the data is created from a parcel. + */ + @Nullable + final SplitContainer mSplitContainer; + + @NonNull + final IBinder mToken; + + @NonNull + private final IBinder mPrimaryContainerToken; + + @NonNull + private final IBinder mSecondaryContainerToken; + + // TODO(b/289875940): making this as non-null once the tag can be auto-generated from the rule. + @Nullable + final String mSplitRuleTag; + + /** + * Whether the selection of which container is primary can be changed at runtime. Runtime + * updates is currently possible only for {@link SplitPinContainer} + * + * @see SplitPinContainer + */ + final boolean mIsPrimaryContainerMutable; + + ParcelableSplitContainerData(@NonNull SplitContainer splitContainer, @NonNull IBinder token, + @NonNull IBinder primaryContainerToken, @NonNull IBinder secondaryContainerToken, + @Nullable String splitRuleTag, boolean isPrimaryContainerMutable) { + mSplitContainer = splitContainer; + mToken = token; + mPrimaryContainerToken = primaryContainerToken; + mSecondaryContainerToken = secondaryContainerToken; + mSplitRuleTag = splitRuleTag; + mIsPrimaryContainerMutable = isPrimaryContainerMutable; + } + + private ParcelableSplitContainerData(Parcel in) { + mSplitContainer = null; + mToken = in.readStrongBinder(); + mPrimaryContainerToken = in.readStrongBinder(); + mSecondaryContainerToken = in.readStrongBinder(); + mSplitRuleTag = in.readString(); + mIsPrimaryContainerMutable = in.readBoolean(); + } + + public static final Creator<ParcelableSplitContainerData> CREATOR = new Creator<>() { + @Override + public ParcelableSplitContainerData createFromParcel(Parcel in) { + return new ParcelableSplitContainerData(in); + } + + @Override + public ParcelableSplitContainerData[] newArray(int size) { + return new ParcelableSplitContainerData[size]; + } + }; + + @NonNull + private IBinder getPrimaryContainerToken() { + return mSplitContainer != null ? mSplitContainer.getPrimaryContainer().getToken() + : mPrimaryContainerToken; + } + + @NonNull + private IBinder getSecondaryContainerToken() { + return mSplitContainer != null ? mSplitContainer.getSecondaryContainer().getToken() + : mSecondaryContainerToken; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeStrongBinder(mToken); + dest.writeStrongBinder(getPrimaryContainerToken()); + dest.writeStrongBinder(getSecondaryContainerToken()); + dest.writeString(mSplitRuleTag); + dest.writeBoolean(mIsPrimaryContainerMutable); + } +} diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/ParcelableTaskContainerData.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/ParcelableTaskContainerData.java new file mode 100644 index 000000000000..7377d005cda4 --- /dev/null +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/ParcelableTaskContainerData.java @@ -0,0 +1,127 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package androidx.window.extensions.embedding; + +import static android.app.ActivityTaskManager.INVALID_TASK_ID; + +import android.os.Parcel; +import android.os.Parcelable; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import java.util.ArrayList; +import java.util.List; + +/** + * This class holds the Parcelable data of a {@link TaskContainer}. + */ +class ParcelableTaskContainerData implements Parcelable { + + /** + * A reference to the target {@link TaskContainer} that owns the data. This will not be + * parcelled and will be {@code null} when the data is created from a parcel. + */ + @Nullable + final TaskContainer mTaskContainer; + + /** + * The unique task id. + */ + final int mTaskId; + + /** + * The parcelable data of the active TaskFragmentContainers in this Task. + * Note that this will only be populated before parcelling, and will not be copied when + * making a new instance copy. + */ + @NonNull + private final List<ParcelableTaskFragmentContainerData> + mParcelableTaskFragmentContainerDataList = new ArrayList<>(); + + /** + * The parcelable data of the SplitContainers in this Task. + * Note that this will only be populated before parcelling, and will not be copied when + * making a new instance copy. + */ + @NonNull + private final List<ParcelableSplitContainerData> mParcelableSplitContainerDataList = + new ArrayList<>(); + + ParcelableTaskContainerData(int taskId, @NonNull TaskContainer taskContainer) { + if (taskId == INVALID_TASK_ID) { + throw new IllegalArgumentException("Invalid Task id"); + } + + mTaskId = taskId; + mTaskContainer = taskContainer; + } + + ParcelableTaskContainerData(@NonNull ParcelableTaskContainerData data, + @NonNull TaskContainer taskContainer) { + mTaskId = data.mTaskId; + mTaskContainer = taskContainer; + } + + private ParcelableTaskContainerData(Parcel in) { + mTaskId = in.readInt(); + mTaskContainer = null; + in.readParcelableList(mParcelableTaskFragmentContainerDataList, + ParcelableTaskFragmentContainerData.class.getClassLoader(), + ParcelableTaskFragmentContainerData.class); + in.readParcelableList(mParcelableSplitContainerDataList, + ParcelableSplitContainerData.class.getClassLoader(), + ParcelableSplitContainerData.class); + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(mTaskId); + dest.writeParcelableList(getParcelableTaskFragmentContainerDataList(), flags); + dest.writeParcelableList(getParcelableSplitContainerDataList(), flags); + } + + @NonNull + List<? extends ParcelableTaskFragmentContainerData> + getParcelableTaskFragmentContainerDataList() { + return mTaskContainer != null ? mTaskContainer.getParcelableTaskFragmentContainerDataList() + : mParcelableTaskFragmentContainerDataList; + } + + @NonNull + List<? extends ParcelableSplitContainerData> getParcelableSplitContainerDataList() { + return mTaskContainer != null ? mTaskContainer.getParcelableSplitContainerDataList() + : mParcelableSplitContainerDataList; + } + + @Override + public int describeContents() { + return 0; + } + + public static final Creator<ParcelableTaskContainerData> CREATOR = new Creator<>() { + @Override + public ParcelableTaskContainerData createFromParcel(Parcel in) { + return new ParcelableTaskContainerData(in); + } + + @Override + public ParcelableTaskContainerData[] newArray(int size) { + return new ParcelableTaskContainerData[size]; + } + }; +} diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/ParcelableTaskFragmentContainerData.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/ParcelableTaskFragmentContainerData.java new file mode 100644 index 000000000000..a79a89a210ac --- /dev/null +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/ParcelableTaskFragmentContainerData.java @@ -0,0 +1,105 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package androidx.window.extensions.embedding; + +import android.app.Activity; +import android.graphics.Rect; +import android.os.IBinder; +import android.os.Parcel; +import android.os.Parcelable; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +/** + * This class holds the Parcelable data of a {@link TaskFragmentContainer}. + */ +class ParcelableTaskFragmentContainerData implements Parcelable { + + /** + * Client-created token that uniquely identifies the task fragment container instance. + */ + @NonNull + final IBinder mToken; + + /** + * The tag specified in launch options. {@code null} if this taskFragment container is not an + * overlay container. + */ + @Nullable + final String mOverlayTag; + + /** + * The associated {@link Activity#getActivityToken()} of the overlay container. + * Must be {@code null} for non-overlay container. + * <p> + * If an overlay container is associated with an activity, this overlay container will be + * dismissed when the associated activity is destroyed. If the overlay container is visible, + * activity will be launched on top of the overlay container and expanded to fill the parent + * container. + */ + @Nullable + final IBinder mAssociatedActivityToken; + + /** + * Bounds that were requested last via {@link android.window.WindowContainerTransaction}. + */ + @NonNull + final Rect mLastRequestedBounds; + + ParcelableTaskFragmentContainerData(@NonNull IBinder token, @Nullable String overlayTag, + @Nullable IBinder associatedActivityToken) { + mToken = token; + mOverlayTag = overlayTag; + mAssociatedActivityToken = associatedActivityToken; + mLastRequestedBounds = new Rect(); + } + + private ParcelableTaskFragmentContainerData(Parcel in) { + mToken = in.readStrongBinder(); + mOverlayTag = in.readString(); + mAssociatedActivityToken = in.readStrongBinder(); + mLastRequestedBounds = in.readTypedObject(Rect.CREATOR); + } + + public static final Creator<ParcelableTaskFragmentContainerData> CREATOR = new Creator<>() { + @Override + public ParcelableTaskFragmentContainerData createFromParcel(Parcel in) { + return new ParcelableTaskFragmentContainerData(in); + } + + @Override + public ParcelableTaskFragmentContainerData[] newArray(int size) { + return new ParcelableTaskFragmentContainerData[size]; + } + }; + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeStrongBinder(mToken); + dest.writeString(mOverlayTag); + dest.writeStrongBinder(mAssociatedActivityToken); + dest.writeTypedObject(mLastRequestedBounds, flags); + } + +} + diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitContainer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitContainer.java index 39cfacec8447..6d436ec01d98 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitContainer.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitContainer.java @@ -33,6 +33,8 @@ import androidx.window.extensions.core.util.function.Function; */ class SplitContainer { @NonNull + private final ParcelableSplitContainerData mParcelableData; + @NonNull private TaskFragmentContainer mPrimaryContainer; @NonNull private final TaskFragmentContainer mSecondaryContainer; @@ -44,16 +46,6 @@ class SplitContainer { /** @see SplitContainer#getDefaultSplitAttributes() */ @NonNull private SplitAttributes mDefaultSplitAttributes; - @NonNull - private final IBinder mToken; - - /** - * Whether the selection of which container is primary can be changed at runtime. Runtime - * updates is currently possible only for {@link SplitPinContainer} - * - * @see SplitPinContainer - */ - private final boolean mIsPrimaryContainerMutable; SplitContainer(@NonNull TaskFragmentContainer primaryContainer, @NonNull Activity primaryActivity, @@ -69,13 +61,14 @@ class SplitContainer { @NonNull TaskFragmentContainer secondaryContainer, @NonNull SplitRule splitRule, @NonNull SplitAttributes splitAttributes, boolean isPrimaryContainerMutable) { + mParcelableData = new ParcelableSplitContainerData(this, new Binder("SplitContainer"), + primaryContainer.getToken(), secondaryContainer.getToken(), splitRule.getTag(), + isPrimaryContainerMutable); mPrimaryContainer = primaryContainer; mSecondaryContainer = secondaryContainer; mSplitRule = splitRule; mDefaultSplitAttributes = splitRule.getDefaultSplitAttributes(); mCurrentSplitAttributes = splitAttributes; - mToken = new Binder("SplitContainer"); - mIsPrimaryContainerMutable = isPrimaryContainerMutable; if (shouldFinishPrimaryWithSecondary(splitRule)) { if (mPrimaryContainer.getRunningActivityCount() == 1 @@ -94,7 +87,7 @@ class SplitContainer { } void setPrimaryContainer(@NonNull TaskFragmentContainer primaryContainer) { - if (!mIsPrimaryContainerMutable) { + if (!mParcelableData.mIsPrimaryContainerMutable) { throw new IllegalStateException("Cannot update primary TaskFragmentContainer"); } mPrimaryContainer = primaryContainer; @@ -150,7 +143,12 @@ class SplitContainer { @NonNull IBinder getToken() { - return mToken; + return mParcelableData.mToken; + } + + @NonNull + ParcelableSplitContainerData getParcelableData() { + return mParcelableData; } /** @@ -201,7 +199,7 @@ class SplitContainer { return null; } return new SplitInfo(primaryActivityStack, secondaryActivityStack, - mCurrentSplitAttributes, SplitInfo.Token.createFromBinder(mToken)); + mCurrentSplitAttributes, SplitInfo.Token.createFromBinder(mParcelableData.mToken)); } static boolean shouldFinishPrimaryWithSecondary(@NonNull SplitRule splitRule) { 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 fb8efc4ad490..563732027a6c 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java @@ -169,7 +169,7 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer { mController = controller; final Bundle outSavedState = new Bundle(); if (Flags.aeBackStackRestore()) { - outSavedState.setClassLoader(TaskContainer.class.getClassLoader()); + outSavedState.setClassLoader(ParcelableTaskContainerData.class.getClassLoader()); registerOrganizer(false /* isSystemOrganizer */, outSavedState); } else { registerOrganizer(); diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java index 5795e8da18c2..82dfda58fc75 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java @@ -16,7 +16,6 @@ package androidx.window.extensions.embedding; -import static android.app.ActivityTaskManager.INVALID_TASK_ID; import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW; import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; @@ -32,8 +31,6 @@ import android.app.WindowConfiguration.WindowingMode; import android.content.res.Configuration; import android.graphics.Rect; import android.os.IBinder; -import android.os.Parcel; -import android.os.Parcelable; import android.util.ArraySet; import android.util.DisplayMetrics; import android.util.Log; @@ -57,11 +54,12 @@ import java.util.List; import java.util.Set; /** Represents TaskFragments and split pairs below a Task. */ -class TaskContainer implements Parcelable { +class TaskContainer { private static final String TAG = TaskContainer.class.getSimpleName(); - /** The unique task id. */ - private final int mTaskId; + /** Parcelable data of this TaskContainer. */ + @NonNull + private final ParcelableTaskContainerData mParcelableTaskContainerData; /** Active TaskFragments in this Task. */ @NonNull @@ -130,11 +128,9 @@ class TaskContainer implements Parcelable { * @param splitController The {@link SplitController}. */ TaskContainer(int taskId, @NonNull Activity activityInTask, - @Nullable SplitController splitController) { - if (taskId == INVALID_TASK_ID) { - throw new IllegalArgumentException("Invalid Task id"); - } - mTaskId = taskId; + @NonNull SplitController splitController) { + mParcelableTaskContainerData = new ParcelableTaskContainerData(taskId, this); + final TaskProperties taskProperties = TaskProperties .getTaskPropertiesFromActivity(activityInTask); mInfo = new TaskFragmentParentInfo( @@ -148,8 +144,44 @@ class TaskContainer implements Parcelable { mSplitController = splitController; } + /** This is only used when restoring it from a {@link ParcelableTaskContainerData}. */ + TaskContainer(@NonNull ParcelableTaskContainerData data, + @NonNull SplitController splitController) { + mParcelableTaskContainerData = new ParcelableTaskContainerData(data, this); + mSplitController = splitController; + for (ParcelableTaskFragmentContainerData tfData : + data.getParcelableTaskFragmentContainerDataList()) { + final TaskFragmentContainer container = + new TaskFragmentContainer(tfData, splitController, this); + mContainers.add(container); + } + } + + @NonNull + ParcelableTaskContainerData getParcelableData() { + return mParcelableTaskContainerData; + } + + @NonNull + List<ParcelableTaskFragmentContainerData> getParcelableTaskFragmentContainerDataList() { + final List<ParcelableTaskFragmentContainerData> data = new ArrayList<>(mContainers.size()); + for (TaskFragmentContainer container : mContainers) { + data.add(container.getParcelableData()); + } + return data; + } + + @NonNull + List<ParcelableSplitContainerData> getParcelableSplitContainerDataList() { + final List<ParcelableSplitContainerData> data = new ArrayList<>(mSplitContainers.size()); + for (SplitContainer splitContainer : mSplitContainers) { + data.add(splitContainer.getParcelableData()); + } + return data; + } + int getTaskId() { - return mTaskId; + return mParcelableTaskContainerData.mTaskId; } int getDisplayId() { @@ -680,34 +712,6 @@ class TaskContainer implements Parcelable { return activityStacks; } - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(Parcel dest, int flags) { - dest.writeInt(mTaskId); - // TODO(b/289875940) - } - - protected TaskContainer(Parcel in) { - mTaskId = in.readInt(); - // TODO(b/289875940) - } - - public static final Creator<TaskContainer> CREATOR = new Creator<>() { - @Override - public TaskContainer createFromParcel(Parcel in) { - return new TaskContainer(in); - } - - @Override - public TaskContainer[] newArray(int size) { - return new TaskContainer[size]; - } - }; - /** A wrapper class which contains the information of {@link TaskContainer} */ static final class TaskProperties { private final int mDisplayId; diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java index dc6506b070af..dc1d983997c6 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java @@ -53,14 +53,12 @@ import java.util.Objects; class TaskFragmentContainer { private static final int APPEAR_EMPTY_TIMEOUT_MS = 3000; + /** Parcelable data of this TaskFragmentContainer. */ @NonNull - private final SplitController mController; + private final ParcelableTaskFragmentContainerData mParcelableData; - /** - * Client-created token that uniquely identifies the task fragment container instance. - */ @NonNull - private final IBinder mToken; + private final SplitController mController; /** Parent leaf Task. */ @NonNull @@ -103,9 +101,6 @@ class TaskFragmentContainer { */ private final List<IBinder> mActivitiesToFinishOnExit = new ArrayList<>(); - @Nullable - private final String mOverlayTag; - /** * The launch options that was used to create this container. Must not {@link Bundle#isEmpty()} * for {@link #isOverlay()} container. @@ -113,29 +108,13 @@ class TaskFragmentContainer { @NonNull private final Bundle mLaunchOptions = new Bundle(); - /** - * The associated {@link Activity#getActivityToken()} of the overlay container. - * Must be {@code null} for non-overlay container. - * <p> - * If an overlay container is associated with an activity, this overlay container will be - * dismissed when the associated activity is destroyed. If the overlay container is visible, - * activity will be launched on top of the overlay container and expanded to fill the parent - * container. - */ - @Nullable - private final IBinder mAssociatedActivityToken; - /** Indicates whether the container was cleaned up after the last activity was removed. */ private boolean mIsFinished; /** - * Bounds that were requested last via {@link android.window.WindowContainerTransaction}. - */ - private final Rect mLastRequestedBounds = new Rect(); - - /** * Windowing mode that was requested last via {@link android.window.WindowContainerTransaction}. */ + // TODO(b/289875940): review this and other field that might need to be moved in the base class. @WindowingMode private int mLastRequestedWindowingMode = WINDOWING_MODE_UNDEFINED; @@ -208,17 +187,17 @@ class TaskFragmentContainer { @NonNull SplitController controller, @Nullable TaskFragmentContainer pairedPrimaryContainer, @Nullable String overlayTag, @Nullable Bundle launchOptions, @Nullable Activity associatedActivity) { + mParcelableData = new ParcelableTaskFragmentContainerData( + new Binder("TaskFragmentContainer"), overlayTag, + associatedActivity != null ? associatedActivity.getActivityToken() : null); + if ((pendingAppearedActivity == null && pendingAppearedIntent == null) || (pendingAppearedActivity != null && pendingAppearedIntent != null)) { throw new IllegalArgumentException( "One and only one of pending activity and intent must be non-null"); } mController = controller; - mToken = new Binder("TaskFragmentContainer"); mTaskContainer = taskContainer; - mOverlayTag = overlayTag; - mAssociatedActivityToken = associatedActivity != null - ? associatedActivity.getActivityToken() : null; if (launchOptions != null) { mLaunchOptions.putAll(launchOptions); @@ -259,18 +238,26 @@ class TaskFragmentContainer { if (overlayTag != null && pendingAppearedIntent != null && associatedActivity != null && !associatedActivity.isFinishing()) { final IBinder associatedActivityToken = associatedActivity.getActivityToken(); - final OverlayContainerRestoreParams params = new OverlayContainerRestoreParams(mToken, - launchOptions, pendingAppearedIntent); + final OverlayContainerRestoreParams params = new OverlayContainerRestoreParams( + mParcelableData.mToken, launchOptions, pendingAppearedIntent); mController.mOverlayRestoreParams.put(associatedActivityToken, params); } } + /** This is only used when restoring it from a {@link ParcelableTaskFragmentContainerData}. */ + TaskFragmentContainer(@NonNull ParcelableTaskFragmentContainerData data, + @NonNull SplitController splitController, @NonNull TaskContainer taskContainer) { + mParcelableData = data; + mController = splitController; + mTaskContainer = taskContainer; + } + /** * Returns the client-created token that uniquely identifies this container. */ @NonNull IBinder getTaskFragmentToken() { - return mToken; + return mParcelableData.mToken; } /** List of non-finishing activities that belong to this container and live in this process. */ @@ -389,7 +376,8 @@ class TaskFragmentContainer { return null; } return new ActivityStack(activities, isEmpty(), - ActivityStack.Token.createFromBinder(mToken), mOverlayTag); + ActivityStack.Token.createFromBinder(mParcelableData.mToken), + mParcelableData.mOverlayTag); } /** Adds the activity that will be reparented to this container. */ @@ -413,7 +401,7 @@ class TaskFragmentContainer { final ActivityThread.ActivityClientRecord record = ActivityThread .currentActivityThread().getActivityClient(activityToken); if (record != null) { - record.mTaskFragmentToken = mToken; + record.mTaskFragmentToken = mParcelableData.mToken; } } @@ -469,7 +457,7 @@ class TaskFragmentContainer { if (!isOverlayWithActivityAssociation()) { return; } - if (mAssociatedActivityToken == activityToken) { + if (mParcelableData.mAssociatedActivityToken == activityToken) { // If the associated activity is destroyed, also finish this overlay container. mController.mPresenter.cleanupContainer(wct, this, false /* shouldFinishDependent */); } @@ -776,8 +764,8 @@ class TaskFragmentContainer { * @see WindowContainerTransaction#setRelativeBounds */ boolean areLastRequestedBoundsEqual(@Nullable Rect relBounds) { - return (relBounds == null && mLastRequestedBounds.isEmpty()) - || mLastRequestedBounds.equals(relBounds); + return (relBounds == null && mParcelableData.mLastRequestedBounds.isEmpty()) + || mParcelableData.mLastRequestedBounds.equals(relBounds); } /** @@ -787,14 +775,14 @@ class TaskFragmentContainer { */ void setLastRequestedBounds(@Nullable Rect relBounds) { if (relBounds == null) { - mLastRequestedBounds.setEmpty(); + mParcelableData.mLastRequestedBounds.setEmpty(); } else { - mLastRequestedBounds.set(relBounds); + mParcelableData.mLastRequestedBounds.set(relBounds); } } @NonNull Rect getLastRequestedBounds() { - return mLastRequestedBounds; + return mParcelableData.mLastRequestedBounds; } /** @@ -965,6 +953,16 @@ class TaskFragmentContainer { return mTaskContainer.getTaskId(); } + @NonNull + IBinder getToken() { + return mParcelableData.mToken; + } + + @NonNull + ParcelableTaskFragmentContainerData getParcelableData() { + return mParcelableData; + } + /** Gets the parent Task. */ @NonNull TaskContainer getTaskContainer() { @@ -1011,7 +1009,7 @@ class TaskFragmentContainer { /** Returns whether this taskFragment container is an overlay container. */ boolean isOverlay() { - return mOverlayTag != null; + return mParcelableData.mOverlayTag != null; } /** @@ -1020,7 +1018,7 @@ class TaskFragmentContainer { */ @Nullable String getOverlayTag() { - return mOverlayTag; + return mParcelableData.mOverlayTag; } /** @@ -1045,7 +1043,7 @@ class TaskFragmentContainer { */ @Nullable IBinder getAssociatedActivityToken() { - return mAssociatedActivityToken; + return mParcelableData.mAssociatedActivityToken; } /** @@ -1053,11 +1051,11 @@ class TaskFragmentContainer { * a non-fill-parent overlay without activity association. */ boolean isAlwaysOnTopOverlay() { - return isOverlay() && mAssociatedActivityToken == null; + return isOverlay() && mParcelableData.mAssociatedActivityToken == null; } boolean isOverlayWithActivityAssociation() { - return isOverlay() && mAssociatedActivityToken != null; + return isOverlay() && mParcelableData.mAssociatedActivityToken != null; } @Override @@ -1074,13 +1072,13 @@ class TaskFragmentContainer { private String toString(boolean includeContainersToFinishOnExit) { return "TaskFragmentContainer{" + " parentTaskId=" + getTaskId() - + " token=" + mToken + + " token=" + mParcelableData.mToken + " topNonFinishingActivity=" + getTopNonFinishingActivity() + " runningActivityCount=" + getRunningActivityCount() + " isFinished=" + mIsFinished - + " overlayTag=" + mOverlayTag - + " associatedActivityToken=" + mAssociatedActivityToken - + " lastRequestedBounds=" + mLastRequestedBounds + + " overlayTag=" + mParcelableData.mOverlayTag + + " associatedActivityToken=" + mParcelableData.mAssociatedActivityToken + + " lastRequestedBounds=" + mParcelableData.mLastRequestedBounds + " pendingAppearedActivities=" + mPendingAppearedActivities + (includeContainersToFinishOnExit ? " containersToFinishOnExit=" + containersToFinishOnExitToString() : "") diff --git a/libs/WindowManager/Shell/res/values-af/strings.xml b/libs/WindowManager/Shell/res/values-af/strings.xml index 08d0dd3d9789..d1b98a693c47 100644 --- a/libs/WindowManager/Shell/res/values-af/strings.xml +++ b/libs/WindowManager/Shell/res/values-af/strings.xml @@ -127,6 +127,5 @@ <string name="expand_menu_text" msgid="3847736164494181168">"Maak kieslys oop"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maksimeer skerm"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Gryp skerm vas"</string> - <!-- no translation found for desktop_mode_non_resizable_snap_text (1049800446363800707) --> - <skip /> + <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Hierdie app se grootte kan nie verander word nie"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-am/strings.xml b/libs/WindowManager/Shell/res/values-am/strings.xml index 9efbcb5729ef..80447192aeae 100644 --- a/libs/WindowManager/Shell/res/values-am/strings.xml +++ b/libs/WindowManager/Shell/res/values-am/strings.xml @@ -127,6 +127,5 @@ <string name="expand_menu_text" msgid="3847736164494181168">"ምናሌን ክፈት"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"የማያ ገጹ መጠን አሳድግ"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"ማያ ገጹን አሳድግ"</string> - <!-- no translation found for desktop_mode_non_resizable_snap_text (1049800446363800707) --> - <skip /> + <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"ይህ መተግበሪያ መጠኑ ሊቀየር አይችልም"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-ar/strings.xml b/libs/WindowManager/Shell/res/values-ar/strings.xml index 433d99ade09e..21aa34e6f526 100644 --- a/libs/WindowManager/Shell/res/values-ar/strings.xml +++ b/libs/WindowManager/Shell/res/values-ar/strings.xml @@ -127,6 +127,5 @@ <string name="expand_menu_text" msgid="3847736164494181168">"فتح القائمة"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"تكبير الشاشة إلى أقصى حدّ"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"التقاط صورة للشاشة"</string> - <!-- no translation found for desktop_mode_non_resizable_snap_text (1049800446363800707) --> - <skip /> + <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"لا يمكن تغيير حجم نافذة هذا التطبيق"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-as/strings.xml b/libs/WindowManager/Shell/res/values-as/strings.xml index 47e78f5991f5..c59f4705f58a 100644 --- a/libs/WindowManager/Shell/res/values-as/strings.xml +++ b/libs/WindowManager/Shell/res/values-as/strings.xml @@ -127,6 +127,5 @@ <string name="expand_menu_text" msgid="3847736164494181168">"মেনু খোলক"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"স্ক্ৰীন মেক্সিমাইজ কৰক"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"স্ক্ৰীন স্নেপ কৰক"</string> - <!-- no translation found for desktop_mode_non_resizable_snap_text (1049800446363800707) --> - <skip /> + <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"এই এপ্টোৰ আকাৰ সলনি কৰিব নোৱাৰি"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-az/strings.xml b/libs/WindowManager/Shell/res/values-az/strings.xml index 380a34adba08..841323ebe777 100644 --- a/libs/WindowManager/Shell/res/values-az/strings.xml +++ b/libs/WindowManager/Shell/res/values-az/strings.xml @@ -127,6 +127,5 @@ <string name="expand_menu_text" msgid="3847736164494181168">"Menyunu açın"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Ekranı maksimum böyüdün"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Ekranı çəkin"</string> - <!-- no translation found for desktop_mode_non_resizable_snap_text (1049800446363800707) --> - <skip /> + <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Bu tətbiqin ölçüsünü dəyişmək olmur"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml b/libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml index b09c7b1cd4c0..86ab548d6478 100644 --- a/libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml +++ b/libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml @@ -127,6 +127,5 @@ <string name="expand_menu_text" msgid="3847736164494181168">"Otvorite meni"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Povećaj ekran"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Uklopi ekran"</string> - <!-- no translation found for desktop_mode_non_resizable_snap_text (1049800446363800707) --> - <skip /> + <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Veličina ove aplikacije ne može da se promeni"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-be/strings.xml b/libs/WindowManager/Shell/res/values-be/strings.xml index f5945b123736..bcbc1ae3c26d 100644 --- a/libs/WindowManager/Shell/res/values-be/strings.xml +++ b/libs/WindowManager/Shell/res/values-be/strings.xml @@ -127,6 +127,5 @@ <string name="expand_menu_text" msgid="3847736164494181168">"Адкрыць меню"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Разгарнуць на ўвесь экран"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Размясціць на палавіне экрана"</string> - <!-- no translation found for desktop_mode_non_resizable_snap_text (1049800446363800707) --> - <skip /> + <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Немагчыма змяніць памер праграмы"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-bn/strings.xml b/libs/WindowManager/Shell/res/values-bn/strings.xml index 6b1f3850e00f..bf8bc99df009 100644 --- a/libs/WindowManager/Shell/res/values-bn/strings.xml +++ b/libs/WindowManager/Shell/res/values-bn/strings.xml @@ -127,6 +127,5 @@ <string name="expand_menu_text" msgid="3847736164494181168">"মেনু খুলুন"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"স্ক্রিন বড় করুন"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"স্ক্রিনে অ্যাপ মানানসই হিসেবে ছোট বড় করুন"</string> - <!-- no translation found for desktop_mode_non_resizable_snap_text (1049800446363800707) --> - <skip /> + <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"এই অ্যাপ ছোট বড় করা যাবে না"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-bs/strings.xml b/libs/WindowManager/Shell/res/values-bs/strings.xml index 6bc5274aecea..cf53d258ab12 100644 --- a/libs/WindowManager/Shell/res/values-bs/strings.xml +++ b/libs/WindowManager/Shell/res/values-bs/strings.xml @@ -127,6 +127,5 @@ <string name="expand_menu_text" msgid="3847736164494181168">"Otvaranje menija"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maksimiziraj ekran"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Snimi ekran"</string> - <!-- no translation found for desktop_mode_non_resizable_snap_text (1049800446363800707) --> - <skip /> + <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Nije moguće promijeniti veličinu aplikacije"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-ca/strings.xml b/libs/WindowManager/Shell/res/values-ca/strings.xml index 5dd65ddccbcf..87ea62e172c5 100644 --- a/libs/WindowManager/Shell/res/values-ca/strings.xml +++ b/libs/WindowManager/Shell/res/values-ca/strings.xml @@ -127,6 +127,5 @@ <string name="expand_menu_text" msgid="3847736164494181168">"Obre el menú"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maximitza la pantalla"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Ajusta la pantalla"</string> - <!-- no translation found for desktop_mode_non_resizable_snap_text (1049800446363800707) --> - <skip /> + <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"No es pot canviar la mida d\'aquesta aplicació"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-cs/strings.xml b/libs/WindowManager/Shell/res/values-cs/strings.xml index b75dbfcaad5b..e21213b6e479 100644 --- a/libs/WindowManager/Shell/res/values-cs/strings.xml +++ b/libs/WindowManager/Shell/res/values-cs/strings.xml @@ -127,6 +127,5 @@ <string name="expand_menu_text" msgid="3847736164494181168">"Otevřít nabídku"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maximalizovat obrazovku"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Rozpůlit obrazovku"</string> - <!-- no translation found for desktop_mode_non_resizable_snap_text (1049800446363800707) --> - <skip /> + <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Velikost aplikace nelze změnit"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-da/strings.xml b/libs/WindowManager/Shell/res/values-da/strings.xml index 40ad85920dbd..1c4647fc2521 100644 --- a/libs/WindowManager/Shell/res/values-da/strings.xml +++ b/libs/WindowManager/Shell/res/values-da/strings.xml @@ -127,6 +127,5 @@ <string name="expand_menu_text" msgid="3847736164494181168">"Åbn menu"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maksimér skærm"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Tilpas skærm"</string> - <!-- no translation found for desktop_mode_non_resizable_snap_text (1049800446363800707) --> - <skip /> + <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Størrelsen på denne app kan ikke justeres"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-de/strings.xml b/libs/WindowManager/Shell/res/values-de/strings.xml index 9389f3e76a1d..88a5789e7222 100644 --- a/libs/WindowManager/Shell/res/values-de/strings.xml +++ b/libs/WindowManager/Shell/res/values-de/strings.xml @@ -127,6 +127,5 @@ <string name="expand_menu_text" msgid="3847736164494181168">"Menü öffnen"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Bildschirm maximieren"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Bildschirm teilen"</string> - <!-- no translation found for desktop_mode_non_resizable_snap_text (1049800446363800707) --> - <skip /> + <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Die Größe dieser App kann nicht geändert werden"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-el/strings.xml b/libs/WindowManager/Shell/res/values-el/strings.xml index a9a14bf044fb..beeefee1835f 100644 --- a/libs/WindowManager/Shell/res/values-el/strings.xml +++ b/libs/WindowManager/Shell/res/values-el/strings.xml @@ -127,6 +127,5 @@ <string name="expand_menu_text" msgid="3847736164494181168">"Άνοιγμα μενού"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Μεγιστοποίηση οθόνης"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Προβολή στο μισό της οθόνης"</string> - <!-- no translation found for desktop_mode_non_resizable_snap_text (1049800446363800707) --> - <skip /> + <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Δεν είναι δυνατή η αλλαγή μεγέθους αυτής της εφαρμογής"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-en-rAU/strings.xml b/libs/WindowManager/Shell/res/values-en-rAU/strings.xml index 78a2c9e661c8..72f4070cfd8d 100644 --- a/libs/WindowManager/Shell/res/values-en-rAU/strings.xml +++ b/libs/WindowManager/Shell/res/values-en-rAU/strings.xml @@ -127,6 +127,5 @@ <string name="expand_menu_text" msgid="3847736164494181168">"Open menu"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maximise screen"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Snap screen"</string> - <!-- no translation found for desktop_mode_non_resizable_snap_text (1049800446363800707) --> - <skip /> + <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"This app can\'t be resized"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-en-rGB/strings.xml b/libs/WindowManager/Shell/res/values-en-rGB/strings.xml index 78a2c9e661c8..72f4070cfd8d 100644 --- a/libs/WindowManager/Shell/res/values-en-rGB/strings.xml +++ b/libs/WindowManager/Shell/res/values-en-rGB/strings.xml @@ -127,6 +127,5 @@ <string name="expand_menu_text" msgid="3847736164494181168">"Open menu"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maximise screen"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Snap screen"</string> - <!-- no translation found for desktop_mode_non_resizable_snap_text (1049800446363800707) --> - <skip /> + <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"This app can\'t be resized"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-en-rIN/strings.xml b/libs/WindowManager/Shell/res/values-en-rIN/strings.xml index 78a2c9e661c8..72f4070cfd8d 100644 --- a/libs/WindowManager/Shell/res/values-en-rIN/strings.xml +++ b/libs/WindowManager/Shell/res/values-en-rIN/strings.xml @@ -127,6 +127,5 @@ <string name="expand_menu_text" msgid="3847736164494181168">"Open menu"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maximise screen"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Snap screen"</string> - <!-- no translation found for desktop_mode_non_resizable_snap_text (1049800446363800707) --> - <skip /> + <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"This app can\'t be resized"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-es-rUS/strings.xml b/libs/WindowManager/Shell/res/values-es-rUS/strings.xml index 8dadc70b8c9d..5756aae321b1 100644 --- a/libs/WindowManager/Shell/res/values-es-rUS/strings.xml +++ b/libs/WindowManager/Shell/res/values-es-rUS/strings.xml @@ -127,6 +127,5 @@ <string name="expand_menu_text" msgid="3847736164494181168">"Abrir el menú"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maximizar pantalla"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Ajustar pantalla"</string> - <!-- no translation found for desktop_mode_non_resizable_snap_text (1049800446363800707) --> - <skip /> + <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"No se puede cambiar el tamaño de esta app"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-es/strings.xml b/libs/WindowManager/Shell/res/values-es/strings.xml index 33eba9300542..3c55bf62fa95 100644 --- a/libs/WindowManager/Shell/res/values-es/strings.xml +++ b/libs/WindowManager/Shell/res/values-es/strings.xml @@ -127,6 +127,5 @@ <string name="expand_menu_text" msgid="3847736164494181168">"Abrir menú"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maximizar pantalla"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Ajustar pantalla"</string> - <!-- no translation found for desktop_mode_non_resizable_snap_text (1049800446363800707) --> - <skip /> + <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"No se puede cambiar el tamaño de esta aplicación"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-et/strings.xml b/libs/WindowManager/Shell/res/values-et/strings.xml index e76f6e882afc..d92196729a00 100644 --- a/libs/WindowManager/Shell/res/values-et/strings.xml +++ b/libs/WindowManager/Shell/res/values-et/strings.xml @@ -127,6 +127,5 @@ <string name="expand_menu_text" msgid="3847736164494181168">"Ava menüü"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Kuva täisekraanil"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Kuva poolel ekraanil"</string> - <!-- no translation found for desktop_mode_non_resizable_snap_text (1049800446363800707) --> - <skip /> + <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Selle rakenduse aknasuurust ei saa muuta"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-eu/strings.xml b/libs/WindowManager/Shell/res/values-eu/strings.xml index 961c784f58a8..f319af1c4e81 100644 --- a/libs/WindowManager/Shell/res/values-eu/strings.xml +++ b/libs/WindowManager/Shell/res/values-eu/strings.xml @@ -127,6 +127,5 @@ <string name="expand_menu_text" msgid="3847736164494181168">"Ireki menua"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Handitu pantaila"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Zatitu pantaila"</string> - <!-- no translation found for desktop_mode_non_resizable_snap_text (1049800446363800707) --> - <skip /> + <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Ezin zaio aldatu tamaina aplikazio honi"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-fa/strings.xml b/libs/WindowManager/Shell/res/values-fa/strings.xml index c004ea8f2197..44a0929ab59b 100644 --- a/libs/WindowManager/Shell/res/values-fa/strings.xml +++ b/libs/WindowManager/Shell/res/values-fa/strings.xml @@ -127,6 +127,5 @@ <string name="expand_menu_text" msgid="3847736164494181168">"باز کردن منو"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"بزرگ کردن صفحه"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"بزرگ کردن صفحه"</string> - <!-- no translation found for desktop_mode_non_resizable_snap_text (1049800446363800707) --> - <skip /> + <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"اندازه این برنامه را نمیتوان تغییر داد"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml b/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml index 288dbb287220..02f832bdc255 100644 --- a/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml +++ b/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml @@ -127,6 +127,5 @@ <string name="expand_menu_text" msgid="3847736164494181168">"Ouvrir le menu"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Agrandir l\'écran"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Aligner l\'écran"</string> - <!-- no translation found for desktop_mode_non_resizable_snap_text (1049800446363800707) --> - <skip /> + <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Impossible de redimensionner cette appli"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-fr/strings.xml b/libs/WindowManager/Shell/res/values-fr/strings.xml index 7a7e59262670..5d916f4b74b9 100644 --- a/libs/WindowManager/Shell/res/values-fr/strings.xml +++ b/libs/WindowManager/Shell/res/values-fr/strings.xml @@ -127,6 +127,5 @@ <string name="expand_menu_text" msgid="3847736164494181168">"Ouvrir le menu"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Mettre en plein écran"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Fractionner l\'écran"</string> - <!-- no translation found for desktop_mode_non_resizable_snap_text (1049800446363800707) --> - <skip /> + <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Impossible de redimensionner cette appli"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-gl/strings.xml b/libs/WindowManager/Shell/res/values-gl/strings.xml index 8f3b132defb7..e1b2a7e1eeb5 100644 --- a/libs/WindowManager/Shell/res/values-gl/strings.xml +++ b/libs/WindowManager/Shell/res/values-gl/strings.xml @@ -127,6 +127,5 @@ <string name="expand_menu_text" msgid="3847736164494181168">"Abrir menú"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maximizar pantalla"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Encaixar pantalla"</string> - <!-- no translation found for desktop_mode_non_resizable_snap_text (1049800446363800707) --> - <skip /> + <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Non se pode cambiar o tamaño desta aplicación"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-gu/strings.xml b/libs/WindowManager/Shell/res/values-gu/strings.xml index 78eebd6ed58d..fecce73ef2fd 100644 --- a/libs/WindowManager/Shell/res/values-gu/strings.xml +++ b/libs/WindowManager/Shell/res/values-gu/strings.xml @@ -127,6 +127,5 @@ <string name="expand_menu_text" msgid="3847736164494181168">"મેનૂ ખોલો"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"સ્ક્રીન કરો મોટી કરો"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"સ્ક્રીન સ્નૅપ કરો"</string> - <!-- no translation found for desktop_mode_non_resizable_snap_text (1049800446363800707) --> - <skip /> + <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"આ ઍપના કદમાં વધઘટ કરી શકાતો નથી"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-hr/strings.xml b/libs/WindowManager/Shell/res/values-hr/strings.xml index dca2431c7f74..04053c862e37 100644 --- a/libs/WindowManager/Shell/res/values-hr/strings.xml +++ b/libs/WindowManager/Shell/res/values-hr/strings.xml @@ -127,6 +127,5 @@ <string name="expand_menu_text" msgid="3847736164494181168">"Otvaranje izbornika"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maksimalno povećaj zaslon"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Izradi snimku zaslona"</string> - <!-- no translation found for desktop_mode_non_resizable_snap_text (1049800446363800707) --> - <skip /> + <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Nije moguće promijeniti veličinu aplikacije"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-hu/strings.xml b/libs/WindowManager/Shell/res/values-hu/strings.xml index a0e9ec5ffc59..bb5264955f97 100644 --- a/libs/WindowManager/Shell/res/values-hu/strings.xml +++ b/libs/WindowManager/Shell/res/values-hu/strings.xml @@ -127,6 +127,5 @@ <string name="expand_menu_text" msgid="3847736164494181168">"Menü megnyitása"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Képernyő méretének maximalizálása"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Igazodás a képernyő adott részéhez"</string> - <!-- no translation found for desktop_mode_non_resizable_snap_text (1049800446363800707) --> - <skip /> + <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Ezt az alkalmazást nem lehet átméretezni"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-hy/strings.xml b/libs/WindowManager/Shell/res/values-hy/strings.xml index 2208a5ce610a..fff5a10a9b1b 100644 --- a/libs/WindowManager/Shell/res/values-hy/strings.xml +++ b/libs/WindowManager/Shell/res/values-hy/strings.xml @@ -127,6 +127,5 @@ <string name="expand_menu_text" msgid="3847736164494181168">"Բացել ընտրացանկը"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Ծավալել էկրանը"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Ծալել էկրանը"</string> - <!-- no translation found for desktop_mode_non_resizable_snap_text (1049800446363800707) --> - <skip /> + <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Այս հավելվածի չափը հնարավոր չէ փոխել"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-in/strings.xml b/libs/WindowManager/Shell/res/values-in/strings.xml index 11de8176a585..a957754a7924 100644 --- a/libs/WindowManager/Shell/res/values-in/strings.xml +++ b/libs/WindowManager/Shell/res/values-in/strings.xml @@ -127,6 +127,5 @@ <string name="expand_menu_text" msgid="3847736164494181168">"Buka Menu"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Perbesar Layar"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Gabungkan Layar"</string> - <!-- no translation found for desktop_mode_non_resizable_snap_text (1049800446363800707) --> - <skip /> + <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Ukuran aplikasi ini tidak dapat diubah"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-is/strings.xml b/libs/WindowManager/Shell/res/values-is/strings.xml index ad679a8dd48b..7b91768d5688 100644 --- a/libs/WindowManager/Shell/res/values-is/strings.xml +++ b/libs/WindowManager/Shell/res/values-is/strings.xml @@ -127,6 +127,5 @@ <string name="expand_menu_text" msgid="3847736164494181168">"Opna valmynd"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Stækka skjá"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Smelluskjár"</string> - <!-- no translation found for desktop_mode_non_resizable_snap_text (1049800446363800707) --> - <skip /> + <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Ekki er hægt að breyta stærð þessa forrits"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-iw/strings.xml b/libs/WindowManager/Shell/res/values-iw/strings.xml index cccb71fd3f49..ea73653b39c3 100644 --- a/libs/WindowManager/Shell/res/values-iw/strings.xml +++ b/libs/WindowManager/Shell/res/values-iw/strings.xml @@ -127,6 +127,5 @@ <string name="expand_menu_text" msgid="3847736164494181168">"פתיחת התפריט"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"הגדלת המסך"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"כיווץ המסך"</string> - <!-- no translation found for desktop_mode_non_resizable_snap_text (1049800446363800707) --> - <skip /> + <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"לא ניתן לשנות את גודל החלון של האפליקציה הזו"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-kk/strings.xml b/libs/WindowManager/Shell/res/values-kk/strings.xml index a46cc810c3e7..c6f558ff83fd 100644 --- a/libs/WindowManager/Shell/res/values-kk/strings.xml +++ b/libs/WindowManager/Shell/res/values-kk/strings.xml @@ -127,6 +127,5 @@ <string name="expand_menu_text" msgid="3847736164494181168">"Мәзірді ашу"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Экранды ұлғайту"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Экранды бөлу"</string> - <!-- no translation found for desktop_mode_non_resizable_snap_text (1049800446363800707) --> - <skip /> + <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Бұл қолданбаның өлшемі өзгертілмейді."</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-km/strings.xml b/libs/WindowManager/Shell/res/values-km/strings.xml index 1187567d6a26..508ea489cab4 100644 --- a/libs/WindowManager/Shell/res/values-km/strings.xml +++ b/libs/WindowManager/Shell/res/values-km/strings.xml @@ -127,6 +127,5 @@ <string name="expand_menu_text" msgid="3847736164494181168">"បើកម៉ឺនុយ"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"ពង្រីកអេក្រង់"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"ថតអេក្រង់"</string> - <!-- no translation found for desktop_mode_non_resizable_snap_text (1049800446363800707) --> - <skip /> + <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"មិនអាចប្ដូរទំហំកម្មវិធីនេះបានទេ"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-kn/strings.xml b/libs/WindowManager/Shell/res/values-kn/strings.xml index c1ae69acf944..1fc627b0eecf 100644 --- a/libs/WindowManager/Shell/res/values-kn/strings.xml +++ b/libs/WindowManager/Shell/res/values-kn/strings.xml @@ -38,16 +38,16 @@ <string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"ಸೆಕೆಂಡರಿ ಡಿಸ್ಪ್ಲೇಗಳಲ್ಲಿ ಪ್ರಾರಂಭಿಸುವಿಕೆಯನ್ನು ಅಪ್ಲಿಕೇಶನ್ ಬೆಂಬಲಿಸುವುದಿಲ್ಲ."</string> <string name="accessibility_divider" msgid="6407584574218956849">"ಸ್ಪ್ಲಿಟ್ ಸ್ಕ್ರೀನ್ ಡಿವೈಡರ್"</string> <string name="divider_title" msgid="1963391955593749442">"ಸ್ಪ್ಲಿಟ್ ಸ್ಕ್ರೀನ್ ಡಿವೈಡರ್"</string> - <string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"ಎಡ ಪೂರ್ಣ ಪರದೆ"</string> + <string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"ಎಡ ಫುಲ್ ಸ್ಕ್ರೀನ್"</string> <string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"70% ಎಡಕ್ಕೆ"</string> <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"50% ಎಡಕ್ಕೆ"</string> <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"30% ಎಡಕ್ಕೆ"</string> - <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"ಬಲ ಪೂರ್ಣ ಪರದೆ"</string> - <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"ಮೇಲಿನ ಪೂರ್ಣ ಪರದೆ"</string> + <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"ಬಲ ಫುಲ್ ಸ್ಕ್ರೀನ್"</string> + <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"ಮೇಲಿನ ಫುಲ್ ಸ್ಕ್ರೀನ್"</string> <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"70% ಮೇಲಕ್ಕೆ"</string> <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"50% ಮೇಲಕ್ಕೆ"</string> <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"30% ಮೇಲಕ್ಕೆ"</string> - <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"ಕೆಳಗಿನ ಪೂರ್ಣ ಪರದೆ"</string> + <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"ಕೆಳಗಿನ ಫುಲ್ ಸ್ಕ್ರೀನ್"</string> <string name="accessibility_split_left" msgid="1713683765575562458">"ಎಡಕ್ಕೆ ವಿಭಜಿಸಿ"</string> <string name="accessibility_split_right" msgid="8441001008181296837">"ಬಲಕ್ಕೆ ವಿಭಜಿಸಿ"</string> <string name="accessibility_split_top" msgid="2789329702027147146">"ಮೇಲಕ್ಕೆ ವಿಭಜಿಸಿ"</string> @@ -127,6 +127,5 @@ <string name="expand_menu_text" msgid="3847736164494181168">"ಮೆನು ತೆರೆಯಿರಿ"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"ಸ್ಕ್ರೀನ್ ಅನ್ನು ಮ್ಯಾಕ್ಸಿಮೈಸ್ ಮಾಡಿ"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"ಸ್ನ್ಯಾಪ್ ಸ್ಕ್ರೀನ್"</string> - <!-- no translation found for desktop_mode_non_resizable_snap_text (1049800446363800707) --> - <skip /> + <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"ಈ ಆ್ಯಪ್ ಅನ್ನು ಮರುಗಾತ್ರಗೊಳಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-kn/strings_tv.xml b/libs/WindowManager/Shell/res/values-kn/strings_tv.xml index 3dfe573a6506..efb79306cb3b 100644 --- a/libs/WindowManager/Shell/res/values-kn/strings_tv.xml +++ b/libs/WindowManager/Shell/res/values-kn/strings_tv.xml @@ -20,7 +20,7 @@ <string name="notification_channel_tv_pip" msgid="2576686079160402435">"ಚಿತ್ರದಲ್ಲಿ ಚಿತ್ರ"</string> <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(ಶೀರ್ಷಿಕೆ ರಹಿತ ಕಾರ್ಯಕ್ರಮ)"</string> <string name="pip_close" msgid="2955969519031223530">"ಮುಚ್ಚಿರಿ"</string> - <string name="pip_fullscreen" msgid="7278047353591302554">"ಪೂರ್ಣ ಪರದೆ"</string> + <string name="pip_fullscreen" msgid="7278047353591302554">"ಫುಲ್ ಸ್ಕ್ರೀನ್"</string> <string name="pip_move" msgid="158770205886688553">"ಸರಿಸಿ"</string> <string name="pip_expand" msgid="1051966011679297308">"ವಿಸ್ತೃತಗೊಳಿಸಿ"</string> <string name="pip_collapse" msgid="3903295106641385962">"ಕುಗ್ಗಿಸಿ"</string> diff --git a/libs/WindowManager/Shell/res/values-ko/strings.xml b/libs/WindowManager/Shell/res/values-ko/strings.xml index 5ff159d5ece6..96d360ecb521 100644 --- a/libs/WindowManager/Shell/res/values-ko/strings.xml +++ b/libs/WindowManager/Shell/res/values-ko/strings.xml @@ -127,6 +127,5 @@ <string name="expand_menu_text" msgid="3847736164494181168">"메뉴 열기"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"화면 최대화"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"화면 분할"</string> - <!-- no translation found for desktop_mode_non_resizable_snap_text (1049800446363800707) --> - <skip /> + <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"이 앱은 크기를 조절할 수 없습니다."</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-ky/strings.xml b/libs/WindowManager/Shell/res/values-ky/strings.xml index 958a2399eba5..662c2eaeed48 100644 --- a/libs/WindowManager/Shell/res/values-ky/strings.xml +++ b/libs/WindowManager/Shell/res/values-ky/strings.xml @@ -127,6 +127,5 @@ <string name="expand_menu_text" msgid="3847736164494181168">"Менюну ачуу"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Экранды чоңойтуу"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Экранды сүрөткө тартып алуу"</string> - <!-- no translation found for desktop_mode_non_resizable_snap_text (1049800446363800707) --> - <skip /> + <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Бул колдонмонун өлчөмүн өзгөртүүгө болбойт"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-lt/strings.xml b/libs/WindowManager/Shell/res/values-lt/strings.xml index 86fc0d83ea28..f71d65047538 100644 --- a/libs/WindowManager/Shell/res/values-lt/strings.xml +++ b/libs/WindowManager/Shell/res/values-lt/strings.xml @@ -127,6 +127,5 @@ <string name="expand_menu_text" msgid="3847736164494181168">"Atidaryti meniu"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Išskleisti ekraną"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Sutraukti ekraną"</string> - <!-- no translation found for desktop_mode_non_resizable_snap_text (1049800446363800707) --> - <skip /> + <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Negalima keisti šios programos dydžio"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-lv/strings.xml b/libs/WindowManager/Shell/res/values-lv/strings.xml index a16cc527ec31..abadef765745 100644 --- a/libs/WindowManager/Shell/res/values-lv/strings.xml +++ b/libs/WindowManager/Shell/res/values-lv/strings.xml @@ -127,6 +127,5 @@ <string name="expand_menu_text" msgid="3847736164494181168">"Atvērt izvēlni"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maksimizēt ekrānu"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Fiksēt ekrānu"</string> - <!-- no translation found for desktop_mode_non_resizable_snap_text (1049800446363800707) --> - <skip /> + <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Šīs lietotnes loga lielumu nevar mainīt."</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-mk/strings.xml b/libs/WindowManager/Shell/res/values-mk/strings.xml index 2878c45237a9..0576fc0df708 100644 --- a/libs/WindowManager/Shell/res/values-mk/strings.xml +++ b/libs/WindowManager/Shell/res/values-mk/strings.xml @@ -127,6 +127,5 @@ <string name="expand_menu_text" msgid="3847736164494181168">"Отвори го менито"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Максимизирај го екранот"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Подели го екранот на половина"</string> - <!-- no translation found for desktop_mode_non_resizable_snap_text (1049800446363800707) --> - <skip /> + <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Не може да се промени големината на апликацијава"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-mn/strings.xml b/libs/WindowManager/Shell/res/values-mn/strings.xml index e64edb1d7ffe..d69ec05ee0b6 100644 --- a/libs/WindowManager/Shell/res/values-mn/strings.xml +++ b/libs/WindowManager/Shell/res/values-mn/strings.xml @@ -127,6 +127,5 @@ <string name="expand_menu_text" msgid="3847736164494181168">"Цэс нээх"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Дэлгэцийг томруулах"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Дэлгэцийг таллах"</string> - <!-- no translation found for desktop_mode_non_resizable_snap_text (1049800446363800707) --> - <skip /> + <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Энэ аппын хэмжээг өөрчлөх боломжгүй"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-mr/strings.xml b/libs/WindowManager/Shell/res/values-mr/strings.xml index 14a6b0e71d37..33ba1c2bf76f 100644 --- a/libs/WindowManager/Shell/res/values-mr/strings.xml +++ b/libs/WindowManager/Shell/res/values-mr/strings.xml @@ -127,6 +127,5 @@ <string name="expand_menu_text" msgid="3847736164494181168">"मेनू उघडा"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"स्क्रीन मोठी करा"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"स्क्रीन स्नॅप करा"</string> - <!-- no translation found for desktop_mode_non_resizable_snap_text (1049800446363800707) --> - <skip /> + <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"या अॅपचा आकार बदलला जाऊ शकत नाही"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-ms/strings.xml b/libs/WindowManager/Shell/res/values-ms/strings.xml index 7e80c846f962..e024e4bfaf51 100644 --- a/libs/WindowManager/Shell/res/values-ms/strings.xml +++ b/libs/WindowManager/Shell/res/values-ms/strings.xml @@ -127,6 +127,5 @@ <string name="expand_menu_text" msgid="3847736164494181168">"Buka Menu"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maksimumkan Skrin"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Tangkap Skrin"</string> - <!-- no translation found for desktop_mode_non_resizable_snap_text (1049800446363800707) --> - <skip /> + <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Apl ini tidak boleh diubah saiz"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-my/strings.xml b/libs/WindowManager/Shell/res/values-my/strings.xml index 32f323497d9e..bd680b4a7a7a 100644 --- a/libs/WindowManager/Shell/res/values-my/strings.xml +++ b/libs/WindowManager/Shell/res/values-my/strings.xml @@ -127,6 +127,5 @@ <string name="expand_menu_text" msgid="3847736164494181168">"မီနူး ဖွင့်ရန်"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"စခရင်ကို ချဲ့မည်"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"စခရင်ကို ချုံ့မည်"</string> - <!-- no translation found for desktop_mode_non_resizable_snap_text (1049800446363800707) --> - <skip /> + <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"ဤအက်ပ်ကို အရွယ်ပြင်၍ မရပါ"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-nb/strings.xml b/libs/WindowManager/Shell/res/values-nb/strings.xml index f965a50dbd88..896d9fd3df9f 100644 --- a/libs/WindowManager/Shell/res/values-nb/strings.xml +++ b/libs/WindowManager/Shell/res/values-nb/strings.xml @@ -127,6 +127,5 @@ <string name="expand_menu_text" msgid="3847736164494181168">"Åpne menyen"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maksimer skjermen"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Fest skjermen"</string> - <!-- no translation found for desktop_mode_non_resizable_snap_text (1049800446363800707) --> - <skip /> + <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Du kan ikke endre størrelse på denne appen"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-nl/strings.xml b/libs/WindowManager/Shell/res/values-nl/strings.xml index 32c87d5a3a5e..a9c06fba4c4a 100644 --- a/libs/WindowManager/Shell/res/values-nl/strings.xml +++ b/libs/WindowManager/Shell/res/values-nl/strings.xml @@ -127,6 +127,5 @@ <string name="expand_menu_text" msgid="3847736164494181168">"Menu openen"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Scherm maximaliseren"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Scherm halveren"</string> - <!-- no translation found for desktop_mode_non_resizable_snap_text (1049800446363800707) --> - <skip /> + <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Het formaat van deze app kan niet worden aangepast"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-or/strings.xml b/libs/WindowManager/Shell/res/values-or/strings.xml index 9151dea3ce4f..a80cfc2dacf2 100644 --- a/libs/WindowManager/Shell/res/values-or/strings.xml +++ b/libs/WindowManager/Shell/res/values-or/strings.xml @@ -66,7 +66,7 @@ <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"ତଳ ବାମକୁ ନିଅନ୍ତୁ"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"ତଳ ଡାହାଣକୁ ନିଅନ୍ତୁ"</string> <string name="bubble_accessibility_action_expand_menu" msgid="8637233525952938845">"ମେନୁକୁ ବିସ୍ତାର କରନ୍ତୁ"</string> - <string name="bubble_accessibility_action_collapse_menu" msgid="2975310870146231463">"ମେନୁକୁ ସଂକୁଚିତ କରନ୍ତୁ"</string> + <string name="bubble_accessibility_action_collapse_menu" msgid="2975310870146231463">"ମେନୁକୁ ସଙ୍କୁଚିତ କରନ୍ତୁ"</string> <string name="bubble_accessibility_action_move_bar_left" msgid="4803535120353716759">"ବାମକୁ ମୁଭ କରନ୍ତୁ"</string> <string name="bubble_accessibility_action_move_bar_right" msgid="7686542531917510421">"ଡାହାଣକୁ ମୁଭ କରନ୍ତୁ"</string> <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> ବିସ୍ତାର କରନ୍ତୁ"</string> @@ -127,6 +127,5 @@ <string name="expand_menu_text" msgid="3847736164494181168">"ମେନୁ ଖୋଲନ୍ତୁ"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"ସ୍କ୍ରିନକୁ ବଡ଼ କରନ୍ତୁ"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"ସ୍କ୍ରିନକୁ ସ୍ନାପ କରନ୍ତୁ"</string> - <!-- no translation found for desktop_mode_non_resizable_snap_text (1049800446363800707) --> - <skip /> + <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"ଏହି ଆପକୁ ରିସାଇଜ କରାଯାଇପାରିବ ନାହିଁ"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-pa/strings.xml b/libs/WindowManager/Shell/res/values-pa/strings.xml index e099cc985873..7257161fdc2f 100644 --- a/libs/WindowManager/Shell/res/values-pa/strings.xml +++ b/libs/WindowManager/Shell/res/values-pa/strings.xml @@ -127,6 +127,5 @@ <string name="expand_menu_text" msgid="3847736164494181168">"ਮੀਨੂ ਖੋਲ੍ਹੋ"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"ਸਕ੍ਰੀਨ ਦਾ ਆਕਾਰ ਵਧਾਓ"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"ਸਕ੍ਰੀਨ ਨੂੰ ਸਨੈਪ ਕਰੋ"</string> - <!-- no translation found for desktop_mode_non_resizable_snap_text (1049800446363800707) --> - <skip /> + <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"ਇਸ ਐਪ ਦਾ ਆਕਾਰ ਬਦਲਿਆ ਨਹੀਂ ਜਾ ਸਕਦਾ"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-pl/strings.xml b/libs/WindowManager/Shell/res/values-pl/strings.xml index f954c9d6d290..7600db0acf9f 100644 --- a/libs/WindowManager/Shell/res/values-pl/strings.xml +++ b/libs/WindowManager/Shell/res/values-pl/strings.xml @@ -127,6 +127,5 @@ <string name="expand_menu_text" msgid="3847736164494181168">"Otwórz menu"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maksymalizuj ekran"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Przyciągnij ekran"</string> - <!-- no translation found for desktop_mode_non_resizable_snap_text (1049800446363800707) --> - <skip /> + <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Nie można zmienić rozmiaru tej aplikacji"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml b/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml index 1e980159dcd2..58c78f314bcb 100644 --- a/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml +++ b/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml @@ -127,6 +127,5 @@ <string name="expand_menu_text" msgid="3847736164494181168">"Abrir o menu"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Ampliar tela"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Ajustar tela"</string> - <!-- no translation found for desktop_mode_non_resizable_snap_text (1049800446363800707) --> - <skip /> + <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Não é possível redimensionar o app"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-pt/strings.xml b/libs/WindowManager/Shell/res/values-pt/strings.xml index 1e980159dcd2..58c78f314bcb 100644 --- a/libs/WindowManager/Shell/res/values-pt/strings.xml +++ b/libs/WindowManager/Shell/res/values-pt/strings.xml @@ -127,6 +127,5 @@ <string name="expand_menu_text" msgid="3847736164494181168">"Abrir o menu"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Ampliar tela"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Ajustar tela"</string> - <!-- no translation found for desktop_mode_non_resizable_snap_text (1049800446363800707) --> - <skip /> + <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Não é possível redimensionar o app"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-ro/strings.xml b/libs/WindowManager/Shell/res/values-ro/strings.xml index 0b96492f6c5f..077503a46576 100644 --- a/libs/WindowManager/Shell/res/values-ro/strings.xml +++ b/libs/WindowManager/Shell/res/values-ro/strings.xml @@ -127,6 +127,5 @@ <string name="expand_menu_text" msgid="3847736164494181168">"Deschide meniul"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maximizează fereastra"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Micșorează fereastra și fixeaz-o"</string> - <!-- no translation found for desktop_mode_non_resizable_snap_text (1049800446363800707) --> - <skip /> + <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Aplicația nu poate fi redimensionată"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-ru/strings.xml b/libs/WindowManager/Shell/res/values-ru/strings.xml index a1b39e430595..547102749d15 100644 --- a/libs/WindowManager/Shell/res/values-ru/strings.xml +++ b/libs/WindowManager/Shell/res/values-ru/strings.xml @@ -127,6 +127,5 @@ <string name="expand_menu_text" msgid="3847736164494181168">"Открыть меню"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Развернуть на весь экран"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Свернуть"</string> - <!-- no translation found for desktop_mode_non_resizable_snap_text (1049800446363800707) --> - <skip /> + <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Изменить размер приложения нельзя."</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-si/strings.xml b/libs/WindowManager/Shell/res/values-si/strings.xml index 1b70ffce6ae0..3f015f606ba6 100644 --- a/libs/WindowManager/Shell/res/values-si/strings.xml +++ b/libs/WindowManager/Shell/res/values-si/strings.xml @@ -127,6 +127,5 @@ <string name="expand_menu_text" msgid="3847736164494181168">"මෙනුව විවෘත කරන්න"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"තිරය උපරිම කරන්න"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"ස්නැප් තිරය"</string> - <!-- no translation found for desktop_mode_non_resizable_snap_text (1049800446363800707) --> - <skip /> + <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"මෙම යෙදුම ප්රතිප්රමාණ කළ නොහැක"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-sk/strings.xml b/libs/WindowManager/Shell/res/values-sk/strings.xml index 344e3c7dd977..fa376e7d51ce 100644 --- a/libs/WindowManager/Shell/res/values-sk/strings.xml +++ b/libs/WindowManager/Shell/res/values-sk/strings.xml @@ -127,6 +127,5 @@ <string name="expand_menu_text" msgid="3847736164494181168">"Otvoriť ponuku"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maximalizovať obrazovku"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Zobraziť polovicu obrazovky"</string> - <!-- no translation found for desktop_mode_non_resizable_snap_text (1049800446363800707) --> - <skip /> + <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Veľkosť tejto aplikácie sa nedá zmeniť"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-sl/strings.xml b/libs/WindowManager/Shell/res/values-sl/strings.xml index 118831d703d8..85386687398d 100644 --- a/libs/WindowManager/Shell/res/values-sl/strings.xml +++ b/libs/WindowManager/Shell/res/values-sl/strings.xml @@ -127,6 +127,5 @@ <string name="expand_menu_text" msgid="3847736164494181168">"Odpri meni"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maksimiraj zaslon"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Pripni zaslon"</string> - <!-- no translation found for desktop_mode_non_resizable_snap_text (1049800446363800707) --> - <skip /> + <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Velikosti te aplikacije ni mogoče spremeniti"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-sq/strings.xml b/libs/WindowManager/Shell/res/values-sq/strings.xml index 7976af0d5cdd..f77a43d5e9fe 100644 --- a/libs/WindowManager/Shell/res/values-sq/strings.xml +++ b/libs/WindowManager/Shell/res/values-sq/strings.xml @@ -127,6 +127,5 @@ <string name="expand_menu_text" msgid="3847736164494181168">"Hap menynë"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maksimizo ekranin"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Regjistro ekranin"</string> - <!-- no translation found for desktop_mode_non_resizable_snap_text (1049800446363800707) --> - <skip /> + <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Përmasat e këtij aplikacioni nuk mund të ndryshohen"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-sr/strings.xml b/libs/WindowManager/Shell/res/values-sr/strings.xml index 6243c52cc03e..af7686aabe67 100644 --- a/libs/WindowManager/Shell/res/values-sr/strings.xml +++ b/libs/WindowManager/Shell/res/values-sr/strings.xml @@ -127,6 +127,5 @@ <string name="expand_menu_text" msgid="3847736164494181168">"Отворите мени"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Повећај екран"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Уклопи екран"</string> - <!-- no translation found for desktop_mode_non_resizable_snap_text (1049800446363800707) --> - <skip /> + <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Величина ове апликације не може да се промени"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-sv/strings.xml b/libs/WindowManager/Shell/res/values-sv/strings.xml index 1584a3daa41b..0d08d8d5ecde 100644 --- a/libs/WindowManager/Shell/res/values-sv/strings.xml +++ b/libs/WindowManager/Shell/res/values-sv/strings.xml @@ -127,6 +127,5 @@ <string name="expand_menu_text" msgid="3847736164494181168">"Öppna menyn"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maximera skärmen"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Fäst skärmen"</string> - <!-- no translation found for desktop_mode_non_resizable_snap_text (1049800446363800707) --> - <skip /> + <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Det går inte att ändra storlek på appen"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-sw/strings.xml b/libs/WindowManager/Shell/res/values-sw/strings.xml index bf6af0c203ca..448f62493d6e 100644 --- a/libs/WindowManager/Shell/res/values-sw/strings.xml +++ b/libs/WindowManager/Shell/res/values-sw/strings.xml @@ -127,6 +127,5 @@ <string name="expand_menu_text" msgid="3847736164494181168">"Fungua Menyu"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Panua Dirisha kwenye Skrini"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Panga Madirisha kwenye Skrini"</string> - <!-- no translation found for desktop_mode_non_resizable_snap_text (1049800446363800707) --> - <skip /> + <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Huwezi kubadilisha ukubwa wa programu hii"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-ta/strings.xml b/libs/WindowManager/Shell/res/values-ta/strings.xml index 825fc8f6b24e..126892984e71 100644 --- a/libs/WindowManager/Shell/res/values-ta/strings.xml +++ b/libs/WindowManager/Shell/res/values-ta/strings.xml @@ -127,6 +127,5 @@ <string name="expand_menu_text" msgid="3847736164494181168">"மெனுவைத் திற"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"திரையைப் பெரிதாக்கு"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"திரையை ஸ்னாப் செய்"</string> - <!-- no translation found for desktop_mode_non_resizable_snap_text (1049800446363800707) --> - <skip /> + <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"இந்த ஆப்ஸின் அளவை மாற்ற முடியாது"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-te/strings.xml b/libs/WindowManager/Shell/res/values-te/strings.xml index 17f33225b369..524e55864f05 100644 --- a/libs/WindowManager/Shell/res/values-te/strings.xml +++ b/libs/WindowManager/Shell/res/values-te/strings.xml @@ -127,6 +127,5 @@ <string name="expand_menu_text" msgid="3847736164494181168">"మెనూను తెరవండి"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"స్క్రీన్ సైజ్ను పెంచండి"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"స్క్రీన్ను స్నాప్ చేయండి"</string> - <!-- no translation found for desktop_mode_non_resizable_snap_text (1049800446363800707) --> - <skip /> + <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"ఈ యాప్ సైజ్ను మార్చడం సాధ్యపడదు"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-th/strings.xml b/libs/WindowManager/Shell/res/values-th/strings.xml index c03600f38602..00a395f953ef 100644 --- a/libs/WindowManager/Shell/res/values-th/strings.xml +++ b/libs/WindowManager/Shell/res/values-th/strings.xml @@ -127,6 +127,5 @@ <string name="expand_menu_text" msgid="3847736164494181168">"เปิดเมนู"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"ขยายหน้าจอให้ใหญ่สุด"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"สแนปหน้าจอ"</string> - <!-- no translation found for desktop_mode_non_resizable_snap_text (1049800446363800707) --> - <skip /> + <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"ปรับขนาดแอปนี้ไม่ได้"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-tl/strings.xml b/libs/WindowManager/Shell/res/values-tl/strings.xml index 174c5245a79d..50a9211f3122 100644 --- a/libs/WindowManager/Shell/res/values-tl/strings.xml +++ b/libs/WindowManager/Shell/res/values-tl/strings.xml @@ -127,6 +127,5 @@ <string name="expand_menu_text" msgid="3847736164494181168">"Buksan ang Menu"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"I-maximize ang Screen"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"I-snap ang Screen"</string> - <!-- no translation found for desktop_mode_non_resizable_snap_text (1049800446363800707) --> - <skip /> + <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Hindi nare-resize ang app na ito"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-tr/strings.xml b/libs/WindowManager/Shell/res/values-tr/strings.xml index 0ae2a6aff5d4..ddd420608e80 100644 --- a/libs/WindowManager/Shell/res/values-tr/strings.xml +++ b/libs/WindowManager/Shell/res/values-tr/strings.xml @@ -127,6 +127,5 @@ <string name="expand_menu_text" msgid="3847736164494181168">"Menüyü Aç"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Ekranı Büyüt"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Ekranın Yarısına Tuttur"</string> - <!-- no translation found for desktop_mode_non_resizable_snap_text (1049800446363800707) --> - <skip /> + <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Bu uygulama yeniden boyutlandırılamaz"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-uk/strings.xml b/libs/WindowManager/Shell/res/values-uk/strings.xml index 64ca28c45d69..1dcdfe6a2ffe 100644 --- a/libs/WindowManager/Shell/res/values-uk/strings.xml +++ b/libs/WindowManager/Shell/res/values-uk/strings.xml @@ -127,6 +127,5 @@ <string name="expand_menu_text" msgid="3847736164494181168">"Відкрити меню"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Розгорнути екран"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Зафіксувати екран"</string> - <!-- no translation found for desktop_mode_non_resizable_snap_text (1049800446363800707) --> - <skip /> + <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Розмір вікна цього додатка не можна змінити"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-ur/strings.xml b/libs/WindowManager/Shell/res/values-ur/strings.xml index 19cef43ce8fd..26ece5ca2fb6 100644 --- a/libs/WindowManager/Shell/res/values-ur/strings.xml +++ b/libs/WindowManager/Shell/res/values-ur/strings.xml @@ -127,6 +127,5 @@ <string name="expand_menu_text" msgid="3847736164494181168">"مینو کھولیں"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"اسکرین کو بڑا کریں"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"اسکرین کا اسناپ شاٹ لیں"</string> - <!-- no translation found for desktop_mode_non_resizable_snap_text (1049800446363800707) --> - <skip /> + <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"اس ایپ کا سائز تبدیل نہیں کیا جا سکتا"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-uz/strings.xml b/libs/WindowManager/Shell/res/values-uz/strings.xml index 6768e07c276a..90b9a3f2f15e 100644 --- a/libs/WindowManager/Shell/res/values-uz/strings.xml +++ b/libs/WindowManager/Shell/res/values-uz/strings.xml @@ -127,6 +127,5 @@ <string name="expand_menu_text" msgid="3847736164494181168">"Menyuni ochish"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Ekranni yoyish"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Ekranni biriktirish"</string> - <!-- no translation found for desktop_mode_non_resizable_snap_text (1049800446363800707) --> - <skip /> + <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Bu ilova hajmini oʻzgartirish imkonsiz"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-vi/strings.xml b/libs/WindowManager/Shell/res/values-vi/strings.xml index eef1e8ec2dd9..90471f9a6a33 100644 --- a/libs/WindowManager/Shell/res/values-vi/strings.xml +++ b/libs/WindowManager/Shell/res/values-vi/strings.xml @@ -127,6 +127,5 @@ <string name="expand_menu_text" msgid="3847736164494181168">"Mở Trình đơn"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Mở rộng màn hình"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Điều chỉnh kích thước màn hình"</string> - <!-- no translation found for desktop_mode_non_resizable_snap_text (1049800446363800707) --> - <skip /> + <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Không thể đổi kích thước của ứng dụng này"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml b/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml index b152c0aa9e34..0aa52ac64da7 100644 --- a/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml +++ b/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml @@ -127,6 +127,5 @@ <string name="expand_menu_text" msgid="3847736164494181168">"打开菜单"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"最大化屏幕"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"屏幕快照"</string> - <!-- no translation found for desktop_mode_non_resizable_snap_text (1049800446363800707) --> - <skip /> + <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"无法调整此应用的大小"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml b/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml index d96739fe8d95..8a5be6a74d31 100644 --- a/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml +++ b/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml @@ -127,6 +127,5 @@ <string name="expand_menu_text" msgid="3847736164494181168">"打開選單"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"畫面最大化"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"貼齊畫面"</string> - <!-- no translation found for desktop_mode_non_resizable_snap_text (1049800446363800707) --> - <skip /> + <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"此應用程式無法調整大小"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml b/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml index 6bc93e6cbd49..d1cc4bb961bf 100644 --- a/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml +++ b/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml @@ -127,6 +127,5 @@ <string name="expand_menu_text" msgid="3847736164494181168">"開啟選單"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"畫面最大化"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"貼齊畫面"</string> - <!-- no translation found for desktop_mode_non_resizable_snap_text (1049800446363800707) --> - <skip /> + <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"這個應用程式無法調整大小"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-zu/strings.xml b/libs/WindowManager/Shell/res/values-zu/strings.xml index e152705b240b..6163a9794d6c 100644 --- a/libs/WindowManager/Shell/res/values-zu/strings.xml +++ b/libs/WindowManager/Shell/res/values-zu/strings.xml @@ -127,6 +127,5 @@ <string name="expand_menu_text" msgid="3847736164494181168">"Vula Imenyu"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Khulisa Isikrini Sifike Ekugcineni"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Thwebula Isikrini"</string> - <!-- no translation found for desktop_mode_non_resizable_snap_text (1049800446363800707) --> - <skip /> + <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Le app ayikwazi ukushintshwa usayizi"</string> </resources> diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeFlags.kt b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeFlags.kt index 47d52744cd74..424d4bf5c6e8 100644 --- a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeFlags.kt +++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeFlags.kt @@ -48,7 +48,7 @@ enum class DesktopModeFlags( TASK_STACK_OBSERVER_IN_SHELL(Flags::enableTaskStackObserverInShell, true), SIZE_CONSTRAINTS(Flags::enableDesktopWindowingSizeConstraints, true), DISABLE_SNAP_RESIZE(Flags::disableNonResizableAppSnapResizing, true), - DYNAMIC_INITIAL_BOUNDS(Flags::enableWindowingDynamicInitialBounds, true), + DYNAMIC_INITIAL_BOUNDS(Flags::enableWindowingDynamicInitialBounds, false), ENABLE_DESKTOP_WINDOWING_TASK_LIMIT(Flags::enableDesktopWindowingTaskLimit, true), BACK_NAVIGATION(Flags::enableDesktopWindowingBackNavigation, true), EDGE_DRAG_RESIZE(Flags::enableWindowingEdgeDragResize, true), 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 27194b344780..33949f5d8d5f 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 @@ -1392,6 +1392,7 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont } // Handle the commit transition if this handler is running the open transition. finishCallback.onTransitionFinished(null); + t.apply(); if (mCloseTransitionRequested) { if (mApps == null || mApps.length == 0) { if (mQueuedTransition == null) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellCoroutinesModule.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellCoroutinesModule.kt index 423fe579a29a..cc47dbb78af2 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellCoroutinesModule.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellCoroutinesModule.kt @@ -61,20 +61,20 @@ class WMShellCoroutinesModule { @WMSingleton @ShellMainThread fun provideApplicationScope( - @ShellMainThread applicationDispatcher: CoroutineDispatcher, + @ShellMainThread applicationDispatcher: MainCoroutineDispatcher, ): CoroutineScope = CoroutineScope(applicationDispatcher) @Provides @WMSingleton @ShellBackgroundThread fun provideBackgroundCoroutineScope( - @ShellBackgroundThread backgroundDispatcher: CoroutineDispatcher, + @ShellBackgroundThread backgroundDispatcher: MainCoroutineDispatcher, ): CoroutineScope = CoroutineScope(backgroundDispatcher) @Provides @WMSingleton @ShellBackgroundThread fun provideBackgroundCoroutineContext( - @ShellBackgroundThread backgroundDispatcher: CoroutineDispatcher + @ShellBackgroundThread backgroundDispatcher: MainCoroutineDispatcher ): CoroutineContext = backgroundDispatcher + SupervisorJob() } 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 4db8a82eb5af..46cb6ec36196 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 @@ -59,6 +59,7 @@ import com.android.wm.shell.common.TaskStackListenerImpl; import com.android.wm.shell.dagger.back.ShellBackAnimationModule; import com.android.wm.shell.dagger.pip.PipModule; import com.android.wm.shell.desktopmode.DefaultDragToDesktopTransitionHandler; +import com.android.wm.shell.desktopmode.DesktopActivityOrientationChangeHandler; import com.android.wm.shell.desktopmode.DesktopModeDragAndDropTransitionHandler; import com.android.wm.shell.desktopmode.DesktopModeEventLogger; import com.android.wm.shell.desktopmode.DesktopModeLoggerTransitionObserver; @@ -237,7 +238,8 @@ public abstract class WMShellModule { InteractionJankMonitor interactionJankMonitor, AppToWebGenericLinksParser genericLinksParser, MultiInstanceHelper multiInstanceHelper, - Optional<DesktopTasksLimiter> desktopTasksLimiter) { + Optional<DesktopTasksLimiter> desktopTasksLimiter, + Optional<DesktopActivityOrientationChangeHandler> desktopActivityOrientationHandler) { if (DesktopModeStatus.canEnterDesktopMode(context)) { return new DesktopModeWindowDecorViewModel( context, @@ -259,7 +261,8 @@ public abstract class WMShellModule { interactionJankMonitor, genericLinksParser, multiInstanceHelper, - desktopTasksLimiter); + desktopTasksLimiter, + desktopActivityOrientationHandler); } return new CaptionWindowDecorViewModel( context, @@ -677,6 +680,24 @@ public abstract class WMShellModule { @WMSingleton @Provides + static Optional<DesktopActivityOrientationChangeHandler> provideActivityOrientationHandler( + Context context, + ShellInit shellInit, + ShellTaskOrganizer shellTaskOrganizer, + TaskStackListenerImpl taskStackListener, + ToggleResizeDesktopTaskTransitionHandler toggleResizeDesktopTaskTransitionHandler, + @DynamicOverride DesktopModeTaskRepository desktopModeTaskRepository + ) { + if (DesktopModeStatus.canEnterDesktopMode(context)) { + return Optional.of(new DesktopActivityOrientationChangeHandler( + context, shellInit, shellTaskOrganizer, taskStackListener, + toggleResizeDesktopTaskTransitionHandler, desktopModeTaskRepository)); + } + return Optional.empty(); + } + + @WMSingleton + @Provides static Optional<DesktopTasksTransitionObserver> provideDesktopTasksTransitionObserver( Context context, Optional<DesktopModeTaskRepository> desktopModeTaskRepository, diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopActivityOrientationChangeHandler.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopActivityOrientationChangeHandler.kt new file mode 100644 index 000000000000..59e006879da8 --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopActivityOrientationChangeHandler.kt @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.wm.shell.desktopmode + +import android.app.ActivityManager.RunningTaskInfo +import android.content.Context +import android.content.pm.ActivityInfo +import android.content.pm.ActivityInfo.ScreenOrientation +import android.content.res.Configuration.ORIENTATION_LANDSCAPE +import android.content.res.Configuration.ORIENTATION_PORTRAIT +import android.graphics.Rect +import android.util.Size +import android.window.WindowContainerTransaction +import com.android.window.flags.Flags +import com.android.wm.shell.ShellTaskOrganizer +import com.android.wm.shell.common.TaskStackListenerCallback +import com.android.wm.shell.common.TaskStackListenerImpl +import com.android.wm.shell.shared.desktopmode.DesktopModeStatus +import com.android.wm.shell.sysui.ShellInit + +/** Handles task resizing to respect orientation change of non-resizeable activities in desktop. */ +class DesktopActivityOrientationChangeHandler( + context: Context, + shellInit: ShellInit, + private val shellTaskOrganizer: ShellTaskOrganizer, + private val taskStackListener: TaskStackListenerImpl, + private val resizeHandler: ToggleResizeDesktopTaskTransitionHandler, + private val taskRepository: DesktopModeTaskRepository, +) { + + init { + if (DesktopModeStatus.canEnterDesktopMode(context)) { + shellInit.addInitCallback({ onInit() }, this) + } + } + + private fun onInit() { + taskStackListener.addListener(object : TaskStackListenerCallback { + override fun onActivityRequestedOrientationChanged( + taskId: Int, + @ScreenOrientation requestedOrientation: Int + ) { + // Handle requested screen orientation changes at runtime. + handleActivityOrientationChange(taskId, requestedOrientation) + } + }) + } + + /** + * Triggered with onTaskInfoChanged to handle: + * * New activity launching from same task with different orientation + * * Top activity closing in same task with different orientation to previous activity + */ + fun handleActivityOrientationChange(oldTask: RunningTaskInfo, newTask: RunningTaskInfo) { + val newTopActivityInfo = newTask.topActivityInfo ?: return + val oldTopActivityInfo = oldTask.topActivityInfo ?: return + // Check if screen orientation is different from old task info so there is no duplicated + // calls to handle runtime requested orientation changes. + if (oldTopActivityInfo.screenOrientation != newTopActivityInfo.screenOrientation) { + handleActivityOrientationChange(newTask.taskId, newTopActivityInfo.screenOrientation) + } + } + + private fun handleActivityOrientationChange( + taskId: Int, + @ScreenOrientation requestedOrientation: Int + ) { + if (!Flags.respectOrientationChangeForUnresizeable()) return + val task = shellTaskOrganizer.getRunningTaskInfo(taskId) ?: return + if (!isDesktopModeShowing(task.displayId) || !task.isFreeform || task.isResizeable) return + + val taskBounds = task.configuration.windowConfiguration.bounds + val taskHeight = taskBounds.height() + val taskWidth = taskBounds.width() + if (taskWidth == taskHeight) return + val orientation = + if (taskWidth > taskHeight) ORIENTATION_LANDSCAPE else ORIENTATION_PORTRAIT + + // Non-resizeable activity requested opposite orientation. + if (orientation == ORIENTATION_PORTRAIT + && ActivityInfo.isFixedOrientationLandscape(requestedOrientation) + || orientation == ORIENTATION_LANDSCAPE + && ActivityInfo.isFixedOrientationPortrait(requestedOrientation)) { + + val finalSize = Size(taskHeight, taskWidth) + // Use the center x as the resizing anchor point. + val left = taskBounds.centerX() - finalSize.width / 2 + val right = left + finalSize.width + val finalBounds = Rect(left, taskBounds.top, right, taskBounds.top + finalSize.height) + + val wct = WindowContainerTransaction().setBounds(task.token, finalBounds) + resizeHandler.startTransition(wct) + } + } + + private fun isDesktopModeShowing(displayId: Int): Boolean = + taskRepository.getVisibleTaskCount(displayId) > 0 +}
\ No newline at end of file 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 336e5e3af2c6..063747494a82 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 @@ -22,6 +22,7 @@ import android.app.TaskInfo import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM import android.content.Context import android.os.IBinder +import android.os.SystemProperties import android.os.Trace import android.util.SparseArray import android.view.SurfaceControl @@ -52,8 +53,6 @@ import com.android.wm.shell.shared.TransitionUtil import com.android.wm.shell.sysui.ShellInit import com.android.wm.shell.transition.Transitions -const val VISIBLE_TASKS_COUNTER_NAME = "DESKTOP_MODE_VISIBLE_TASKS" - /** * A [Transitions.TransitionObserver] that observes transitions and the proposed changes to log * appropriate desktop mode session log events. This observes transitions related to desktop mode @@ -307,6 +306,8 @@ class DesktopModeLoggerTransitionObserver( VISIBLE_TASKS_COUNTER_NAME, postTransitionVisibleFreeformTasks.size().toLong() ) + SystemProperties.set(VISIBLE_TASKS_COUNTER_SYSTEM_PROPERTY, + postTransitionVisibleFreeformTasks.size().toString()) } // old tasks that were resized or repositioned // TODO(b/347935387): Log changes only once they are stable. @@ -326,6 +327,8 @@ class DesktopModeLoggerTransitionObserver( VISIBLE_TASKS_COUNTER_NAME, postTransitionVisibleFreeformTasks.size().toLong() ) + SystemProperties.set(VISIBLE_TASKS_COUNTER_SYSTEM_PROPERTY, + postTransitionVisibleFreeformTasks.size().toString()) } } } @@ -431,4 +434,12 @@ class DesktopModeLoggerTransitionObserver( return this.type == WindowManager.TRANSIT_TO_FRONT && this.flags == WindowManager.TRANSIT_FLAG_IS_RECENTS } + + companion object { + @VisibleForTesting + const val VISIBLE_TASKS_COUNTER_NAME = "desktop_mode_visible_tasks" + @VisibleForTesting + const val VISIBLE_TASKS_COUNTER_SYSTEM_PROPERTY = + "debug.tracing." + VISIBLE_TASKS_COUNTER_NAME + } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeUtils.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeUtils.kt index b68b436f2c1b..c8ffe28da79c 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeUtils.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeUtils.kt @@ -171,6 +171,18 @@ fun calculateAspectRatio(taskInfo: RunningTaskInfo): Float { minOf(appBounds.height(), appBounds.width()).toFloat() } +/** Returns true if task's width or height is maximized else returns false. */ +fun isTaskWidthOrHeightEqual(taskBounds: Rect, stableBounds: Rect): Boolean { + return taskBounds.width() == stableBounds.width() || + taskBounds.height() == stableBounds.height() +} + +/** Returns true if task bound is equal to stable bounds else returns false. */ +fun isTaskBoundsEqual(taskBounds: Rect, stableBounds: Rect): Boolean { + return taskBounds.width() == stableBounds.width() && + taskBounds.height() == stableBounds.height() +} + /** * Calculates the desired initial bounds for applications in desktop windowing. This is done as a * scale of the screen bounds. 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 2852631656b5..90f8276240a7 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 @@ -169,6 +169,9 @@ class DesktopTasksController( } } + @VisibleForTesting + var taskbarDesktopTaskListener: TaskbarDesktopTaskListener? = null + /** Task id of the task currently being dragged from fullscreen/split. */ val draggingTaskId get() = dragToDesktopTransitionHandler.draggingTaskId @@ -610,13 +613,10 @@ class DesktopTasksController( val currentTaskBounds = taskInfo.configuration.windowConfiguration.bounds val destinationBounds = Rect() - val isMaximized = if (taskInfo.isResizeable) { - currentTaskBounds == stableBounds - } else { - currentTaskBounds.width() == stableBounds.width() - || currentTaskBounds.height() == stableBounds.height() - } - + val isMaximized = isTaskMaximized(taskInfo, stableBounds) + // If the task is currently maximized, we will toggle it not to be and vice versa. This is + // helpful to eliminate the current task from logic to calculate taskbar corner rounding. + val willMaximize = !isMaximized if (isMaximized) { // The desktop task is at the maximized width and/or height of the stable bounds. // If the task's pre-maximize stable bounds were saved, toggle the task to those bounds. @@ -651,6 +651,18 @@ class DesktopTasksController( } } + + + val shouldRestoreToSnap = + isMaximized && isTaskSnappedToHalfScreen(taskInfo, destinationBounds) + + logD("willMaximize = %s", willMaximize) + logD("shouldRestoreToSnap = %s", shouldRestoreToSnap) + + val doesAnyTaskRequireTaskbarRounding = willMaximize || shouldRestoreToSnap || + doesAnyTaskRequireTaskbarRounding(taskInfo.displayId, taskInfo.taskId) + + taskbarDesktopTaskListener?.onTaskbarCornerRoundingUpdate(doesAnyTaskRequireTaskbarRounding) val wct = WindowContainerTransaction().setBounds(taskInfo.token, destinationBounds) if (Transitions.ENABLE_SHELL_TRANSITIONS) { toggleResizeDesktopTaskTransitionHandler.startTransition(wct) @@ -659,6 +671,65 @@ class DesktopTasksController( } } + private fun isTaskMaximized( + taskInfo: RunningTaskInfo, + stableBounds: Rect + ): Boolean { + val currentTaskBounds = taskInfo.configuration.windowConfiguration.bounds + + return if (taskInfo.isResizeable) { + isTaskBoundsEqual(currentTaskBounds, stableBounds) + } else { + isTaskWidthOrHeightEqual(currentTaskBounds, stableBounds) + } + } + + private fun isMaximizedToStableBoundsEdges( + taskInfo: RunningTaskInfo, + stableBounds: Rect + ): Boolean { + val currentTaskBounds = taskInfo.configuration.windowConfiguration.bounds + return isTaskBoundsEqual(currentTaskBounds, stableBounds) + } + + /** Returns if current task bound is snapped to half screen */ + private fun isTaskSnappedToHalfScreen( + taskInfo: RunningTaskInfo, + taskBounds: Rect = taskInfo.configuration.windowConfiguration.bounds + ): Boolean = + getSnapBounds(taskInfo, SnapPosition.LEFT) == taskBounds || + getSnapBounds(taskInfo, SnapPosition.RIGHT) == taskBounds + + @VisibleForTesting + fun doesAnyTaskRequireTaskbarRounding( + displayId: Int, + excludeTaskId: Int? = null, + ): Boolean { + val doesAnyTaskRequireTaskbarRounding = + taskRepository.getActiveNonMinimizedOrderedTasks(displayId) + // exclude current task since maximize/restore transition has not taken place yet. + .filterNot { taskId -> taskId == excludeTaskId } + .any { taskId -> + val taskInfo = shellTaskOrganizer.getRunningTaskInfo(taskId)!! + val displayLayout = displayController.getDisplayLayout(taskInfo.displayId) + val stableBounds = Rect().apply { displayLayout?.getStableBounds(this) } + logD("taskInfo = %s", taskInfo) + logD( + "isTaskSnappedToHalfScreen(taskInfo) = %s", + isTaskSnappedToHalfScreen(taskInfo) + ) + logD( + "isMaximizedToStableBoundsEdges(taskInfo, stableBounds) = %s", + isMaximizedToStableBoundsEdges(taskInfo, stableBounds) + ) + isTaskSnappedToHalfScreen(taskInfo) + || isMaximizedToStableBoundsEdges(taskInfo, stableBounds) + } + + logD("doesAnyTaskRequireTaskbarRounding = %s", doesAnyTaskRequireTaskbarRounding) + return doesAnyTaskRequireTaskbarRounding + } + /** * Quick-resize to the right or left half of the stable bounds. * @@ -673,9 +744,9 @@ class DesktopTasksController( position: SnapPosition ) { val destinationBounds = getSnapBounds(taskInfo, position) - if (destinationBounds == taskInfo.configuration.windowConfiguration.bounds) return + taskbarDesktopTaskListener?.onTaskbarCornerRoundingUpdate(true) val wct = WindowContainerTransaction().setBounds(taskInfo.token, destinationBounds) if (Transitions.ENABLE_SHELL_TRANSITIONS) { toggleResizeDesktopTaskTransitionHandler.startTransition(wct, currentDragBounds) @@ -806,6 +877,10 @@ class DesktopTasksController( .mapNotNull { taskId -> shellTaskOrganizer.getRunningTaskInfo(taskId) } .reversed() // Start from the back so the front task is brought forward last .forEach { task -> wct.reorder(task.token, /* onTop= */ true) } + + taskbarDesktopTaskListener?. + onTaskbarCornerRoundingUpdate(doesAnyTaskRequireTaskbarRounding(displayId)) + return taskToMinimize } @@ -1156,6 +1231,12 @@ class DesktopTasksController( ) { wct.removeTask(task.token) } + taskbarDesktopTaskListener?.onTaskbarCornerRoundingUpdate( + doesAnyTaskRequireTaskbarRounding( + task.displayId, + task.id + ) + ) return if (wct.isEmpty) null else wct } @@ -1450,6 +1531,8 @@ class DesktopTasksController( } // A freeform drag-move ended, remove the indicator immediately. releaseVisualIndicator() + taskbarDesktopTaskListener + ?.onTaskbarCornerRoundingUpdate(doesAnyTaskRequireTaskbarRounding(taskInfo.displayId)) } /** @@ -1686,17 +1769,39 @@ class DesktopTasksController( } } + private val mTaskbarDesktopTaskListener: TaskbarDesktopTaskListener = + object : TaskbarDesktopTaskListener { + override fun onTaskbarCornerRoundingUpdate( + hasTasksRequiringTaskbarRounding: Boolean) { + ProtoLog.v( + WM_SHELL_DESKTOP_MODE, + "IDesktopModeImpl: onTaskbarCornerRoundingUpdate " + + "doesAnyTaskRequireTaskbarRounding=%s", + hasTasksRequiringTaskbarRounding + ) + + remoteListener.call { l -> + l.onTaskbarCornerRoundingUpdate(hasTasksRequiringTaskbarRounding) + } + } + } + init { remoteListener = SingleInstanceRemoteListener<DesktopTasksController, IDesktopTaskListener>( controller, { c -> - c.taskRepository.addVisibleTasksListener( - listener, - c.mainExecutor - ) + run { + c.taskRepository.addVisibleTasksListener(listener, c.mainExecutor) + c.taskbarDesktopTaskListener = mTaskbarDesktopTaskListener + } }, - { c -> c.taskRepository.removeVisibleTasksListener(listener) } + { c -> + run { + c.taskRepository.removeVisibleTasksListener(listener) + c.taskbarDesktopTaskListener = null + } + } ) } @@ -1779,6 +1884,16 @@ class DesktopTasksController( private const val TAG = "DesktopTasksController" } + /** Defines interface for classes that can listen to changes for task resize. */ + // TODO(b/343931111): Migrate to using TransitionObservers when ready + interface TaskbarDesktopTaskListener { + /** + * [hasTasksRequiringTaskbarRounding] is true when a task is either maximized or snapped + * left/right and rounded corners are enabled. + */ + fun onTaskbarCornerRoundingUpdate(hasTasksRequiringTaskbarRounding: Boolean) + } + /** The positions on a screen that a task can snap to. */ enum class SnapPosition { RIGHT, diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopTaskListener.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopTaskListener.aidl index 8ebdfdcf4731..c2acb87222d1 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopTaskListener.aidl +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopTaskListener.aidl @@ -27,4 +27,10 @@ interface IDesktopTaskListener { /** @deprecated this is no longer supported. */ oneway void onStashedChanged(int displayId, boolean stashed); + + /** + * Shows taskbar corner radius when running desktop tasks are updated if + * [hasTasksRequiringTaskbarRounding] is true. + */ + oneway void onTaskbarCornerRoundingUpdate(boolean hasTasksRequiringTaskbarRounding); }
\ No newline at end of file diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java index 4fc6c4489f2b..9b0fb20f9777 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java @@ -18,8 +18,8 @@ package com.android.wm.shell.transition; import static android.app.ActivityOptions.ANIM_CLIP_REVEAL; import static android.app.ActivityOptions.ANIM_CUSTOM; -import static android.app.ActivityOptions.ANIM_NONE; import static android.app.ActivityOptions.ANIM_FROM_STYLE; +import static android.app.ActivityOptions.ANIM_NONE; import static android.app.ActivityOptions.ANIM_OPEN_CROSS_PROFILE_APPS; import static android.app.ActivityOptions.ANIM_SCALE_UP; import static android.app.ActivityOptions.ANIM_SCENE_TRANSITION; @@ -473,7 +473,7 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler { change.getLeash(), startTransaction); } else if (isOnlyTranslucent && TransitionUtil.isOpeningType(info.getType()) - && TransitionUtil.isClosingType(mode)) { + && TransitionUtil.isClosingType(mode)) { // If there is a closing translucent task in an OPENING transition, we will // actually select a CLOSING animation, so move the closing task into // the animating part of the z-order. @@ -767,12 +767,12 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler { a = mTransitionAnimation.loadKeyguardExitAnimation(flags, (changeFlags & FLAG_SHOW_WALLPAPER) != 0); } else if (type == TRANSIT_KEYGUARD_UNOCCLUDE) { - a = mTransitionAnimation.loadKeyguardUnoccludeAnimation(); + a = mTransitionAnimation.loadKeyguardUnoccludeAnimation(options.getUserId()); } else if ((changeFlags & FLAG_IS_VOICE_INTERACTION) != 0) { if (isOpeningType) { - a = mTransitionAnimation.loadVoiceActivityOpenAnimation(enter); + a = mTransitionAnimation.loadVoiceActivityOpenAnimation(enter, options.getUserId()); } else { - a = mTransitionAnimation.loadVoiceActivityExitAnimation(enter); + a = mTransitionAnimation.loadVoiceActivityExitAnimation(enter, options.getUserId()); } } else if (changeMode == TRANSIT_CHANGE) { // In the absence of a specific adapter, we just want to keep everything stationary. @@ -783,9 +783,9 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler { } else if (overrideType == ANIM_CUSTOM && (!isTask || options.getOverrideTaskTransition())) { a = mTransitionAnimation.loadAnimationRes(options.getPackageName(), enter - ? options.getEnterResId() : options.getExitResId()); + ? options.getEnterResId() : options.getExitResId(), options.getUserId()); } else if (overrideType == ANIM_OPEN_CROSS_PROFILE_APPS && enter) { - a = mTransitionAnimation.loadCrossProfileAppEnterAnimation(); + a = mTransitionAnimation.loadCrossProfileAppEnterAnimation(options.getUserId()); } else if (overrideType == ANIM_CLIP_REVEAL) { a = mTransitionAnimation.createClipRevealAnimationLocked(type, wallpaperTransit, enter, endBounds, endBounds, options.getTransitionBounds()); @@ -905,9 +905,9 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler { final Rect bounds = change.getEndAbsBounds(); // Show the right drawable depending on the user we're transitioning to. final Drawable thumbnailDrawable = change.hasFlags(FLAG_CROSS_PROFILE_OWNER_THUMBNAIL) - ? mContext.getDrawable(R.drawable.ic_account_circle) - : change.hasFlags(FLAG_CROSS_PROFILE_WORK_THUMBNAIL) - ? mEnterpriseThumbnailDrawable : null; + ? mContext.getDrawable(R.drawable.ic_account_circle) + : change.hasFlags(FLAG_CROSS_PROFILE_WORK_THUMBNAIL) + ? mEnterpriseThumbnailDrawable : null; if (thumbnailDrawable == null) { return; } 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 1f95667f4e35..ac35459347c6 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 @@ -97,6 +97,7 @@ import com.android.wm.shell.common.DisplayLayout; import com.android.wm.shell.common.MultiInstanceHelper; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.SyncTransactionQueue; +import com.android.wm.shell.desktopmode.DesktopActivityOrientationChangeHandler; import com.android.wm.shell.desktopmode.DesktopModeVisualIndicator; import com.android.wm.shell.desktopmode.DesktopTasksController; import com.android.wm.shell.desktopmode.DesktopTasksController.SnapPosition; @@ -166,6 +167,8 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { private TaskOperations mTaskOperations; private final Supplier<SurfaceControl.Transaction> mTransactionFactory; private final Transitions mTransitions; + private final Optional<DesktopActivityOrientationChangeHandler> + mActivityOrientationChangeHandler; private SplitScreenController mSplitScreenController; @@ -215,7 +218,8 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { InteractionJankMonitor interactionJankMonitor, AppToWebGenericLinksParser genericLinksParser, MultiInstanceHelper multiInstanceHelper, - Optional<DesktopTasksLimiter> desktopTasksLimiter + Optional<DesktopTasksLimiter> desktopTasksLimiter, + Optional<DesktopActivityOrientationChangeHandler> activityOrientationChangeHandler ) { this( context, @@ -241,7 +245,8 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { rootTaskDisplayAreaOrganizer, new SparseArray<>(), interactionJankMonitor, - desktopTasksLimiter); + desktopTasksLimiter, + activityOrientationChangeHandler); } @VisibleForTesting @@ -269,7 +274,8 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer, SparseArray<DesktopModeWindowDecoration> windowDecorByTaskId, InteractionJankMonitor interactionJankMonitor, - Optional<DesktopTasksLimiter> desktopTasksLimiter) { + Optional<DesktopTasksLimiter> desktopTasksLimiter, + Optional<DesktopActivityOrientationChangeHandler> activityOrientationChangeHandler) { mContext = context; mMainExecutor = shellExecutor; mMainHandler = mainHandler; @@ -297,6 +303,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { com.android.internal.R.string.config_systemUi); mInteractionJankMonitor = interactionJankMonitor; mDesktopTasksLimiter = desktopTasksLimiter; + mActivityOrientationChangeHandler = activityOrientationChangeHandler; mOnDisplayChangingListener = (displayId, fromRotation, toRotation, displayAreaInfo, t) -> { DesktopModeWindowDecoration decoration; RunningTaskInfo taskInfo; @@ -388,6 +395,8 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { incrementEventReceiverTasks(taskInfo.displayId); } decoration.relayout(taskInfo); + mActivityOrientationChangeHandler.ifPresent(handler -> + handler.handleActivityOrientationChange(oldTaskInfo, taskInfo)); } @Override @@ -496,16 +505,16 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { if (decoration == null) { return; } - openInBrowser(uri); + openInBrowser(uri, decoration.getUser()); decoration.closeHandleMenu(); decoration.closeMaximizeMenu(); } - private void openInBrowser(Uri uri) { + private void openInBrowser(Uri uri, @NonNull UserHandle userHandle) { final Intent intent = Intent.makeMainSelectorActivity(ACTION_MAIN, CATEGORY_APP_BROWSER) .setData(uri) .addFlags(FLAG_ACTIVITY_NEW_TASK); - mContext.startActivity(intent); + mContext.startActivityAsUser(intent, userHandle); } private void onToDesktop(int taskId, DesktopModeTransitionSource source) { 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 2bec3fa6418d..81251b81e239 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 @@ -54,6 +54,7 @@ import android.graphics.drawable.Drawable; import android.net.Uri; import android.os.Handler; import android.os.Trace; +import android.os.UserHandle; import android.util.Size; import android.util.Slog; import android.view.Choreographer; @@ -480,6 +481,10 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin return mGenericLink; } + UserHandle getUser() { + return mUserContext.getUser(); + } + private void updateDragResizeListener(SurfaceControl oldDecorationSurface) { if (!isDragResizable(mTaskInfo)) { if (!mTaskInfo.positionInParent.equals(mPositionInParent)) { 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 deef37874e79..9c73e4a38aa9 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 @@ -121,8 +121,14 @@ class MaximizeMenu( /** Closes the maximize window and releases its view. */ fun close() { - maximizeMenuView?.animateCloseMenu { - maximizeMenu?.releaseView() + val view = maximizeMenuView + val menu = maximizeMenu + if (view == null) { + menu?.releaseView() + } else { + view.animateCloseMenu { + menu?.releaseView() + } } maximizeMenu = null maximizeMenuView = null diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositioner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositioner.java index 7f2c1a81d20c..4a884eb50595 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositioner.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositioner.java @@ -104,9 +104,6 @@ public class VeiledResizeTaskPositioner implements DragPositioningCallback, wct.reorder(mDesktopWindowDecoration.mTaskInfo.token, true); mTaskOrganizer.applyTransaction(wct); } - } else { - mInteractionJankMonitor.begin(mDesktopWindowDecoration.mTaskSurface, - mDesktopWindowDecoration.mContext, CUJ_DESKTOP_MODE_DRAG_WINDOW); } mDragStartListener.onDragStart(mDesktopWindowDecoration.mTaskInfo.taskId); mRepositionTaskBounds.set(mTaskBoundsAtDragStart); @@ -133,6 +130,9 @@ public class VeiledResizeTaskPositioner implements DragPositioningCallback, mDesktopWindowDecoration.updateResizeVeil(mRepositionTaskBounds); } } else if (mCtrlType == CTRL_TYPE_UNDEFINED) { + // Begin window drag CUJ instrumentation only when drag position moves. + mInteractionJankMonitor.begin(mDesktopWindowDecoration.mTaskSurface, + mDesktopWindowDecoration.mContext, CUJ_DESKTOP_MODE_DRAG_WINDOW); final SurfaceControl.Transaction t = mTransactionSupplier.get(); DragPositioningCallbackUtility.setPositionOnDrag(mDesktopWindowDecoration, mRepositionTaskBounds, mTaskBoundsAtDragStart, mRepositionStartPoint, t, x, y); diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/DesktopModeFlickerScenarios.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/DesktopModeFlickerScenarios.kt index 3fb67cd522c0..507ad647a788 100644 --- a/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/DesktopModeFlickerScenarios.kt +++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/DesktopModeFlickerScenarios.kt @@ -17,11 +17,17 @@ package com.android.wm.shell.flicker import android.tools.flicker.AssertionInvocationGroup +import android.tools.flicker.assertors.assertions.AppLayerIncreasesInSize import android.tools.flicker.assertors.assertions.AppLayerIsInvisibleAtEnd import android.tools.flicker.assertors.assertions.AppLayerIsVisibleAlways import android.tools.flicker.assertors.assertions.AppLayerIsVisibleAtStart import android.tools.flicker.assertors.assertions.AppWindowBecomesVisible +import android.tools.flicker.assertors.assertions.AppWindowCoversLeftHalfScreenAtEnd +import android.tools.flicker.assertors.assertions.AppWindowCoversRightHalfScreenAtEnd import android.tools.flicker.assertors.assertions.AppWindowHasDesktopModeInitialBoundsAtTheEnd +import android.tools.flicker.assertors.assertions.AppWindowHasMaxBoundsInOnlyOneDimension +import android.tools.flicker.assertors.assertions.AppWindowHasMaxDisplayHeight +import android.tools.flicker.assertors.assertions.AppWindowHasMaxDisplayWidth import android.tools.flicker.assertors.assertions.AppWindowHasSizeOfAtLeast import android.tools.flicker.assertors.assertions.AppWindowIsInvisibleAtEnd import android.tools.flicker.assertors.assertions.AppWindowIsVisibleAlways @@ -51,21 +57,21 @@ class DesktopModeFlickerScenarios { FlickerConfigEntry( scenarioId = ScenarioId("END_DRAG_TO_DESKTOP"), extractor = - ShellTransitionScenarioExtractor( - transitionMatcher = - object : ITransitionMatcher { - override fun findAll( - transitions: Collection<Transition> - ): Collection<Transition> { - return transitions.filter { - // TODO(351168217) Use jank CUJ to extract a longer trace - it.type == TransitionType.DESKTOP_MODE_END_DRAG_TO_DESKTOP - } - } + ShellTransitionScenarioExtractor( + transitionMatcher = + object : ITransitionMatcher { + override fun findAll( + transitions: Collection<Transition> + ): Collection<Transition> { + return transitions.filter { + // TODO(351168217) Use jank CUJ to extract a longer trace + it.type == TransitionType.DESKTOP_MODE_END_DRAG_TO_DESKTOP } - ), + } + } + ), assertions = - AssertionTemplates.COMMON_ASSERTIONS + + AssertionTemplates.COMMON_ASSERTIONS + listOf( AppLayerIsVisibleAlways(DESKTOP_MODE_APP), AppWindowOnTopAtEnd(DESKTOP_MODE_APP), @@ -81,24 +87,24 @@ class DesktopModeFlickerScenarios { FlickerConfigEntry( scenarioId = ScenarioId("CLOSE_APP"), extractor = - ShellTransitionScenarioExtractor( - transitionMatcher = - object : ITransitionMatcher { - override fun findAll( - transitions: Collection<Transition> - ): Collection<Transition> { - // In case there are multiple windows closing, filter out the - // last window closing. It should use the CLOSE_LAST_APP - // scenario below. - return transitions - .filter { it.type == TransitionType.CLOSE } - .sortedByDescending { it.id } - .drop(1) - } - } - ), + ShellTransitionScenarioExtractor( + transitionMatcher = + object : ITransitionMatcher { + override fun findAll( + transitions: Collection<Transition> + ): Collection<Transition> { + // In case there are multiple windows closing, filter out the + // last window closing. It should use the CLOSE_LAST_APP + // scenario below. + return transitions + .filter { it.type == TransitionType.CLOSE } + .sortedByDescending { it.id } + .drop(1) + } + } + ), assertions = - AssertionTemplates.COMMON_ASSERTIONS + + AssertionTemplates.COMMON_ASSERTIONS + listOf( AppWindowOnTopAtStart(DESKTOP_MODE_APP), AppLayerIsVisibleAtStart(DESKTOP_MODE_APP), @@ -110,22 +116,22 @@ class DesktopModeFlickerScenarios { FlickerConfigEntry( scenarioId = ScenarioId("CLOSE_LAST_APP"), extractor = - ShellTransitionScenarioExtractor( - transitionMatcher = - object : ITransitionMatcher { - override fun findAll( - transitions: Collection<Transition> - ): Collection<Transition> { - val lastTransition = - transitions - .filter { it.type == TransitionType.CLOSE } - .maxByOrNull { it.id }!! - return listOf(lastTransition) - } - } - ), + ShellTransitionScenarioExtractor( + transitionMatcher = + object : ITransitionMatcher { + override fun findAll( + transitions: Collection<Transition> + ): Collection<Transition> { + val lastTransition = + transitions + .filter { it.type == TransitionType.CLOSE } + .maxByOrNull { it.id }!! + return listOf(lastTransition) + } + } + ), assertions = - AssertionTemplates.COMMON_ASSERTIONS + + AssertionTemplates.COMMON_ASSERTIONS + listOf( AppWindowIsInvisibleAtEnd(DESKTOP_MODE_APP), LauncherWindowReplacesAppAsTopWindow(DESKTOP_MODE_APP), @@ -138,12 +144,12 @@ class DesktopModeFlickerScenarios { FlickerConfigEntry( scenarioId = ScenarioId("CORNER_RESIZE"), extractor = - TaggedScenarioExtractorBuilder() - .setTargetTag(CujType.CUJ_DESKTOP_MODE_RESIZE_WINDOW) - .setTransitionMatcher( - TaggedCujTransitionMatcher(associatedTransitionRequired = false) - ) - .build(), + TaggedScenarioExtractorBuilder() + .setTargetTag(CujType.CUJ_DESKTOP_MODE_RESIZE_WINDOW) + .setTransitionMatcher( + TaggedCujTransitionMatcher(associatedTransitionRequired = false) + ) + .build(), assertions = AssertionTemplates.DESKTOP_MODE_APP_VISIBILITY_ASSERTIONS ) @@ -151,18 +157,78 @@ class DesktopModeFlickerScenarios { FlickerConfigEntry( scenarioId = ScenarioId("CORNER_RESIZE_TO_MINIMUM_SIZE"), extractor = - TaggedScenarioExtractorBuilder() - .setTargetTag(CujType.CUJ_DESKTOP_MODE_RESIZE_WINDOW) - .setTransitionMatcher( - TaggedCujTransitionMatcher(associatedTransitionRequired = false) - ) - .build(), + TaggedScenarioExtractorBuilder() + .setTargetTag(CujType.CUJ_DESKTOP_MODE_RESIZE_WINDOW) + .setTransitionMatcher( + TaggedCujTransitionMatcher(associatedTransitionRequired = false) + ) + .build(), assertions = AssertionTemplates.DESKTOP_MODE_APP_VISIBILITY_ASSERTIONS + listOf(AppWindowHasSizeOfAtLeast(DESKTOP_MODE_APP, 770, 700)) .associateBy({ it }, { AssertionInvocationGroup.BLOCKING }), ) + val SNAP_RESIZE_LEFT_WITH_BUTTON = + FlickerConfigEntry( + scenarioId = ScenarioId("SNAP_RESIZE_LEFT_WITH_BUTTON"), + extractor = + TaggedScenarioExtractorBuilder() + .setTargetTag(CujType.CUJ_DESKTOP_MODE_SNAP_RESIZE) + .setTransitionMatcher( + TaggedCujTransitionMatcher(associatedTransitionRequired = false) + ) + .build(), + assertions = AssertionTemplates.DESKTOP_MODE_APP_VISIBILITY_ASSERTIONS + + listOf(AppWindowCoversLeftHalfScreenAtEnd(DESKTOP_MODE_APP)) + .associateBy({ it }, { AssertionInvocationGroup.BLOCKING }), + ) + + val SNAP_RESIZE_RIGHT_WITH_BUTTON = + FlickerConfigEntry( + scenarioId = ScenarioId("SNAP_RESIZE_RIGHT_WITH_BUTTON"), + extractor = + TaggedScenarioExtractorBuilder() + .setTargetTag(CujType.CUJ_DESKTOP_MODE_SNAP_RESIZE) + .setTransitionMatcher( + TaggedCujTransitionMatcher(associatedTransitionRequired = false) + ) + .build(), + assertions = AssertionTemplates.DESKTOP_MODE_APP_VISIBILITY_ASSERTIONS + + listOf(AppWindowCoversRightHalfScreenAtEnd(DESKTOP_MODE_APP)) + .associateBy({ it }, { AssertionInvocationGroup.BLOCKING }), + ) + + val SNAP_RESIZE_LEFT_WITH_DRAG = + FlickerConfigEntry( + scenarioId = ScenarioId("SNAP_RESIZE_LEFT_WITH_DRAG"), + extractor = + TaggedScenarioExtractorBuilder() + .setTargetTag(CujType.CUJ_DESKTOP_MODE_SNAP_RESIZE) + .setTransitionMatcher( + TaggedCujTransitionMatcher(associatedTransitionRequired = false) + ) + .build(), + assertions = AssertionTemplates.DESKTOP_MODE_APP_VISIBILITY_ASSERTIONS + + listOf(AppWindowCoversLeftHalfScreenAtEnd(DESKTOP_MODE_APP)) + .associateBy({ it }, { AssertionInvocationGroup.BLOCKING }), + ) + + val SNAP_RESIZE_RIGHT_WITH_DRAG = + FlickerConfigEntry( + scenarioId = ScenarioId("SNAP_RESIZE_RIGHT_WITH_DRAG"), + extractor = + TaggedScenarioExtractorBuilder() + .setTargetTag(CujType.CUJ_DESKTOP_MODE_SNAP_RESIZE) + .setTransitionMatcher( + TaggedCujTransitionMatcher(associatedTransitionRequired = false) + ) + .build(), + assertions = AssertionTemplates.DESKTOP_MODE_APP_VISIBILITY_ASSERTIONS + + listOf(AppWindowCoversRightHalfScreenAtEnd(DESKTOP_MODE_APP)) + .associateBy({ it }, { AssertionInvocationGroup.BLOCKING }), + ) + val SNAP_RESIZE_WITH_DRAG_NON_RESIZABLE = FlickerConfigEntry( scenarioId = ScenarioId("SNAP_RESIZE_WITH_DRAG_NON_RESIZABLE"), @@ -181,5 +247,42 @@ class DesktopModeFlickerScenarios { AppWindowReturnsToStartBoundsAndPosition(NON_RESIZABLE_APP) ).associateBy({ it }, { AssertionInvocationGroup.BLOCKING }), ) + + val MAXIMIZE_APP = + FlickerConfigEntry( + scenarioId = ScenarioId("MAXIMIZE_APP"), + extractor = + TaggedScenarioExtractorBuilder() + .setTargetTag(CujType.CUJ_DESKTOP_MODE_MAXIMIZE_WINDOW) + .setTransitionMatcher( + TaggedCujTransitionMatcher(associatedTransitionRequired = false) + ) + .build(), + assertions = AssertionTemplates.DESKTOP_MODE_APP_VISIBILITY_ASSERTIONS + + listOf( + AppLayerIncreasesInSize(DESKTOP_MODE_APP), + AppWindowHasMaxDisplayHeight(DESKTOP_MODE_APP), + AppWindowHasMaxDisplayWidth(DESKTOP_MODE_APP) + ).associateBy({ it }, { AssertionInvocationGroup.BLOCKING }), + ) + + val MAXIMIZE_APP_NON_RESIZABLE = + FlickerConfigEntry( + scenarioId = ScenarioId("MAXIMIZE_APP_NON_RESIZABLE"), + extractor = + TaggedScenarioExtractorBuilder() + .setTargetTag(CujType.CUJ_DESKTOP_MODE_MAXIMIZE_WINDOW) + .setTransitionMatcher( + TaggedCujTransitionMatcher(associatedTransitionRequired = false) + ) + .build(), + assertions = + AssertionTemplates.DESKTOP_MODE_APP_VISIBILITY_ASSERTIONS + + listOf( + AppLayerIncreasesInSize(DESKTOP_MODE_APP), + AppWindowMaintainsAspectRatioAlways(DESKTOP_MODE_APP), + AppWindowHasMaxBoundsInOnlyOneDimension(DESKTOP_MODE_APP) + ).associateBy({ it }, { AssertionInvocationGroup.BLOCKING }), + ) } } diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/MaximizeAppLandscape.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/MaximizeAppLandscape.kt new file mode 100644 index 000000000000..217956671554 --- /dev/null +++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/MaximizeAppLandscape.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.wm.shell.flicker + +import android.tools.Rotation.ROTATION_90 +import android.tools.flicker.FlickerConfig +import android.tools.flicker.annotation.ExpectedScenarios +import android.tools.flicker.annotation.FlickerConfigProvider +import android.tools.flicker.config.FlickerConfig +import android.tools.flicker.config.FlickerServiceConfig +import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner +import com.android.wm.shell.flicker.DesktopModeFlickerScenarios.Companion.MAXIMIZE_APP +import com.android.wm.shell.scenarios.MaximizeAppWindow +import org.junit.Test +import org.junit.runner.RunWith + +/** + * Maximize app window by pressing the maximize button on the app header. + * + * Assert that the app window keeps the same increases in size, filling the vertical and horizontal + * stable display bounds. + */ +@RunWith(FlickerServiceJUnit4ClassRunner::class) +class MaximizeAppLandscape : MaximizeAppWindow(rotation = ROTATION_90) { + @ExpectedScenarios(["MAXIMIZE_APP"]) + @Test + override fun maximizeAppWindow() = super.maximizeAppWindow() + + + companion object { + @JvmStatic + @FlickerConfigProvider + fun flickerConfigProvider(): FlickerConfig = + FlickerConfig().use(FlickerServiceConfig.DEFAULT).use(MAXIMIZE_APP) + } +}
\ No newline at end of file diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/MaximizeAppNonResizableLandscape.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/MaximizeAppNonResizableLandscape.kt new file mode 100644 index 000000000000..b173a60132b2 --- /dev/null +++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/MaximizeAppNonResizableLandscape.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.wm.shell.flicker + +import android.tools.Rotation.ROTATION_90 +import android.tools.flicker.FlickerConfig +import android.tools.flicker.annotation.ExpectedScenarios +import android.tools.flicker.annotation.FlickerConfigProvider +import android.tools.flicker.config.FlickerConfig +import android.tools.flicker.config.FlickerServiceConfig +import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner +import com.android.wm.shell.flicker.DesktopModeFlickerScenarios.Companion.MAXIMIZE_APP_NON_RESIZABLE +import com.android.wm.shell.scenarios.MaximizeAppWindow +import org.junit.Test +import org.junit.runner.RunWith + +/** + * Maximize non-resizable app window by pressing the maximize button on the app header. + * + * Assert that the app window keeps the same increases in size, maintaining its aspect ratio, until + * filling the vertical or horizontal stable display bounds. + */ +@RunWith(FlickerServiceJUnit4ClassRunner::class) +class MaximizeAppNonResizableLandscape : MaximizeAppWindow( + rotation = ROTATION_90, + isResizable = false +) { + @ExpectedScenarios(["MAXIMIZE_APP_NON_RESIZABLE"]) + @Test + override fun maximizeAppWindow() = super.maximizeAppWindow() + + + companion object { + @JvmStatic + @FlickerConfigProvider + fun flickerConfigProvider(): FlickerConfig = + FlickerConfig().use(FlickerServiceConfig.DEFAULT).use(MAXIMIZE_APP_NON_RESIZABLE) + } +}
\ No newline at end of file diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/MaximizeAppNonResizablePortrait.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/MaximizeAppNonResizablePortrait.kt new file mode 100644 index 000000000000..88888eee8378 --- /dev/null +++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/MaximizeAppNonResizablePortrait.kt @@ -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.wm.shell.flicker + +import android.tools.flicker.FlickerConfig +import android.tools.flicker.annotation.ExpectedScenarios +import android.tools.flicker.annotation.FlickerConfigProvider +import android.tools.flicker.config.FlickerConfig +import android.tools.flicker.config.FlickerServiceConfig +import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner +import com.android.wm.shell.flicker.DesktopModeFlickerScenarios.Companion.MAXIMIZE_APP_NON_RESIZABLE +import com.android.wm.shell.scenarios.MaximizeAppWindow +import org.junit.Test +import org.junit.runner.RunWith + +/** + * Maximize non-resizable app window by pressing the maximize button on the app header. + * + * Assert that the app window keeps the same increases in size, maintaining its aspect ratio, until + * filling the vertical or horizontal stable display bounds. + */ +@RunWith(FlickerServiceJUnit4ClassRunner::class) +class MaximizeAppNonResizablePortrait : MaximizeAppWindow(isResizable = false) { + @ExpectedScenarios(["MAXIMIZE_APP_NON_RESIZABLE"]) + @Test + override fun maximizeAppWindow() = super.maximizeAppWindow() + + + companion object { + @JvmStatic + @FlickerConfigProvider + fun flickerConfigProvider(): FlickerConfig = + FlickerConfig().use(FlickerServiceConfig.DEFAULT).use(MAXIMIZE_APP_NON_RESIZABLE) + } +}
\ No newline at end of file diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/MaximizeAppPortrait.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/MaximizeAppPortrait.kt new file mode 100644 index 000000000000..b79fd203fe1e --- /dev/null +++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/MaximizeAppPortrait.kt @@ -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.wm.shell.flicker + +import android.tools.flicker.FlickerConfig +import android.tools.flicker.annotation.ExpectedScenarios +import android.tools.flicker.annotation.FlickerConfigProvider +import android.tools.flicker.config.FlickerConfig +import android.tools.flicker.config.FlickerServiceConfig +import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner +import com.android.wm.shell.flicker.DesktopModeFlickerScenarios.Companion.MAXIMIZE_APP +import com.android.wm.shell.scenarios.MaximizeAppWindow +import org.junit.Test +import org.junit.runner.RunWith + +/** + * Maximize app window by pressing the maximize button on the app header. + * + * Assert that the app window keeps the same increases in size, filling the vertical and horizontal + * stable display bounds. + */ +@RunWith(FlickerServiceJUnit4ClassRunner::class) +class MaximizeAppPortrait : MaximizeAppWindow() { + @ExpectedScenarios(["MAXIMIZE_APP"]) + @Test + override fun maximizeAppWindow() = super.maximizeAppWindow() + + + companion object { + @JvmStatic + @FlickerConfigProvider + fun flickerConfigProvider(): FlickerConfig = + FlickerConfig().use(FlickerServiceConfig.DEFAULT).use(MAXIMIZE_APP) + } +}
\ No newline at end of file diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/SnapResizeAppWindowLeftWithButton.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/SnapResizeAppWindowLeftWithButton.kt new file mode 100644 index 000000000000..b5090086f129 --- /dev/null +++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/SnapResizeAppWindowLeftWithButton.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.wm.shell.flicker + +import android.tools.flicker.FlickerConfig +import android.tools.flicker.annotation.ExpectedScenarios +import android.tools.flicker.annotation.FlickerConfigProvider +import android.tools.flicker.config.FlickerConfig +import android.tools.flicker.config.FlickerServiceConfig +import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner +import com.android.wm.shell.flicker.DesktopModeFlickerScenarios.Companion.SNAP_RESIZE_LEFT_WITH_BUTTON +import com.android.wm.shell.scenarios.SnapResizeAppWindowWithButton +import org.junit.Test +import org.junit.runner.RunWith + +/** + * Snap resize app window using the Snap Left button from the maximize menu. + * + * Assert that the app window fills the left half the display after being snap resized. + */ +@RunWith(FlickerServiceJUnit4ClassRunner::class) +class SnapResizeAppWindowLeftWithButton : SnapResizeAppWindowWithButton(toLeft = true) { + @ExpectedScenarios(["SNAP_RESIZE_LEFT_WITH_BUTTON"]) + @Test + override fun snapResizeAppWindowWithButton() = super.snapResizeAppWindowWithButton() + + companion object { + @JvmStatic + @FlickerConfigProvider + fun flickerConfigProvider(): FlickerConfig = + FlickerConfig().use(FlickerServiceConfig.DEFAULT).use(SNAP_RESIZE_LEFT_WITH_BUTTON) + } +}
\ No newline at end of file diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/SnapResizeAppWindowLeftWithDrag.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/SnapResizeAppWindowLeftWithDrag.kt new file mode 100644 index 000000000000..a22e7603bf0f --- /dev/null +++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/SnapResizeAppWindowLeftWithDrag.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.wm.shell.flicker + +import android.tools.flicker.FlickerConfig +import android.tools.flicker.annotation.ExpectedScenarios +import android.tools.flicker.annotation.FlickerConfigProvider +import android.tools.flicker.config.FlickerConfig +import android.tools.flicker.config.FlickerServiceConfig +import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner +import com.android.wm.shell.flicker.DesktopModeFlickerScenarios.Companion.SNAP_RESIZE_LEFT_WITH_DRAG +import com.android.wm.shell.scenarios.SnapResizeAppWindowWithDrag +import org.junit.Test +import org.junit.runner.RunWith + +/** + * Snap resize app window by dragging it to the left edge of the screen. + * + * Assert that the app window fills the left half the display after being snap resized. + */ +@RunWith(FlickerServiceJUnit4ClassRunner::class) +class SnapResizeAppWindowLeftWithDrag : SnapResizeAppWindowWithDrag(toLeft = true) { + @ExpectedScenarios(["SNAP_RESIZE_LEFT_WITH_DRAG"]) + @Test + override fun snapResizeAppWindowWithDrag() = super.snapResizeAppWindowWithDrag() + + companion object { + @JvmStatic + @FlickerConfigProvider + fun flickerConfigProvider(): FlickerConfig = + FlickerConfig().use(FlickerServiceConfig.DEFAULT).use(SNAP_RESIZE_LEFT_WITH_DRAG) + } +}
\ No newline at end of file diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/SnapResizeAppWindowRightWithButton.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/SnapResizeAppWindowRightWithButton.kt new file mode 100644 index 000000000000..375a2b8a61ac --- /dev/null +++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/SnapResizeAppWindowRightWithButton.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.wm.shell.flicker + +import android.tools.flicker.FlickerConfig +import android.tools.flicker.annotation.ExpectedScenarios +import android.tools.flicker.annotation.FlickerConfigProvider +import android.tools.flicker.config.FlickerConfig +import android.tools.flicker.config.FlickerServiceConfig +import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner +import com.android.wm.shell.flicker.DesktopModeFlickerScenarios.Companion.SNAP_RESIZE_RIGHT_WITH_BUTTON +import com.android.wm.shell.scenarios.SnapResizeAppWindowWithButton +import org.junit.Test +import org.junit.runner.RunWith + +/** + * Snap resize app window using the Snap Right button from the maximize menu. + * + * Assert that the app window fills the right half the display after being snap resized. + */ +@RunWith(FlickerServiceJUnit4ClassRunner::class) +class SnapResizeAppWindowRightWithButton : SnapResizeAppWindowWithButton(toLeft = false) { + @ExpectedScenarios(["SNAP_RESIZE_RIGHT_WITH_BUTTON"]) + @Test + override fun snapResizeAppWindowWithButton() = super.snapResizeAppWindowWithButton() + + companion object { + @JvmStatic + @FlickerConfigProvider + fun flickerConfigProvider(): FlickerConfig = + FlickerConfig().use(FlickerServiceConfig.DEFAULT).use(SNAP_RESIZE_RIGHT_WITH_BUTTON) + } +}
\ No newline at end of file diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/SnapResizeAppWindowRightWithDrag.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/SnapResizeAppWindowRightWithDrag.kt new file mode 100644 index 000000000000..4a9daf7e2ea1 --- /dev/null +++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/SnapResizeAppWindowRightWithDrag.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.wm.shell.flicker + +import android.tools.flicker.FlickerConfig +import android.tools.flicker.annotation.ExpectedScenarios +import android.tools.flicker.annotation.FlickerConfigProvider +import android.tools.flicker.config.FlickerConfig +import android.tools.flicker.config.FlickerServiceConfig +import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner +import com.android.wm.shell.flicker.DesktopModeFlickerScenarios.Companion.SNAP_RESIZE_RIGHT_WITH_DRAG +import com.android.wm.shell.scenarios.SnapResizeAppWindowWithDrag +import org.junit.Test +import org.junit.runner.RunWith + +/** + * Snap resize app window by dragging it to the right edge of the screen. + * + * Assert that the app window fills the right half the display after being snap resized. + */ +@RunWith(FlickerServiceJUnit4ClassRunner::class) +class SnapResizeAppWindowRightWithDrag : SnapResizeAppWindowWithDrag(toLeft = false) { + @ExpectedScenarios(["SNAP_RESIZE_RIGHT_WITH_DRAG"]) + @Test + override fun snapResizeAppWindowWithDrag() = super.snapResizeAppWindowWithDrag() + + companion object { + @JvmStatic + @FlickerConfigProvider + fun flickerConfigProvider(): FlickerConfig = + FlickerConfig().use(FlickerServiceConfig.DEFAULT).use(SNAP_RESIZE_RIGHT_WITH_DRAG) + } +}
\ No newline at end of file diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/PipDragThenSnapTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/PipDragThenSnapTest.kt index 90b9798c6329..cbd4a528474a 100644 --- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/PipDragThenSnapTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/PipDragThenSnapTest.kt @@ -181,6 +181,13 @@ class PipDragThenSnapTest(flicker: LegacyFlickerTest) : PipTransition(flicker) { super.taskBarWindowIsAlwaysVisible() } + // Overridden to remove @Postsubmit annotation + @Test + @FlakyTest(bugId = 294993100) + override fun pipLayerHasCorrectCornersAtEnd() { + // No rounded corners as we go back to fullscreen in new orientation. + } + companion object { /** * Creates the test configurations. diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinned.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinned.kt index ed2a0a718c6c..578a9b536289 100644 --- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinned.kt +++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinned.kt @@ -147,6 +147,12 @@ open class SetRequestedOrientationWhilePinned(flicker: LegacyFlickerTest) : PipT @Test override fun entireScreenCovered() = super.entireScreenCovered() + @Postsubmit + @Test + override fun pipLayerHasCorrectCornersAtEnd() { + flicker.assertLayersEnd { hasNoRoundedCorners(pipApp) } + } + companion object { @Parameterized.Parameters(name = "{0}") @JvmStatic diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/common/ClosePipTransition.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/common/ClosePipTransition.kt index 8cb81b46cf4d..f57335c2081b 100644 --- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/common/ClosePipTransition.kt +++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/common/ClosePipTransition.kt @@ -70,6 +70,11 @@ abstract class ClosePipTransition(flicker: LegacyFlickerTest) : PipTransition(fl } } + @Test + override fun pipLayerHasCorrectCornersAtEnd() { + // PiP might have completely faded out by this point, so corner radii not applicable. + } + companion object { /** * Creates the test configurations. diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/common/ExitPipToAppTransition.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/common/ExitPipToAppTransition.kt index 0742cf9c5887..ce84eb644042 100644 --- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/common/ExitPipToAppTransition.kt +++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/common/ExitPipToAppTransition.kt @@ -16,6 +16,7 @@ package com.android.wm.shell.flicker.pip.common +import android.platform.test.annotations.Postsubmit import android.platform.test.annotations.Presubmit import android.tools.Rotation import android.tools.flicker.legacy.LegacyFlickerTest @@ -123,6 +124,12 @@ abstract class ExitPipToAppTransition(flicker: LegacyFlickerTest) : PipTransitio } } + @Postsubmit + @Test + override fun pipLayerHasCorrectCornersAtEnd() { + flicker.assertLayersEnd { hasNoRoundedCorners(pipApp) } + } + /** {@inheritDoc} */ @Presubmit @Test override fun entireScreenCovered() = super.entireScreenCovered() diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/common/PipTransition.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/common/PipTransition.kt index 99c1ad2aaa4e..bc2bfdbe1df1 100644 --- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/common/PipTransition.kt +++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/common/PipTransition.kt @@ -18,6 +18,7 @@ package com.android.wm.shell.flicker.pip.common import android.app.Instrumentation import android.content.Intent +import android.platform.test.annotations.Postsubmit import android.platform.test.annotations.Presubmit import android.tools.Rotation import android.tools.flicker.legacy.FlickerBuilder @@ -105,4 +106,10 @@ abstract class PipTransition(flicker: LegacyFlickerTest) : BaseTest(flicker) { .doesNotContain(false) } } + + @Postsubmit + @Test + open fun pipLayerHasCorrectCornersAtEnd() { + flicker.assertLayersEnd { hasRoundedCorners(pipApp) } + } } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopActivityOrientationChangeHandlerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopActivityOrientationChangeHandlerTest.kt new file mode 100644 index 000000000000..b14f1633e8fd --- /dev/null +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopActivityOrientationChangeHandlerTest.kt @@ -0,0 +1,263 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.wm.shell.desktopmode + +import android.app.ActivityManager.RunningTaskInfo +import android.content.pm.ActivityInfo +import android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE +import android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT +import android.content.pm.ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT +import android.graphics.Rect +import android.os.Binder +import android.platform.test.annotations.EnableFlags +import android.platform.test.flag.junit.SetFlagsRule +import android.testing.AndroidTestingRunner +import android.view.Display.DEFAULT_DISPLAY +import android.window.WindowContainerTransaction +import androidx.test.filters.SmallTest +import com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn +import com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession +import com.android.dx.mockito.inline.extended.ExtendedMockito.never +import com.android.dx.mockito.inline.extended.StaticMockitoSession +import com.android.window.flags.Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE +import com.android.window.flags.Flags.FLAG_RESPECT_ORIENTATION_CHANGE_FOR_UNRESIZEABLE +import com.android.wm.shell.ShellTaskOrganizer +import com.android.wm.shell.ShellTestCase +import com.android.wm.shell.common.ShellExecutor +import com.android.wm.shell.common.TaskStackListenerImpl +import com.android.wm.shell.desktopmode.DesktopTestHelpers.Companion.createFreeformTask +import com.android.wm.shell.desktopmode.DesktopTestHelpers.Companion.createFullscreenTask +import com.android.wm.shell.shared.desktopmode.DesktopModeStatus +import com.android.wm.shell.sysui.ShellInit +import com.android.wm.shell.transition.Transitions +import junit.framework.Assert.assertEquals +import junit.framework.Assert.assertTrue +import kotlin.test.assertNotNull +import org.junit.After +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.ArgumentCaptor +import org.mockito.ArgumentMatchers.isNull +import org.mockito.Mock +import org.mockito.Mockito.anyInt +import org.mockito.Mockito.clearInvocations +import org.mockito.Mockito.spy +import org.mockito.Mockito.verify +import org.mockito.kotlin.any +import org.mockito.kotlin.atLeastOnce +import org.mockito.kotlin.capture +import org.mockito.kotlin.eq +import org.mockito.kotlin.whenever +import org.mockito.quality.Strictness + +/** + * Test class for {@link DesktopActivityOrientationChangeHandler} + * + * Usage: atest WMShellUnitTests:DesktopActivityOrientationChangeHandlerTest + */ +@SmallTest +@RunWith(AndroidTestingRunner::class) +@EnableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_MODE, FLAG_RESPECT_ORIENTATION_CHANGE_FOR_UNRESIZEABLE) +class DesktopActivityOrientationChangeHandlerTest : ShellTestCase() { + @JvmField @Rule val setFlagsRule = SetFlagsRule() + + @Mock lateinit var testExecutor: ShellExecutor + @Mock lateinit var shellTaskOrganizer: ShellTaskOrganizer + @Mock lateinit var transitions: Transitions + @Mock lateinit var resizeTransitionHandler: ToggleResizeDesktopTaskTransitionHandler + @Mock lateinit var taskStackListener: TaskStackListenerImpl + + private lateinit var mockitoSession: StaticMockitoSession + private lateinit var handler: DesktopActivityOrientationChangeHandler + private lateinit var shellInit: ShellInit + private lateinit var taskRepository: DesktopModeTaskRepository + // Mock running tasks are registered here so we can get the list from mock shell task organizer. + private val runningTasks = mutableListOf<RunningTaskInfo>() + + @Before + fun setUp() { + mockitoSession = + mockitoSession() + .strictness(Strictness.LENIENT) + .spyStatic(DesktopModeStatus::class.java) + .startMocking() + doReturn(true).`when` { DesktopModeStatus.isDesktopModeSupported(any()) } + + shellInit = spy(ShellInit(testExecutor)) + taskRepository = DesktopModeTaskRepository() + whenever(shellTaskOrganizer.getRunningTasks(anyInt())).thenAnswer { runningTasks } + whenever(transitions.startTransition(anyInt(), any(), isNull())).thenAnswer { Binder() } + + handler = DesktopActivityOrientationChangeHandler(context, shellInit, shellTaskOrganizer, + taskStackListener, resizeTransitionHandler, taskRepository) + + shellInit.init() + } + + @After + fun tearDown() { + mockitoSession.finishMocking() + + runningTasks.clear() + } + + @Test + fun instantiate_addInitCallback() { + verify(shellInit).addInitCallback(any(), any<DesktopActivityOrientationChangeHandler>()) + } + + @Test + fun instantiate_cannotEnterDesktopMode_doNotAddInitCallback() { + whenever(DesktopModeStatus.canEnterDesktopMode(context)).thenReturn(false) + clearInvocations(shellInit) + + handler = DesktopActivityOrientationChangeHandler(context, shellInit, shellTaskOrganizer, + taskStackListener, resizeTransitionHandler, taskRepository) + + verify(shellInit, never()).addInitCallback(any(), + any<DesktopActivityOrientationChangeHandler>()) + } + + @Test + fun handleActivityOrientationChange_resizeable_doNothing() { + val task = setUpFreeformTask() + + taskStackListener.onActivityRequestedOrientationChanged(task.taskId, + SCREEN_ORIENTATION_LANDSCAPE) + + verify(resizeTransitionHandler, never()).startTransition(any(), any()) + } + + @Test + fun handleActivityOrientationChange_nonResizeableFullscreen_doNothing() { + val task = createFullscreenTask() + task.isResizeable = false + val activityInfo = ActivityInfo() + activityInfo.screenOrientation = SCREEN_ORIENTATION_PORTRAIT + task.topActivityInfo = activityInfo + whenever(shellTaskOrganizer.getRunningTaskInfo(task.taskId)).thenReturn(task) + taskRepository.addActiveTask(DEFAULT_DISPLAY, task.taskId) + taskRepository.updateTaskVisibility(DEFAULT_DISPLAY, task.taskId, visible = true) + runningTasks.add(task) + + taskStackListener.onActivityRequestedOrientationChanged(task.taskId, + SCREEN_ORIENTATION_LANDSCAPE) + + verify(resizeTransitionHandler, never()).startTransition(any(), any()) + } + + @Test + fun handleActivityOrientationChange_nonResizeablePortrait_requestSameOrientation_doNothing() { + val task = setUpFreeformTask(isResizeable = false) + val newTask = setUpFreeformTask(isResizeable = false, + orientation = SCREEN_ORIENTATION_SENSOR_PORTRAIT) + + handler.handleActivityOrientationChange(task, newTask) + + verify(resizeTransitionHandler, never()).startTransition(any(), any()) + } + + @Test + fun handleActivityOrientationChange_notInDesktopMode_doNothing() { + val task = setUpFreeformTask(isResizeable = false) + taskRepository.updateTaskVisibility(task.displayId, task.taskId, visible = false) + + taskStackListener.onActivityRequestedOrientationChanged(task.taskId, + SCREEN_ORIENTATION_LANDSCAPE) + + verify(resizeTransitionHandler, never()).startTransition(any(), any()) + } + + @Test + fun handleActivityOrientationChange_nonResizeablePortrait_respectLandscapeRequest() { + val task = setUpFreeformTask(isResizeable = false) + val oldBounds = task.configuration.windowConfiguration.bounds + val newTask = setUpFreeformTask(isResizeable = false, + orientation = SCREEN_ORIENTATION_LANDSCAPE) + + handler.handleActivityOrientationChange(task, newTask) + + val wct = getLatestResizeDesktopTaskWct() + val finalBounds = findBoundsChange(wct, newTask) + assertNotNull(finalBounds) + val finalWidth = finalBounds.width() + val finalHeight = finalBounds.height() + // Bounds is landscape. + assertTrue(finalWidth > finalHeight) + // Aspect ratio remains the same. + assertEquals(oldBounds.height() / oldBounds.width(), finalWidth / finalHeight) + // Anchor point for resizing is at the center. + assertEquals(oldBounds.centerX(), finalBounds.centerX()) + } + + @Test + fun handleActivityOrientationChange_nonResizeableLandscape_respectPortraitRequest() { + val oldBounds = Rect(0, 0, 500, 200) + val task = setUpFreeformTask( + isResizeable = false, orientation = SCREEN_ORIENTATION_LANDSCAPE, bounds = oldBounds + ) + val newTask = setUpFreeformTask(isResizeable = false, bounds = oldBounds) + + handler.handleActivityOrientationChange(task, newTask) + + val wct = getLatestResizeDesktopTaskWct() + val finalBounds = findBoundsChange(wct, newTask) + assertNotNull(finalBounds) + val finalWidth = finalBounds.width() + val finalHeight = finalBounds.height() + // Bounds is portrait. + assertTrue(finalHeight > finalWidth) + // Aspect ratio remains the same. + assertEquals(oldBounds.width() / oldBounds.height(), finalHeight / finalWidth) + // Anchor point for resizing is at the center. + assertEquals(oldBounds.centerX(), finalBounds.centerX()) + } + + private fun setUpFreeformTask( + displayId: Int = DEFAULT_DISPLAY, + isResizeable: Boolean = true, + orientation: Int = SCREEN_ORIENTATION_PORTRAIT, + bounds: Rect? = Rect(0, 0, 200, 500) + ): RunningTaskInfo { + val task = createFreeformTask(displayId, bounds) + val activityInfo = ActivityInfo() + activityInfo.screenOrientation = orientation + task.topActivityInfo = activityInfo + task.isResizeable = isResizeable + whenever(shellTaskOrganizer.getRunningTaskInfo(task.taskId)).thenReturn(task) + taskRepository.addActiveTask(displayId, task.taskId) + taskRepository.updateTaskVisibility(displayId, task.taskId, visible = true) + taskRepository.addOrMoveFreeformTaskToTop(displayId, task.taskId) + runningTasks.add(task) + return task + } + + private fun getLatestResizeDesktopTaskWct( + currentBounds: Rect? = null + ): WindowContainerTransaction { + val arg: ArgumentCaptor<WindowContainerTransaction> = + ArgumentCaptor.forClass(WindowContainerTransaction::class.java) + verify(resizeTransitionHandler, atLeastOnce()) + .startTransition(capture(arg), eq(currentBounds)) + return arg.value + } + + private fun findBoundsChange(wct: WindowContainerTransaction, task: RunningTaskInfo): Rect? = + wct.changes[task.token.asBinder()]?.configuration?.windowConfiguration?.bounds +}
\ No newline at end of file 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 e49eb36fc04a..d399b20abb2a 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,6 +22,8 @@ import android.content.Context import android.graphics.Point import android.graphics.Rect import android.os.IBinder +import android.os.SystemProperties +import android.os.Trace import android.testing.AndroidTestingRunner import android.view.SurfaceControl import android.view.WindowManager.TRANSIT_CHANGE @@ -38,6 +40,7 @@ import android.window.TransitionInfo import android.window.TransitionInfo.Change import android.window.WindowContainerToken import androidx.test.filters.SmallTest +import com.android.dx.mockito.inline.extended.ExtendedMockito import com.android.modules.utils.testing.ExtendedMockitoRule import com.android.wm.shell.ShellTestCase import com.android.wm.shell.common.ShellExecutor @@ -86,7 +89,11 @@ class DesktopModeLoggerTransitionObserverTest : ShellTestCase() { @JvmField @Rule val extendedMockitoRule = - ExtendedMockitoRule.Builder(this).mockStatic(DesktopModeStatus::class.java).build()!! + ExtendedMockitoRule.Builder(this) + .mockStatic(DesktopModeStatus::class.java) + .mockStatic(SystemProperties::class.java) + .mockStatic(Trace::class.java) + .build()!! private val testExecutor = mock<ShellExecutor>() private val mockShellInit = mock<ShellInit>() @@ -695,6 +702,17 @@ class DesktopModeLoggerTransitionObserverTest : ShellTestCase() { assertNotNull(sessionId) verify(desktopModeEventLogger, times(1)).logSessionEnter(eq(sessionId!!), eq(enterReason)) verify(desktopModeEventLogger, times(1)).logTaskAdded(eq(sessionId), eq(taskUpdate)) + ExtendedMockito.verify { + Trace.setCounter( + eq(Trace.TRACE_TAG_WINDOW_MANAGER), + eq(DesktopModeLoggerTransitionObserver.VISIBLE_TASKS_COUNTER_NAME), + eq(taskUpdate.visibleTaskCount.toLong())) + } + ExtendedMockito.verify { + SystemProperties.set( + eq(DesktopModeLoggerTransitionObserver.VISIBLE_TASKS_COUNTER_SYSTEM_PROPERTY), + eq(taskUpdate.visibleTaskCount.toString())) + } verifyZeroInteractions(desktopModeEventLogger) } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeUtilsTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeUtilsTest.kt new file mode 100644 index 000000000000..8fab410adc30 --- /dev/null +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeUtilsTest.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.desktopmode + +import android.graphics.Rect +import android.testing.AndroidTestingRunner +import androidx.test.filters.SmallTest +import com.google.common.truth.Truth.assertThat +import org.junit.Test +import org.junit.runner.RunWith + +@SmallTest +@RunWith(AndroidTestingRunner::class) +class DesktopModeUtilsTest { + @Test + fun isTaskBoundsEqual_stableBoundsAreEqual_returnTrue() { + assertThat(isTaskBoundsEqual(task2Bounds, stableBounds)).isTrue() + } + + @Test + fun isTaskBoundsEqual_stableBoundsAreNotEqual_returnFalse() { + assertThat(isTaskBoundsEqual(task4Bounds, stableBounds)).isFalse() + } + + @Test + fun isTaskWidthOrHeightEqual_stableBoundsAreEqual_returnTrue() { + assertThat(isTaskWidthOrHeightEqual(task2Bounds, stableBounds)).isTrue() + } + + @Test + fun isTaskWidthOrHeightEqual_stableBoundWidthIsEquals_returnTrue() { + assertThat(isTaskWidthOrHeightEqual(task3Bounds, stableBounds)).isTrue() + } + + @Test + fun isTaskWidthOrHeightEqual_stableBoundHeightIsEquals_returnTrue() { + assertThat(isTaskWidthOrHeightEqual(task3Bounds, stableBounds)).isTrue() + } + + @Test + fun isTaskWidthOrHeightEqual_stableBoundsWidthOrHeightAreNotEquals_returnFalse() { + assertThat(isTaskWidthOrHeightEqual(task1Bounds, stableBounds)).isTrue() + } + + private companion object { + val task1Bounds = Rect(0, 0, 0, 0) + val task2Bounds = Rect(1, 1, 1, 1) + val task3Bounds = Rect(0, 1, 0, 1) + val task4Bounds = Rect(1, 2, 2, 1) + val stableBounds = Rect(1, 1, 1, 1) + } +} 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 d2487209dcef..5474e539f286 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 @@ -84,6 +84,7 @@ import com.android.wm.shell.common.MultiInstanceHelper import com.android.wm.shell.common.ShellExecutor import com.android.wm.shell.common.SyncTransactionQueue import com.android.wm.shell.desktopmode.DesktopTasksController.SnapPosition +import com.android.wm.shell.desktopmode.DesktopTasksController.TaskbarDesktopTaskListener import com.android.wm.shell.desktopmode.DesktopTestHelpers.Companion.createFreeformTask import com.android.wm.shell.desktopmode.DesktopTestHelpers.Companion.createFullscreenTask import com.android.wm.shell.desktopmode.DesktopTestHelpers.Companion.createHomeTask @@ -175,6 +176,7 @@ class DesktopTasksControllerTest : ShellTestCase() { @Mock private lateinit var mockInteractionJankMonitor: InteractionJankMonitor @Mock private lateinit var mockSurface: SurfaceControl + @Mock private lateinit var taskbarDesktopTaskListener: TaskbarDesktopTaskListener private lateinit var mockitoSession: StaticMockitoSession private lateinit var controller: DesktopTasksController @@ -237,6 +239,8 @@ class DesktopTasksControllerTest : ShellTestCase() { val captor = ArgumentCaptor.forClass(RecentsTransitionStateListener::class.java) verify(recentsTransitionHandler).addTransitionStateListener(captor.capture()) recentsTransitionStateListener = captor.value + + controller.taskbarDesktopTaskListener = taskbarDesktopTaskListener } private fun createController(): DesktopTasksController { @@ -282,6 +286,52 @@ class DesktopTasksControllerTest : ShellTestCase() { } @Test + fun doesAnyTaskRequireTaskbarRounding_onlyFreeFormTaskIsRunning_returnFalse() { + setUpFreeformTask() + + assertThat(controller.doesAnyTaskRequireTaskbarRounding(DEFAULT_DISPLAY)).isFalse() + } + + @Test + fun doesAnyTaskRequireTaskbarRounding_toggleResizeOfFreeFormTask_returnTrue() { + val task1 = setUpFreeformTask() + + val argumentCaptor = ArgumentCaptor.forClass(Boolean::class.java) + controller.toggleDesktopTaskSize(task1) + verify(taskbarDesktopTaskListener).onTaskbarCornerRoundingUpdate(argumentCaptor.capture()) + + assertThat(argumentCaptor.value).isTrue() + } + + @Test + fun doesAnyTaskRequireTaskbarRounding_fullScreenTaskIsRunning_returnTrue() { + val stableBounds = Rect().apply { displayLayout.getStableBounds(this) } + setUpFreeformTask(bounds = stableBounds, active = true) + assertThat(controller.doesAnyTaskRequireTaskbarRounding(DEFAULT_DISPLAY)).isTrue() + } + + @Test + fun doesAnyTaskRequireTaskbarRounding_toggleResizeOfFullScreenTask_returnFalse() { + val stableBounds = Rect().apply { displayLayout.getStableBounds(this) } + val task1 = setUpFreeformTask(bounds = stableBounds, active = true) + + val argumentCaptor = ArgumentCaptor.forClass(Boolean::class.java) + controller.toggleDesktopTaskSize(task1) + verify(taskbarDesktopTaskListener).onTaskbarCornerRoundingUpdate(argumentCaptor.capture()) + + assertThat(argumentCaptor.value).isFalse() + } + + @Test + fun doesAnyTaskRequireTaskbarRounding_splitScreenTaskIsRunning_returnTrue() { + val stableBounds = Rect().apply { displayLayout.getStableBounds(this) } + setUpFreeformTask(bounds = Rect(stableBounds.left, stableBounds.top, 500, stableBounds.bottom)) + + assertThat(controller.doesAnyTaskRequireTaskbarRounding(DEFAULT_DISPLAY)).isTrue() + } + + + @Test fun instantiate_cannotEnterDesktopMode_doNotAddInitCallback() { whenever(DesktopModeStatus.canEnterDesktopMode(context)).thenReturn(false) clearInvocations(shellInit) diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt index da0aca7b3b0f..0b5c6784b73d 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt @@ -34,6 +34,7 @@ import android.hardware.display.VirtualDisplay import android.hardware.input.InputManager import android.net.Uri import android.os.Handler +import android.os.UserHandle import android.platform.test.annotations.DisableFlags import android.platform.test.annotations.EnableFlags import android.platform.test.flag.junit.CheckFlagsRule @@ -79,6 +80,7 @@ import com.android.wm.shell.common.DisplayLayout import com.android.wm.shell.common.MultiInstanceHelper import com.android.wm.shell.common.ShellExecutor import com.android.wm.shell.common.SyncTransactionQueue +import com.android.wm.shell.desktopmode.DesktopActivityOrientationChangeHandler import com.android.wm.shell.desktopmode.DesktopTasksController import com.android.wm.shell.desktopmode.DesktopTasksController.SnapPosition import com.android.wm.shell.desktopmode.DesktopTasksLimiter @@ -162,11 +164,14 @@ class DesktopModeWindowDecorViewModelTests : ShellTestCase() { @Mock private lateinit var mockWindowManager: IWindowManager @Mock private lateinit var mockInteractionJankMonitor: InteractionJankMonitor @Mock private lateinit var mockGenericLinksParser: AppToWebGenericLinksParser + @Mock private lateinit var mockUserHandle: UserHandle @Mock private lateinit var mockToast: Toast private val bgExecutor = TestShellExecutor() @Mock private lateinit var mockMultiInstanceHelper: MultiInstanceHelper @Mock private lateinit var mockTasksLimiter: DesktopTasksLimiter @Mock private lateinit var mockFreeformTaskTransitionStarter: FreeformTaskTransitionStarter + @Mock private lateinit var mockActivityOrientationChangeHandler: + DesktopActivityOrientationChangeHandler private lateinit var spyContext: TestableContext private val transactionFactory = Supplier<SurfaceControl.Transaction> { @@ -220,7 +225,8 @@ class DesktopModeWindowDecorViewModelTests : ShellTestCase() { mockRootTaskDisplayAreaOrganizer, windowDecorByTaskIdSpy, mockInteractionJankMonitor, - Optional.of(mockTasksLimiter) + Optional.of(mockTasksLimiter), + Optional.of(mockActivityOrientationChangeHandler) ) desktopModeWindowDecorViewModel.setSplitScreenController(mockSplitScreenController) whenever(mockDisplayController.getDisplayLayout(any())).thenReturn(mockDisplayLayout) @@ -888,12 +894,12 @@ class DesktopModeWindowDecorViewModelTests : ShellTestCase() { openInBrowserListenerCaptor.value.accept(uri) - verify(spyContext).startActivity(argThat { intent -> + verify(spyContext).startActivityAsUser(argThat { intent -> intent.data == uri && ((intent.flags and Intent.FLAG_ACTIVITY_NEW_TASK) != 0) && intent.categories.contains(Intent.CATEGORY_LAUNCHER) && intent.action == Intent.ACTION_MAIN - }) + }, eq(mockUserHandle)) } @Test @@ -1104,6 +1110,7 @@ class DesktopModeWindowDecorViewModelTests : ShellTestCase() { ).thenReturn(decoration) decoration.mTaskInfo = task whenever(decoration.isFocused).thenReturn(task.isFocused) + whenever(decoration.user).thenReturn(mockUserHandle) if (task.windowingMode == WINDOWING_MODE_MULTI_WINDOW) { whenever(mockSplitScreenController.isTaskInSplitScreen(task.taskId)) .thenReturn(true) diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java index 029e6f49b062..25fae76768d9 100644 --- a/media/java/android/media/AudioManager.java +++ b/media/java/android/media/AudioManager.java @@ -4567,8 +4567,8 @@ public class AudioManager { * when the request is flagged with {@link #AUDIOFOCUS_FLAG_DELAY_OK}. * @param requestAttributes non null {@link AudioAttributes} describing the main reason for * requesting audio focus. - * @param durationHint use {@link #AUDIOFOCUS_GAIN_TRANSIENT} to indicate this focus request - * is temporary, and focus will be abandonned shortly. Examples of transient requests are + * @param focusReqType use {@link #AUDIOFOCUS_GAIN_TRANSIENT} to indicate this focus request + * is temporary, and focus will be abandoned shortly. Examples of transient requests are * for the playback of driving directions, or notifications sounds. * Use {@link #AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK} to indicate also that it's ok for * the previous focus owner to keep playing if it ducks its audio output. @@ -4593,13 +4593,13 @@ public class AudioManager { @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE) public int requestAudioFocus(OnAudioFocusChangeListener l, @NonNull AudioAttributes requestAttributes, - int durationHint, + int focusReqType, int flags) throws IllegalArgumentException { if (flags != (flags & AUDIOFOCUS_FLAGS_APPS)) { throw new IllegalArgumentException("Invalid flags 0x" + Integer.toHexString(flags).toUpperCase()); } - return requestAudioFocus(l, requestAttributes, durationHint, + return requestAudioFocus(l, requestAttributes, focusReqType, flags & AUDIOFOCUS_FLAGS_APPS, null /* no AudioPolicy*/); } @@ -4614,7 +4614,7 @@ public class AudioManager { * {@link #requestAudioFocus(OnAudioFocusChangeListener, AudioAttributes, int, int)} * @param requestAttributes non null {@link AudioAttributes} describing the main reason for * requesting audio focus. - * @param durationHint see the description of the same parameter in + * @param focusReqType see the description of the same parameter in * {@link #requestAudioFocus(OnAudioFocusChangeListener, AudioAttributes, int, int)} * @param flags 0 or a combination of {link #AUDIOFOCUS_FLAG_DELAY_OK}, * {@link #AUDIOFOCUS_FLAG_PAUSES_ON_DUCKABLE_LOSS}, and {@link #AUDIOFOCUS_FLAG_LOCK}. @@ -4636,14 +4636,14 @@ public class AudioManager { }) public int requestAudioFocus(OnAudioFocusChangeListener l, @NonNull AudioAttributes requestAttributes, - int durationHint, + int focusReqType, int flags, AudioPolicy ap) throws IllegalArgumentException { // parameter checking if (requestAttributes == null) { throw new IllegalArgumentException("Illegal null AudioAttributes argument"); } - if (!AudioFocusRequest.isValidFocusGain(durationHint)) { + if (!AudioFocusRequest.isValidFocusGain(focusReqType)) { throw new IllegalArgumentException("Invalid duration hint"); } if (flags != (flags & AUDIOFOCUS_FLAGS_SYSTEM)) { @@ -4664,7 +4664,7 @@ public class AudioManager { "Illegal null audio policy when locking audio focus"); } - final AudioFocusRequest afr = new AudioFocusRequest.Builder(durationHint) + final AudioFocusRequest afr = new AudioFocusRequest.Builder(focusReqType) .setOnAudioFocusChangeListenerInt(l, null /* no Handler for this legacy API */) .setAudioAttributes(requestAttributes) .setAcceptsDelayedFocusGain((flags & AUDIOFOCUS_FLAG_DELAY_OK) @@ -5025,16 +5025,16 @@ public class AudioManager { * to identify this use case. * @param streamType use STREAM_RING for focus requests when ringing, VOICE_CALL for * the establishment of the call - * @param durationHint the type of focus request. AUDIOFOCUS_GAIN_TRANSIENT is recommended so + * @param focusReqType the type of focus request. AUDIOFOCUS_GAIN_TRANSIENT is recommended so * media applications resume after a call */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - public void requestAudioFocusForCall(int streamType, int durationHint) { + public void requestAudioFocusForCall(int streamType, int focusReqType) { final IAudioService service = getService(); try { service.requestAudioFocus(new AudioAttributes.Builder() .setInternalLegacyStreamType(streamType).build(), - durationHint, mICallBack, null, + focusReqType, mICallBack, null, AudioSystem.IN_VOICE_COMM_FOCUS_ID, getContext().getOpPackageName(), getContext().getAttributionTag(), diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl index e0c346100d03..a96562d55f67 100644 --- a/media/java/android/media/IAudioService.aidl +++ b/media/java/android/media/IAudioService.aidl @@ -252,7 +252,7 @@ interface IAudioService { boolean isBluetoothA2dpOn(); - int requestAudioFocus(in AudioAttributes aa, int durationHint, IBinder cb, + int requestAudioFocus(in AudioAttributes aa, int focusReqType, IBinder cb, IAudioFocusDispatcher fd, in String clientId, in String callingPackageName, in String attributionTag, int flags, IAudioPolicyCallback pcb, int sdk); @@ -562,7 +562,7 @@ interface IAudioService { long getMaxAdditionalOutputDeviceDelay(in AudioDeviceAttributes device); - int requestAudioFocusForTest(in AudioAttributes aa, int durationHint, IBinder cb, + int requestAudioFocusForTest(in AudioAttributes aa, int focusReqType, IBinder cb, in IAudioFocusDispatcher fd, in String clientId, in String callingPackageName, int flags, int uid, int sdk); diff --git a/media/java/android/media/tv/tuner/Tuner.java b/media/java/android/media/tv/tuner/Tuner.java index d14275ff2fd6..92f6eaf33c88 100644 --- a/media/java/android/media/tv/tuner/Tuner.java +++ b/media/java/android/media/tv/tuner/Tuner.java @@ -2533,16 +2533,19 @@ public class Tuner implements AutoCloseable { /** * Request a frontend by frontend type. * - * <p> This API is used if the applications want to select a frontend with desired type when - * there are multiple frontends of the same type is there before {@link tune}. The applied - * frontend will be one of the not in-use frontend. If all frontends are in-use, this API will - * reclaim and apply the frontend owned by the lowest priority client if current client has - * higher priority. Otherwise, this API will not apply any frontend and return - * {@link #RESULT_UNAVAILABLE}. + * <p> This API is used (before {@link #tune(FrontendSettings)}) if the applications want to + * select a frontend of a particular type for {@link #tune(FrontendSettings)} when there are + * multiple frontends of the same type present, allowing the system to select which one is + * applied. The applied frontend will be one of the not-in-use frontends. If all frontends are + * in-use, this API will reclaim and apply the frontend owned by the lowest priority client if + * current client has higher priority. Otherwise, this API will not apply any frontend and + * return {@link #RESULT_UNAVAILABLE}. * * @param desiredFrontendType the Type of the desired fronted. Should be one of * {@link android.media.tv.tuner.frontend.FrontendSettings.Type} * @return result status of open operation. + * @see #applyFrontend(FrontendInfo) + * @see #tune(FrontendSettings) */ @Result @FlaggedApi(Flags.FLAG_TUNER_W_APIS) diff --git a/nfc/api/current.txt b/nfc/api/current.txt index 447e980adaee..5b6b6c0b192e 100644 --- a/nfc/api/current.txt +++ b/nfc/api/current.txt @@ -220,14 +220,15 @@ package android.nfc.cardemulation { field @Deprecated public static final String ACTION_CHANGE_DEFAULT = "android.nfc.cardemulation.action.ACTION_CHANGE_DEFAULT"; field public static final String CATEGORY_OTHER = "other"; field public static final String CATEGORY_PAYMENT = "payment"; - field @FlaggedApi("android.nfc.nfc_override_recover_routing_table") public static final String DH = "DH"; - field @FlaggedApi("android.nfc.nfc_override_recover_routing_table") public static final String ESE = "ESE"; field public static final String EXTRA_CATEGORY = "category"; field public static final String EXTRA_SERVICE_COMPONENT = "component"; + field @FlaggedApi("android.nfc.nfc_override_recover_routing_table") public static final int PROTOCOL_AND_TECHNOLOGY_ROUTE_DH = 0; // 0x0 + field @FlaggedApi("android.nfc.nfc_override_recover_routing_table") public static final int PROTOCOL_AND_TECHNOLOGY_ROUTE_ESE = 1; // 0x1 + field @FlaggedApi("android.nfc.nfc_override_recover_routing_table") public static final int PROTOCOL_AND_TECHNOLOGY_ROUTE_UICC = 2; // 0x2 + field @FlaggedApi("android.nfc.nfc_override_recover_routing_table") public static final int PROTOCOL_AND_TECHNOLOGY_ROUTE_UNSET = -1; // 0xffffffff field public static final int SELECTION_MODE_ALWAYS_ASK = 1; // 0x1 field public static final int SELECTION_MODE_ASK_IF_CONFLICT = 2; // 0x2 field public static final int SELECTION_MODE_PREFER_DEFAULT = 0; // 0x0 - field @FlaggedApi("android.nfc.nfc_override_recover_routing_table") public static final String UICC = "UICC"; } public abstract class HostApduService extends android.app.Service { diff --git a/nfc/api/system-current.txt b/nfc/api/system-current.txt index 25a01b9c1b8d..717e01e18dbd 100644 --- a/nfc/api/system-current.txt +++ b/nfc/api/system-current.txt @@ -94,7 +94,7 @@ package android.nfc.cardemulation { public final class CardEmulation { method @FlaggedApi("android.permission.flags.wallet_role_enabled") @Nullable @RequiresPermission(android.Manifest.permission.NFC_PREFERRED_PAYMENT_INFO) public static android.content.ComponentName getPreferredPaymentService(@NonNull android.content.Context); method @FlaggedApi("android.nfc.enable_nfc_mainline") @NonNull public java.util.List<android.nfc.cardemulation.ApduServiceInfo> getServices(@NonNull String, int); - method @FlaggedApi("android.nfc.nfc_override_recover_routing_table") public void overrideRoutingTable(@NonNull android.app.Activity, @Nullable String, @Nullable String); + method @FlaggedApi("android.nfc.nfc_override_recover_routing_table") public void overrideRoutingTable(@NonNull android.app.Activity, int, int); method @FlaggedApi("android.nfc.nfc_override_recover_routing_table") public void recoverRoutingTable(@NonNull android.app.Activity); } diff --git a/nfc/java/android/nfc/cardemulation/CardEmulation.java b/nfc/java/android/nfc/cardemulation/CardEmulation.java index 0605dbe130d3..497309c12d8a 100644 --- a/nfc/java/android/nfc/cardemulation/CardEmulation.java +++ b/nfc/java/android/nfc/cardemulation/CardEmulation.java @@ -18,12 +18,12 @@ package android.nfc.cardemulation; import android.Manifest; import android.annotation.FlaggedApi; +import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; -import android.annotation.StringDef; import android.annotation.SystemApi; import android.annotation.UserHandleAware; import android.annotation.UserIdInt; @@ -155,17 +155,23 @@ public final class CardEmulation { * Route to Device Host (DH). */ @FlaggedApi(Flags.FLAG_NFC_OVERRIDE_RECOVER_ROUTING_TABLE) - public static final String DH = "DH"; + public static final int PROTOCOL_AND_TECHNOLOGY_ROUTE_DH = 0; /** * Route to eSE. */ @FlaggedApi(Flags.FLAG_NFC_OVERRIDE_RECOVER_ROUTING_TABLE) - public static final String ESE = "ESE"; + public static final int PROTOCOL_AND_TECHNOLOGY_ROUTE_ESE = 1; /** * Route to UICC. */ @FlaggedApi(Flags.FLAG_NFC_OVERRIDE_RECOVER_ROUTING_TABLE) - public static final String UICC = "UICC"; + public static final int PROTOCOL_AND_TECHNOLOGY_ROUTE_UICC = 2; + + /** + * Route unset. + */ + @FlaggedApi(Flags.FLAG_NFC_OVERRIDE_RECOVER_ROUTING_TABLE) + public static final int PROTOCOL_AND_TECHNOLOGY_ROUTE_UNSET = -1; static boolean sIsInitialized = false; static HashMap<Context, CardEmulation> sCardEmus = new HashMap<Context, CardEmulation>(); @@ -734,7 +740,7 @@ public final class CardEmulation { * * @return the preferred payment service description */ - @RequiresPermission(android.Manifest.permission.NFC_PREFERRED_PAYMENT_INFO) + @RequiresPermission(Manifest.permission.NFC_PREFERRED_PAYMENT_INFO) @Nullable public CharSequence getDescriptionForPreferredPaymentService() { ApduServiceInfo serviceInfo = callServiceReturn(() -> @@ -884,10 +890,12 @@ public final class CardEmulation { } /** @hide */ - @StringDef({ - DH, - ESE, - UICC + @IntDef(prefix = "PROTOCOL_AND_TECHNOLOGY_ROUTE_", + value = { + PROTOCOL_AND_TECHNOLOGY_ROUTE_DH, + PROTOCOL_AND_TECHNOLOGY_ROUTE_ESE, + PROTOCOL_AND_TECHNOLOGY_ROUTE_UICC, + PROTOCOL_AND_TECHNOLOGY_ROUTE_UNSET }) @Retention(RetentionPolicy.SOURCE) public @interface ProtocolAndTechnologyRoute {} @@ -896,29 +904,32 @@ public final class CardEmulation { * Setting NFC controller routing table, which includes Protocol Route and Technology Route, * while this Activity is in the foreground. * - * The parameter set to null can be used to keep current values for that entry. Either + * The parameter set to {@link #PROTOCOL_AND_TECHNOLOGY_ROUTE_UNSET} + * can be used to keep current values for that entry. Either * Protocol Route or Technology Route should be override when calling this API, otherwise * throw {@link IllegalArgumentException}. * <p> * Example usage in an Activity that requires to set proto route to "ESE" and keep tech route: * <pre> * protected void onResume() { - * mNfcAdapter.overrideRoutingTable(this , "ESE" , null); + * mNfcAdapter.overrideRoutingTable( + * this, {@link #PROTOCOL_AND_TECHNOLOGY_ROUTE_ESE}, null); * }</pre> * </p> * Also activities must call {@link #recoverRoutingTable(Activity)} * when it goes to the background. Only the package of the * currently preferred service (the service set as preferred by the current foreground * application via {@link CardEmulation#setPreferredService(Activity, ComponentName)} or the - * current Default Wallet Role Holder {@link android.app.role.RoleManager#ROLE_WALLET}), + * current Default Wallet Role Holder {@link RoleManager#ROLE_WALLET}), * otherwise a call to this method will fail and throw {@link SecurityException}. * @param activity The Activity that requests NFC controller routing table to be changed. - * @param protocol ISO-DEP route destination, which can be "DH" or "UICC" or "ESE". - * @param technology Tech-A, Tech-B and Tech-F route destination, which can be "DH" or "UICC" - * or "ESE". + * @param protocol ISO-DEP route destination, where the possible inputs are defined + * in {@link ProtocolAndTechnologyRoute}. + * @param technology Tech-A, Tech-B and Tech-F route destination, where the possible inputs + * are defined in {@link ProtocolAndTechnologyRoute} * @throws SecurityException if the caller is not the preferred NFC service * @throws IllegalArgumentException if the activity is not resumed or the caller is not in the - * foreground, or both protocol route and technology route are null. + * foreground. * <p> * This is a high risk API and only included to support mainline effort * @hide @@ -926,25 +937,36 @@ public final class CardEmulation { @SystemApi @FlaggedApi(Flags.FLAG_NFC_OVERRIDE_RECOVER_ROUTING_TABLE) public void overrideRoutingTable( - @NonNull Activity activity, @ProtocolAndTechnologyRoute @Nullable String protocol, - @ProtocolAndTechnologyRoute @Nullable String technology) { + @NonNull Activity activity, @ProtocolAndTechnologyRoute int protocol, + @ProtocolAndTechnologyRoute int technology) { if (!activity.isResumed()) { throw new IllegalArgumentException("Activity must be resumed."); } - if (protocol == null && technology == null) { - throw new IllegalArgumentException(("Both Protocol and Technology are null.")); - } + String protocolRoute = switch (protocol) { + case PROTOCOL_AND_TECHNOLOGY_ROUTE_DH -> "DH"; + case PROTOCOL_AND_TECHNOLOGY_ROUTE_ESE -> "ESE"; + case PROTOCOL_AND_TECHNOLOGY_ROUTE_UICC -> "UICC"; + case PROTOCOL_AND_TECHNOLOGY_ROUTE_UNSET -> null; + default -> throw new IllegalStateException("Unexpected value: " + protocol); + }; + String technologyRoute = switch (technology) { + case PROTOCOL_AND_TECHNOLOGY_ROUTE_DH -> "DH"; + case PROTOCOL_AND_TECHNOLOGY_ROUTE_ESE -> "ESE"; + case PROTOCOL_AND_TECHNOLOGY_ROUTE_UICC -> "UICC"; + case PROTOCOL_AND_TECHNOLOGY_ROUTE_UNSET -> null; + default -> throw new IllegalStateException("Unexpected value: " + protocol); + }; callService(() -> sService.overrideRoutingTable( mContext.getUser().getIdentifier(), - protocol, - technology, + protocolRoute, + technologyRoute, mContext.getPackageName())); } /** * Restore the NFC controller routing table, - * which was changed by {@link #overrideRoutingTable(Activity, String, String)} + * which was changed by {@link #overrideRoutingTable(Activity, int, int)} * * @param activity The Activity that requested NFC controller routing table to be changed. * @throws IllegalArgumentException if the caller is not in the foreground. diff --git a/packages/CredentialManager/wear/res/values-sv/strings.xml b/packages/CredentialManager/wear/res/values-sv/strings.xml index 2d0254a60465..0d4d12f967d5 100644 --- a/packages/CredentialManager/wear/res/values-sv/strings.xml +++ b/packages/CredentialManager/wear/res/values-sv/strings.xml @@ -23,7 +23,7 @@ <string name="dialog_dismiss_button" msgid="989567669882005067">"Stäng"</string> <string name="dialog_continue_button" msgid="8630290044077052145">"Fortsätt"</string> <string name="dialog_sign_in_options_button" msgid="448002958902615054">"Inloggningsalternativ"</string> - <string name="sign_in_options_title" msgid="6720572645638986680">"Inloggningsalternativ"</string> + <string name="sign_in_options_title" msgid="6720572645638986680">"Inloggningsalternativ"</string> <string name="provider_list_title" msgid="6803918216129492212">"Hantera inloggningar"</string> <string name="choose_sign_in_title" msgid="3616025924746872202">"Välj en inloggning"</string> <string name="choose_passkey_title" msgid="8459270617632817465">"Välj nyckel"</string> diff --git a/packages/PrintSpooler/res/values-zh-rCN/strings.xml b/packages/PrintSpooler/res/values-zh-rCN/strings.xml index 94d835b1b6f6..3c95fd84e6bd 100644 --- a/packages/PrintSpooler/res/values-zh-rCN/strings.xml +++ b/packages/PrintSpooler/res/values-zh-rCN/strings.xml @@ -29,7 +29,7 @@ <string name="label_pages" msgid="7768589729282182230">"页数"</string> <string name="destination_default_text" msgid="5422708056807065710">"选择打印机"</string> <string name="template_all_pages" msgid="3322235982020148762">"全部<xliff:g id="PAGE_COUNT">%1$s</xliff:g>页"</string> - <string name="template_page_range" msgid="428638530038286328">"范围 <xliff:g id="PAGE_COUNT">%1$s</xliff:g>页"</string> + <string name="template_page_range" msgid="428638530038286328">"<xliff:g id="PAGE_COUNT">%1$s</xliff:g>页"</string> <string name="pages_range_example" msgid="8558694453556945172">"例如:1-5、8、11-13"</string> <string name="print_preview" msgid="8010217796057763343">"打印预览"</string> <string name="install_for_print_preview" msgid="6366303997385509332">"安装 PDF 查看器以便预览"</string> diff --git a/packages/PrintSpooler/res/values-zh-rTW/strings.xml b/packages/PrintSpooler/res/values-zh-rTW/strings.xml index 5ca45794fc50..10932bee6446 100644 --- a/packages/PrintSpooler/res/values-zh-rTW/strings.xml +++ b/packages/PrintSpooler/res/values-zh-rTW/strings.xml @@ -29,7 +29,7 @@ <string name="label_pages" msgid="7768589729282182230">"頁面"</string> <string name="destination_default_text" msgid="5422708056807065710">"選取印表機"</string> <string name="template_all_pages" msgid="3322235982020148762">"全部 <xliff:g id="PAGE_COUNT">%1$s</xliff:g> 頁"</string> - <string name="template_page_range" msgid="428638530038286328">"範圍是 <xliff:g id="PAGE_COUNT">%1$s</xliff:g> 頁"</string> + <string name="template_page_range" msgid="428638530038286328">"<xliff:g id="PAGE_COUNT">%1$s</xliff:g> 頁"</string> <string name="pages_range_example" msgid="8558694453556945172">"例如:1—5,8,11—13"</string> <string name="print_preview" msgid="8010217796057763343">"列印預覽"</string> <string name="install_for_print_preview" msgid="6366303997385509332">"安裝預覽所需的 PDF 檢視器"</string> diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/half_rounded_left_bk.xml b/packages/SettingsLib/ActionButtonsPreference/res/drawable/half_rounded_left_bk.xml index 8a25726ecd17..0d4ef3c52e4c 100644 --- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/half_rounded_left_bk.xml +++ b/packages/SettingsLib/ActionButtonsPreference/res/drawable/half_rounded_left_bk.xml @@ -20,7 +20,7 @@ xmlns:tools="http://schemas.android.com/tools" tools:targetApi="28" android:shape="rectangle"> - <solid android:color="?androidprv:attr/colorSurface" /> + <solid android:color="?androidprv:attr/materialColorSurfaceContainerHigh" /> <corners android:topLeftRadius="?android:attr/dialogCornerRadius" android:topRightRadius="0dp" diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/half_rounded_right_bk.xml b/packages/SettingsLib/ActionButtonsPreference/res/drawable/half_rounded_right_bk.xml index 7e626e50255a..307277264ff6 100644 --- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/half_rounded_right_bk.xml +++ b/packages/SettingsLib/ActionButtonsPreference/res/drawable/half_rounded_right_bk.xml @@ -20,7 +20,7 @@ xmlns:tools="http://schemas.android.com/tools" tools:targetApi="28" android:shape="rectangle"> - <solid android:color="?androidprv:attr/colorSurface" /> + <solid android:color="?androidprv:attr/materialColorSurfaceContainerHigh" /> <corners android:topLeftRadius="0dp" android:topRightRadius="?android:attr/dialogCornerRadius" diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/rounded_bk.xml b/packages/SettingsLib/ActionButtonsPreference/res/drawable/rounded_bk.xml index 9f4980beb76f..f1790f9ba351 100644 --- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/rounded_bk.xml +++ b/packages/SettingsLib/ActionButtonsPreference/res/drawable/rounded_bk.xml @@ -20,7 +20,7 @@ xmlns:tools="http://schemas.android.com/tools" tools:targetApi="28" android:shape="rectangle"> - <solid android:color="?androidprv:attr/colorSurface" /> + <solid android:color="?androidprv:attr/materialColorSurfaceContainerHigh" /> <corners android:radius="?android:attr/dialogCornerRadius" /> diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/square_bk.xml b/packages/SettingsLib/ActionButtonsPreference/res/drawable/square_bk.xml index 67b5107576a7..f0da7b4f457f 100644 --- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/square_bk.xml +++ b/packages/SettingsLib/ActionButtonsPreference/res/drawable/square_bk.xml @@ -18,7 +18,7 @@ <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="?androidprv:attr/colorSurface" /> + <solid android:color="?androidprv:attr/materialColorSurfaceContainerHigh" /> <corners android:radius="0dp" /> diff --git a/packages/SettingsLib/res/drawable/audio_sharing_rounded_bg.xml b/packages/SettingsLib/res/drawable/audio_sharing_rounded_bg.xml new file mode 100644 index 000000000000..35517ea0ec11 --- /dev/null +++ b/packages/SettingsLib/res/drawable/audio_sharing_rounded_bg.xml @@ -0,0 +1,24 @@ +<?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. + --> + +<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="?androidprv:attr/colorAccentPrimary" /> + <corners android:radius="12dp" /> +</shape>
\ No newline at end of file diff --git a/packages/SettingsLib/res/drawable/audio_sharing_rounded_bg_ripple.xml b/packages/SettingsLib/res/drawable/audio_sharing_rounded_bg_ripple.xml new file mode 100644 index 000000000000..18696c627ec6 --- /dev/null +++ b/packages/SettingsLib/res/drawable/audio_sharing_rounded_bg_ripple.xml @@ -0,0 +1,21 @@ +<?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. + --> + +<ripple xmlns:android="http://schemas.android.com/apk/res/android" + android:color="?android:attr/colorControlHighlight"> + <item android:drawable="@drawable/audio_sharing_rounded_bg"/> +</ripple>
\ No newline at end of file diff --git a/packages/SettingsLib/res/values-es/arrays.xml b/packages/SettingsLib/res/values-es/arrays.xml index 1489e5f41ec5..b99219cadbe9 100644 --- a/packages/SettingsLib/res/values-es/arrays.xml +++ b/packages/SettingsLib/res/values-es/arrays.xml @@ -243,7 +243,7 @@ <item msgid="8612549335720461635">"4K (seguro)"</item> <item msgid="7322156123728520872">"4K (mejorado)"</item> <item msgid="7735692090314849188">"4K (mejorado, seguro)"</item> - <item msgid="7346816300608639624">"720p, 1080p (pantalla doble)"</item> + <item msgid="7346816300608639624">"720p, 1080p (pantalla dual)"</item> </string-array> <string-array name="enable_opengl_traces_entries"> <item msgid="4433736508877934305">"Desactivado"</item> diff --git a/packages/SettingsLib/res/values-eu/arrays.xml b/packages/SettingsLib/res/values-eu/arrays.xml index 00f43a3bf51b..6ed484c40e57 100644 --- a/packages/SettingsLib/res/values-eu/arrays.xml +++ b/packages/SettingsLib/res/values-eu/arrays.xml @@ -27,7 +27,7 @@ <item msgid="8356618438494652335">"Autentifikatzen…"</item> <item msgid="2837871868181677206">"IP helbidea lortzen…"</item> <item msgid="4613015005934755724">"Konektatuta"</item> - <item msgid="3763530049995655072">"Etenda"</item> + <item msgid="3763530049995655072">"Aldi baterako etenda"</item> <item msgid="7852381437933824454">"Deskonektatzen…"</item> <item msgid="5046795712175415059">"Deskonektatuta"</item> <item msgid="2473654476624070462">"Ezin izan da konektatu"</item> @@ -41,7 +41,7 @@ <item msgid="3028983857109369308">"<xliff:g id="NETWORK_NAME">%1$s</xliff:g> sarearekin autentifikatzen…"</item> <item msgid="4287401332778341890">"<xliff:g id="NETWORK_NAME">%1$s</xliff:g> sarearen IP helbidea lortzen…"</item> <item msgid="1043944043827424501">"<xliff:g id="NETWORK_NAME">%1$s</xliff:g> sarera konektatuta"</item> - <item msgid="7445993821842009653">"Etenda"</item> + <item msgid="7445993821842009653">"Aldi baterako etenda"</item> <item msgid="1175040558087735707">"<xliff:g id="NETWORK_NAME">%1$s</xliff:g> saretik deskonektatzen…"</item> <item msgid="699832486578171722">"Deskonektatuta"</item> <item msgid="522383512264986901">"Ezin izan da konektatu"</item> diff --git a/packages/SettingsLib/res/values-fa/arrays.xml b/packages/SettingsLib/res/values-fa/arrays.xml index 5834982ba39c..d1505386ec02 100644 --- a/packages/SettingsLib/res/values-fa/arrays.xml +++ b/packages/SettingsLib/res/values-fa/arrays.xml @@ -243,7 +243,7 @@ <item msgid="8612549335720461635">"4K (ایمن)"</item> <item msgid="7322156123728520872">"4K (ارتقا یافته)"</item> <item msgid="7735692090314849188">"4K (ارتقا یافته، ایمن)"</item> - <item msgid="7346816300608639624">"720p، 1080p (Dual Screen)"</item> + <item msgid="7346816300608639624">"720p، 1080p (صفحهنمایش دوگانه)"</item> </string-array> <string-array name="enable_opengl_traces_entries"> <item msgid="4433736508877934305">"خالی"</item> diff --git a/packages/SettingsLib/res/values-in/strings.xml b/packages/SettingsLib/res/values-in/strings.xml index 042504aa64a7..2297376da636 100644 --- a/packages/SettingsLib/res/values-in/strings.xml +++ b/packages/SettingsLib/res/values-in/strings.xml @@ -81,7 +81,7 @@ <string name="speed_label_fast" msgid="2677719134596044051">"Cepat"</string> <string name="speed_label_very_fast" msgid="8215718029533182439">"Sangat Cepat"</string> <string name="wifi_passpoint_expired" msgid="6540867261754427561">"Sudah tidak berlaku"</string> - <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="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">"Sambungan terputus"</string> <string name="bluetooth_disconnecting" msgid="7638892134401574338">"Memutus sambungan..."</string> <string name="bluetooth_connecting" msgid="5871702668260192755">"Menghubungkan…"</string> diff --git a/packages/SettingsLib/res/values-sv/strings.xml b/packages/SettingsLib/res/values-sv/strings.xml index a6bb5b804556..2404dc27ba90 100644 --- a/packages/SettingsLib/res/values-sv/strings.xml +++ b/packages/SettingsLib/res/values-sv/strings.xml @@ -235,7 +235,7 @@ <item msgid="6946761421234586000">"400 %"</item> </string-array> <string name="choose_profile" msgid="343803890897657450">"Välj profil"</string> - <string name="category_personal" msgid="6236798763159385225">"Personlig"</string> + <string name="category_personal" msgid="6236798763159385225">"Privat"</string> <string name="category_work" msgid="4014193632325996115">"Jobb"</string> <string name="category_private" msgid="4244892185452788977">"Privat"</string> <string name="category_clone" msgid="1554511758987195974">"Klon"</string> diff --git a/packages/SettingsLib/res/values-zh-rCN/strings.xml b/packages/SettingsLib/res/values-zh-rCN/strings.xml index e59b427d7490..88e67f684d52 100644 --- a/packages/SettingsLib/res/values-zh-rCN/strings.xml +++ b/packages/SettingsLib/res/values-zh-rCN/strings.xml @@ -81,7 +81,7 @@ <string name="speed_label_fast" msgid="2677719134596044051">"快"</string> <string name="speed_label_very_fast" msgid="8215718029533182439">"很快"</string> <string name="wifi_passpoint_expired" msgid="6540867261754427561">"已失效"</string> - <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="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> diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java index 055afedd9422..0ffb76307b3c 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java @@ -1,5 +1,6 @@ package com.android.settingslib.bluetooth; +import static com.android.settingslib.bluetooth.LocalBluetoothLeBroadcast.UNKNOWN_VALUE_PLACEHOLDER; import static com.android.settingslib.widget.AdaptiveOutlineDrawable.ICON_TYPE_ADVANCED; import android.annotation.SuppressLint; @@ -704,12 +705,50 @@ public class BluetoothUtils { return !sourceList.isEmpty() && sourceList.stream().anyMatch(BluetoothUtils::isConnected); } + /** + * Check if {@link BluetoothDevice} has a active local broadcast source. + * + * @param device The bluetooth device to check. + * @param localBtManager The BT manager to provide BT functions. + * @return Whether the device has a active local broadcast source. + */ + @WorkerThread + public static boolean hasActiveLocalBroadcastSourceForBtDevice( + @Nullable BluetoothDevice device, @Nullable LocalBluetoothManager localBtManager) { + LocalBluetoothLeBroadcastAssistant assistant = + localBtManager == null + ? null + : localBtManager.getProfileManager().getLeAudioBroadcastAssistantProfile(); + LocalBluetoothLeBroadcast broadcast = + localBtManager == null + ? null + : localBtManager.getProfileManager().getLeAudioBroadcastProfile(); + if (device == null || assistant == null || broadcast == null) { + Log.d(TAG, "Skip check hasActiveLocalBroadcastSourceForBtDevice due to arg is null"); + return false; + } + List<BluetoothLeBroadcastReceiveState> sourceList = assistant.getAllSources(device); + int broadcastId = broadcast.getLatestBroadcastId(); + return !sourceList.isEmpty() + && broadcastId != UNKNOWN_VALUE_PLACEHOLDER + && sourceList.stream() + .anyMatch( + source -> isSourceMatched(source, broadcastId)); + } + /** Checks the connectivity status based on the provided broadcast receive state. */ @WorkerThread public static boolean isConnected(BluetoothLeBroadcastReceiveState state) { return state.getBisSyncState().stream().anyMatch(bitmap -> bitmap != 0); } + /** Checks if the broadcast id is matched based on the provided broadcast receive state. */ + @WorkerThread + public static boolean isSourceMatched( + @Nullable BluetoothLeBroadcastReceiveState state, int broadcastId) { + return state != null && state.getBroadcastId() == broadcastId; + } + /** * Checks if the Bluetooth device is an available hearing device, which means: 1) currently * connected 2) is Hearing Aid 3) connected profile match hearing aid related profiles (e.g. diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcast.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcast.java index 26905b1d86d2..6f2567b9c5dc 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcast.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcast.java @@ -101,7 +101,7 @@ public class LocalBluetoothLeBroadcast implements LocalBluetoothProfile { private static final int DEFAULT_CODE_MIN = 1000; // Order of this profile in device profiles list private static final int ORDINAL = 1; - private static final int UNKNOWN_VALUE_PLACEHOLDER = -1; + static final int UNKNOWN_VALUE_PLACEHOLDER = -1; private static final Uri[] SETTINGS_URIS = new Uri[] { Settings.Secure.getUriFor(Settings.Secure.BLUETOOTH_LE_BROADCAST_NAME), diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcastAssistantCallbackExt.kt b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcastAssistantCallbackExt.kt index 91a99aed6db5..24815fabfdf2 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcastAssistantCallbackExt.kt +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcastAssistantCallbackExt.kt @@ -16,6 +16,7 @@ package com.android.settingslib.bluetooth +import android.bluetooth.BluetoothAdapter import android.bluetooth.BluetoothDevice import android.bluetooth.BluetoothLeBroadcastAssistant import android.bluetooth.BluetoothLeBroadcastMetadata @@ -81,5 +82,9 @@ val LocalBluetoothLeBroadcastAssistant.onSourceConnectedOrRemoved: Flow<Unit> ConcurrentUtils.DIRECT_EXECUTOR, callback, ) - awaitClose { unregisterServiceCallBack(callback) } + awaitClose { + if (BluetoothAdapter.getDefaultAdapter()?.isEnabled == true) { + unregisterServiceCallBack(callback) + } + } } diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/DeviceSettingFooterPreference.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/DeviceSettingFooterPreference.java index 2099b33b78b5..e84a5d2874ec 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/DeviceSettingFooterPreference.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/DeviceSettingFooterPreference.java @@ -31,7 +31,7 @@ public class DeviceSettingFooterPreference extends DeviceSettingPreference imple DeviceSettingFooterPreference( @NonNull String footerText, Bundle extras) { - super(DeviceSettingType.DEVICE_SETTING_TYPE_MULTI_TOGGLE); + super(DeviceSettingType.DEVICE_SETTING_TYPE_FOOTER); mFooterText = footerText; mExtras = extras; } diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/DeviceSettingHelpPreference.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/DeviceSettingHelpPreference.java new file mode 100644 index 000000000000..953e7cb0fc5d --- /dev/null +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/DeviceSettingHelpPreference.java @@ -0,0 +1,132 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settingslib.bluetooth.devicesettings; + +import android.content.Intent; +import android.os.Bundle; +import android.os.Parcel; +import android.os.Parcelable; + +import androidx.annotation.NonNull; + +/** A data class representing a help button displayed on the top right corner of the page. */ +public class DeviceSettingHelpPreference extends DeviceSettingPreference implements Parcelable { + + private final Intent mIntent; + private final Bundle mExtras; + + DeviceSettingHelpPreference(@NonNull Intent intent, Bundle extras) { + super(DeviceSettingType.DEVICE_SETTING_TYPE_HELP); + mIntent = intent; + mExtras = extras; + } + + /** Read a {@link DeviceSettingHelpPreference} from {@link Parcel}. */ + @NonNull + public static DeviceSettingHelpPreference readFromParcel(@NonNull Parcel in) { + Intent intent = in.readParcelable(Intent.class.getClassLoader()); + Bundle extras = in.readBundle(Bundle.class.getClassLoader()); + return new DeviceSettingHelpPreference(intent, extras); + } + + public static final Creator<DeviceSettingHelpPreference> CREATOR = + new Creator<>() { + @Override + @NonNull + public DeviceSettingHelpPreference createFromParcel(@NonNull Parcel in) { + in.readInt(); + return readFromParcel(in); + } + + @Override + @NonNull + public DeviceSettingHelpPreference[] newArray(int size) { + return new DeviceSettingHelpPreference[size]; + } + }; + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + super.writeToParcel(dest, flags); + dest.writeParcelable(mIntent, flags); + dest.writeBundle(mExtras); + } + + /** Builder class for {@link DeviceSettingHelpPreference}. */ + public static final class Builder { + private Intent mIntent; + private Bundle mExtras = Bundle.EMPTY; + + /** + * Sets the intent of the preference, should be an activity intent. + * + * @param intent The intent to launch when clicked. + * @return Returns the Builder object. + */ + @NonNull + public DeviceSettingHelpPreference.Builder setIntent(@NonNull Intent intent) { + mIntent = intent; + return this; + } + + /** + * Sets the extras bundle. + * + * @return Returns the Builder object. + */ + @NonNull + public DeviceSettingHelpPreference.Builder setExtras(@NonNull Bundle extras) { + mExtras = extras; + return this; + } + + /** + * Builds the {@link DeviceSettingHelpPreference} object. + * + * @return Returns the built {@link DeviceSettingHelpPreference} object. + */ + @NonNull + public DeviceSettingHelpPreference build() { + return new DeviceSettingHelpPreference(mIntent, mExtras); + } + } + + /** + * Gets the intent to launch when clicked. + * + * @return The intent. + */ + @NonNull + public Intent getIntent() { + return mIntent; + } + + /** + * Gets the extras Bundle. + * + * @return Returns a Bundle object. + */ + @NonNull + public Bundle getExtras() { + return mExtras; + } +} diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/DeviceSettingType.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/DeviceSettingType.java index 441e3f86708d..ae3bf5e3fc72 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/DeviceSettingType.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/DeviceSettingType.java @@ -42,4 +42,7 @@ public @interface DeviceSettingType { /** Device setting type is footer preference. */ int DEVICE_SETTING_TYPE_FOOTER = 3; + + /** Device setting type is "help" preference. */ + int DEVICE_SETTING_TYPE_HELP = 4; } diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/DeviceSettingsConfig.kt b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/DeviceSettingsConfig.kt index 127275fe28d2..5656f38a0a11 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/DeviceSettingsConfig.kt +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/DeviceSettingsConfig.kt @@ -25,12 +25,13 @@ import android.os.Parcelable * * @property mainContentItems The setting items to be shown in main page. * @property moreSettingsItems The setting items to be shown in more settings page. - * @property moreSettingsFooter The footer in more settings page. + * @property moreSettingsHelpItem The help item displayed on the top right corner of the page. * @property extras Extra bundle */ data class DeviceSettingsConfig( val mainContentItems: List<DeviceSettingItem>, val moreSettingsItems: List<DeviceSettingItem>, + val moreSettingsHelpItem: DeviceSettingItem?, val extras: Bundle = Bundle.EMPTY, ) : Parcelable { @@ -40,6 +41,7 @@ data class DeviceSettingsConfig( parcel.run { writeTypedList(mainContentItems) writeTypedList(moreSettingsItems) + writeParcelable(moreSettingsHelpItem, flags) writeBundle(extras) } } @@ -59,7 +61,9 @@ data class DeviceSettingsConfig( arrayListOf<DeviceSettingItem>().also { readTypedList(it, DeviceSettingItem.CREATOR) }, - extras = readBundle((Bundle::class.java.classLoader))!!, + moreSettingsHelpItem = readParcelable( + DeviceSettingItem::class.java.classLoader + ) ) } diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/data/repository/DeviceSettingRepository.kt b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/data/repository/DeviceSettingRepository.kt index cded014feda1..457d6a3a714d 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/data/repository/DeviceSettingRepository.kt +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/data/repository/DeviceSettingRepository.kt @@ -26,6 +26,7 @@ import com.android.settingslib.bluetooth.devicesettings.DeviceSettingId import com.android.settingslib.bluetooth.devicesettings.DeviceSettingItem import com.android.settingslib.bluetooth.devicesettings.DeviceSettingsConfig import com.android.settingslib.bluetooth.devicesettings.DeviceSettingFooterPreference +import com.android.settingslib.bluetooth.devicesettings.DeviceSettingHelpPreference import com.android.settingslib.bluetooth.devicesettings.MultiTogglePreference import com.android.settingslib.bluetooth.devicesettings.ToggleInfo import com.android.settingslib.bluetooth.devicesettings.shared.model.DeviceSettingConfigItemModel @@ -97,7 +98,8 @@ class DeviceSettingRepositoryImpl( private fun DeviceSettingsConfig.toModel(): DeviceSettingConfigModel = DeviceSettingConfigModel( mainItems = mainContentItems.map { it.toModel() }, - moreSettingsItems = moreSettingsItems.map { it.toModel() }) + moreSettingsItems = moreSettingsItems.map { it.toModel() }, + moreSettingsHelpItem = moreSettingsHelpItem?.toModel(), ) private fun DeviceSettingItem.toModel(): DeviceSettingConfigItemModel { return if (!TextUtils.isEmpty(preferenceKey)) { @@ -154,6 +156,9 @@ class DeviceSettingRepositoryImpl( is DeviceSettingFooterPreference -> DeviceSettingModel.FooterPreference( cachedDevice = cachedDevice, id = settingId, footerText = pref.footerText) + is DeviceSettingHelpPreference -> DeviceSettingModel.HelpPreference( + cachedDevice = cachedDevice, + id = settingId, intent = pref.intent) else -> DeviceSettingModel.Unknown(cachedDevice, settingId) } diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/shared/model/DeviceSettingConfigModel.kt b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/shared/model/DeviceSettingConfigModel.kt index 406246246e7e..c1ac763929cd 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/shared/model/DeviceSettingConfigModel.kt +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/shared/model/DeviceSettingConfigModel.kt @@ -24,6 +24,11 @@ data class DeviceSettingConfigModel( val mainItems: List<DeviceSettingConfigItemModel>, /** Items need to be shown in device details more settings page. */ val moreSettingsItems: List<DeviceSettingConfigItemModel>, + /** + * Help button which need to be shown on the top right corner of device details more settings + * page. + */ + val moreSettingsHelpItem: DeviceSettingConfigItemModel?, ) /** Models a device setting item in config. */ diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/shared/model/DeviceSettingModel.kt b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/shared/model/DeviceSettingModel.kt index 5fd4d06872c8..73648acd801e 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/shared/model/DeviceSettingModel.kt +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/shared/model/DeviceSettingModel.kt @@ -59,6 +59,13 @@ sealed interface DeviceSettingModel { val footerText: String, ) : DeviceSettingModel + /** Models a help preference displayed on the top right corner of the fragment. */ + data class HelpPreference( + override val cachedDevice: CachedBluetoothDevice, + @DeviceSettingId override val id: Int, + val intent: Intent, + ) : DeviceSettingModel + /** Models an unknown preference. */ data class Unknown( override val cachedDevice: CachedBluetoothDevice, diff --git a/packages/SettingsLib/src/com/android/settingslib/fuelgauge/PowerAllowlistBackend.java b/packages/SettingsLib/src/com/android/settingslib/fuelgauge/PowerAllowlistBackend.java index 4f2329bb75c2..47a08eb9a72b 100644 --- a/packages/SettingsLib/src/com/android/settingslib/fuelgauge/PowerAllowlistBackend.java +++ b/packages/SettingsLib/src/com/android/settingslib/fuelgauge/PowerAllowlistBackend.java @@ -136,12 +136,10 @@ public class PowerAllowlistBackend { return true; } - if (android.app.admin.flags.Flags.disallowUserControlBgUsageFix()) { - // App is subject to DevicePolicyManager.setUserControlDisabledPackages() policy. - final int userId = UserHandle.getUserId(uid); - if (mAppContext.getPackageManager().isPackageStateProtected(pkg, userId)) { - return true; - } + // App is subject to DevicePolicyManager.setUserControlDisabledPackages() policy. + final int userId = UserHandle.getUserId(uid); + if (mAppContext.getPackageManager().isPackageStateProtected(pkg, userId)) { + return true; } return false; diff --git a/packages/SettingsLib/src/com/android/settingslib/media/data/repository/AudioManagerVolumeControllerExt.kt b/packages/SettingsLib/src/com/android/settingslib/media/data/repository/AudioManagerVolumeControllerExt.kt deleted file mode 100644 index 02d684d7d0ce..000000000000 --- a/packages/SettingsLib/src/com/android/settingslib/media/data/repository/AudioManagerVolumeControllerExt.kt +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Copyright (C) 2024 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.settingslib.media.data.repository - -import android.media.AudioManager -import android.media.IVolumeController -import kotlinx.coroutines.channels.awaitClose -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.buffer -import kotlinx.coroutines.flow.callbackFlow -import kotlinx.coroutines.launch - -/** Returns [AudioManager.setVolumeController] events as a [Flow] */ -fun AudioManager.volumeControllerEvents(): Flow<VolumeControllerEvent> = - callbackFlow { - volumeController = - object : IVolumeController.Stub() { - override fun displaySafeVolumeWarning(flags: Int) { - launch { send(VolumeControllerEvent.DisplaySafeVolumeWarning(flags)) } - } - - override fun volumeChanged(streamType: Int, flags: Int) { - launch { send(VolumeControllerEvent.VolumeChanged(streamType, flags)) } - } - - override fun masterMuteChanged(flags: Int) { - launch { send(VolumeControllerEvent.MasterMuteChanged(flags)) } - } - - override fun setLayoutDirection(layoutDirection: Int) { - launch { send(VolumeControllerEvent.SetLayoutDirection(layoutDirection)) } - } - - override fun dismiss() { - launch { send(VolumeControllerEvent.Dismiss) } - } - - override fun setA11yMode(mode: Int) { - launch { send(VolumeControllerEvent.SetA11yMode(mode)) } - } - - override fun displayCsdWarning( - csdWarning: Int, - displayDurationMs: Int, - ) { - launch { - send( - VolumeControllerEvent.DisplayCsdWarning( - csdWarning, - displayDurationMs, - ) - ) - } - } - } - awaitClose { volumeController = null } - } - .buffer() - -/** Models events received via [IVolumeController] */ -sealed interface VolumeControllerEvent { - - /** @see [IVolumeController.displaySafeVolumeWarning] */ - data class DisplaySafeVolumeWarning(val flags: Int) : VolumeControllerEvent - - /** @see [IVolumeController.volumeChanged] */ - data class VolumeChanged(val streamType: Int, val flags: Int) : VolumeControllerEvent - - /** @see [IVolumeController.masterMuteChanged] */ - data class MasterMuteChanged(val flags: Int) : VolumeControllerEvent - - /** @see [IVolumeController.setLayoutDirection] */ - data class SetLayoutDirection(val layoutDirection: Int) : VolumeControllerEvent - - /** @see [IVolumeController.setA11yMode] */ - data class SetA11yMode(val mode: Int) : VolumeControllerEvent - - /** @see [IVolumeController.displayCsdWarning] */ - data class DisplayCsdWarning( - val csdWarning: Int, - val displayDurationMs: Int, - ) : VolumeControllerEvent - - /** @see [IVolumeController.dismiss] */ - data object Dismiss : VolumeControllerEvent -}
\ No newline at end of file diff --git a/packages/SettingsLib/src/com/android/settingslib/notification/modes/ZenIconLoader.java b/packages/SettingsLib/src/com/android/settingslib/notification/modes/ZenIconLoader.java index fe0f98af1186..43c6c5050850 100644 --- a/packages/SettingsLib/src/com/android/settingslib/notification/modes/ZenIconLoader.java +++ b/packages/SettingsLib/src/com/android/settingslib/notification/modes/ZenIconLoader.java @@ -31,7 +31,6 @@ import android.util.LruCache; import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import androidx.annotation.VisibleForTesting; import com.google.common.util.concurrent.FluentFuture; import com.google.common.util.concurrent.ListenableFuture; @@ -53,25 +52,22 @@ public class ZenIconLoader { private final LruCache<ZenIcon.Key, Drawable> mCache; private final ListeningExecutorService mBackgroundExecutor; + /** Obtains the singleton {@link ZenIconLoader}. */ public static ZenIconLoader getInstance() { if (sInstance == null) { - sInstance = new ZenIconLoader(); + sInstance = new ZenIconLoader(Executors.newFixedThreadPool(4)); } return sInstance; } - /** Replaces the singleton instance of {@link ZenIconLoader} by the provided one. */ - @VisibleForTesting(otherwise = VisibleForTesting.NONE) - public static void setInstance(@Nullable ZenIconLoader instance) { - sInstance = instance; - } - - private ZenIconLoader() { - this(Executors.newFixedThreadPool(4)); - } - - @VisibleForTesting - public ZenIconLoader(ExecutorService backgroundExecutor) { + /** + * Constructs a ZenIconLoader with the specified {@code backgroundExecutor}. + * + * <p>ZenIconLoader <em>should be a singleton</em>, so this should only be used to instantiate + * and provide the singleton instance in a module. If the app doesn't support dependency + * injection, use {@link #getInstance} instead. + */ + public ZenIconLoader(@NonNull ExecutorService backgroundExecutor) { mCache = new LruCache<>(50); mBackgroundExecutor = MoreExecutors.listeningDecorator(backgroundExecutor); diff --git a/packages/SettingsLib/src/com/android/settingslib/volume/data/model/VolumeControllerEvent.kt b/packages/SettingsLib/src/com/android/settingslib/volume/data/model/VolumeControllerEvent.kt new file mode 100644 index 000000000000..0fe385b3d02a --- /dev/null +++ b/packages/SettingsLib/src/com/android/settingslib/volume/data/model/VolumeControllerEvent.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.settingslib.volume.data.model + +import android.media.IVolumeController + +/** Models events received via [IVolumeController] */ +sealed interface VolumeControllerEvent { + + /** @see [IVolumeController.displaySafeVolumeWarning] */ + data class DisplaySafeVolumeWarning(val flags: Int) : VolumeControllerEvent + + /** @see [IVolumeController.volumeChanged] */ + data class VolumeChanged(val streamType: Int, val flags: Int) : VolumeControllerEvent + + /** @see [IVolumeController.masterMuteChanged] */ + data class MasterMuteChanged(val flags: Int) : VolumeControllerEvent + + /** @see [IVolumeController.setLayoutDirection] */ + data class SetLayoutDirection(val layoutDirection: Int) : VolumeControllerEvent + + /** @see [IVolumeController.setA11yMode] */ + data class SetA11yMode(val mode: Int) : VolumeControllerEvent + + /** @see [IVolumeController.displayCsdWarning] */ + data class DisplayCsdWarning( + val csdWarning: Int, + val displayDurationMs: Int, + ) : VolumeControllerEvent + + /** @see [IVolumeController.dismiss] */ + data object Dismiss : VolumeControllerEvent +} 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 0e71116db6cc..3e2d8328f21e 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 @@ -22,9 +22,12 @@ import android.media.AudioDeviceInfo import android.media.AudioManager import android.media.AudioManager.AudioDeviceCategory import android.media.AudioManager.OnCommunicationDeviceChangedListener +import android.media.IVolumeController import android.provider.Settings +import android.util.Log import androidx.concurrent.futures.DirectExecutor import com.android.internal.util.ConcurrentUtils +import com.android.settingslib.volume.data.model.VolumeControllerEvent import com.android.settingslib.volume.shared.AudioLogger import com.android.settingslib.volume.shared.AudioManagerEventsReceiver import com.android.settingslib.volume.shared.model.AudioManagerEvent @@ -36,10 +39,13 @@ import kotlin.coroutines.CoroutineContext import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asSharedFlow import kotlinx.coroutines.flow.callbackFlow import kotlinx.coroutines.flow.conflate +import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.filterIsInstance @@ -73,6 +79,11 @@ interface AudioRepository { */ val communicationDevice: StateFlow<AudioDeviceInfo?> + /** Events from [AudioManager.setVolumeController] */ + val volumeControllerEvents: Flow<VolumeControllerEvent> + + fun init() + /** State of the [AudioStream]. */ fun getAudioStream(audioStream: AudioStream): Flow<AudioStreamModel> @@ -90,8 +101,9 @@ interface AudioRepository { suspend fun setRingerMode(audioStream: AudioStream, mode: RingerMode) /** Gets audio device category. */ - @AudioDeviceCategory - suspend fun getBluetoothAudioDeviceCategory(bluetoothAddress: String): Int + @AudioDeviceCategory suspend fun getBluetoothAudioDeviceCategory(bluetoothAddress: String): Int + + suspend fun notifyVolumeControllerVisible(isVisible: Boolean) } class AudioRepositoryImpl( @@ -101,8 +113,10 @@ class AudioRepositoryImpl( private val backgroundCoroutineContext: CoroutineContext, private val coroutineScope: CoroutineScope, private val logger: AudioLogger, + shouldUseVolumeController: Boolean, ) : AudioRepository { + private val volumeController = ProducingVolumeController() private val streamSettingNames: Map<AudioStream, String> = mapOf( AudioStream(AudioManager.STREAM_VOICE_CALL) to Settings.System.VOLUME_VOICE, @@ -116,12 +130,19 @@ class AudioRepositoryImpl( AudioStream(AudioManager.STREAM_ASSISTANT) to Settings.System.VOLUME_ASSISTANT, ) + override val volumeControllerEvents: Flow<VolumeControllerEvent> = + if (shouldUseVolumeController) { + volumeController.events + } else { + emptyFlow() + } + override val mode: StateFlow<Int> = callbackFlow { - val listener = AudioManager.OnModeChangedListener { newMode -> trySend(newMode) } - audioManager.addOnModeChangedListener(ConcurrentUtils.DIRECT_EXECUTOR, listener) - awaitClose { audioManager.removeOnModeChangedListener(listener) } - } + val listener = AudioManager.OnModeChangedListener { newMode -> trySend(newMode) } + audioManager.addOnModeChangedListener(ConcurrentUtils.DIRECT_EXECUTOR, listener) + awaitClose { audioManager.removeOnModeChangedListener(listener) } + } .onStart { emit(audioManager.mode) } .flowOn(backgroundCoroutineContext) .stateIn(coroutineScope, SharingStarted.WhileSubscribed(), audioManager.mode) @@ -141,14 +162,14 @@ class AudioRepositoryImpl( override val communicationDevice: StateFlow<AudioDeviceInfo?> get() = callbackFlow { - val listener = OnCommunicationDeviceChangedListener { trySend(Unit) } - audioManager.addOnCommunicationDeviceChangedListener( - ConcurrentUtils.DIRECT_EXECUTOR, - listener, - ) + val listener = OnCommunicationDeviceChangedListener { trySend(Unit) } + audioManager.addOnCommunicationDeviceChangedListener( + ConcurrentUtils.DIRECT_EXECUTOR, + listener, + ) - awaitClose { audioManager.removeOnCommunicationDeviceChangedListener(listener) } - } + awaitClose { audioManager.removeOnCommunicationDeviceChangedListener(listener) } + } .filterNotNull() .map { audioManager.communicationDevice } .onStart { emit(audioManager.communicationDevice) } @@ -159,20 +180,30 @@ class AudioRepositoryImpl( audioManager.communicationDevice, ) + override fun init() { + try { + audioManager.volumeController = volumeController + } catch (error: SecurityException) { + Log.wtf("AudioManager", "Unable to set the volume controller", error) + } + } + override fun getAudioStream(audioStream: AudioStream): Flow<AudioStreamModel> { return merge( - audioManagerEventsReceiver.events.filter { - if (it is StreamAudioManagerEvent) { - it.audioStream == audioStream - } else { - true - } - }, - volumeSettingChanges(audioStream), - ) + audioManagerEventsReceiver.events.filter { + if (it is StreamAudioManagerEvent) { + it.audioStream == audioStream + } else { + true + } + }, + volumeSettingChanges(audioStream), + volumeControllerEvents.filter { it is VolumeControllerEvent.VolumeChanged }, + ) .conflate() .map { getCurrentAudioStream(audioStream) } .onStart { emit(getCurrentAudioStream(audioStream)) } + .distinctUntilChanged() .onEach { logger.onVolumeUpdateReceived(audioStream, it) } .flowOn(backgroundCoroutineContext) } @@ -228,6 +259,12 @@ class AudioRepositoryImpl( } } + override suspend fun notifyVolumeControllerVisible(isVisible: Boolean) { + withContext(backgroundCoroutineContext) { + audioManager.notifyVolumeControllerVisible(volumeController, isVisible) + } + } + private fun getMinVolume(stream: AudioStream): Int = try { audioManager.getStreamMinVolume(stream.value) @@ -253,3 +290,45 @@ class AudioRepositoryImpl( } } } + +private class ProducingVolumeController : IVolumeController.Stub() { + + private val mutableEvents = MutableSharedFlow<VolumeControllerEvent>(extraBufferCapacity = 32) + val events = mutableEvents.asSharedFlow() + + override fun displaySafeVolumeWarning(flags: Int) { + mutableEvents.tryEmit(VolumeControllerEvent.DisplaySafeVolumeWarning(flags)) + } + + override fun volumeChanged(streamType: Int, flags: Int) { + mutableEvents.tryEmit(VolumeControllerEvent.VolumeChanged(streamType, flags)) + } + + override fun masterMuteChanged(flags: Int) { + mutableEvents.tryEmit(VolumeControllerEvent.MasterMuteChanged(flags)) + } + + override fun setLayoutDirection(layoutDirection: Int) { + mutableEvents.tryEmit(VolumeControllerEvent.SetLayoutDirection(layoutDirection)) + } + + override fun dismiss() { + mutableEvents.tryEmit(VolumeControllerEvent.Dismiss) + } + + override fun setA11yMode(mode: Int) { + mutableEvents.tryEmit(VolumeControllerEvent.SetA11yMode(mode)) + } + + override fun displayCsdWarning( + csdWarning: Int, + displayDurationMs: Int, + ) { + mutableEvents.tryEmit( + VolumeControllerEvent.DisplayCsdWarning( + csdWarning, + displayDurationMs, + ) + ) + } +} diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/data/repository/AudioRepositoryTest.kt b/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/data/repository/AudioRepositoryTest.kt index 0e43acb04c91..52e639172af5 100644 --- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/data/repository/AudioRepositoryTest.kt +++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/data/repository/AudioRepositoryTest.kt @@ -44,6 +44,7 @@ import org.mockito.ArgumentMatchers.anyBoolean import org.mockito.ArgumentMatchers.anyInt import org.mockito.Captor import org.mockito.Mock +import org.mockito.Mockito.never import org.mockito.Mockito.verify import org.mockito.Mockito.`when` import org.mockito.MockitoAnnotations @@ -111,6 +112,7 @@ class AudioRepositoryTest { testScope.testScheduler, testScope.backgroundScope, logger, + true, ) } @@ -261,8 +263,8 @@ class AudioRepositoryTest { @Test fun getBluetoothAudioDeviceCategory() { testScope.runTest { - `when`(audioManager.getBluetoothAudioDeviceCategory("12:34:56:78")).thenReturn( - AudioManager.AUDIO_DEVICE_CATEGORY_HEADPHONES) + `when`(audioManager.getBluetoothAudioDeviceCategory("12:34:56:78")) + .thenReturn(AudioManager.AUDIO_DEVICE_CATEGORY_HEADPHONES) val category = underTest.getBluetoothAudioDeviceCategory("12:34:56:78") runCurrent() @@ -271,6 +273,27 @@ class AudioRepositoryTest { } } + @Test + fun useVolumeControllerDisabled_setVolumeController_notCalled() { + testScope.runTest { + underTest = + AudioRepositoryImpl( + eventsReceiver, + audioManager, + contentResolver, + testScope.testScheduler, + testScope.backgroundScope, + logger, + false, + ) + + underTest.volumeControllerEvents.launchIn(backgroundScope) + runCurrent() + + verify(audioManager, never()).volumeController = any() + } + } + private fun triggerConnectedDeviceChange(communicationDevice: AudioDeviceInfo?) { verify(audioManager) .addOnCommunicationDeviceChangedListener( diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/media/data/repository/AudioManagerVolumeControllerExtTest.kt b/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/data/repository/AudioRepositoryVolumeControllerEventsTest.kt index 83b612df8dc9..f5c2f0174456 100644 --- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/media/data/repository/AudioManagerVolumeControllerExtTest.kt +++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/data/repository/AudioRepositoryVolumeControllerEventsTest.kt @@ -14,12 +14,15 @@ * limitations under the License. */ -package com.android.settingslib.media.data.repository +package com.android.settingslib.volume.data.repository +import android.content.ContentResolver import android.media.AudioManager import android.media.IVolumeController import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest +import com.android.settingslib.volume.data.model.VolumeControllerEvent +import com.android.settingslib.volume.shared.FakeAudioManagerEventsReceiver import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.launchIn @@ -39,16 +42,32 @@ import org.mockito.MockitoAnnotations @OptIn(ExperimentalCoroutinesApi::class) @SmallTest @RunWith(AndroidJUnit4::class) -class AudioManagerVolumeControllerExtTest { +class AudioRepositoryVolumeControllerEventsTest { private val testScope = TestScope() @Captor private lateinit var volumeControllerCaptor: ArgumentCaptor<IVolumeController> @Mock private lateinit var audioManager: AudioManager + @Mock private lateinit var contentResolver: ContentResolver + + private val logger = FakeAudioRepositoryLogger() + private val eventsReceiver = FakeAudioManagerEventsReceiver() + + private lateinit var underTest: AudioRepository @Before fun setup() { MockitoAnnotations.initMocks(this) + underTest = + AudioRepositoryImpl( + eventsReceiver, + audioManager, + contentResolver, + testScope.testScheduler, + testScope.backgroundScope, + logger, + true, + ) } @Test @@ -83,7 +102,7 @@ class AudioManagerVolumeControllerExtTest { ) = testScope.runTest { var event: VolumeControllerEvent? = null - audioManager.volumeControllerEvents().onEach { event = it }.launchIn(backgroundScope) + underTest.volumeControllerEvents.onEach { event = it }.launchIn(backgroundScope) runCurrent() verify(audioManager).volumeController = volumeControllerCaptor.capture() diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java index 3a7b0c7bb104..a0e764ad3d5d 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java @@ -16,6 +16,7 @@ package com.android.settingslib.bluetooth; import static com.android.settingslib.bluetooth.BluetoothUtils.isAvailableAudioSharingMediaBluetoothDevice; +import static com.android.settingslib.bluetooth.LocalBluetoothLeBroadcast.UNKNOWN_VALUE_PLACEHOLDER; import static com.android.settingslib.flags.Flags.FLAG_ENABLE_DETERMINING_ADVANCED_DETAILS_HEADER_WITH_METADATA; import static com.google.common.truth.Truth.assertThat; @@ -96,6 +97,7 @@ public class BluetoothUtilsTest { + "</HEARABLE_CONTROL_SLICE_WITH_WIDTH>"; private static final String TEST_EXCLUSIVE_MANAGER_PACKAGE = "com.test.manager"; private static final String TEST_EXCLUSIVE_MANAGER_COMPONENT = "com.test.manager/.component"; + private static final int TEST_BROADCAST_ID = 25; @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); @@ -671,6 +673,34 @@ public class BluetoothUtilsTest { } @Test + public void testHasActiveLocalBroadcastSourceForBtDevice_hasActiveLocalSource() { + when(mBroadcast.getLatestBroadcastId()).thenReturn(TEST_BROADCAST_ID); + when(mLeBroadcastReceiveState.getBroadcastId()).thenReturn(TEST_BROADCAST_ID); + List<BluetoothLeBroadcastReceiveState> sourceList = new ArrayList<>(); + sourceList.add(mLeBroadcastReceiveState); + when(mAssistant.getAllSources(mBluetoothDevice)).thenReturn(sourceList); + + assertThat( + BluetoothUtils.hasActiveLocalBroadcastSourceForBtDevice( + mBluetoothDevice, mLocalBluetoothManager)) + .isTrue(); + } + + @Test + public void testHasActiveLocalBroadcastSourceForBtDevice_noActiveLocalSource() { + when(mLeBroadcastReceiveState.getBroadcastId()).thenReturn(UNKNOWN_VALUE_PLACEHOLDER); + List<BluetoothLeBroadcastReceiveState> sourceList = new ArrayList<>(); + sourceList.add(mLeBroadcastReceiveState); + when(mAssistant.getAllSources(mBluetoothDevice)).thenReturn(sourceList); + + assertThat( + BluetoothUtils.hasActiveLocalBroadcastSourceForBtDevice( + mBluetoothDevice, mLocalBluetoothManager)) + .isFalse(); + } + + + @Test public void isAvailableHearingDevice_isConnectedHearingAid_returnTure() { when(mCachedBluetoothDevice.isConnectedHearingAidDevice()).thenReturn(true); when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice); diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/devicesettings/DeviceSettingHelpPreferenceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/devicesettings/DeviceSettingHelpPreferenceTest.java new file mode 100644 index 000000000000..5620ba31758c --- /dev/null +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/devicesettings/DeviceSettingHelpPreferenceTest.java @@ -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.settingslib.bluetooth.devicesettings; + +import static com.google.common.truth.Truth.assertThat; + +import android.content.Intent; +import android.os.Bundle; +import android.os.Parcel; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; + +@RunWith(RobolectricTestRunner.class) +public final class DeviceSettingHelpPreferenceTest { + + @Test + public void getMethods() { + Intent intent = new Intent(); + DeviceSettingHelpPreference preference = + new DeviceSettingHelpPreference.Builder() + .setIntent(intent) + .setExtras(buildBundle("key1", "value1")) + .build(); + + assertThat(preference.getIntent()).isSameInstanceAs(intent); + assertThat(preference.getExtras().getString("key1")).isEqualTo("value1"); + } + + @Test + public void parcelOperation() { + Intent intent = new Intent("intent_action"); + DeviceSettingHelpPreference preference = + new DeviceSettingHelpPreference.Builder() + .setIntent(intent) + .setExtras(buildBundle("key1", "value1")) + .build(); + + DeviceSettingHelpPreference fromParcel = writeAndRead(preference); + + assertThat(fromParcel.getIntent().getAction()) + .isEqualTo(preference.getIntent().getAction()); + assertThat(fromParcel.getExtras().getString("key1")) + .isEqualTo(preference.getExtras().getString("key1")); + } + + private Bundle buildBundle(String key, String value) { + Bundle bundle = new Bundle(); + bundle.putString(key, value); + return bundle; + } + + private DeviceSettingHelpPreference writeAndRead(DeviceSettingHelpPreference preference) { + Parcel parcel = Parcel.obtain(); + preference.writeToParcel(parcel, 0); + parcel.setDataPosition(0); + DeviceSettingHelpPreference fromParcel = + DeviceSettingHelpPreference.CREATOR.createFromParcel(parcel); + return fromParcel; + } +} diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/devicesettings/DeviceSettingsConfigTest.kt b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/devicesettings/DeviceSettingsConfigTest.kt index 7223e9032648..a0a2658b05d3 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/devicesettings/DeviceSettingsConfigTest.kt +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/devicesettings/DeviceSettingsConfigTest.kt @@ -50,6 +50,14 @@ class DeviceSettingsConfigTest { null, Bundle(), )), + moreSettingsHelpItem = DeviceSettingItem( + 3, + "package_name_2", + "class_name_2", + "intent_action_2", + null, + Bundle(), + ), extras = Bundle().apply { putString("key1", "value1") }, ) @@ -71,6 +79,10 @@ class DeviceSettingsConfigTest { .containsExactly("class_name_2") assertThat(fromParcel.moreSettingsItems.stream().map { it.intentAction }.toList()) .containsExactly("intent_action_2") + assertThat(fromParcel.moreSettingsHelpItem?.settingId).isEqualTo(3) + assertThat(fromParcel.moreSettingsHelpItem?.packageName).isEqualTo("package_name_2") + assertThat(fromParcel.moreSettingsHelpItem?.className).isEqualTo("class_name_2") + assertThat(fromParcel.moreSettingsHelpItem?.intentAction).isEqualTo("intent_action_2") } private fun writeAndRead(item: DeviceSettingsConfig): DeviceSettingsConfig { diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/devicesettings/data/repository/DeviceSettingRepositoryTest.kt b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/devicesettings/data/repository/DeviceSettingRepositoryTest.kt index 061d5150f60a..95ee46e4fdb9 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/devicesettings/data/repository/DeviceSettingRepositoryTest.kt +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/devicesettings/data/repository/DeviceSettingRepositoryTest.kt @@ -28,6 +28,7 @@ import com.android.settingslib.bluetooth.devicesettings.ActionSwitchPreference import com.android.settingslib.bluetooth.devicesettings.ActionSwitchPreferenceState import com.android.settingslib.bluetooth.devicesettings.DeviceInfo import com.android.settingslib.bluetooth.devicesettings.DeviceSetting +import com.android.settingslib.bluetooth.devicesettings.DeviceSettingHelpPreference import com.android.settingslib.bluetooth.devicesettings.DeviceSettingId import com.android.settingslib.bluetooth.devicesettings.DeviceSettingItem import com.android.settingslib.bluetooth.devicesettings.DeviceSettingState @@ -246,6 +247,28 @@ class DeviceSettingRepositoryTest { } @Test + fun getDeviceSetting_helpPreference_success() { + testScope.runTest { + `when`(configService.getDeviceSettingsConfig(any())).thenReturn(DEVICE_SETTING_CONFIG) + `when`(settingProviderService2.registerDeviceSettingsListener(any(), any())).then { + input -> + input + .getArgument<IDeviceSettingsListener>(1) + .onDeviceSettingsChanged(listOf(DEVICE_SETTING_HELP)) + } + var setting: DeviceSettingModel? = null + + underTest + .getDeviceSetting(cachedDevice, DEVICE_SETTING_ID_HELP) + .onEach { setting = it } + .launchIn(backgroundScope) + runCurrent() + + assertDeviceSetting(setting!!, DEVICE_SETTING_HELP) + } + } + + @Test fun getDeviceSetting_noConfig_returnNull() { testScope.runTest { `when`(settingProviderService1.registerDeviceSettingsListener(any(), any())).then { @@ -359,6 +382,12 @@ class DeviceSettingRepositoryTest { assertToggle(actual.toggles[i], pref.toggleInfos[i]) } } + is DeviceSettingModel.HelpPreference -> { + assertThat(serviceResponse.preference) + .isInstanceOf(DeviceSettingHelpPreference::class.java) + val pref = serviceResponse.preference as DeviceSettingHelpPreference + assertThat(actual.intent).isSameInstanceAs(pref.intent) + } else -> {} } } @@ -418,7 +447,7 @@ class DeviceSettingRepositoryTest { CONFIG_SERVICE_INTENT_ACTION + "</DEVICE_SETTINGS_CONFIG_ACTION>" val DEVICE_INFO = DeviceInfo.Builder().setBluetoothAddress(BLUETOOTH_ADDRESS).build() - + const val DEVICE_SETTING_ID_HELP = 12345 val DEVICE_SETTING_ITEM_1 = DeviceSettingItem( DeviceSettingId.DEVICE_SETTING_ID_HEADER, @@ -431,6 +460,12 @@ class DeviceSettingRepositoryTest { SETTING_PROVIDER_SERVICE_PACKAGE_NAME_2, SETTING_PROVIDER_SERVICE_CLASS_NAME_2, SETTING_PROVIDER_SERVICE_INTENT_ACTION_2) + val DEVICE_SETTING_HELP_ITEM = + DeviceSettingItem( + DEVICE_SETTING_ID_HELP, + SETTING_PROVIDER_SERVICE_PACKAGE_NAME_2, + SETTING_PROVIDER_SERVICE_CLASS_NAME_2, + SETTING_PROVIDER_SERVICE_INTENT_ACTION_2) val DEVICE_SETTING_1 = DeviceSetting.Builder() .setSettingId(DeviceSettingId.DEVICE_SETTING_ID_HEADER) @@ -460,10 +495,15 @@ class DeviceSettingRepositoryTest { .build()) .build()) .build() + val DEVICE_SETTING_HELP = DeviceSetting.Builder() + .setSettingId(DEVICE_SETTING_ID_HELP) + .setPreference(DeviceSettingHelpPreference.Builder().setIntent(Intent()).build()) + .build() val DEVICE_SETTING_CONFIG = DeviceSettingsConfig( listOf(DEVICE_SETTING_ITEM_1), listOf(DEVICE_SETTING_ITEM_2), + DEVICE_SETTING_HELP_ITEM, ) } } diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java index 65937ea067c6..f6e10570e656 100644 --- a/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java +++ b/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java @@ -340,7 +340,8 @@ public class GlobalSettingsValidators { new String[] { String.valueOf(Global.Wearable.PAIRED_DEVICE_OS_TYPE_UNKNOWN), String.valueOf(Global.Wearable.PAIRED_DEVICE_OS_TYPE_ANDROID), - String.valueOf(Global.Wearable.PAIRED_DEVICE_OS_TYPE_IOS) + String.valueOf(Global.Wearable.PAIRED_DEVICE_OS_TYPE_IOS), + String.valueOf(Global.Wearable.PAIRED_DEVICE_OS_TYPE_NONE) })); VALIDATORS.put( Global.Wearable.COMPANION_BLE_ROLE, diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp index c1bb55c70073..d26a9066e075 100644 --- a/packages/SystemUI/Android.bp +++ b/packages/SystemUI/Android.bp @@ -949,36 +949,37 @@ android_robolectric_test { strict_mode: false, } -// Disable for now. TODO(b/356666754) Re-enable it -// android_ravenwood_test { -// name: "SystemUiRavenTests", -// srcs: [ -// ":SystemUI-tests-utils", -// ":SystemUI-tests-multivalent", -// // TODO(b/294256649): pivot to using {.aapt.jar} and re-enable -// // use_resource_processor: true when better supported by soong -// ":SystemUIRobo-stub{.aapt.srcjar}", -// ], -// static_libs: [ -// "SystemUI-core", -// "SystemUI-res", -// "SystemUI-tests-base", -// "androidx.test.uiautomator_uiautomator", -// "androidx.core_core-animation-testing", -// "androidx.test.ext.junit", -// "kosmos", -// "mockito-kotlin-nodeps", -// ], -// libs: [ -// "android.test.runner", -// "android.test.base", -// "android.test.mock", -// ], -// auto_gen_config: true, -// plugins: [ -// "dagger2-compiler", -// ], -// } +android_ravenwood_test { + name: "SystemUiRavenTests", + srcs: [ + ":SystemUI-tests-utils", + ":SystemUI-tests-multivalent", + // TODO(b/294256649): pivot to using {.aapt.jar} and re-enable + // use_resource_processor: true when better supported by soong + ":SystemUIRobo-stub{.aapt.srcjar}", + ], + static_libs: [ + "SystemUI-core", + "SystemUI-res", + "SystemUI-tests-base", + "androidx.test.uiautomator_uiautomator", + "androidx.core_core-animation-testing", + "androidx.test.ext.junit", + "kosmos", + "kotlin-test", + "mockito-kotlin-nodeps", + "androidx.compose.runtime_runtime", + ], + libs: [ + "android.test.runner", + "android.test.base", + "android.test.mock", + ], + auto_gen_config: true, + plugins: [ + "dagger2-compiler", + ], +} // Opt-out config for optimizing the SystemUI target using R8. // Disabled via `export SYSTEMUI_OPTIMIZE_JAVA=false`, or explicitly in Make via diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml index f98b29a51604..d3949769cd58 100644 --- a/packages/SystemUI/AndroidManifest.xml +++ b/packages/SystemUI/AndroidManifest.xml @@ -370,6 +370,9 @@ <uses-permission android:name="android.permission.MONITOR_STICKY_MODIFIER_STATE" /> + <!-- Listen to keyboard shortcut events from input manager --> + <uses-permission android:name="android.permission.MANAGE_KEY_GESTURES" /> + <!-- To follow the grammatical gender preference --> <uses-permission android:name="android.permission.READ_SYSTEM_GRAMMATICAL_GENDER" /> diff --git a/packages/SystemUI/aconfig/biometrics_framework.aconfig b/packages/SystemUI/aconfig/biometrics_framework.aconfig index 95e4b593a72f..10d7352da7dc 100644 --- a/packages/SystemUI/aconfig/biometrics_framework.aconfig +++ b/packages/SystemUI/aconfig/biometrics_framework.aconfig @@ -3,3 +3,12 @@ container: "system" # NOTE: Keep alphabetized to help limit merge conflicts from multiple simultaneous editors. +flag { + name: "bp_icon_a11y" + namespace: "biometrics_framework" + description: "Fixes biometric prompt icon not working as button with a11y" + bug: "359423579" + metadata { + purpose: PURPOSE_BUGFIX + } +} diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig index 8a1d81be5e11..97206de346c5 100644 --- a/packages/SystemUI/aconfig/systemui.aconfig +++ b/packages/SystemUI/aconfig/systemui.aconfig @@ -139,13 +139,6 @@ flag { } flag { - name: "notifications_heads_up_refactor" - namespace: "systemui" - description: "Use HeadsUpInteractor to feed HUN updates to the NSSL." - bug: "325936094" -} - -flag { name: "notification_transparent_header_fix" namespace: "systemui" description: "fix the transparent group header issue for async header inflation." @@ -370,6 +363,13 @@ flag { } flag { + name: "status_bar_signal_policy_refactor" + namespace: "systemui" + description: "Use a settings observer for airplane mode and make StatusBarSignalPolicy startable" + bug: "264539100" +} + +flag { name: "status_bar_swipe_over_chip" namespace: "systemui" description: "Allow users to swipe over the status bar chip to open the shade" @@ -380,6 +380,17 @@ flag { } flag { + name: "status_bar_always_check_underlying_networks" + namespace: "systemui" + description: "For status bar connectivity UI, always check underlying networks for wifi and " + "carrier merged information, regardless of the sepcified transport type" + bug: "352162710" + metadata { + purpose: PURPOSE_BUGFIX + } +} + +flag { name: "status_bar_stop_updating_window_height" namespace: "systemui" description: "Don't have PhoneStatusBarView manually trigger an update of the height in " @@ -391,6 +402,13 @@ flag { } flag { + name: "status_bar_ron_chips" + namespace: "systemui" + description: "Show rich ongoing notifications as chips in the status bar" + bug: "361346412" +} + +flag { name: "compose_bouncer" namespace: "systemui" description: "Use the new compose bouncer in SystemUI" diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerScene.kt index 5a4020d3b1fb..270d751d2d3c 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerScene.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerScene.kt @@ -76,7 +76,7 @@ constructor( modifier: Modifier, ) = BouncerScene( - viewModel = rememberViewModel { contentViewModelFactory.create() }, + viewModel = rememberViewModel("BouncerScene") { contentViewModelFactory.create() }, dialogFactory = dialogFactory, modifier = modifier, ) 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 b0590e06d3bd..be6a0f9346a3 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 @@ -567,16 +567,8 @@ private fun ScrollOnUpdatedLiveContentEffect( // Do nothing if there is no new live content val indexOfFirstUpdatedContent = newLiveContentKeys.indexOfFirst { !prevLiveContentKeys.contains(it) } - if (indexOfFirstUpdatedContent < 0) { - return@LaunchedEffect - } - - // Scroll if the live content is not visible - val lastVisibleItemIndex = gridState.layoutInfo.visibleItemsInfo.lastOrNull()?.index - if (lastVisibleItemIndex != null && indexOfFirstUpdatedContent > lastVisibleItemIndex) { - // Launching with a scope to prevent the job from being canceled in the case of a - // recomposition during scrolling - coroutineScope.launch { gridState.animateScrollToItem(indexOfFirstUpdatedContent) } + if (indexOfFirstUpdatedContent in 0 until gridState.firstVisibleItemIndex) { + gridState.scrollToItem(indexOfFirstUpdatedContent) } } } diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenContent.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenContent.kt index 672b8a703e2b..dbe75382556f 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenContent.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenContent.kt @@ -50,7 +50,7 @@ class LockscreenContent( fun SceneScope.Content( modifier: Modifier = Modifier, ) { - val viewModel = rememberViewModel { viewModelFactory.create() } + val viewModel = rememberViewModel("LockscreenContent") { viewModelFactory.create() } val isContentVisible: Boolean by viewModel.isContentVisible.collectAsStateWithLifecycle() if (!isContentVisible) { // If the content isn't supposed to be visible, show a large empty box as it's needed diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/NotificationSection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/NotificationSection.kt index 5f4dc6e77c63..18e1092fba2e 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/NotificationSection.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/NotificationSection.kt @@ -83,7 +83,7 @@ constructor( fun SceneScope.HeadsUpNotifications() { SnoozeableHeadsUpNotificationSpace( stackScrollView = stackScrollView.get(), - viewModel = rememberViewModel { viewModelFactory.create() }, + viewModel = rememberViewModel("HeadsUpNotifications") { viewModelFactory.create() }, ) } @@ -107,7 +107,7 @@ constructor( ConstrainedNotificationStack( stackScrollView = stackScrollView.get(), - viewModel = rememberViewModel { viewModelFactory.create() }, + viewModel = rememberViewModel("Notifications") { viewModelFactory.create() }, modifier = modifier .fillMaxWidth() diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/media/controls/ui/composable/MediaCarousel.kt b/packages/SystemUI/compose/features/src/com/android/systemui/media/controls/ui/composable/MediaCarousel.kt index 808e6666e6e6..5f7b1adca6a0 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/media/controls/ui/composable/MediaCarousel.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/media/controls/ui/composable/MediaCarousel.kt @@ -35,6 +35,7 @@ import com.android.systemui.media.controls.ui.controller.MediaCarouselController import com.android.systemui.media.controls.ui.view.MediaHost import com.android.systemui.res.R import com.android.systemui.util.animation.MeasurementInput +import kotlinx.coroutines.ExperimentalCoroutinesApi object MediaCarousel { object Elements { @@ -46,6 +47,7 @@ object MediaCarousel { } } +@ExperimentalCoroutinesApi @Composable fun SceneScope.MediaCarousel( isVisible: Boolean, @@ -54,7 +56,7 @@ fun SceneScope.MediaCarousel( carouselController: MediaCarouselController, offsetProvider: (() -> IntOffset)? = null, ) { - if (!isVisible) { + if (!isVisible || carouselController.isLockedAndHidden()) { return } diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt index 9e292d06613b..a2beba849b89 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt @@ -321,7 +321,9 @@ fun SceneScope.NotificationScrollingStack( val minScrimOffset: () -> Float = { minScrimTop - maxScrimTop() } // The height of the scrim visible on screen when it is in its resting (collapsed) state. - val minVisibleScrimHeight: () -> Float = { screenHeight - maxScrimTop() } + val minVisibleScrimHeight: () -> Float = { + screenHeight - maxScrimTop() - with(density) { navBarHeight.toPx() } + } // we are not scrolled to the top unless the scrim is at its maximum offset. LaunchedEffect(viewModel, scrimOffset) { 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 62213bd22cbd..e9c96eaafc74 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 @@ -81,9 +81,10 @@ constructor( override fun SceneScope.Content( modifier: Modifier, ) { - val notificationsPlaceholderViewModel = rememberViewModel { - notificationsPlaceholderViewModelFactory.create() - } + val notificationsPlaceholderViewModel = + rememberViewModel("NotificationsShadeScene") { + notificationsPlaceholderViewModelFactory.create() + } OverlayShade( modifier = modifier, 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 192162475c9f..671b0128b621 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 @@ -98,7 +98,7 @@ private fun SceneScope.stateForQuickSettingsContent( else -> QSSceneAdapter.State.CLOSED } } - is TransitionState.Transition.ChangeCurrentScene -> + is TransitionState.Transition.ChangeScene -> with(transitionState) { when { isSplitShade -> UnsquishingQS(squishiness) 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 f11f8bb94a52..d372577142c1 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 @@ -152,7 +152,9 @@ constructor( notificationStackScrollView = notificationStackScrollView.get(), viewModelFactory = contentViewModelFactory, notificationsPlaceholderViewModel = - rememberViewModel { notificationsPlaceholderViewModelFactory.create() }, + rememberViewModel("QuickSettingsScene-notifPlaceholderViewModel") { + notificationsPlaceholderViewModelFactory.create() + }, createTintedIconManager = tintedIconManagerFactory::create, createBatteryMeterViewController = batteryMeterViewControllerFactory::create, statusBarIconController = statusBarIconController, @@ -179,10 +181,11 @@ private fun SceneScope.QuickSettingsScene( ) { val cutoutLocation = LocalDisplayCutout.current.location - val viewModel = rememberViewModel { viewModelFactory.create() } - val brightnessMirrorViewModel = rememberViewModel { - viewModel.brightnessMirrorViewModelFactory.create() - } + val viewModel = rememberViewModel("QuickSettingsScene-viewModel") { viewModelFactory.create() } + val brightnessMirrorViewModel = + rememberViewModel("QuickSettingsScene-brightnessMirrorViewModel") { + viewModel.brightnessMirrorViewModelFactory.create() + } val brightnessMirrorShowing by brightnessMirrorViewModel.isShowing.collectAsStateWithLifecycle() val contentAlpha by animateFloatAsState( 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 b25773b68471..90d7da65b29f 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 @@ -94,7 +94,8 @@ constructor( override fun SceneScope.Content( modifier: Modifier, ) { - val viewModel = rememberViewModel { contentViewModelFactory.create() } + val viewModel = + rememberViewModel("QuickSettingsShadeScene") { contentViewModelFactory.create() } OverlayShade( viewModelFactory = viewModel.overlayShadeViewModelFactory, lockscreenContent = lockscreenContent, 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 d489d731b5fb..cbbace47ecc4 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 @@ -75,7 +75,10 @@ constructor( Spacer(modifier.fillMaxSize()) SnoozeableHeadsUpNotificationSpace( stackScrollView = notificationStackScrolLView.get(), - viewModel = rememberViewModel { notificationsPlaceholderViewModelFactory.create() }, + viewModel = + rememberViewModel("GoneScene") { + notificationsPlaceholderViewModelFactory.create() + }, ) } } diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/Overlay.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/Overlay.kt new file mode 100644 index 000000000000..d62befd10745 --- /dev/null +++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/Overlay.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.scene.ui.composable + +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import com.android.compose.animation.scene.ContentScope +import com.android.compose.animation.scene.OverlayKey +import com.android.systemui.lifecycle.Activatable + +/** + * Defines interface for classes that can describe an "overlay". + * + * In the scene framework, there can be multiple overlays in a single scene "container". The + * container takes care of rendering any current overlays and allowing overlays to be shown, hidden, + * or replaced based on a user action. + */ +interface Overlay : Activatable { + /** Uniquely-identifying key for this overlay. The key must be unique within its container. */ + val key: OverlayKey + + @Composable fun ContentScope.Content(modifier: Modifier) +} diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt index e17cb31407da..f9723d99656b 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt @@ -31,7 +31,9 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.input.pointer.pointerInput +import com.android.compose.animation.scene.ContentKey import com.android.compose.animation.scene.MutableSceneTransitionLayoutState +import com.android.compose.animation.scene.OverlayKey import com.android.compose.animation.scene.SceneKey import com.android.compose.animation.scene.SceneTransitionLayout import com.android.compose.animation.scene.UserAction @@ -57,12 +59,18 @@ import kotlinx.coroutines.flow.collectLatest * and only the scenes on this container. In other words: (a) there should be no scene in this map * that is not in the configuration for this container and (b) all scenes in the configuration * must have entries in this map. + * @param overlayByKey Mapping of [Overlay] by [OverlayKey], ordered by z-order such that the last + * overlay is rendered on top of all other overlays. It's critical that this map contains exactly + * and only the overlays on this container. In other words: (a) there should be no overlay in this + * map that is not in the configuration for this container and (b) all overlays in the + * configuration must have entries in this map. * @param modifier A modifier. */ @Composable fun SceneContainer( viewModel: SceneContainerViewModel, sceneByKey: Map<SceneKey, ComposableScene>, + overlayByKey: Map<OverlayKey, Overlay>, initialSceneKey: SceneKey, dataSourceDelegator: SceneDataSourceDelegator, modifier: Modifier = Modifier, @@ -89,16 +97,19 @@ fun SceneContainer( onDispose { viewModel.setTransitionState(null) } } - val userActionsBySceneKey: MutableMap<SceneKey, Map<UserAction, UserActionResult>> = remember { - mutableStateMapOf() - } + val userActionsByContentKey: MutableMap<ContentKey, Map<UserAction, UserActionResult>> = + remember { + mutableStateMapOf() + } + // TODO(b/359173565): Add overlay user actions when the API is final. LaunchedEffect(currentSceneKey) { try { sceneByKey[currentSceneKey]?.destinationScenes?.collectLatest { userActions -> - userActionsBySceneKey[currentSceneKey] = viewModel.resolveSceneFamilies(userActions) + userActionsByContentKey[currentSceneKey] = + viewModel.resolveSceneFamilies(userActions) } } finally { - userActionsBySceneKey[currentSceneKey] = emptyMap() + userActionsByContentKey[currentSceneKey] = emptyMap() } } @@ -115,7 +126,7 @@ fun SceneContainer( sceneByKey.forEach { (sceneKey, composableScene) -> scene( key = sceneKey, - userActions = userActionsBySceneKey.getOrDefault(sceneKey, emptyMap()) + userActions = userActionsByContentKey.getOrDefault(sceneKey, emptyMap()) ) { // Activate the scene. LaunchedEffect(composableScene) { composableScene.activate() } @@ -128,6 +139,15 @@ fun SceneContainer( } } } + overlayByKey.forEach { (overlayKey, composableOverlay) -> + overlay( + key = overlayKey, + userActions = userActionsByContentKey.getOrDefault(overlayKey, emptyMap()) + ) { + // Render the overlay. + with(composableOverlay) { this@overlay.Content(Modifier) } + } + } } BottomRightCornerRibbon( diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneTransitionLayoutDataSource.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneTransitionLayoutDataSource.kt index 4b4b7ed33458..e12a8bde7c8f 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneTransitionLayoutDataSource.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneTransitionLayoutDataSource.kt @@ -18,7 +18,9 @@ package com.android.systemui.scene.ui.composable +import androidx.compose.runtime.snapshotFlow import com.android.compose.animation.scene.MutableSceneTransitionLayoutState +import com.android.compose.animation.scene.OverlayKey import com.android.compose.animation.scene.SceneKey import com.android.compose.animation.scene.TransitionKey import com.android.compose.animation.scene.observableTransitionState @@ -52,6 +54,14 @@ class SceneTransitionLayoutDataSource( initialValue = state.transitionState.currentScene, ) + override val currentOverlays: StateFlow<Set<OverlayKey>> = + snapshotFlow { state.currentOverlays } + .stateIn( + scope = coroutineScope, + started = SharingStarted.WhileSubscribed(), + initialValue = emptySet(), + ) + override fun changeScene( toScene: SceneKey, transitionKey: TransitionKey?, @@ -68,4 +78,29 @@ class SceneTransitionLayoutDataSource( scene = toScene, ) } + + override fun showOverlay(overlay: OverlayKey, transitionKey: TransitionKey?) { + state.showOverlay( + overlay = overlay, + animationScope = coroutineScope, + transitionKey = transitionKey, + ) + } + + override fun hideOverlay(overlay: OverlayKey, transitionKey: TransitionKey?) { + state.hideOverlay( + overlay = overlay, + animationScope = coroutineScope, + transitionKey = transitionKey, + ) + } + + override fun replaceOverlay(from: OverlayKey, to: OverlayKey, transitionKey: TransitionKey?) { + state.replaceOverlay( + from = from, + to = to, + animationScope = coroutineScope, + transitionKey = transitionKey, + ) + } } diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromLockscreenToBouncerTransition.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromLockscreenToBouncerTransition.kt index 1fee8741fe48..022eb1f5ef77 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromLockscreenToBouncerTransition.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromLockscreenToBouncerTransition.kt @@ -5,10 +5,16 @@ import androidx.compose.ui.unit.dp import com.android.compose.animation.scene.TransitionBuilder import com.android.systemui.bouncer.ui.composable.Bouncer +const val FROM_LOCK_SCREEN_TO_BOUNCER_FADE_FRACTION = 0.5f + fun TransitionBuilder.lockscreenToBouncerTransition() { spec = tween(durationMillis = 500) translate(Bouncer.Elements.Content, y = 300.dp) - fractionRange(end = 0.5f) { fade(Bouncer.Elements.Background) } - fractionRange(start = 0.5f) { fade(Bouncer.Elements.Content) } + fractionRange(end = FROM_LOCK_SCREEN_TO_BOUNCER_FADE_FRACTION) { + fade(Bouncer.Elements.Background) + } + fractionRange(start = FROM_LOCK_SCREEN_TO_BOUNCER_FADE_FRACTION) { + fade(Bouncer.Elements.Content) + } } 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 445ffcb0c60c..595bbb035f21 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 @@ -69,7 +69,7 @@ fun SceneScope.OverlayShade( modifier: Modifier = Modifier, content: @Composable () -> Unit, ) { - val viewModel = rememberViewModel { viewModelFactory.create() } + val viewModel = rememberViewModel("OverlayShade") { viewModelFactory.create() } val backgroundScene by viewModel.backgroundScene.collectAsStateWithLifecycle() Box(modifier) { 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 8c53740ebfd0..05a0119d68e4 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 @@ -129,7 +129,7 @@ fun SceneScope.CollapsedShadeHeader( statusBarIconController: StatusBarIconController, modifier: Modifier = Modifier, ) { - val viewModel = rememberViewModel { viewModelFactory.create() } + val viewModel = rememberViewModel("CollapsedShadeHeader") { viewModelFactory.create() } val isDisabled by viewModel.isDisabled.collectAsStateWithLifecycle() if (isDisabled) { return @@ -287,7 +287,7 @@ fun SceneScope.ExpandedShadeHeader( statusBarIconController: StatusBarIconController, modifier: Modifier = Modifier, ) { - val viewModel = rememberViewModel { viewModelFactory.create() } + val viewModel = rememberViewModel("ExpandedShadeHeader") { viewModelFactory.create() } val isDisabled by viewModel.isDisabled.collectAsStateWithLifecycle() if (isDisabled) { return 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 f8513a8c4dd4..b7c6edce12d7 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 @@ -182,9 +182,12 @@ constructor( ) = ShadeScene( notificationStackScrollView.get(), - viewModel = rememberViewModel { contentViewModelFactory.create() }, + viewModel = + rememberViewModel("ShadeScene-viewModel") { contentViewModelFactory.create() }, notificationsPlaceholderViewModel = - rememberViewModel { notificationsPlaceholderViewModelFactory.create() }, + rememberViewModel("ShadeScene-notifPlaceholderViewModel") { + notificationsPlaceholderViewModelFactory.create() + }, createTintedIconManager = tintedIconManagerFactory::create, createBatteryMeterViewController = batteryMeterViewControllerFactory::create, statusBarIconController = statusBarIconController, @@ -493,9 +496,10 @@ private fun SceneScope.SplitShade( } } - val brightnessMirrorViewModel = rememberViewModel { - viewModel.brightnessMirrorViewModelFactory.create() - } + val brightnessMirrorViewModel = + rememberViewModel("SplitShade-brightnessMirrorViewModel") { + viewModel.brightnessMirrorViewModelFactory.create() + } val brightnessMirrorShowing by brightnessMirrorViewModel.isShowing.collectAsStateWithLifecycle() val contentAlpha by animateFloatAsState( 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 abe079a4ab64..e15bc1243dd9 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 @@ -28,7 +28,7 @@ internal fun CoroutineScope.animateToScene( layoutState: MutableSceneTransitionLayoutStateImpl, target: SceneKey, transitionKey: TransitionKey?, -): TransitionState.Transition.ChangeCurrentScene? { +): TransitionState.Transition.ChangeScene? { val transitionState = layoutState.transitionState if (transitionState.currentScene == target) { // This can happen in 3 different situations, for which there isn't anything else to do: @@ -55,7 +55,7 @@ internal fun CoroutineScope.animateToScene( replacedTransition = null, ) } - is TransitionState.Transition.ChangeCurrentScene -> { + is TransitionState.Transition.ChangeScene -> { val isInitiatedByUserInput = transitionState.isInitiatedByUserInput // A transition is currently running: first check whether `transition.toScene` or @@ -139,7 +139,7 @@ private fun CoroutineScope.animateToScene( reversed: Boolean = false, fromScene: SceneKey = layoutState.transitionState.currentScene, chain: Boolean = true, -): TransitionState.Transition.ChangeCurrentScene { +): TransitionState.Transition.ChangeScene { val oneOffAnimation = OneOffAnimation() val targetProgress = if (reversed) 0f else 1f val transition = @@ -184,7 +184,7 @@ private class OneOffSceneTransition( override val isInitiatedByUserInput: Boolean, replacedTransition: TransitionState.Transition?, private val oneOffAnimation: OneOffAnimation, -) : TransitionState.Transition.ChangeCurrentScene(fromScene, toScene, replacedTransition) { +) : TransitionState.Transition.ChangeScene(fromScene, toScene, replacedTransition) { override val progress: Float get() = oneOffAnimation.progress 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 778745869bad..37e4daafdc7b 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 @@ -18,27 +18,17 @@ package com.android.compose.animation.scene -import androidx.compose.animation.core.Animatable -import androidx.compose.animation.core.AnimationVector1D import androidx.compose.foundation.gestures.Orientation -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableFloatStateOf -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.setValue import androidx.compose.ui.geometry.Offset -import androidx.compose.ui.unit.IntSize import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.round import androidx.compose.ui.util.fastCoerceIn -import com.android.compose.animation.scene.content.Scene +import com.android.compose.animation.scene.content.Content import com.android.compose.animation.scene.content.state.TransitionState import com.android.compose.animation.scene.content.state.TransitionState.HasOverscrollProperties.Companion.DistanceUnspecified import com.android.compose.nestedscroll.PriorityNestedScrollConnection import kotlin.math.absoluteValue import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.CoroutineStart -import kotlinx.coroutines.Job -import kotlinx.coroutines.launch internal interface DraggableHandler { /** @@ -63,11 +53,11 @@ internal interface DragController { fun onDrag(delta: Float): Float /** - * Starts a transition to a target scene. + * Stop the current drag with the given [velocity]. * * @return the consumed [velocity] */ - fun onStop(velocity: Float, canChangeScene: Boolean): Float + fun onStop(velocity: Float, canChangeContent: Boolean): Float } internal class DraggableHandlerImpl( @@ -109,22 +99,30 @@ internal class DraggableHandlerImpl( return false } - val swipeTransition = dragController.swipeTransition + val swipeAnimation = dragController.swipeAnimation // Don't intercept a transition that is finishing. - if (swipeTransition.isFinishing) { + if (swipeAnimation.isFinishing) { return false } // Only intercept the current transition if one of the 2 swipes results is also a transition - // between the same pair of scenes. - val fromScene = swipeTransition._currentScene - val swipes = computeSwipes(fromScene, startedPosition, pointersDown = 1) - val (upOrLeft, downOrRight) = swipes.computeSwipesResults(fromScene) + // between the same pair of contents. + val swipes = computeSwipes(startedPosition, pointersDown = 1) + val fromContent = layoutImpl.content(swipeAnimation.currentContent) + val (upOrLeft, downOrRight) = swipes.computeSwipesResults(fromContent) + val currentScene = layoutImpl.state.currentScene + val contentTransition = swipeAnimation.contentTransition return (upOrLeft != null && - swipeTransition.isTransitioningBetween(fromScene.key, upOrLeft.toScene)) || + contentTransition.isTransitioningBetween( + fromContent.key, + upOrLeft.toContent(currentScene) + )) || (downOrRight != null && - swipeTransition.isTransitioningBetween(fromScene.key, downOrRight.toScene)) + contentTransition.isTransitioningBetween( + fromContent.key, + downOrRight.toContent(currentScene) + )) } override fun onDragStarted( @@ -140,65 +138,75 @@ internal class DraggableHandlerImpl( } // This [transition] was already driving the animation: simply take over it. - // Stop animating and start from where the current offset. - oldDragController.swipeTransition.cancelOffsetAnimation() + // Stop animating and start from the current offset. + val oldSwipeAnimation = oldDragController.swipeAnimation + oldSwipeAnimation.cancelOffsetAnimation() // We need to recompute the swipe results since this is a new gesture, and the // fromScene.userActions may have changed. val swipes = oldDragController.swipes - swipes.updateSwipesResults(oldDragController.swipeTransition._fromScene) + swipes.updateSwipesResults( + fromContent = layoutImpl.content(oldSwipeAnimation.fromContent) + ) - // A new gesture should always create a new SwipeTransition. This way there cannot be + // A new gesture should always create a new SwipeAnimation. This way there cannot be // different gestures controlling the same transition. - val swipeTransition = SwipeTransition(oldDragController.swipeTransition) - swipes.updateSwipesResults(fromScene = swipeTransition._fromScene) - return updateDragController(swipes, swipeTransition) + val swipeAnimation = createSwipeAnimation(oldSwipeAnimation) + return updateDragController(swipes, swipeAnimation) } - val transitionState = layoutImpl.state.transitionState - val fromScene = layoutImpl.scene(transitionState.currentScene) - val swipes = computeSwipes(fromScene, startedPosition, pointersDown) + val swipes = computeSwipes(startedPosition, pointersDown) + val fromContent = layoutImpl.contentForUserActions() + + swipes.updateSwipesResults(fromContent) val result = - swipes.findUserActionResult(fromScene, overSlop, true) - // As we were unable to locate a valid target scene, the initial SwipeTransition + swipes.findUserActionResult(overSlop) + // As we were unable to locate a valid target scene, the initial SwipeAnimation // cannot be defined. Consequently, a simple NoOp Controller will be returned. ?: return NoOpDragController - return updateDragController( - swipes = swipes, - swipeTransition = - SwipeTransition( - layoutImpl.state, - coroutineScope, - fromScene, - result, - swipes, - layoutImpl, - orientation, - ) - ) + val swipeAnimation = createSwipeAnimation(swipes, result) + return updateDragController(swipes, swipeAnimation) } private fun updateDragController( swipes: Swipes, - swipeTransition: SwipeTransition - ): DragController { - val newDragController = DragControllerImpl(this, swipes, swipeTransition) - newDragController.updateTransition(swipeTransition, force = true) + swipeAnimation: SwipeAnimation<*> + ): DragControllerImpl { + val newDragController = DragControllerImpl(this, swipes, swipeAnimation) + newDragController.updateTransition(swipeAnimation, force = true) dragController = newDragController return newDragController } - private fun computeSwipes( - fromScene: Scene, - startedPosition: Offset?, - pointersDown: Int - ): Swipes { + internal fun createSwipeAnimation( + swipes: Swipes, + result: UserActionResult, + ): SwipeAnimation<*> { + val upOrLeftResult = swipes.upOrLeftResult + val downOrRightResult = swipes.downOrRightResult + val isUpOrLeft = + when (result) { + upOrLeftResult -> true + downOrRightResult -> false + else -> error("Unknown result $result ($upOrLeftResult $downOrRightResult)") + } + + return createSwipeAnimation( + layoutImpl, + layoutImpl.coroutineScope, + result, + isUpOrLeft, + orientation + ) + } + + private fun computeSwipes(startedPosition: Offset?, pointersDown: Int): Swipes { val fromSource = startedPosition?.let { position -> layoutImpl.swipeSourceDetector .source( - fromScene.targetSize, + layoutImpl.lastSize, position.round(), layoutImpl.density, orientation, @@ -254,215 +262,197 @@ internal class DraggableHandlerImpl( private class DragControllerImpl( private val draggableHandler: DraggableHandlerImpl, val swipes: Swipes, - var swipeTransition: SwipeTransition, + var swipeAnimation: SwipeAnimation<*>, ) : DragController { val layoutState = draggableHandler.layoutImpl.state /** * Whether this handle is active. If this returns false, calling [onDrag] and [onStop] will do - * nothing. We should have only one active controller at a time + * nothing. */ val isDrivingTransition: Boolean - get() = layoutState.transitionState == swipeTransition + get() = layoutState.transitionState == swipeAnimation.contentTransition init { check(!isDrivingTransition) { "Multiple controllers with the same SwipeTransition" } } - fun updateTransition(newTransition: SwipeTransition, force: Boolean = false) { - if (isDrivingTransition || force) { - layoutState.startTransition(newTransition) + fun updateTransition(newTransition: SwipeAnimation<*>, force: Boolean = false) { + if (force || isDrivingTransition) { + layoutState.startTransition(newTransition.contentTransition) } - swipeTransition = newTransition + val previous = swipeAnimation + swipeAnimation = newTransition + + // Finish the previous transition. + if (previous != newTransition) { + layoutState.finishTransition(previous.contentTransition) + } } /** * We receive a [delta] that can be consumed to change the offset of the current - * [SwipeTransition]. + * [SwipeAnimation]. * * @return the consumed delta */ override fun onDrag(delta: Float): Float { - if (delta == 0f || !isDrivingTransition || swipeTransition.isFinishing) { + return onDrag(delta, swipeAnimation) + } + + private fun <T : ContentKey> onDrag(delta: Float, swipeAnimation: SwipeAnimation<T>): Float { + if (delta == 0f || !isDrivingTransition || swipeAnimation.isFinishing) { return 0f } - val toScene = swipeTransition._toScene - val distance = swipeTransition.distance() - val previousOffset = swipeTransition.dragOffset + val toContent = swipeAnimation.toContent + val distance = swipeAnimation.distance() + val previousOffset = swipeAnimation.dragOffset val desiredOffset = previousOffset + delta fun hasReachedToSceneUpOrLeft() = distance < 0 && desiredOffset <= distance && - swipes.upOrLeftResult?.toScene == toScene.key + swipes.upOrLeftResult?.toContent(layoutState.currentScene) == toContent fun hasReachedToSceneDownOrRight() = distance > 0 && desiredOffset >= distance && - swipes.downOrRightResult?.toScene == toScene.key + swipes.downOrRightResult?.toContent(layoutState.currentScene) == toContent - // Considering accelerated swipe: Change fromScene in the case where the user quickly swiped - // multiple times in the same direction to accelerate the transition from A => B then B => C + // Considering accelerated swipe: Change fromContent in the case where the user quickly + // swiped multiple times in the same direction to accelerate the transition from A => B then + // B => C. // // TODO(b/290184746): the second drag needs to pass B to work. Add support for flinging // twice before B has been reached - val hasReachedToScene = - swipeTransition._currentScene == toScene && + val hasReachedToContent = + swipeAnimation.currentContent == toContent && (hasReachedToSceneUpOrLeft() || hasReachedToSceneDownOrRight()) - val fromScene: Scene + val fromContent: ContentKey val currentTransitionOffset: Float val newOffset: Float val consumedDelta: Float - if (hasReachedToScene) { - // The new transition will start from the current toScene - fromScene = toScene - // The current transition is completed (we have reached the distance) + if (hasReachedToContent) { + // The new transition will start from the current toContent. + fromContent = toContent + + // The current transition is completed (we have reached the full swipe distance). currentTransitionOffset = distance - // The next transition will start with the remaining offset + + // The next transition will start with the remaining offset. newOffset = desiredOffset - distance consumedDelta = delta } else { - fromScene = swipeTransition._fromScene - val desiredProgress = swipeTransition.computeProgress(desiredOffset) - // note: the distance could be negative if fromScene is aboveOrLeft of toScene. + fromContent = swipeAnimation.fromContent + val desiredProgress = swipeAnimation.computeProgress(desiredOffset) + + // Note: the distance could be negative if fromContent is above or to the left of + // toContent. currentTransitionOffset = when { distance == DistanceUnspecified || - swipeTransition.isWithinProgressRange(desiredProgress) -> desiredOffset + swipeAnimation.contentTransition.isWithinProgressRange(desiredProgress) -> + desiredOffset distance > 0f -> desiredOffset.fastCoerceIn(0f, distance) else -> desiredOffset.fastCoerceIn(distance, 0f) } + // If there is a new transition, we will use the same offset newOffset = currentTransitionOffset consumedDelta = newOffset - previousOffset } - swipeTransition.dragOffset = currentTransitionOffset + swipeAnimation.dragOffset = currentTransitionOffset - val result = - swipes.findUserActionResult( - fromScene = fromScene, - directionOffset = newOffset, - updateSwipesResults = hasReachedToScene - ) + if (hasReachedToContent) { + swipes.updateSwipesResults(draggableHandler.layoutImpl.content(fromContent)) + } + val result = swipes.findUserActionResult(directionOffset = newOffset) if (result == null) { - onStop(velocity = delta, canChangeScene = true) + onStop(velocity = delta, canChangeContent = true) return 0f } val needNewTransition = - hasReachedToScene || - result.toScene != swipeTransition.toScene || - result.transitionKey != swipeTransition.key + hasReachedToContent || + result.toContent(layoutState.currentScene) != swipeAnimation.toContent || + result.transitionKey != swipeAnimation.contentTransition.key if (needNewTransition) { // Make sure the current transition will finish to the right current scene. - swipeTransition._currentScene = fromScene - - val newSwipeTransition = - SwipeTransition( - layoutState = layoutState, - coroutineScope = draggableHandler.coroutineScope, - fromScene = fromScene, - result = result, - swipes = swipes, - layoutImpl = draggableHandler.layoutImpl, - orientation = draggableHandler.orientation, - ) - newSwipeTransition.dragOffset = newOffset - updateTransition(newSwipeTransition) + swipeAnimation.currentContent = fromContent + + val newSwipeAnimation = draggableHandler.createSwipeAnimation(swipes, result) + newSwipeAnimation.dragOffset = newOffset + updateTransition(newSwipeAnimation) } return consumedDelta } - override fun onStop(velocity: Float, canChangeScene: Boolean): Float { + override fun onStop(velocity: Float, canChangeContent: Boolean): Float { + return onStop(velocity, canChangeContent, swipeAnimation) + } + + private fun <T : ContentKey> onStop( + velocity: Float, + canChangeContent: Boolean, + + // Important: Make sure that this has the same name as [this.swipeAnimation] so that all the + // code here references the current animation when [onDragStopped] is called, otherwise the + // callbacks (like onAnimationCompleted()) might incorrectly finish a new transition that + // replaced this one. + swipeAnimation: SwipeAnimation<T>, + ): Float { // The state was changed since the drag started; don't do anything. - if (!isDrivingTransition || swipeTransition.isFinishing) { + if (!isDrivingTransition || swipeAnimation.isFinishing) { return 0f } - // Important: Make sure that all the code here references the current transition when - // [onDragStopped] is called, otherwise the callbacks (like onAnimationCompleted()) might - // incorrectly finish a new transition that replaced this one. - val swipeTransition = this.swipeTransition - - fun animateTo(targetScene: Scene, targetOffset: Float) { - // If the effective current scene changed, it should be reflected right now in the - // current scene state, even before the settle animation is ongoing. That way all the - // swipeables and back handlers will be refreshed and the user can for instance quickly - // swipe vertically from A => B then horizontally from B => C, or swipe from A => B then - // immediately go back B => A. - if (targetScene != swipeTransition._currentScene) { - swipeTransition._currentScene = targetScene - } - - swipeTransition.animateOffset( - coroutineScope = draggableHandler.coroutineScope, + fun animateTo(targetContent: T) { + swipeAnimation.animateOffset( initialVelocity = velocity, - targetOffset = targetOffset, - targetScene = targetScene.key, + targetContent = targetContent, ) } - val fromScene = swipeTransition._fromScene - if (canChangeScene) { - // If we are halfway between two scenes, we check what the target will be based on the + val fromContent = swipeAnimation.fromContent + if (canChangeContent) { + // If we are halfway between two contents, we check what the target will be based on the // velocity and offset of the transition, then we launch the animation. - val toScene = swipeTransition._toScene - - // Compute the destination scene (and therefore offset) to settle in. - val offset = swipeTransition.dragOffset - val distance = swipeTransition.distance() - var targetScene: Scene - var targetOffset: Float - if ( - distance != DistanceUnspecified && - shouldCommitSwipe( - offset = offset, - distance = distance, - velocity = velocity, - wasCommitted = swipeTransition._currentScene == toScene, - requiresFullDistanceSwipe = swipeTransition.requiresFullDistanceSwipe, - ) - ) { - targetScene = toScene - targetOffset = distance - } else { - targetScene = fromScene - targetOffset = 0f - } - - if ( - targetScene != swipeTransition._currentScene && - !layoutState.canChangeScene(targetScene.key) - ) { - // We wanted to change to a new scene but we are not allowed to, so we animate back - // to the current scene. - targetScene = swipeTransition._currentScene - targetOffset = - if (targetScene == fromScene) { - 0f - } else { - check(distance != DistanceUnspecified) { - "distance is equal to $DistanceUnspecified" - } - distance - } - } + val toContent = swipeAnimation.toContent + + // Compute the destination content (and therefore offset) to settle in. + val offset = swipeAnimation.dragOffset + val distance = swipeAnimation.distance() + val targetContent = + if ( + distance != DistanceUnspecified && + shouldCommitSwipe( + offset = offset, + distance = distance, + velocity = velocity, + wasCommitted = swipeAnimation.currentContent == toContent, + requiresFullDistanceSwipe = swipeAnimation.requiresFullDistanceSwipe, + ) + ) { + toContent + } else { + fromContent + } - animateTo(targetScene = targetScene, targetOffset = targetOffset) + animateTo(targetContent = targetContent) } else { // We are doing an overscroll preview animation between scenes. - check(fromScene == swipeTransition._currentScene) { - "canChangeScene is false but currentScene != fromScene" + check(fromContent == swipeAnimation.currentContent) { + "canChangeContent is false but currentContent != fromContent" } - animateTo(targetScene = fromScene, targetOffset = 0f) + animateTo(targetContent = fromContent) } // The onStop animation consumes any remaining velocity. @@ -513,329 +503,8 @@ private class DragControllerImpl( } } -private fun SwipeTransition( - layoutState: MutableSceneTransitionLayoutStateImpl, - coroutineScope: CoroutineScope, - fromScene: Scene, - result: UserActionResult, - swipes: Swipes, - layoutImpl: SceneTransitionLayoutImpl, - orientation: Orientation, -): SwipeTransition { - val upOrLeftResult = swipes.upOrLeftResult - val downOrRightResult = swipes.downOrRightResult - val isUpOrLeft = - when (result) { - upOrLeftResult -> true - downOrRightResult -> false - else -> error("Unknown result $result ($upOrLeftResult $downOrRightResult)") - } - - return SwipeTransition( - layoutImpl = layoutImpl, - layoutState = layoutState, - coroutineScope = coroutineScope, - key = result.transitionKey, - _fromScene = fromScene, - _toScene = layoutImpl.scene(result.toScene), - userActionDistanceScope = layoutImpl.userActionDistanceScope, - orientation = orientation, - isUpOrLeft = isUpOrLeft, - requiresFullDistanceSwipe = result.requiresFullDistanceSwipe, - replacedTransition = null, - ) -} - -private fun SwipeTransition(old: SwipeTransition): SwipeTransition { - return SwipeTransition( - layoutImpl = old.layoutImpl, - layoutState = old.layoutState, - coroutineScope = old.coroutineScope, - key = old.key, - _fromScene = old._fromScene, - _toScene = old._toScene, - userActionDistanceScope = old.userActionDistanceScope, - orientation = old.orientation, - isUpOrLeft = old.isUpOrLeft, - lastDistance = old.lastDistance, - requiresFullDistanceSwipe = old.requiresFullDistanceSwipe, - replacedTransition = old, - ) - .apply { - _currentScene = old._currentScene - dragOffset = old.dragOffset - } -} - -private class SwipeTransition( - val layoutImpl: SceneTransitionLayoutImpl, - val layoutState: MutableSceneTransitionLayoutStateImpl, - val coroutineScope: CoroutineScope, - override val key: TransitionKey?, - val _fromScene: Scene, - val _toScene: Scene, - val userActionDistanceScope: UserActionDistanceScope, - override val orientation: Orientation, - override val isUpOrLeft: Boolean, - val requiresFullDistanceSwipe: Boolean, - replacedTransition: SwipeTransition?, - var lastDistance: Float = DistanceUnspecified, -) : - TransitionState.Transition.ChangeCurrentScene(_fromScene.key, _toScene.key, replacedTransition), - TransitionState.HasOverscrollProperties { - var _currentScene by mutableStateOf(_fromScene) - override val currentScene: SceneKey - get() = _currentScene.key - - override val progress: Float - get() { - // Important: If we are going to return early because distance is equal to 0, we should - // still make sure we read the offset before returning so that the calling code still - // subscribes to the offset value. - val offset = offsetAnimation?.animatable?.value ?: dragOffset - - return computeProgress(offset) - } - - fun computeProgress(offset: Float): Float { - val distance = distance() - if (distance == DistanceUnspecified) { - return 0f - } - return offset / distance - } - - override val progressVelocity: Float - get() { - val animatable = offsetAnimation?.animatable ?: return 0f - val distance = distance() - if (distance == DistanceUnspecified) { - return 0f - } - - val velocityInDistanceUnit = animatable.velocity - return velocityInDistanceUnit / distance.absoluteValue - } - - override val isInitiatedByUserInput = true - - override var bouncingContent: SceneKey? = null - - /** The current offset caused by the drag gesture. */ - var dragOffset by mutableFloatStateOf(0f) - - /** The offset animation that animates the offset once the user lifts their finger. */ - private var offsetAnimation: OffsetAnimation? by mutableStateOf(null) - - override val isUserInputOngoing: Boolean - get() = offsetAnimation == null - - override val overscrollScope: OverscrollScope = - object : OverscrollScope { - override val density: Float - get() = layoutImpl.density.density - - override val fontScale: Float - get() = layoutImpl.density.fontScale - - override val absoluteDistance: Float - get() = distance().absoluteValue - } - - /** Whether [TransitionState.Transition.finish] was called on this transition. */ - var isFinishing = false - private set - - /** - * The signed distance between [fromScene] and [toScene]. It is negative if [fromScene] is above - * or to the left of [toScene]. - * - * Note that this distance can be equal to [DistanceUnspecified] during the first frame of a - * transition when the distance depends on the size or position of an element that is composed - * in the scene we are going to. - */ - fun distance(): Float { - if (lastDistance != DistanceUnspecified) { - return lastDistance - } - - val absoluteDistance = - with(transformationSpec.distance ?: DefaultSwipeDistance) { - userActionDistanceScope.absoluteDistance( - _fromScene.targetSize, - orientation, - ) - } - - if (absoluteDistance <= 0f) { - return DistanceUnspecified - } - - val distance = if (isUpOrLeft) -absoluteDistance else absoluteDistance - lastDistance = distance - return distance - } - - /** Ends any previous [offsetAnimation] and runs the new [animation]. */ - private fun startOffsetAnimation(animation: () -> OffsetAnimation): OffsetAnimation { - cancelOffsetAnimation() - return animation().also { offsetAnimation = it } - } - - /** Cancel any ongoing offset animation. */ - // TODO(b/317063114) This should be a suspended function to avoid multiple jobs running at - // the same time. - fun cancelOffsetAnimation() { - val animation = offsetAnimation ?: return - offsetAnimation = null - - dragOffset = animation.animatable.value - animation.job.cancel() - } - - fun animateOffset( - // TODO(b/317063114) The CoroutineScope should be removed. - coroutineScope: CoroutineScope, - initialVelocity: Float, - targetOffset: Float, - targetScene: SceneKey, - ): OffsetAnimation { - val initialProgress = progress - // Skip the animation if we have already reached the target scene and the overscroll does - // not animate anything. - val hasReachedTargetScene = - (targetScene == toScene && initialProgress >= 1f) || - (targetScene == fromScene && initialProgress <= 0f) - val skipAnimation = hasReachedTargetScene && !isWithinProgressRange(initialProgress) - - return startOffsetAnimation { - val animatable = Animatable(dragOffset, OffsetVisibilityThreshold) - val isTargetGreater = targetOffset > animatable.value - val startedWhenOvercrollingTargetScene = - if (targetScene == fromScene) initialProgress < 0f else initialProgress > 1f - val job = - coroutineScope - // Important: We start atomically to make sure that we start the coroutine even - // if it is cancelled right after it is launched, so that snapToScene() is - // correctly called. Otherwise, this transition will never be stopped and we - // will never settle to Idle. - .launch(start = CoroutineStart.ATOMIC) { - // TODO(b/327249191): Refactor the code so that we don't even launch a - // coroutine if we don't need to animate. - if (skipAnimation) { - snapToScene(targetScene) - cancelOffsetAnimation() - dragOffset = targetOffset - return@launch - } - - try { - val swipeSpec = - transformationSpec.swipeSpec - ?: layoutState.transitions.defaultSwipeSpec - animatable.animateTo( - targetValue = targetOffset, - animationSpec = swipeSpec, - initialVelocity = initialVelocity, - ) { - if (bouncingContent == null) { - val isBouncing = - if (isTargetGreater) { - if (startedWhenOvercrollingTargetScene) { - value >= targetOffset - } else { - value > targetOffset - } - } else { - if (startedWhenOvercrollingTargetScene) { - value <= targetOffset - } else { - value < targetOffset - } - } - - if (isBouncing) { - bouncingContent = targetScene - - // Immediately stop this transition if we are bouncing on a - // scene that does not bounce. - if (!isWithinProgressRange(progress)) { - snapToScene(targetScene) - } - } - } - } - } finally { - snapToScene(targetScene) - } - } - - OffsetAnimation(animatable, job) - } - } - - fun snapToScene(scene: SceneKey) { - cancelOffsetAnimation() - check(currentScene == scene) - layoutState.finishTransition(this) - } - - override fun finish(): Job { - if (isFinishing) return requireNotNull(offsetAnimation).job - isFinishing = true - - // If we were already animating the offset, simply return the job. - offsetAnimation?.let { - return it.job - } - - // Animate to the current scene. - val targetScene = currentScene - val targetOffset = - if (targetScene == fromScene) { - 0f - } else { - val distance = distance() - check(distance != DistanceUnspecified) { - "targetScene != fromScene but distance is unspecified" - } - distance - } - - val animation = - animateOffset( - coroutineScope = coroutineScope, - initialVelocity = 0f, - targetOffset = targetOffset, - targetScene = currentScene, - ) - check(offsetAnimation == animation) - return animation.job - } - - internal class OffsetAnimation( - /** The animatable used to animate the offset. */ - val animatable: Animatable<Float, AnimationVector1D>, - - /** The job in which [animatable] is animated. */ - val job: Job, - ) -} - -private object DefaultSwipeDistance : UserActionDistance { - override fun UserActionDistanceScope.absoluteDistance( - fromSceneSize: IntSize, - orientation: Orientation, - ): Float { - return when (orientation) { - Orientation.Horizontal -> fromSceneSize.width - Orientation.Vertical -> fromSceneSize.height - }.toFloat() - } -} - /** The [Swipe] associated to a given fromScene, startedPosition and pointersDown. */ -private class Swipes( +internal class Swipes( val upOrLeft: Swipe.Resolved?, val downOrRight: Swipe.Resolved?, val upOrLeftNoSource: Swipe.Resolved?, @@ -845,8 +514,8 @@ private class Swipes( var upOrLeftResult: UserActionResult? = null var downOrRightResult: UserActionResult? = null - fun computeSwipesResults(fromScene: Scene): Pair<UserActionResult?, UserActionResult?> { - val userActions = fromScene.userActions + fun computeSwipesResults(fromContent: Content): Pair<UserActionResult?, UserActionResult?> { + val userActions = fromContent.userActions fun result(swipe: Swipe.Resolved?): UserActionResult? { return userActions[swipe ?: return null] } @@ -856,39 +525,33 @@ private class Swipes( return upOrLeftResult to downOrRightResult } - fun updateSwipesResults(fromScene: Scene) { - val (upOrLeftResult, downOrRightResult) = computeSwipesResults(fromScene) + /** + * Update the swipes results. + * + * Usually we don't want to update them while doing a drag, because this could change the target + * content (jump cutting) to a different content, when some system state changed the targets the + * background. However, an update is needed any time we calculate the targets for a new + * fromContent. + */ + fun updateSwipesResults(fromContent: Content) { + val (upOrLeftResult, downOrRightResult) = computeSwipesResults(fromContent) this.upOrLeftResult = upOrLeftResult this.downOrRightResult = downOrRightResult } /** - * Returns the [UserActionResult] from [fromScene] in the direction of [directionOffset]. + * Returns the [UserActionResult] in the direction of [directionOffset]. * - * @param fromScene the scene from which we look for the target * @param directionOffset signed float that indicates the direction. Positive is down or right * negative is up or left. - * @param updateSwipesResults whether the target scenes should be updated to the current values - * held in the Scenes map. Usually we don't want to update them while doing a drag, because - * this could change the target scene (jump cutting) to a different scene, when some system - * state changed the targets the background. However, an update is needed any time we - * calculate the targets for a new fromScene. * @return null when there are no targets in either direction. If one direction is null and you * drag into the null direction this function will return the opposite direction, assuming * that the users intention is to start the drag into the other direction eventually. If * [directionOffset] is 0f and both direction are available, it will default to * [upOrLeftResult]. */ - fun findUserActionResult( - fromScene: Scene, - directionOffset: Float, - updateSwipesResults: Boolean, - ): UserActionResult? { - if (updateSwipesResults) { - updateSwipesResults(fromScene) - } - + fun findUserActionResult(directionOffset: Float): UserActionResult? { return when { upOrLeftResult == null && downOrRightResult == null -> null (directionOffset < 0f && upOrLeftResult != null) || downOrRightResult == null -> @@ -896,18 +559,6 @@ private class Swipes( else -> downOrRightResult } } - - /** - * A strict version of [findUserActionResult] that will return null when there is no Scene in - * [directionOffset] direction - */ - fun findUserActionResultStrict(directionOffset: Float): UserActionResult? { - return when { - directionOffset > 0f -> upOrLeftResult - directionOffset < 0f -> downOrRightResult - else -> null - } - } } internal class NestedScrollHandlerImpl( @@ -1085,7 +736,7 @@ internal class NestedScrollHandlerImpl( val controller = dragController ?: error("Should be called after onStart") controller - .onStop(velocity = velocityAvailable, canChangeScene = canChangeScene) + .onStop(velocity = velocityAvailable, canChangeContent = canChangeScene) .also { dragController = null } }, ) @@ -1103,5 +754,5 @@ internal const val OffsetVisibilityThreshold = 0.5f private object NoOpDragController : DragController { override fun onDrag(delta: Float) = 0f - override fun onStop(velocity: Float, canChangeScene: Boolean) = 0f + override fun onStop(velocity: Float, canChangeContent: Boolean) = 0f } diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/InterruptionHandler.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/InterruptionHandler.kt index 6181cfbb10eb..cb18c6729170 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/InterruptionHandler.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/InterruptionHandler.kt @@ -37,7 +37,7 @@ interface InterruptionHandler { * @see InterruptionResult */ fun onInterruption( - interrupted: TransitionState.Transition.ChangeCurrentScene, + interrupted: TransitionState.Transition.ChangeScene, newTargetScene: SceneKey, ): InterruptionResult? } @@ -76,7 +76,7 @@ class InterruptionResult( */ object DefaultInterruptionHandler : InterruptionHandler { override fun onInterruption( - interrupted: TransitionState.Transition.ChangeCurrentScene, + interrupted: TransitionState.Transition.ChangeScene, newTargetScene: SceneKey, ): InterruptionResult { return InterruptionResult( 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 3487730945da..fd4c3100aa8d 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 @@ -269,13 +269,13 @@ internal class MultiPointerDraggableNode( velocityTracker.calculateVelocity(maxVelocity) } .toFloat(), - onFling = { controller.onStop(it, canChangeScene = true) } + onFling = { controller.onStop(it, canChangeContent = true) } ) }, onDragCancel = { controller -> startFlingGesture( initialVelocity = 0f, - onFling = { controller.onStop(it, canChangeScene = true) } + onFling = { controller.onStop(it, canChangeContent = true) } ) }, swipeDetector = swipeDetector, diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/ObservableTransitionState.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/ObservableTransitionState.kt index 236e202749b2..3a7c2bf5d331 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/ObservableTransitionState.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/ObservableTransitionState.kt @@ -43,7 +43,7 @@ sealed interface ObservableTransitionState { fun currentScene(): Flow<SceneKey> { return when (this) { is Idle -> flowOf(currentScene) - is Transition.ChangeCurrentScene -> currentScene + is Transition.ChangeScene -> currentScene is Transition.ShowOrHideOverlay -> flowOf(currentScene) is Transition.ReplaceOverlay -> flowOf(currentScene) } @@ -94,7 +94,7 @@ sealed interface ObservableTransitionState { .trimMargin() /** A transition animating between [fromScene] and [toScene]. */ - class ChangeCurrentScene( + class ChangeScene( override val fromScene: SceneKey, override val toScene: SceneKey, val currentScene: Flow<SceneKey>, @@ -174,8 +174,8 @@ sealed interface ObservableTransitionState { previewProgress: Flow<Float> = flowOf(0f), isInPreviewStage: Flow<Boolean> = flowOf(false), currentOverlays: Flow<Set<OverlayKey>> = flowOf(emptySet()), - ): ChangeCurrentScene { - return ChangeCurrentScene( + ): ChangeScene { + return ChangeScene( fromScene, toScene, currentScene, @@ -190,7 +190,7 @@ sealed interface ObservableTransitionState { } } - fun isIdle(scene: SceneKey?): Boolean { + fun isIdle(scene: SceneKey? = null): Boolean { return this is Idle && (scene == null || this.currentScene == scene) } @@ -210,8 +210,8 @@ fun SceneTransitionLayoutState.observableTransitionState(): Flow<ObservableTrans return snapshotFlow { when (val state = transitionState) { is TransitionState.Idle -> ObservableTransitionState.Idle(state.currentScene) - is TransitionState.Transition.ChangeCurrentScene -> { - ObservableTransitionState.Transition.ChangeCurrentScene( + is TransitionState.Transition.ChangeScene -> { + ObservableTransitionState.Transition.ChangeScene( fromScene = state.fromScene, toScene = state.toScene, currentScene = snapshotFlow { state.currentScene }, diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/PredictiveBackHandler.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/PredictiveBackHandler.kt index e7e6b2a257d8..be4fea10602f 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/PredictiveBackHandler.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/PredictiveBackHandler.kt @@ -18,120 +18,75 @@ package com.android.compose.animation.scene import androidx.activity.BackEventCompat import androidx.activity.compose.PredictiveBackHandler -import androidx.compose.animation.core.Animatable -import androidx.compose.animation.core.AnimationVector1D +import androidx.compose.animation.core.spring +import androidx.compose.foundation.gestures.Orientation import androidx.compose.runtime.Composable -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableFloatStateOf -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.setValue -import com.android.compose.animation.scene.content.state.TransitionState import kotlin.coroutines.cancellation.CancellationException -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.CoroutineStart -import kotlinx.coroutines.Job import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.first -import kotlinx.coroutines.launch @Composable internal fun PredictiveBackHandler( - state: MutableSceneTransitionLayoutStateImpl, - coroutineScope: CoroutineScope, - targetSceneForBack: SceneKey? = null, + layoutImpl: SceneTransitionLayoutImpl, + result: UserActionResult?, ) { PredictiveBackHandler( - enabled = targetSceneForBack != null, + enabled = result != null, ) { progress: Flow<BackEventCompat> -> - val fromScene = state.transitionState.currentScene - if (targetSceneForBack == null || targetSceneForBack == fromScene) { + if (result == null) { // Note: We have to collect progress otherwise PredictiveBackHandler will throw. progress.first() return@PredictiveBackHandler } - val transition = - PredictiveBackTransition(state, coroutineScope, fromScene, toScene = targetSceneForBack) - state.startTransition(transition) - try { - progress.collect { backEvent -> transition.dragProgress = backEvent.progress } - - // Back gesture successful. - transition.animateTo(targetSceneForBack) - } catch (e: CancellationException) { - // Back gesture cancelled. - transition.animateTo(fromScene) - } + val animation = + createSwipeAnimation( + layoutImpl, + layoutImpl.coroutineScope, + result, + isUpOrLeft = false, + // Note that the orientation does not matter here given that it's only used to + // compute the distance. In our case the distance is always 1f. + orientation = Orientation.Horizontal, + distance = 1f, + ) + + animate(layoutImpl, animation, progress) } } -private class PredictiveBackTransition( - val state: MutableSceneTransitionLayoutStateImpl, - val coroutineScope: CoroutineScope, - fromScene: SceneKey, - toScene: SceneKey, -) : TransitionState.Transition.ChangeCurrentScene(fromScene, toScene) { - override var currentScene by mutableStateOf(fromScene) - private set - - /** The animated progress once the gesture was committed or cancelled. */ - private var progressAnimatable by mutableStateOf<Animatable<Float, AnimationVector1D>?>(null) - var dragProgress: Float by mutableFloatStateOf(0f) - - override val previewProgress: Float - get() = dragProgress - - override val previewProgressVelocity: Float - get() = 0f // Currently, velocity is not exposed by predictive back API - - override val isInPreviewStage: Boolean - get() = previewTransformationSpec != null && currentScene == fromScene - - override val progress: Float - get() = progressAnimatable?.value ?: previewTransformationSpec?.let { 0f } ?: dragProgress - - override val progressVelocity: Float - get() = progressAnimatable?.velocity ?: 0f - - override val isInitiatedByUserInput: Boolean - get() = true - - override val isUserInputOngoing: Boolean - get() = progressAnimatable == null - - private var animationJob: Job? = null - - override fun finish(): Job = animateTo(currentScene) - - fun animateTo(scene: SceneKey): Job { - check(scene == fromScene || scene == toScene) - animationJob?.let { - return it +private suspend fun <T : ContentKey> animate( + layoutImpl: SceneTransitionLayoutImpl, + animation: SwipeAnimation<T>, + progress: Flow<BackEventCompat>, +) { + fun animateOffset(targetContent: T) { + if ( + layoutImpl.state.transitionState != animation.contentTransition || animation.isFinishing + ) { + return } - if (scene != currentScene && state.transitionState == this && state.canChangeScene(scene)) { - currentScene = scene - } + animation.animateOffset( + initialVelocity = 0f, + targetContent = targetContent, + + // TODO(b/350705972): Allow to customize or reuse the same customization endpoints as + // the normal swipe transitions. We can't just reuse them here because other swipe + // transitions animate pixels while this transition animates progress, so the visibility + // thresholds will be completely different. + spec = spring(), + ) + } - val targetProgress = - when (currentScene) { - fromScene -> 0f - toScene -> 1f - else -> error("scene $currentScene should be either $fromScene or $toScene") - } - val startProgress = if (previewTransformationSpec != null) 0f else dragProgress - val animatable = Animatable(startProgress).also { progressAnimatable = it } + layoutImpl.state.startTransition(animation.contentTransition) + try { + progress.collect { backEvent -> animation.dragOffset = backEvent.progress } - // Important: We start atomically to make sure that we start the coroutine even if it is - // cancelled right after it is launched, so that finishTransition() is correctly called. - return coroutineScope - .launch(start = CoroutineStart.ATOMIC) { - try { - animatable.animateTo(targetProgress) - } finally { - state.finishTransition(this@PredictiveBackTransition) - } - } - .also { animationJob = it } + // Back gesture successful. + animateOffset(animation.toContent) + } catch (e: CancellationException) { + // Back gesture cancelled. + animateOffset(animation.fromContent) } } 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 aaa2546b1d4b..b3f74f749a0e 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 @@ -104,10 +104,10 @@ interface SceneTransitionLayoutScope { * call order. Calling overlay(A) followed by overlay(B) will mean that overlay B renders * after/above overlay A. */ - // TODO(b/353679003): Allow to specify user actions. When overlays are shown, the user actions - // of the top-most overlay in currentOverlays will be used. fun overlay( key: OverlayKey, + userActions: Map<UserAction, UserActionResult> = + mapOf(Back to UserActionResult.HideOverlay(key)), alignment: Alignment = Alignment.Center, content: @Composable ContentScope.() -> Unit, ) @@ -479,20 +479,79 @@ interface SwipeSourceDetector { } /** The result of performing a [UserAction]. */ -data class UserActionResult( - /** The scene we should be transitioning to during the [UserAction]. */ - val toScene: SceneKey, - +sealed class UserActionResult( /** The key of the transition that should be used. */ - val transitionKey: TransitionKey? = null, + open val transitionKey: TransitionKey? = null, /** * If `true`, the swipe will be committed and we will settle to [toScene] if only if the user * swiped at least the swipe distance, i.e. the transition progress was already equal to or * bigger than 100% when the user released their finger. ` */ - val requiresFullDistanceSwipe: Boolean = false, -) + open val requiresFullDistanceSwipe: Boolean, +) { + internal abstract fun toContent(currentScene: SceneKey): ContentKey + + data class ChangeScene + internal constructor( + /** The scene we should be transitioning to during the [UserAction]. */ + val toScene: SceneKey, + override val transitionKey: TransitionKey? = null, + override val requiresFullDistanceSwipe: Boolean = false, + ) : UserActionResult(transitionKey, requiresFullDistanceSwipe) { + override fun toContent(currentScene: SceneKey): ContentKey = toScene + } + + /** A [UserActionResult] that shows [overlay]. */ + class ShowOverlay( + val overlay: OverlayKey, + transitionKey: TransitionKey? = null, + requiresFullDistanceSwipe: Boolean = false, + ) : UserActionResult(transitionKey, requiresFullDistanceSwipe) { + override fun toContent(currentScene: SceneKey): ContentKey = overlay + } + + /** A [UserActionResult] that hides [overlay]. */ + class HideOverlay( + val overlay: OverlayKey, + transitionKey: TransitionKey? = null, + requiresFullDistanceSwipe: Boolean = false, + ) : UserActionResult(transitionKey, requiresFullDistanceSwipe) { + override fun toContent(currentScene: SceneKey): ContentKey = currentScene + } + + /** + * A [UserActionResult] that replaces the current overlay by [overlay]. + * + * Note: This result can only be used for user actions of overlays and an exception will be + * thrown if it is used for a scene. + */ + class ReplaceByOverlay( + val overlay: OverlayKey, + transitionKey: TransitionKey? = null, + requiresFullDistanceSwipe: Boolean = false, + ) : UserActionResult(transitionKey, requiresFullDistanceSwipe) { + override fun toContent(currentScene: SceneKey): ContentKey = overlay + } + + companion object { + /** A [UserActionResult] that changes the current scene to [toScene]. */ + operator fun invoke( + /** The scene we should be transitioning to during the [UserAction]. */ + toScene: SceneKey, + + /** The key of the transition that should be used. */ + transitionKey: TransitionKey? = null, + + /** + * If `true`, the swipe will be committed if only if the user swiped at least the swipe + * distance, i.e. the transition progress was already equal to or bigger than 100% when + * the user released their finger. + */ + requiresFullDistanceSwipe: Boolean = false, + ): UserActionResult = ChangeScene(toScene, transitionKey, requiresFullDistanceSwipe) + } +} fun interface UserActionDistance { /** 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 5f5141e1f153..b33b4f6c5019 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 @@ -16,6 +16,7 @@ package com.android.compose.animation.scene +import androidx.annotation.VisibleForTesting import androidx.compose.foundation.gestures.Orientation import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.BoxScope @@ -31,6 +32,7 @@ import androidx.compose.ui.layout.ApproachMeasureScope import androidx.compose.ui.layout.LookaheadScope import androidx.compose.ui.layout.Measurable import androidx.compose.ui.layout.MeasureResult +import androidx.compose.ui.node.LayoutAwareModifierNode import androidx.compose.ui.node.ModifierNodeElement import androidx.compose.ui.unit.Constraints import androidx.compose.ui.unit.Density @@ -132,6 +134,8 @@ internal class SceneTransitionLayoutImpl( internal lateinit var lookaheadScope: LookaheadScope private set + internal var lastSize: IntSize = IntSize.Zero + init { updateContents(builder, layoutDirection) @@ -179,6 +183,28 @@ internal class SceneTransitionLayoutImpl( } } + internal fun contentForUserActions(): Content { + return findOverlayWithHighestZIndex() ?: scene(state.transitionState.currentScene) + } + + private fun findOverlayWithHighestZIndex(): Overlay? { + val currentOverlays = state.transitionState.currentOverlays + if (currentOverlays.isEmpty()) { + return null + } + + var overlay: Overlay? = null + currentOverlays.forEach { key -> + val previousZIndex = overlay?.zIndex + val candidate = overlay(key) + if (previousZIndex == null || candidate.zIndex > previousZIndex) { + overlay = candidate + } + } + + return overlay + } + internal fun updateContents( builder: SceneTransitionLayoutScope.() -> Unit, layoutDirection: LayoutDirection, @@ -203,8 +229,7 @@ internal class SceneTransitionLayoutImpl( scenesToRemove.remove(key) - val resolvedUserActions = - userActions.mapKeys { it.key.resolve(layoutDirection) } + val resolvedUserActions = resolveUserActions(key, userActions, layoutDirection) val scene = scenes[key] if (scene != null) { // Update an existing scene. @@ -228,6 +253,7 @@ internal class SceneTransitionLayoutImpl( override fun overlay( key: OverlayKey, + userActions: Map<UserAction, UserActionResult>, alignment: Alignment, content: @Composable (ContentScope.() -> Unit) ) { @@ -235,10 +261,12 @@ internal class SceneTransitionLayoutImpl( overlaysToRemove.remove(key) val overlay = overlays[key] + val resolvedUserActions = resolveUserActions(key, userActions, layoutDirection) if (overlay != null) { // Update an existing overlay. overlay.content = content overlay.zIndex = zIndex + overlay.userActions = resolvedUserActions overlay.alignment = alignment } else { // New overlay. @@ -247,8 +275,7 @@ internal class SceneTransitionLayoutImpl( key, this@SceneTransitionLayoutImpl, content, - // TODO(b/353679003): Allow to specify user actions - actions = emptyMap(), + resolvedUserActions, zIndex, alignment, ) @@ -263,6 +290,46 @@ internal class SceneTransitionLayoutImpl( overlaysToRemove.forEach { overlays.remove(it) } } + private fun resolveUserActions( + key: ContentKey, + userActions: Map<UserAction, UserActionResult>, + layoutDirection: LayoutDirection + ): Map<UserAction.Resolved, UserActionResult> { + return userActions + .mapKeys { it.key.resolve(layoutDirection) } + .also { checkUserActions(key, it) } + } + + private fun checkUserActions( + key: ContentKey, + userActions: Map<UserAction.Resolved, UserActionResult>, + ) { + userActions.forEach { (action, result) -> + fun details() = "Content $key, action $action, result $result." + + when (result) { + is UserActionResult.ChangeScene -> { + check(key != result.toScene) { + error("Transition to the same scene is not supported. ${details()}") + } + } + is UserActionResult.ReplaceByOverlay -> { + check(key is OverlayKey) { + "ReplaceByOverlay() can only be used for overlays, not scenes. ${details()}" + } + + check(key != result.overlay) { + "Transition to the same overlay is not supported. ${details()}" + } + } + is UserActionResult.ShowOverlay, + is UserActionResult.HideOverlay -> { + /* Always valid. */ + } + } + } + } + @Composable internal fun Content(modifier: Modifier, swipeDetector: SwipeDetector) { Box( @@ -286,9 +353,8 @@ internal class SceneTransitionLayoutImpl( @Composable private fun BackHandler() { - val targetSceneForBack = - scene(state.transitionState.currentScene).userActions[Back.Resolved]?.toScene - PredictiveBackHandler(state, coroutineScope, targetSceneForBack) + val result = contentForUserActions().userActions[Back.Resolved] + PredictiveBackHandler(layoutImpl = this, result = result) } @Composable @@ -312,7 +378,7 @@ internal class SceneTransitionLayoutImpl( // Compose the new scene we are going to first. transitions.fastForEachReversed { transition -> when (transition) { - is TransitionState.Transition.ChangeCurrentScene -> { + is TransitionState.Transition.ChangeScene -> { maybeAdd(transition.toScene) maybeAdd(transition.fromScene) } @@ -362,7 +428,7 @@ internal class SceneTransitionLayoutImpl( transitions.fastForEach { transition -> when (transition) { - is TransitionState.Transition.ChangeCurrentScene -> {} + is TransitionState.Transition.ChangeScene -> {} is TransitionState.Transition.ShowOrHideOverlay -> maybeAdd(transition.overlay) is TransitionState.Transition.ReplaceOverlay -> { @@ -379,8 +445,10 @@ internal class SceneTransitionLayoutImpl( .sortedBy { it.zIndex } } - internal fun setScenesTargetSizeForTest(size: IntSize) { - scenes.values.forEach { it.targetSize = size } + @VisibleForTesting + internal fun setContentsAndLayoutTargetSizeForTest(size: IntSize) { + lastSize = size + (scenes.values + overlays.values).forEach { it.targetSize = size } } internal fun overlaysOrNullForTest(): Map<OverlayKey, Overlay>? = _overlays @@ -396,7 +464,11 @@ private data class LayoutElement(private val layoutImpl: SceneTransitionLayoutIm } private class LayoutNode(var layoutImpl: SceneTransitionLayoutImpl) : - Modifier.Node(), ApproachLayoutModifierNode { + Modifier.Node(), ApproachLayoutModifierNode, LayoutAwareModifierNode { + override fun onRemeasured(size: IntSize) { + layoutImpl.lastSize = size + } + override fun isMeasurementApproachInProgress(lookaheadSize: IntSize): Boolean { return layoutImpl.state.isTransitioning() } @@ -412,7 +484,7 @@ private class LayoutNode(var layoutImpl: SceneTransitionLayoutImpl) : val width: Int val height: Int val transition = - layoutImpl.state.currentTransition as? TransitionState.Transition.ChangeCurrentScene + layoutImpl.state.currentTransition as? TransitionState.Transition.ChangeScene if (transition == null) { width = placeable.width height = placeable.height 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 0ac69124f7bc..f3128f1bf5c7 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 @@ -183,6 +183,12 @@ sealed interface MutableSceneTransitionLayoutState : SceneTransitionLayoutState * commits a transition to a new scene because of a [UserAction]. If [canChangeScene] returns * `true`, then the gesture will be committed and we will animate to the other scene. Otherwise, * the gesture will be cancelled and we will animate back to the current scene. + * @param canShowOverlay whether we should commit a user action that will result in showing the + * given overlay. + * @param canHideOverlay whether we should commit a user action that will result in hiding the given + * overlay. + * @param canReplaceOverlay whether we should commit a user action that will result in replacing + * `from` overlay by `to` overlay. * @param stateLinks the [StateLink] connecting this [SceneTransitionLayoutState] to other * [SceneTransitionLayoutState]s. */ @@ -191,6 +197,9 @@ fun MutableSceneTransitionLayoutState( transitions: SceneTransitions = SceneTransitions.Empty, initialOverlays: Set<OverlayKey> = emptySet(), canChangeScene: (SceneKey) -> Boolean = { true }, + canShowOverlay: (OverlayKey) -> Boolean = { true }, + canHideOverlay: (OverlayKey) -> Boolean = { true }, + canReplaceOverlay: (from: OverlayKey, to: OverlayKey) -> Boolean = { _, _ -> true }, stateLinks: List<StateLink> = emptyList(), enableInterruptions: Boolean = DEFAULT_INTERRUPTIONS_ENABLED, ): MutableSceneTransitionLayoutState { @@ -199,6 +208,9 @@ fun MutableSceneTransitionLayoutState( transitions, initialOverlays, canChangeScene, + canShowOverlay, + canHideOverlay, + canReplaceOverlay, stateLinks, enableInterruptions, ) @@ -210,6 +222,11 @@ internal class MutableSceneTransitionLayoutStateImpl( override var transitions: SceneTransitions = transitions {}, initialOverlays: Set<OverlayKey> = emptySet(), internal val canChangeScene: (SceneKey) -> Boolean = { true }, + internal val canShowOverlay: (OverlayKey) -> Boolean = { true }, + internal val canHideOverlay: (OverlayKey) -> Boolean = { true }, + internal val canReplaceOverlay: (from: OverlayKey, to: OverlayKey) -> Boolean = { _, _ -> + true + }, private val stateLinks: List<StateLink> = emptyList(), // TODO(b/290930950): Remove this flag. @@ -282,7 +299,7 @@ internal class MutableSceneTransitionLayoutStateImpl( targetScene: SceneKey, coroutineScope: CoroutineScope, transitionKey: TransitionKey?, - ): TransitionState.Transition.ChangeCurrentScene? { + ): TransitionState.Transition.ChangeScene? { checkThread() return coroutineScope.animateToScene( diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeAnimation.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeAnimation.kt new file mode 100644 index 000000000000..57ff597d7314 --- /dev/null +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeAnimation.kt @@ -0,0 +1,649 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.compose.animation.scene + +import androidx.compose.animation.core.Animatable +import androidx.compose.animation.core.AnimationVector1D +import androidx.compose.animation.core.SpringSpec +import androidx.compose.foundation.gestures.Orientation +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableFloatStateOf +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.setValue +import androidx.compose.ui.unit.IntSize +import com.android.compose.animation.scene.content.state.TransitionState +import com.android.compose.animation.scene.content.state.TransitionState.HasOverscrollProperties.Companion.DistanceUnspecified +import kotlin.math.absoluteValue +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.CoroutineStart +import kotlinx.coroutines.Job +import kotlinx.coroutines.launch + +internal fun createSwipeAnimation( + layoutState: MutableSceneTransitionLayoutStateImpl, + animationScope: CoroutineScope, + result: UserActionResult, + isUpOrLeft: Boolean, + orientation: Orientation, + distance: Float, +): SwipeAnimation<*> { + return createSwipeAnimation( + layoutState, + animationScope, + result, + isUpOrLeft, + orientation, + distance = { distance }, + contentForUserActions = { + error("Computing contentForUserActions requires a SceneTransitionLayoutImpl") + }, + ) +} + +internal fun createSwipeAnimation( + layoutImpl: SceneTransitionLayoutImpl, + animationScope: CoroutineScope, + result: UserActionResult, + isUpOrLeft: Boolean, + orientation: Orientation, + distance: Float = DistanceUnspecified +): SwipeAnimation<*> { + var lastDistance = distance + + fun distance(animation: SwipeAnimation<*>): Float { + if (lastDistance != DistanceUnspecified) { + return lastDistance + } + + val absoluteDistance = + with(animation.contentTransition.transformationSpec.distance ?: DefaultSwipeDistance) { + layoutImpl.userActionDistanceScope.absoluteDistance( + layoutImpl.content(animation.fromContent).targetSize, + orientation, + ) + } + + if (absoluteDistance <= 0f) { + return DistanceUnspecified + } + + val distance = if (isUpOrLeft) -absoluteDistance else absoluteDistance + lastDistance = distance + return distance + } + + return createSwipeAnimation( + layoutImpl.state, + animationScope, + result, + isUpOrLeft, + orientation, + distance = ::distance, + contentForUserActions = { layoutImpl.contentForUserActions().key }, + ) +} + +private fun createSwipeAnimation( + layoutState: MutableSceneTransitionLayoutStateImpl, + animationScope: CoroutineScope, + result: UserActionResult, + isUpOrLeft: Boolean, + orientation: Orientation, + distance: (SwipeAnimation<*>) -> Float, + contentForUserActions: () -> ContentKey, +): SwipeAnimation<*> { + fun <T : ContentKey> swipeAnimation(fromContent: T, toContent: T): SwipeAnimation<T> { + return SwipeAnimation( + layoutState = layoutState, + animationScope = animationScope, + fromContent = fromContent, + toContent = toContent, + orientation = orientation, + isUpOrLeft = isUpOrLeft, + requiresFullDistanceSwipe = result.requiresFullDistanceSwipe, + distance = distance, + ) + } + + return when (result) { + is UserActionResult.ChangeScene -> { + val fromScene = layoutState.currentScene + val toScene = result.toScene + ChangeSceneSwipeTransition( + layoutState = layoutState, + swipeAnimation = swipeAnimation(fromContent = fromScene, toContent = toScene), + key = result.transitionKey, + replacedTransition = null, + ) + .swipeAnimation + } + is UserActionResult.ShowOverlay -> { + val fromScene = layoutState.currentScene + val overlay = result.overlay + ShowOrHideOverlaySwipeTransition( + layoutState = layoutState, + fromOrToScene = fromScene, + overlay = overlay, + swipeAnimation = swipeAnimation(fromContent = fromScene, toContent = overlay), + key = result.transitionKey, + replacedTransition = null, + ) + .swipeAnimation + } + is UserActionResult.HideOverlay -> { + val toScene = layoutState.currentScene + val overlay = result.overlay + ShowOrHideOverlaySwipeTransition( + layoutState = layoutState, + fromOrToScene = toScene, + overlay = overlay, + swipeAnimation = swipeAnimation(fromContent = overlay, toContent = toScene), + key = result.transitionKey, + replacedTransition = null, + ) + .swipeAnimation + } + is UserActionResult.ReplaceByOverlay -> { + val fromOverlay = + when (val contentForUserActions = contentForUserActions()) { + is SceneKey -> + error("ReplaceByOverlay can only be called when an overlay is shown") + is OverlayKey -> contentForUserActions + } + + val toOverlay = result.overlay + ReplaceOverlaySwipeTransition( + layoutState = layoutState, + swipeAnimation = + swipeAnimation(fromContent = fromOverlay, toContent = toOverlay), + key = result.transitionKey, + replacedTransition = null, + ) + .swipeAnimation + } + } +} + +internal fun createSwipeAnimation(old: SwipeAnimation<*>): SwipeAnimation<*> { + return when (val transition = old.contentTransition) { + is TransitionState.Transition.ChangeScene -> { + ChangeSceneSwipeTransition(transition as ChangeSceneSwipeTransition).swipeAnimation + } + is TransitionState.Transition.ShowOrHideOverlay -> { + ShowOrHideOverlaySwipeTransition(transition as ShowOrHideOverlaySwipeTransition) + .swipeAnimation + } + is TransitionState.Transition.ReplaceOverlay -> { + ReplaceOverlaySwipeTransition(transition as ReplaceOverlaySwipeTransition) + .swipeAnimation + } + } +} + +/** A helper class that contains the main logic for swipe transitions. */ +internal class SwipeAnimation<T : ContentKey>( + val layoutState: MutableSceneTransitionLayoutStateImpl, + val animationScope: CoroutineScope, + val fromContent: T, + val toContent: T, + override val orientation: Orientation, + override val isUpOrLeft: Boolean, + val requiresFullDistanceSwipe: Boolean, + private val distance: (SwipeAnimation<T>) -> Float, + currentContent: T = fromContent, + dragOffset: Float = 0f, +) : TransitionState.HasOverscrollProperties { + /** The [TransitionState.Transition] whose implementation delegates to this [SwipeAnimation]. */ + lateinit var contentTransition: TransitionState.Transition + + var currentContent by mutableStateOf(currentContent) + + val progress: Float + get() { + // Important: If we are going to return early because distance is equal to 0, we should + // still make sure we read the offset before returning so that the calling code still + // subscribes to the offset value. + val animatable = offsetAnimation?.animatable + val offset = + when { + animatable != null -> animatable.value + contentTransition.previewTransformationSpec != null -> 0f + else -> dragOffset + } + + return computeProgress(offset) + } + + fun computeProgress(offset: Float): Float { + val distance = distance() + if (distance == DistanceUnspecified) { + return 0f + } + return offset / distance + } + + val progressVelocity: Float + get() { + val animatable = offsetAnimation?.animatable ?: return 0f + val distance = distance() + if (distance == DistanceUnspecified) { + return 0f + } + + val velocityInDistanceUnit = animatable.velocity + return velocityInDistanceUnit / distance.absoluteValue + } + + val previewProgress: Float + get() = computeProgress(dragOffset) + + val previewProgressVelocity: Float + get() = 0f + + val isInPreviewStage: Boolean + get() = contentTransition.previewTransformationSpec != null && currentContent == fromContent + + override var bouncingContent: ContentKey? = null + + /** The current offset caused by the drag gesture. */ + var dragOffset by mutableFloatStateOf(dragOffset) + + /** The offset animation that animates the offset once the user lifts their finger. */ + private var offsetAnimation: OffsetAnimation? by mutableStateOf(null) + + val isUserInputOngoing: Boolean + get() = offsetAnimation == null + + override val absoluteDistance: Float + get() = distance().absoluteValue + + /** Whether [finish] was called on this animation. */ + var isFinishing = false + private set + + constructor( + other: SwipeAnimation<T> + ) : this( + layoutState = other.layoutState, + animationScope = other.animationScope, + fromContent = other.fromContent, + toContent = other.toContent, + orientation = other.orientation, + isUpOrLeft = other.isUpOrLeft, + requiresFullDistanceSwipe = other.requiresFullDistanceSwipe, + distance = other.distance, + currentContent = other.currentContent, + dragOffset = other.dragOffset, + ) + + /** + * The signed distance between [fromContent] and [toContent]. It is negative if [fromContent] is + * above or to the left of [toContent]. + * + * Note that this distance can be equal to [DistanceUnspecified] during the first frame of a + * transition when the distance depends on the size or position of an element that is composed + * in the content we are going to. + */ + fun distance(): Float = distance(this) + + /** Ends any previous [offsetAnimation] and runs the new [animation]. */ + private fun startOffsetAnimation(animation: () -> OffsetAnimation): OffsetAnimation { + cancelOffsetAnimation() + return animation().also { offsetAnimation = it } + } + + /** Cancel any ongoing offset animation. */ + // TODO(b/317063114) This should be a suspended function to avoid multiple jobs running at + // the same time. + fun cancelOffsetAnimation() { + val animation = offsetAnimation ?: return + offsetAnimation = null + + dragOffset = animation.animatable.value + animation.job.cancel() + } + + fun animateOffset( + initialVelocity: Float, + targetContent: T, + spec: SpringSpec<Float>? = null, + ): OffsetAnimation { + val initialProgress = progress + // Skip the animation if we have already reached the target content and the overscroll does + // not animate anything. + val hasReachedTargetContent = + (targetContent == toContent && initialProgress >= 1f) || + (targetContent == fromContent && initialProgress <= 0f) + val skipAnimation = + hasReachedTargetContent && !contentTransition.isWithinProgressRange(initialProgress) + + val targetContent = + if (targetContent != currentContent && !canChangeContent(targetContent)) { + currentContent + } else { + targetContent + } + + val targetOffset = + if (targetContent == fromContent) { + 0f + } else { + val distance = distance() + check(distance != DistanceUnspecified) { + "distance is equal to $DistanceUnspecified" + } + distance + } + + // If the effective current content changed, it should be reflected right now in the + // current state, even before the settle animation is ongoing. That way all the + // swipeables and back handlers will be refreshed and the user can for instance quickly + // swipe vertically from A => B then horizontally from B => C, or swipe from A => B then + // immediately go back B => A. + if (targetContent != currentContent) { + currentContent = targetContent + } + + return startOffsetAnimation { + val startProgress = + if (contentTransition.previewTransformationSpec != null) 0f else dragOffset + val animatable = Animatable(startProgress, OffsetVisibilityThreshold) + val isTargetGreater = targetOffset > animatable.value + val startedWhenOvercrollingTargetContent = + if (targetContent == fromContent) initialProgress < 0f else initialProgress > 1f + val job = + animationScope + // Important: We start atomically to make sure that we start the coroutine even + // if it is cancelled right after it is launched, so that snapToContent() is + // correctly called. Otherwise, this transition will never be stopped and we + // will never settle to Idle. + .launch(start = CoroutineStart.ATOMIC) { + // TODO(b/327249191): Refactor the code so that we don't even launch a + // coroutine if we don't need to animate. + if (skipAnimation) { + snapToContent(targetContent) + dragOffset = targetOffset + return@launch + } + + try { + val swipeSpec = + spec + ?: contentTransition.transformationSpec.swipeSpec + ?: layoutState.transitions.defaultSwipeSpec + animatable.animateTo( + targetValue = targetOffset, + animationSpec = swipeSpec, + initialVelocity = initialVelocity, + ) { + if (bouncingContent == null) { + val isBouncing = + if (isTargetGreater) { + if (startedWhenOvercrollingTargetContent) { + value >= targetOffset + } else { + value > targetOffset + } + } else { + if (startedWhenOvercrollingTargetContent) { + value <= targetOffset + } else { + value < targetOffset + } + } + + if (isBouncing) { + bouncingContent = targetContent + + // Immediately stop this transition if we are bouncing on a + // content that does not bounce. + if (!contentTransition.isWithinProgressRange(progress)) { + snapToContent(targetContent) + } + } + } + } + } finally { + snapToContent(targetContent) + } + } + + OffsetAnimation(animatable, job) + } + } + + private fun canChangeContent(targetContent: ContentKey): Boolean { + return when (val transition = contentTransition) { + is TransitionState.Transition.ChangeScene -> + layoutState.canChangeScene(targetContent as SceneKey) + is TransitionState.Transition.ShowOrHideOverlay -> { + if (targetContent == transition.overlay) { + layoutState.canShowOverlay(transition.overlay) + } else { + layoutState.canHideOverlay(transition.overlay) + } + } + is TransitionState.Transition.ReplaceOverlay -> { + val to = targetContent as OverlayKey + val from = + if (to == transition.toOverlay) transition.fromOverlay else transition.toOverlay + layoutState.canReplaceOverlay(from, to) + } + } + } + + private fun snapToContent(content: T) { + cancelOffsetAnimation() + check(currentContent == content) + layoutState.finishTransition(contentTransition) + } + + fun finish(): Job { + if (isFinishing) return requireNotNull(offsetAnimation).job + isFinishing = true + + // If we were already animating the offset, simply return the job. + offsetAnimation?.let { + return it.job + } + + // Animate to the current content. + val animation = animateOffset(initialVelocity = 0f, targetContent = currentContent) + check(offsetAnimation == animation) + return animation.job + } + + internal class OffsetAnimation( + /** The animatable used to animate the offset. */ + val animatable: Animatable<Float, AnimationVector1D>, + + /** The job in which [animatable] is animated. */ + val job: Job, + ) +} + +private object DefaultSwipeDistance : UserActionDistance { + override fun UserActionDistanceScope.absoluteDistance( + fromSceneSize: IntSize, + orientation: Orientation, + ): Float { + return when (orientation) { + Orientation.Horizontal -> fromSceneSize.width + Orientation.Vertical -> fromSceneSize.height + }.toFloat() + } +} + +private class ChangeSceneSwipeTransition( + val layoutState: MutableSceneTransitionLayoutStateImpl, + val swipeAnimation: SwipeAnimation<SceneKey>, + override val key: TransitionKey?, + replacedTransition: ChangeSceneSwipeTransition?, +) : + TransitionState.Transition.ChangeScene( + swipeAnimation.fromContent, + swipeAnimation.toContent, + replacedTransition, + ), + TransitionState.HasOverscrollProperties by swipeAnimation { + + constructor( + other: ChangeSceneSwipeTransition + ) : this( + layoutState = other.layoutState, + swipeAnimation = SwipeAnimation(other.swipeAnimation), + key = other.key, + replacedTransition = other, + ) + + init { + swipeAnimation.contentTransition = this + } + + override val currentScene: SceneKey + get() = swipeAnimation.currentContent + + override val progress: Float + get() = swipeAnimation.progress + + override val progressVelocity: Float + get() = swipeAnimation.progressVelocity + + override val previewProgress: Float + get() = swipeAnimation.previewProgress + + override val previewProgressVelocity: Float + get() = swipeAnimation.previewProgressVelocity + + override val isInPreviewStage: Boolean + get() = swipeAnimation.isInPreviewStage + + override val isInitiatedByUserInput: Boolean = true + + override val isUserInputOngoing: Boolean + get() = swipeAnimation.isUserInputOngoing + + override fun finish(): Job = swipeAnimation.finish() +} + +private class ShowOrHideOverlaySwipeTransition( + val layoutState: MutableSceneTransitionLayoutStateImpl, + val swipeAnimation: SwipeAnimation<ContentKey>, + overlay: OverlayKey, + fromOrToScene: SceneKey, + override val key: TransitionKey?, + replacedTransition: ShowOrHideOverlaySwipeTransition?, +) : + TransitionState.Transition.ShowOrHideOverlay( + overlay, + fromOrToScene, + swipeAnimation.fromContent, + swipeAnimation.toContent, + replacedTransition, + ), + TransitionState.HasOverscrollProperties by swipeAnimation { + constructor( + other: ShowOrHideOverlaySwipeTransition + ) : this( + layoutState = other.layoutState, + swipeAnimation = SwipeAnimation(other.swipeAnimation), + overlay = other.overlay, + fromOrToScene = other.fromOrToScene, + key = other.key, + replacedTransition = other, + ) + + init { + swipeAnimation.contentTransition = this + } + + override val isEffectivelyShown: Boolean + get() = swipeAnimation.currentContent == overlay + + override val progress: Float + get() = swipeAnimation.progress + + override val progressVelocity: Float + get() = swipeAnimation.progressVelocity + + override val previewProgress: Float + get() = swipeAnimation.previewProgress + + override val previewProgressVelocity: Float + get() = swipeAnimation.previewProgressVelocity + + override val isInPreviewStage: Boolean + get() = swipeAnimation.isInPreviewStage + + override val isInitiatedByUserInput: Boolean = true + + override val isUserInputOngoing: Boolean + get() = swipeAnimation.isUserInputOngoing + + override fun finish(): Job = swipeAnimation.finish() +} + +private class ReplaceOverlaySwipeTransition( + val layoutState: MutableSceneTransitionLayoutStateImpl, + val swipeAnimation: SwipeAnimation<OverlayKey>, + override val key: TransitionKey?, + replacedTransition: ReplaceOverlaySwipeTransition?, +) : + TransitionState.Transition.ReplaceOverlay( + swipeAnimation.fromContent, + swipeAnimation.toContent, + replacedTransition, + ), + TransitionState.HasOverscrollProperties by swipeAnimation { + constructor( + other: ReplaceOverlaySwipeTransition + ) : this( + layoutState = other.layoutState, + swipeAnimation = SwipeAnimation(other.swipeAnimation), + key = other.key, + replacedTransition = other, + ) + + init { + swipeAnimation.contentTransition = this + } + + override val effectivelyShownOverlay: OverlayKey + get() = swipeAnimation.currentContent + + override val progress: Float + get() = swipeAnimation.progress + + override val progressVelocity: Float + get() = swipeAnimation.progressVelocity + + override val previewProgress: Float + get() = swipeAnimation.previewProgress + + override val previewProgressVelocity: Float + get() = swipeAnimation.previewProgressVelocity + + override val isInPreviewStage: Boolean + get() = swipeAnimation.isInPreviewStage + + override val isInitiatedByUserInput: Boolean = true + + override val isUserInputOngoing: Boolean + get() = swipeAnimation.isUserInputOngoing + + override fun finish(): Job = swipeAnimation.finish() +} diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeToScene.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeToScene.kt index d1e83bacf40a..dc7eda5b9cf6 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeToScene.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeToScene.kt @@ -31,7 +31,7 @@ import androidx.compose.ui.node.PointerInputModifierNode import androidx.compose.ui.node.TraversableNode import androidx.compose.ui.node.findNearestAncestor import androidx.compose.ui.unit.IntSize -import com.android.compose.animation.scene.content.Scene +import com.android.compose.animation.scene.content.Content /** * Configures the swipeable behavior of a [SceneTransitionLayout] depending on the current state. @@ -126,16 +126,15 @@ private class SwipeToSceneNode( private fun enabled(): Boolean { return draggableHandler.isDrivingTransition || - currentScene().shouldEnableSwipes(multiPointerDraggableNode.orientation) + contentForSwipes().shouldEnableSwipes(multiPointerDraggableNode.orientation) } - private fun currentScene(): Scene { - val layoutImpl = draggableHandler.layoutImpl - return layoutImpl.scene(layoutImpl.state.transitionState.currentScene) + private fun contentForSwipes(): Content { + return draggableHandler.layoutImpl.contentForUserActions() } /** Whether swipe should be enabled in the given [orientation]. */ - private fun Scene.shouldEnableSwipes(orientation: Orientation): Boolean { + private fun Content.shouldEnableSwipes(orientation: Orientation): Boolean { return userActions.keys.any { it is Swipe.Resolved && it.direction.orientation == orientation } @@ -153,7 +152,7 @@ private class SwipeToSceneNode( Orientation.Vertical -> Orientation.Horizontal Orientation.Horizontal -> Orientation.Vertical } - return currentScene().shouldEnableSwipes(oppositeOrientation) + return contentForSwipes().shouldEnableSwipes(oppositeOrientation) } } diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/content/Content.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/content/Content.kt index 6bc1754150fe..59dd896ad9ea 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/content/Content.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/content/Content.kt @@ -66,27 +66,7 @@ internal sealed class Content( var content by mutableStateOf(content) var zIndex by mutableFloatStateOf(zIndex) var targetSize by mutableStateOf(IntSize.Zero) - - private var _userActions by mutableStateOf(checkValid(actions)) - var userActions - get() = _userActions - set(value) { - _userActions = checkValid(value) - } - - private fun checkValid( - userActions: Map<UserAction.Resolved, UserActionResult> - ): Map<UserAction.Resolved, UserActionResult> { - userActions.forEach { (action, result) -> - if (key == result.toScene) { - error( - "Transition to the same content (scene/overlay) is not supported. Content " + - "$key, action $action, result $result" - ) - } - } - return userActions - } + var userActions by mutableStateOf(actions) @Composable fun Content(modifier: Modifier = Modifier) { diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/content/state/TransitionState.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/content/state/TransitionState.kt index fdb019f5a604..0cd8c1af0507 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/content/state/TransitionState.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/content/state/TransitionState.kt @@ -26,7 +26,6 @@ import androidx.compose.runtime.getValue import com.android.compose.animation.scene.ContentKey import com.android.compose.animation.scene.MutableSceneTransitionLayoutState import com.android.compose.animation.scene.OverlayKey -import com.android.compose.animation.scene.OverscrollScope import com.android.compose.animation.scene.OverscrollSpecImpl import com.android.compose.animation.scene.ProgressVisibilityThreshold import com.android.compose.animation.scene.SceneKey @@ -75,7 +74,7 @@ sealed interface TransitionState { val replacedTransition: Transition? = null, ) : TransitionState { /** A transition animating between [fromScene] and [toScene]. */ - abstract class ChangeCurrentScene( + abstract class ChangeScene( /** The scene this transition is starting from. Can't be the same as toScene */ val fromScene: SceneKey, @@ -386,10 +385,10 @@ sealed interface TransitionState { val orientation: Orientation /** - * Scope which can be used in the Overscroll DSL to define a transformation based on the - * distance between [Transition.fromContent] and [Transition.toContent]. + * Return the absolute distance between fromScene and toScene, if available, otherwise + * [DistanceUnspecified]. */ - val overscrollScope: OverscrollScope + val absoluteDistance: Float /** * The content (scene or overlay) around which the transition is currently bouncing. When 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 59bca50f7d5b..8f845866a0f3 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 @@ -17,6 +17,7 @@ package com.android.compose.animation.scene.transformation import androidx.compose.ui.geometry.Offset +import androidx.compose.ui.unit.Density import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import com.android.compose.animation.scene.ContentKey @@ -53,6 +54,8 @@ internal class OverscrollTranslate( val x: OverscrollScope.() -> Float = { 0f }, val y: OverscrollScope.() -> Float = { 0f }, ) : PropertyTransformation<Offset> { + private val cachedOverscrollScope = CachedOverscrollScope() + override fun transform( layoutImpl: SceneTransitionLayoutImpl, content: ContentKey, @@ -65,10 +68,47 @@ internal class OverscrollTranslate( // OverscrollSpec only when the transition implements HasOverscrollProperties, we can assume // that this method was invoked after performing this check. val overscrollProperties = transition as TransitionState.HasOverscrollProperties + val overscrollScope = + cachedOverscrollScope.getFromCacheOrCompute(layoutImpl.density, overscrollProperties) return Offset( - x = value.x + overscrollProperties.overscrollScope.x(), - y = value.y + overscrollProperties.overscrollScope.y(), + x = value.x + overscrollScope.x(), + y = value.y + overscrollScope.y(), ) } } + +/** + * A helper class to cache a [OverscrollScope] given a [Density] and + * [TransitionState.HasOverscrollProperties]. This helps avoid recreating a scope every frame + * whenever an overscroll transition is computed. + */ +private class CachedOverscrollScope() { + private var previousScope: OverscrollScope? = null + private var previousDensity: Density? = null + private var previousOverscrollProperties: TransitionState.HasOverscrollProperties? = null + + fun getFromCacheOrCompute( + density: Density, + overscrollProperties: TransitionState.HasOverscrollProperties, + ): OverscrollScope { + if ( + previousScope == null || + density != previousDensity || + previousOverscrollProperties != overscrollProperties + ) { + val scope = + object : OverscrollScope, Density by density { + override val absoluteDistance: Float + get() = overscrollProperties.absoluteDistance + } + + previousScope = scope + previousDensity = density + previousOverscrollProperties = overscrollProperties + return scope + } + + return checkNotNull(previousScope) + } +} 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 59ddb1354073..564d4b3a3c5a 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 @@ -27,7 +27,7 @@ internal class LinkedTransition( fromScene: SceneKey, toScene: SceneKey, override val key: TransitionKey? = null, -) : TransitionState.Transition.ChangeCurrentScene(fromScene, toScene) { +) : TransitionState.Transition.ChangeScene(fromScene, toScene) { override val currentScene: SceneKey get() { diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/DraggableHandlerTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/DraggableHandlerTest.kt index 7d8e898e9ab2..9fa4722cf86f 100644 --- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/DraggableHandlerTest.kt +++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/DraggableHandlerTest.kt @@ -31,6 +31,8 @@ import androidx.test.ext.junit.runners.AndroidJUnit4 import com.android.compose.animation.scene.NestedScrollBehavior.EdgeAlways import com.android.compose.animation.scene.NestedScrollBehavior.EdgeNoPreview import com.android.compose.animation.scene.NestedScrollBehavior.EdgeWithPreview +import com.android.compose.animation.scene.TestOverlays.OverlayA +import com.android.compose.animation.scene.TestOverlays.OverlayB import com.android.compose.animation.scene.TestScenes.SceneA import com.android.compose.animation.scene.TestScenes.SceneB import com.android.compose.animation.scene.TestScenes.SceneC @@ -52,7 +54,7 @@ private val LAYOUT_SIZE = IntSize(SCREEN_SIZE.toInt(), SCREEN_SIZE.toInt()) @RunWith(AndroidJUnit4::class) class DraggableHandlerTest { private class TestGestureScope( - private val testScope: MonotonicClockTestScope, + val testScope: MonotonicClockTestScope, ) { var canChangeScene: (SceneKey) -> Boolean = { true } val layoutState = @@ -103,6 +105,21 @@ class DraggableHandlerTest { ) { Text("SceneC") } + overlay( + key = OverlayA, + userActions = + mapOf( + Swipe.Up to UserActionResult.HideOverlay(OverlayA), + Swipe.Down to UserActionResult.ReplaceByOverlay(OverlayB) + ), + ) { + Text("OverlayA") + } + overlay( + key = OverlayB, + ) { + Text("OverlayB") + } } val transitionInterceptionThreshold = 0.05f @@ -117,7 +134,7 @@ class DraggableHandlerTest { builder = scenesBuilder, coroutineScope = testScope, ) - .apply { setScenesTargetSizeForTest(LAYOUT_SIZE) } + .apply { setContentsAndLayoutTargetSizeForTest(LAYOUT_SIZE) } val draggableHandler = layoutImpl.draggableHandler(Orientation.Vertical) val horizontalDraggableHandler = layoutImpl.draggableHandler(Orientation.Horizontal) @@ -459,13 +476,14 @@ class DraggableHandlerTest { private fun TestGestureScope.navigateToSceneC() { assertIdle(currentScene = SceneA) val dragController = onDragStarted(overSlop = down(fractionOfScreen = 1f)) + assertTransition(currentScene = SceneA, fromScene = SceneA, toScene = SceneC) dragController.onDragStopped(velocity = 0f) advanceUntilIdle() assertIdle(currentScene = SceneC) } @Test - fun onAccelaratedScroll_scrollToThirdScene() = runGestureTest { + fun onAcceleratedScroll_scrollToThirdScene() = runGestureTest { // Drag A -> B with progress 0.2 val dragController1 = onDragStarted(overSlop = up(fractionOfScreen = 0.2f)) assertTransition( @@ -500,7 +518,7 @@ class DraggableHandlerTest { } @Test - fun onAccelaratedScrollBothTargetsBecomeNull_settlesToIdle() = runGestureTest { + fun onAcceleratedScrollBothTargetsBecomeNull_settlesToIdle() = runGestureTest { val dragController1 = onDragStarted(overSlop = up(fractionOfScreen = 0.2f)) dragController1.onDragDelta(pixels = up(fractionOfScreen = 0.2f)) dragController1.onDragStopped(velocity = -velocityThreshold) @@ -1276,4 +1294,87 @@ class DraggableHandlerTest { assertThat(newTransition).isNotSameInstanceAs(transition) assertThat(newTransition.replacedTransition).isSameInstanceAs(transition) } + + @Test + fun showOverlay() = runGestureTest { + mutableUserActionsA = mapOf(Swipe.Down to UserActionResult.ShowOverlay(OverlayA)) + + // Initial state. + assertThat(layoutState.transitionState).isIdle() + assertThat(layoutState.transitionState).hasCurrentScene(SceneA) + assertThat(layoutState.transitionState).hasCurrentOverlays(/* empty */ ) + + // Swipe down to show overlay A. + val controller = onDragStarted(overSlop = down(0.1f)) + val transition = assertThat(layoutState.transitionState).isShowOrHideOverlayTransition() + assertThat(transition).hasCurrentScene(SceneA) + assertThat(transition).hasFromOrToScene(SceneA) + assertThat(transition).hasOverlay(OverlayA) + assertThat(transition).hasCurrentOverlays(/* empty, gesture not committed yet. */ ) + assertThat(transition).hasProgress(0.1f) + + // Commit the gesture. The overlay is instantly added in the set of current overlays. + controller.onDragStopped(velocityThreshold) + assertThat(transition).hasCurrentOverlays(OverlayA) + advanceUntilIdle() + assertThat(layoutState.transitionState).isIdle() + assertThat(layoutState.transitionState).hasCurrentScene(SceneA) + assertThat(layoutState.transitionState).hasCurrentOverlays(OverlayA) + } + + @Test + fun hideOverlay() = runGestureTest { + layoutState.showOverlay(OverlayA, animationScope = testScope) + advanceUntilIdle() + + // Initial state. + assertThat(layoutState.transitionState).isIdle() + assertThat(layoutState.transitionState).hasCurrentScene(SceneA) + assertThat(layoutState.transitionState).hasCurrentOverlays(OverlayA) + + // Swipe up to hide overlay A. + val controller = onDragStarted(overSlop = up(0.1f)) + val transition = assertThat(layoutState.transitionState).isShowOrHideOverlayTransition() + assertThat(transition).hasCurrentScene(SceneA) + assertThat(transition).hasFromOrToScene(SceneA) + assertThat(transition).hasOverlay(OverlayA) + assertThat(transition).hasCurrentOverlays(OverlayA) + assertThat(transition).hasProgress(0.1f) + + // Commit the gesture. The overlay is instantly removed from the set of current overlays. + controller.onDragStopped(-velocityThreshold) + assertThat(transition).hasCurrentOverlays(/* empty */ ) + advanceUntilIdle() + assertThat(layoutState.transitionState).isIdle() + assertThat(layoutState.transitionState).hasCurrentScene(SceneA) + assertThat(layoutState.transitionState).hasCurrentOverlays(/* empty */ ) + } + + @Test + fun replaceOverlay() = runGestureTest { + layoutState.showOverlay(OverlayA, animationScope = testScope) + advanceUntilIdle() + + // Initial state. + assertThat(layoutState.transitionState).isIdle() + assertThat(layoutState.transitionState).hasCurrentScene(SceneA) + assertThat(layoutState.transitionState).hasCurrentOverlays(OverlayA) + + // Swipe down to replace overlay A by overlay B. + val controller = onDragStarted(overSlop = down(0.1f)) + val transition = assertThat(layoutState.transitionState).isReplaceOverlayTransition() + assertThat(transition).hasCurrentScene(SceneA) + assertThat(transition).hasFromOverlay(OverlayA) + assertThat(transition).hasToOverlay(OverlayB) + assertThat(transition).hasCurrentOverlays(OverlayA) + assertThat(transition).hasProgress(0.1f) + + // Commit the gesture. The overlays are instantly swapped in the set of current overlays. + controller.onDragStopped(velocityThreshold) + assertThat(transition).hasCurrentOverlays(OverlayB) + advanceUntilIdle() + assertThat(layoutState.transitionState).isIdle() + assertThat(layoutState.transitionState).hasCurrentScene(SceneA) + assertThat(layoutState.transitionState).hasCurrentOverlays(OverlayB) + } } diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/InterruptionHandlerTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/InterruptionHandlerTest.kt index f4e60a2a4100..3f6bd2c38792 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 @@ -69,7 +69,7 @@ class InterruptionHandlerTest { interruptionHandler = object : InterruptionHandler { override fun onInterruption( - interrupted: TransitionState.Transition.ChangeCurrentScene, + interrupted: TransitionState.Transition.ChangeScene, newTargetScene: SceneKey ): InterruptionResult { return InterruptionResult( @@ -104,7 +104,7 @@ class InterruptionHandlerTest { interruptionHandler = object : InterruptionHandler { override fun onInterruption( - interrupted: TransitionState.Transition.ChangeCurrentScene, + interrupted: TransitionState.Transition.ChangeScene, newTargetScene: SceneKey ): InterruptionResult { return InterruptionResult( @@ -198,7 +198,7 @@ class InterruptionHandlerTest { companion object { val FromToCurrentTriple = Correspondence.transforming( - { transition: TransitionState.Transition.ChangeCurrentScene? -> + { transition: TransitionState.Transition.ChangeScene? -> Triple(transition?.fromScene, transition?.toScene, transition?.currentScene) }, "(from, to, current) triple" diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/MovableElementTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/MovableElementTest.kt index a549d0355a26..e4879d9d8a31 100644 --- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/MovableElementTest.kt +++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/MovableElementTest.kt @@ -163,7 +163,7 @@ class MovableElementTest { fromContentZIndex: Float, toContentZIndex: Float ): ContentKey { - transition as TransitionState.Transition.ChangeCurrentScene + transition as TransitionState.Transition.ChangeScene assertThat(transition).hasFromScene(SceneA) assertThat(transition).hasToScene(SceneB) assertThat(fromContentZIndex).isEqualTo(0) 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 2d37a0d23320..d742592b518a 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 @@ -68,7 +68,7 @@ class MultiPointerDraggableTest { return delta } - override fun onStop(velocity: Float, canChangeScene: Boolean): Float { + override fun onStop(velocity: Float, canChangeContent: Boolean): Float { onStop.invoke(velocity) return velocity } diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/OverlayTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/OverlayTest.kt index 85db418f6020..bec2bb2baa3c 100644 --- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/OverlayTest.kt +++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/OverlayTest.kt @@ -275,7 +275,7 @@ class OverlayTest { rule.setContent { SceneTransitionLayout(state, Modifier.size(200.dp)) { scene(SceneA) { Box(Modifier.fillMaxSize()) { Foo() } } - overlay(OverlayA, alignment) { Foo() } + overlay(OverlayA, alignment = alignment) { Foo() } } } diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/PredictiveBackHandlerTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/PredictiveBackHandlerTest.kt index 00c75882a587..c5b6cdf12385 100644 --- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/PredictiveBackHandlerTest.kt +++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/PredictiveBackHandlerTest.kt @@ -20,10 +20,16 @@ import androidx.activity.BackEventCompat import androidx.activity.ComponentActivity import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.size import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Modifier +import androidx.compose.ui.test.assertIsDisplayed +import androidx.compose.ui.test.hasTestTag import androidx.compose.ui.test.junit4.createAndroidComposeRule +import androidx.compose.ui.unit.dp import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.android.compose.animation.scene.TestOverlays.OverlayA +import com.android.compose.animation.scene.TestOverlays.OverlayB import com.android.compose.animation.scene.TestScenes.SceneA import com.android.compose.animation.scene.TestScenes.SceneB import com.android.compose.animation.scene.TestScenes.SceneC @@ -198,6 +204,42 @@ class PredictiveBackHandlerTest { assertThat(canChangeSceneCalled).isFalse() } + @Test + fun backDismissesOverlayWithHighestZIndexByDefault() { + val state = + rule.runOnUiThread { + MutableSceneTransitionLayoutState( + SceneA, + initialOverlays = setOf(OverlayA, OverlayB) + ) + } + + rule.setContent { + SceneTransitionLayout(state, Modifier.size(200.dp)) { + scene(SceneA) { Box(Modifier.fillMaxSize()) } + overlay(OverlayA) { Box(Modifier.fillMaxSize()) } + overlay(OverlayB) { Box(Modifier.fillMaxSize()) } + } + } + + // Initial state. + rule.onNode(hasTestTag(SceneA.testTag)).assertIsDisplayed() + rule.onNode(hasTestTag(OverlayA.testTag)).assertIsDisplayed() + rule.onNode(hasTestTag(OverlayB.testTag)).assertIsDisplayed() + + // Press back. This should hide overlay B because it has a higher zIndex than overlay A. + rule.runOnUiThread { rule.activity.onBackPressedDispatcher.onBackPressed() } + rule.onNode(hasTestTag(SceneA.testTag)).assertIsDisplayed() + rule.onNode(hasTestTag(OverlayA.testTag)).assertIsDisplayed() + rule.onNode(hasTestTag(OverlayB.testTag)).assertDoesNotExist() + + // Press back again. This should hide overlay A. + rule.runOnUiThread { rule.activity.onBackPressedDispatcher.onBackPressed() } + rule.onNode(hasTestTag(SceneA.testTag)).assertIsDisplayed() + rule.onNode(hasTestTag(OverlayA.testTag)).assertDoesNotExist() + rule.onNode(hasTestTag(OverlayB.testTag)).assertDoesNotExist() + } + private fun backEvent(progress: Float = 0f): BackEventCompat { return BackEventCompat( touchX = 0f, 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 1f7fe3766971..467031afb262 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 @@ -42,9 +42,9 @@ fun transition( orientation: Orientation = Orientation.Horizontal, onFinish: ((TransitionState.Transition) -> Job)? = null, replacedTransition: TransitionState.Transition? = null, -): TransitionState.Transition.ChangeCurrentScene { +): TransitionState.Transition.ChangeScene { return object : - TransitionState.Transition.ChangeCurrentScene(from, to, replacedTransition), + TransitionState.Transition.ChangeScene(from, to, replacedTransition), TransitionState.HasOverscrollProperties { override val currentScene: SceneKey get() = current() @@ -69,12 +69,7 @@ fun transition( override val isUpOrLeft: Boolean = isUpOrLeft override val bouncingContent: ContentKey? = bouncingContent override val orientation: Orientation = orientation - override val overscrollScope: OverscrollScope = - object : OverscrollScope { - override val density: Float = 1f - override val fontScale: Float = 1f - override val absoluteDistance = 0f - } + override val absoluteDistance = 0f override fun finish(): Job { val onFinish = diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/subjects/TransitionStateSubject.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/subjects/TransitionStateSubject.kt index a98bd7652c4e..44e0ba51f713 100644 --- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/subjects/TransitionStateSubject.kt +++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/subjects/TransitionStateSubject.kt @@ -17,6 +17,7 @@ package com.android.compose.animation.scene.subjects import com.android.compose.animation.scene.ContentKey +import com.android.compose.animation.scene.OverlayKey import com.android.compose.animation.scene.OverscrollSpec import com.android.compose.animation.scene.SceneKey import com.android.compose.animation.scene.content.state.TransitionState @@ -31,9 +32,25 @@ fun assertThat(state: TransitionState): TransitionStateSubject { return Truth.assertAbout(TransitionStateSubject.transitionStates()).that(state) } -/** Assert on a [TransitionState.Transition.ChangeCurrentScene]. */ -fun assertThat(transition: TransitionState.Transition.ChangeCurrentScene): SceneTransitionSubject { - return Truth.assertAbout(SceneTransitionSubject.transitions()).that(transition) +/** Assert on a [TransitionState.Transition.ChangeScene]. */ +fun assertThat(transition: TransitionState.Transition.ChangeScene): SceneTransitionSubject { + return Truth.assertAbout(SceneTransitionSubject.sceneTransitions()).that(transition) +} + +/** Assert on a [TransitionState.Transition.ShowOrHideOverlay]. */ +fun assertThat( + transition: TransitionState.Transition.ShowOrHideOverlay, +): ShowOrHideOverlayTransitionSubject { + return Truth.assertAbout(ShowOrHideOverlayTransitionSubject.showOrHideOverlayTransitions()) + .that(transition) +} + +/** Assert on a [TransitionState.Transition.ReplaceOverlay]. */ +fun assertThat( + transition: TransitionState.Transition.ReplaceOverlay, +): ReplaceOverlayTransitionSubject { + return Truth.assertAbout(ReplaceOverlayTransitionSubject.replaceOverlayTransitions()) + .that(transition) } class TransitionStateSubject @@ -45,6 +62,10 @@ private constructor( check("currentScene").that(actual.currentScene).isEqualTo(sceneKey) } + fun hasCurrentOverlays(vararg overlays: OverlayKey) { + check("currentOverlays").that(actual.currentOverlays).containsExactlyElementsIn(overlays) + } + fun isIdle(): TransitionState.Idle { if (actual !is TransitionState.Idle) { failWithActual(simpleFact("expected to be TransitionState.Idle")) @@ -53,14 +74,32 @@ private constructor( return actual as TransitionState.Idle } - fun isSceneTransition(): TransitionState.Transition.ChangeCurrentScene { - if (actual !is TransitionState.Transition.ChangeCurrentScene) { + fun isSceneTransition(): TransitionState.Transition.ChangeScene { + if (actual !is TransitionState.Transition.ChangeScene) { failWithActual( simpleFact("expected to be TransitionState.Transition.ChangeCurrentScene") ) } - return actual as TransitionState.Transition.ChangeCurrentScene + return actual as TransitionState.Transition.ChangeScene + } + + fun isShowOrHideOverlayTransition(): TransitionState.Transition.ShowOrHideOverlay { + if (actual !is TransitionState.Transition.ShowOrHideOverlay) { + failWithActual( + simpleFact("expected to be TransitionState.Transition.ShowOrHideOverlay") + ) + } + + return actual as TransitionState.Transition.ShowOrHideOverlay + } + + fun isReplaceOverlayTransition(): TransitionState.Transition.ReplaceOverlay { + if (actual !is TransitionState.Transition.ReplaceOverlay) { + failWithActual(simpleFact("expected to be TransitionState.Transition.ReplaceOverlay")) + } + + return actual as TransitionState.Transition.ReplaceOverlay } companion object { @@ -70,21 +109,16 @@ private constructor( } } -class SceneTransitionSubject -private constructor( +abstract class BaseTransitionSubject<T : TransitionState.Transition>( metadata: FailureMetadata, - private val actual: TransitionState.Transition.ChangeCurrentScene, + protected val actual: T, ) : Subject(metadata, actual) { fun hasCurrentScene(sceneKey: SceneKey) { check("currentScene").that(actual.currentScene).isEqualTo(sceneKey) } - fun hasFromScene(sceneKey: SceneKey) { - check("fromScene").that(actual.fromScene).isEqualTo(sceneKey) - } - - fun hasToScene(sceneKey: SceneKey) { - check("toScene").that(actual.toScene).isEqualTo(sceneKey) + fun hasCurrentOverlays(vararg overlays: OverlayKey) { + check("currentOverlays").that(actual.currentOverlays).containsExactlyElementsIn(overlays) } fun hasProgress(progress: Float, tolerance: Float = 0f) { @@ -144,11 +178,67 @@ private constructor( .that((actual as TransitionState.HasOverscrollProperties).bouncingContent) .isEqualTo(content) } +} + +class SceneTransitionSubject +private constructor( + metadata: FailureMetadata, + actual: TransitionState.Transition.ChangeScene, +) : BaseTransitionSubject<TransitionState.Transition.ChangeScene>(metadata, actual) { + fun hasFromScene(sceneKey: SceneKey) { + check("fromScene").that(actual.fromScene).isEqualTo(sceneKey) + } + + fun hasToScene(sceneKey: SceneKey) { + check("toScene").that(actual.toScene).isEqualTo(sceneKey) + } companion object { - fun transitions() = - Factory { metadata, actual: TransitionState.Transition.ChangeCurrentScene -> + fun sceneTransitions() = + Factory { metadata, actual: TransitionState.Transition.ChangeScene -> SceneTransitionSubject(metadata, actual) } } } + +class ShowOrHideOverlayTransitionSubject +private constructor( + metadata: FailureMetadata, + actual: TransitionState.Transition.ShowOrHideOverlay, +) : BaseTransitionSubject<TransitionState.Transition.ShowOrHideOverlay>(metadata, actual) { + fun hasFromOrToScene(fromOrToScene: SceneKey) { + check("fromOrToScene").that(actual.fromOrToScene).isEqualTo(fromOrToScene) + } + + fun hasOverlay(overlay: OverlayKey) { + check("overlay").that(actual.overlay).isEqualTo(overlay) + } + + companion object { + fun showOrHideOverlayTransitions() = + Factory { metadata, actual: TransitionState.Transition.ShowOrHideOverlay -> + ShowOrHideOverlayTransitionSubject(metadata, actual) + } + } +} + +class ReplaceOverlayTransitionSubject +private constructor( + metadata: FailureMetadata, + actual: TransitionState.Transition.ReplaceOverlay, +) : BaseTransitionSubject<TransitionState.Transition.ReplaceOverlay>(metadata, actual) { + fun hasFromOverlay(fromOverlay: OverlayKey) { + check("fromOverlay").that(actual.fromOverlay).isEqualTo(fromOverlay) + } + + fun hasToOverlay(toOverlay: OverlayKey) { + check("toOverlay").that(actual.toOverlay).isEqualTo(toOverlay) + } + + companion object { + fun replaceOverlayTransitions() = + Factory { metadata, actual: TransitionState.Transition.ReplaceOverlay -> + ReplaceOverlayTransitionSubject(metadata, actual) + } + } +} diff --git a/packages/SystemUI/compose/scene/tests/utils/src/com/android/compose/animation/scene/TestTransition.kt b/packages/SystemUI/compose/scene/tests/utils/src/com/android/compose/animation/scene/TestTransition.kt index 1ebd3d98471b..c5a5173cb037 100644 --- a/packages/SystemUI/compose/scene/tests/utils/src/com/android/compose/animation/scene/TestTransition.kt +++ b/packages/SystemUI/compose/scene/tests/utils/src/com/android/compose/animation/scene/TestTransition.kt @@ -200,8 +200,8 @@ fun ComposeContentTestRule.testReplaceOverlayTransition( transitionLayout = { state -> SceneTransitionLayout(state) { scene(currentScene) { currentSceneContent() } - overlay(from, fromAlignment) { fromContent() } - overlay(to, toAlignment) { toContent() } + overlay(from, alignment = fromAlignment) { fromContent() } + overlay(to, alignment = toAlignment) { toContent() } } }, changeState = { state -> state.replaceOverlay(from, to, animationScope = this) }, diff --git a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardPasswordViewControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardPasswordViewControllerTest.kt index 7707a600c691..fe9105ed195e 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardPasswordViewControllerTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardPasswordViewControllerTest.kt @@ -29,6 +29,7 @@ import com.android.internal.util.LatencyTracker import com.android.internal.widget.LockPatternUtils import com.android.internal.widget.LockscreenCredential import com.android.keyguard.domain.interactor.KeyguardKeyboardInteractor +import com.android.systemui.Flags as AconfigFlags import com.android.systemui.SysuiTestCase import com.android.systemui.classifier.FalsingCollector import com.android.systemui.flags.FakeFeatureFlags @@ -56,7 +57,6 @@ import org.mockito.Mockito.never import org.mockito.Mockito.verify import org.mockito.Mockito.`when` import org.mockito.MockitoAnnotations -import com.android.systemui.Flags as AconfigFlags @SmallTest @RunWith(AndroidJUnit4::class) @@ -66,8 +66,7 @@ import com.android.systemui.Flags as AconfigFlags class KeyguardPasswordViewControllerTest : SysuiTestCase() { @Mock private lateinit var keyguardPasswordView: KeyguardPasswordView @Mock private lateinit var passwordEntry: EditText - private var passwordEntryLayoutParams = - ViewGroup.LayoutParams(/* width = */ 0, /* height = */ 0) + private var passwordEntryLayoutParams = ViewGroup.LayoutParams(/* width= */ 0, /* height= */ 0) @Mock lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor @Mock lateinit var securityMode: KeyguardSecurityModel.SecurityMode @Mock lateinit var lockPatternUtils: LockPatternUtils @@ -106,6 +105,8 @@ class KeyguardPasswordViewControllerTest : SysuiTestCase() { whenever(keyguardPasswordView.findViewById<ImageView>(R.id.switch_ime_button)) .thenReturn(mock(ImageView::class.java)) `when`(keyguardPasswordView.resources).thenReturn(context.resources) + // TODO(b/362362385): No need to mock keyguardPasswordView.context once this bug is fixed. + `when`(keyguardPasswordView.context).thenReturn(context) whenever(passwordEntry.layoutParams).thenReturn(passwordEntryLayoutParams) val keyguardKeyboardInteractor = KeyguardKeyboardInteractor(FakeKeyboardRepository()) val fakeFeatureFlags = FakeFeatureFlags() @@ -187,9 +188,11 @@ class KeyguardPasswordViewControllerTest : SysuiTestCase() { verify(passwordEntry).setOnKeyListener(keyListenerArgumentCaptor.capture()) val eventHandled = - keyListenerArgumentCaptor.value.onKey(keyguardPasswordView, + keyListenerArgumentCaptor.value.onKey( + keyguardPasswordView, KeyEvent.KEYCODE_SPACE, - KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_SPACE)) + KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_SPACE) + ) assertFalse("Unlock attempted.", eventHandled) } @@ -204,9 +207,11 @@ class KeyguardPasswordViewControllerTest : SysuiTestCase() { verify(passwordEntry).setOnKeyListener(keyListenerArgumentCaptor.capture()) val eventHandled = - keyListenerArgumentCaptor.value.onKey(keyguardPasswordView, + keyListenerArgumentCaptor.value.onKey( + keyguardPasswordView, KeyEvent.KEYCODE_ENTER, - KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_ENTER)) + KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_ENTER) + ) assertTrue("Unlock not attempted.", eventHandled) } diff --git a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt index f7f69d373959..ca585abf5100 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt @@ -56,6 +56,7 @@ import com.android.systemui.flags.Flags import com.android.systemui.keyboard.data.repository.FakeKeyboardRepository import com.android.systemui.keyguard.data.repository.fakeDeviceEntryFingerprintAuthRepository import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor +import com.android.systemui.keyguard.domain.interactor.keyguardDismissTransitionInteractor import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor import com.android.systemui.keyguard.shared.model.SuccessFingerprintAuthenticationStatus import com.android.systemui.kosmos.Kosmos @@ -279,7 +280,7 @@ class KeyguardSecurityContainerControllerTest : SysuiTestCase() { deviceProvisionedController, faceAuthAccessibilityDelegate, devicePolicyManager, - keyguardTransitionInteractor, + kosmos.keyguardDismissTransitionInteractor, { primaryBouncerInteractor }, ) { deviceEntryInteractor diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/ambient/touch/BouncerFullscreenSwipeTouchHandlerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/ambient/touch/BouncerFullscreenSwipeTouchHandlerTest.java index d244482c05ee..58c3fec5b45e 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/ambient/touch/BouncerFullscreenSwipeTouchHandlerTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/ambient/touch/BouncerFullscreenSwipeTouchHandlerTest.java @@ -18,6 +18,8 @@ package com.android.systemui.ambient.touch; import static com.google.common.truth.Truth.assertThat; +import static kotlinx.coroutines.flow.StateFlowKt.MutableStateFlow; + import static org.mockito.ArgumentMatchers.anyFloat; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -35,15 +37,14 @@ import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.internal.logging.UiEventLogger; -import com.android.internal.widget.LockPatternUtils; import com.android.systemui.Flags; import com.android.systemui.SysuiTestCase; import com.android.systemui.ambient.touch.scrim.ScrimController; import com.android.systemui.ambient.touch.scrim.ScrimManager; import com.android.systemui.communal.ui.viewmodel.CommunalViewModel; +import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor; import com.android.systemui.kosmos.KosmosJavaAdapter; import com.android.systemui.plugins.ActivityStarter; -import com.android.systemui.settings.FakeUserTracker; import com.android.systemui.shared.system.InputChannelCompat; import com.android.systemui.statusbar.NotificationShadeWindowController; import com.android.systemui.statusbar.phone.CentralSurfaces; @@ -57,7 +58,6 @@ import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.MockitoAnnotations; -import java.util.Collections; import java.util.Optional; @SmallTest @@ -106,15 +106,13 @@ public class BouncerFullscreenSwipeTouchHandlerTest extends SysuiTestCase { UiEventLogger mUiEventLogger; @Mock - LockPatternUtils mLockPatternUtils; - - @Mock ActivityStarter mActivityStarter; @Mock CommunalViewModel mCommunalViewModel; - FakeUserTracker mUserTracker; + @Mock + KeyguardInteractor mKeyguardInteractor; private static final float TOUCH_REGION = .3f; private static final float MIN_BOUNCER_HEIGHT = .05f; @@ -130,7 +128,6 @@ public class BouncerFullscreenSwipeTouchHandlerTest extends SysuiTestCase { public void setup() { mKosmos = new KosmosJavaAdapter(this); MockitoAnnotations.initMocks(this); - mUserTracker = new FakeUserTracker(); mTouchHandler = new BouncerSwipeTouchHandler( mKosmos.getTestScope(), mScrimManager, @@ -138,24 +135,21 @@ public class BouncerFullscreenSwipeTouchHandlerTest extends SysuiTestCase { mNotificationShadeWindowController, mValueAnimatorCreator, mVelocityTrackerFactory, - mLockPatternUtils, - mUserTracker, mCommunalViewModel, mFlingAnimationUtils, mFlingAnimationUtilsClosing, TOUCH_REGION, MIN_BOUNCER_HEIGHT, mUiEventLogger, - mActivityStarter); + mActivityStarter, + mKeyguardInteractor); when(mScrimManager.getCurrentController()).thenReturn(mScrimController); when(mValueAnimatorCreator.create(anyFloat(), anyFloat())).thenReturn(mValueAnimator); when(mVelocityTrackerFactory.obtain()).thenReturn(mVelocityTracker); when(mFlingAnimationUtils.getMinVelocityPxPerSecond()).thenReturn(Float.MAX_VALUE); when(mTouchSession.getBounds()).thenReturn(SCREEN_BOUNDS); - when(mLockPatternUtils.isSecure(CURRENT_USER_INFO.id)).thenReturn(true); - - mUserTracker.set(Collections.singletonList(CURRENT_USER_INFO), 0); + when(mKeyguardInteractor.isKeyguardDismissible()).thenReturn(MutableStateFlow(false)); } /** diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/ambient/touch/BouncerSwipeTouchHandlerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/ambient/touch/BouncerSwipeTouchHandlerTest.java index b85e32b381df..95681671b545 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/ambient/touch/BouncerSwipeTouchHandlerTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/ambient/touch/BouncerSwipeTouchHandlerTest.java @@ -18,6 +18,8 @@ package com.android.systemui.ambient.touch; import static com.google.common.truth.Truth.assertThat; +import static kotlinx.coroutines.flow.StateFlowKt.MutableStateFlow; + import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyFloat; import static org.mockito.ArgumentMatchers.eq; @@ -44,16 +46,15 @@ import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.internal.logging.UiEventLogger; -import com.android.internal.widget.LockPatternUtils; import com.android.systemui.Flags; import com.android.systemui.SysuiTestCase; import com.android.systemui.ambient.touch.scrim.ScrimController; import com.android.systemui.ambient.touch.scrim.ScrimManager; import com.android.systemui.bouncer.shared.constants.KeyguardBouncerConstants; import com.android.systemui.communal.ui.viewmodel.CommunalViewModel; +import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor; import com.android.systemui.kosmos.KosmosJavaAdapter; import com.android.systemui.plugins.ActivityStarter; -import com.android.systemui.settings.FakeUserTracker; import com.android.systemui.shade.ShadeExpansionChangeEvent; import com.android.systemui.shared.system.InputChannelCompat; import com.android.systemui.statusbar.NotificationShadeWindowController; @@ -69,7 +70,6 @@ import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.MockitoAnnotations; -import java.util.Collections; import java.util.Optional; @SmallTest @@ -116,9 +116,6 @@ public class BouncerSwipeTouchHandlerTest extends SysuiTestCase { UiEventLogger mUiEventLogger; @Mock - LockPatternUtils mLockPatternUtils; - - @Mock ActivityStarter mActivityStarter; @Mock @@ -127,11 +124,12 @@ public class BouncerSwipeTouchHandlerTest extends SysuiTestCase { @Mock CommunalViewModel mCommunalViewModel; + @Mock + KeyguardInteractor mKeyguardInteractor; + @Captor ArgumentCaptor<Rect> mRectCaptor; - FakeUserTracker mUserTracker; - private static final float TOUCH_REGION = .3f; private static final int SCREEN_WIDTH_PX = 1024; private static final int SCREEN_HEIGHT_PX = 100; @@ -148,7 +146,6 @@ public class BouncerSwipeTouchHandlerTest extends SysuiTestCase { public void setup() { mKosmos = new KosmosJavaAdapter(this); MockitoAnnotations.initMocks(this); - mUserTracker = new FakeUserTracker(); mTouchHandler = new BouncerSwipeTouchHandler( mKosmos.getTestScope(), mScrimManager, @@ -156,24 +153,21 @@ public class BouncerSwipeTouchHandlerTest extends SysuiTestCase { mNotificationShadeWindowController, mValueAnimatorCreator, mVelocityTrackerFactory, - mLockPatternUtils, - mUserTracker, mCommunalViewModel, mFlingAnimationUtils, mFlingAnimationUtilsClosing, TOUCH_REGION, MIN_BOUNCER_HEIGHT, mUiEventLogger, - mActivityStarter); + mActivityStarter, + mKeyguardInteractor); when(mScrimManager.getCurrentController()).thenReturn(mScrimController); when(mValueAnimatorCreator.create(anyFloat(), anyFloat())).thenReturn(mValueAnimator); when(mVelocityTrackerFactory.obtain()).thenReturn(mVelocityTracker); when(mFlingAnimationUtils.getMinVelocityPxPerSecond()).thenReturn(Float.MAX_VALUE); when(mTouchSession.getBounds()).thenReturn(SCREEN_BOUNDS); - when(mLockPatternUtils.isSecure(CURRENT_USER_INFO.id)).thenReturn(true); - - mUserTracker.set(Collections.singletonList(CURRENT_USER_INFO), 0); + when(mKeyguardInteractor.isKeyguardDismissible()).thenReturn(MutableStateFlow(false)); } /** @@ -391,7 +385,7 @@ public class BouncerSwipeTouchHandlerTest extends SysuiTestCase { */ @Test public void testSwipeUp_keyguardNotSecure_doesNotExpand() { - when(mLockPatternUtils.isSecure(CURRENT_USER_INFO.id)).thenReturn(false); + when(mKeyguardInteractor.isKeyguardDismissible()).thenReturn(MutableStateFlow(true)); mTouchHandler.onSessionStart(mTouchSession); ArgumentCaptor<GestureDetector.OnGestureListener> gestureListenerCaptor = ArgumentCaptor.forClass(GestureDetector.OnGestureListener.class); @@ -426,7 +420,7 @@ public class BouncerSwipeTouchHandlerTest extends SysuiTestCase { */ @Test public void testSwipeDown_keyguardNotSecure_doesNotExpand() { - when(mLockPatternUtils.isSecure(CURRENT_USER_INFO.id)).thenReturn(false); + when(mKeyguardInteractor.isKeyguardDismissible()).thenReturn(MutableStateFlow(true)); mTouchHandler.onSessionStart(mTouchSession); ArgumentCaptor<GestureDetector.OnGestureListener> gestureListenerCaptor = ArgumentCaptor.forClass(GestureDetector.OnGestureListener.class); diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/FaceHelpMessageDebouncerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/FaceHelpMessageDebouncerTest.kt index baef620ad556..a36b0bca42b5 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/FaceHelpMessageDebouncerTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/FaceHelpMessageDebouncerTest.kt @@ -218,4 +218,60 @@ class FaceHelpMessageDebouncerTest : SysuiTestCase() { assertThat(underTest.getMessageToShow(startWindow)?.msgId) .isEqualTo(BiometricFaceConstants.FACE_ACQUIRED_TOO_CLOSE) } + + @Test + fun messageMustMeetThreshold() { + underTest = + FaceHelpMessageDebouncer( + window = window, + startWindow = 0, + shownFaceMessageFrequencyBoost = 0, + threshold = .8f, + ) + + underTest.addMessage( + HelpFaceAuthenticationStatus( + BiometricFaceConstants.FACE_ACQUIRED_TOO_CLOSE, + "tooClose", + 0 + ) + ) + underTest.addMessage( + HelpFaceAuthenticationStatus( + BiometricFaceConstants.FACE_ACQUIRED_TOO_CLOSE, + "tooClose", + 0 + ) + ) + underTest.addMessage( + HelpFaceAuthenticationStatus( + BiometricFaceConstants.FACE_ACQUIRED_TOO_BRIGHT, + "tooBright", + 0 + ) + ) + + // although tooClose message is the majority, it doesn't meet the 80% threshold + assertThat(underTest.getMessageToShow(startWindow)).isNull() + + underTest.addMessage( + HelpFaceAuthenticationStatus( + BiometricFaceConstants.FACE_ACQUIRED_TOO_CLOSE, + "tooClose", + 0 + ) + ) + underTest.addMessage( + HelpFaceAuthenticationStatus( + BiometricFaceConstants.FACE_ACQUIRED_TOO_CLOSE, + "tooClose", + 0 + ) + ) + + // message shows once it meets the threshold + assertThat(underTest.getMessageToShow(startWindow)?.msgId) + .isEqualTo(BiometricFaceConstants.FACE_ACQUIRED_TOO_CLOSE) + assertThat(underTest.getMessageToShow(startWindow)?.msg).isEqualTo("tooClose") + } } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/FaceHelpMessageDeferralTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/FaceHelpMessageDeferralTest.kt index b31f6f5b096b..add7a7fbb8a1 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/FaceHelpMessageDeferralTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/FaceHelpMessageDeferralTest.kt @@ -16,11 +16,15 @@ package com.android.systemui.biometrics -import androidx.test.ext.junit.runners.AndroidJUnit4 +import android.platform.test.annotations.DisableFlags +import android.platform.test.annotations.EnableFlags +import android.platform.test.flag.junit.FlagsParameterization import androidx.test.filters.SmallTest import com.android.keyguard.logging.BiometricMessageDeferralLogger +import com.android.systemui.Flags import com.android.systemui.SysuiTestCase import com.android.systemui.dump.DumpManager +import com.android.systemui.util.time.FakeSystemClock import org.junit.Assert.assertEquals import org.junit.Assert.assertFalse import org.junit.Assert.assertNull @@ -31,14 +35,29 @@ import org.junit.runner.RunWith import org.mockito.Mock import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations +import platform.test.runner.parameterized.ParameterizedAndroidJunit4 +import platform.test.runner.parameterized.Parameters @SmallTest -@RunWith(AndroidJUnit4::class) +@RunWith(ParameterizedAndroidJunit4::class) @android.platform.test.annotations.EnabledOnRavenwood -class FaceHelpMessageDeferralTest : SysuiTestCase() { +class FaceHelpMessageDeferralTest(flags: FlagsParameterization) : SysuiTestCase() { val threshold = .75f @Mock lateinit var logger: BiometricMessageDeferralLogger @Mock lateinit var dumpManager: DumpManager + val systemClock = FakeSystemClock() + + companion object { + @JvmStatic + @Parameters(name = "{0}") + fun getParams(): List<FlagsParameterization> { + return FlagsParameterization.allCombinationsOf(Flags.FLAG_FACE_MESSAGE_DEFER_UPDATE) + } + } + + init { + mSetFlagsRule.setFlagsParameterization(flags) + } @Before fun setUp() { @@ -111,10 +130,11 @@ class FaceHelpMessageDeferralTest : SysuiTestCase() { } @Test + @DisableFlags(Flags.FLAG_FACE_MESSAGE_DEFER_UPDATE) fun testReturnsMostFrequentDeferredMessage() { val biometricMessageDeferral = createMsgDeferral(setOf(1, 2)) - // WHEN there's 80%of the messages are msgId=1 and 20% is msgId=2 + // WHEN there's 80% of the messages are msgId=1 and 20% is msgId=2 biometricMessageDeferral.processFrame(1) biometricMessageDeferral.processFrame(1) biometricMessageDeferral.processFrame(1) @@ -124,7 +144,41 @@ class FaceHelpMessageDeferralTest : SysuiTestCase() { biometricMessageDeferral.processFrame(2) biometricMessageDeferral.updateMessage(2, "msgId-2") - // THEN the most frequent deferred message is that meets the threshold is returned + // THEN the most frequent deferred message that meets the threshold is returned + assertEquals("msgId-1", biometricMessageDeferral.getDeferredMessage()) + } + + @Test + @EnableFlags(Flags.FLAG_FACE_MESSAGE_DEFER_UPDATE) + fun testReturnsMostFrequentDeferredMessage_onlyAnalyzesLastNWindow() { + val biometricMessageDeferral = createMsgDeferral(setOf(1, 2)) + + // WHEN there's 80% of the messages are msgId=1 and 20% is msgId=2, but the last + // N window only contains messages with msgId=2 + repeat(80) { biometricMessageDeferral.processFrame(1) } + biometricMessageDeferral.updateMessage(1, "msgId-1") + systemClock.setElapsedRealtime(systemClock.elapsedRealtime() + 501L) + repeat(20) { biometricMessageDeferral.processFrame(2) } + biometricMessageDeferral.updateMessage(2, "msgId-2") + + // THEN the most frequent deferred message in the last N window (500L) is returned + assertEquals("msgId-2", biometricMessageDeferral.getDeferredMessage()) + } + + @Test + @DisableFlags(Flags.FLAG_FACE_MESSAGE_DEFER_UPDATE) + fun testReturnsMostFrequentDeferredMessage_analyzesAllFrames() { + val biometricMessageDeferral = createMsgDeferral(setOf(1, 2)) + + // WHEN there's 80% of the messages are msgId=1 and 20% is msgId=2, but the last + // N window only contains messages with msgId=2 + repeat(80) { biometricMessageDeferral.processFrame(1) } + biometricMessageDeferral.updateMessage(1, "msgId-1") + systemClock.setElapsedRealtime(systemClock.elapsedRealtime() + 501L) + repeat(20) { biometricMessageDeferral.processFrame(2) } + biometricMessageDeferral.updateMessage(2, "msgId-2") + + // THEN the most frequent deferred message is returned assertEquals("msgId-1", biometricMessageDeferral.getDeferredMessage()) } @@ -213,14 +267,17 @@ class FaceHelpMessageDeferralTest : SysuiTestCase() { private fun createMsgDeferral( messagesToDefer: Set<Int>, acquiredInfoToIgnore: Set<Int> = emptySet(), + windowToAnalyzeLastNFrames: Long = 500L, ): BiometricMessageDeferral { return BiometricMessageDeferral( - messagesToDefer, - acquiredInfoToIgnore, - threshold, - logger, - dumpManager, - "0", + messagesToDefer = messagesToDefer, + acquiredInfoToIgnore = acquiredInfoToIgnore, + threshold = threshold, + windowToAnalyzeLastNFrames = windowToAnalyzeLastNFrames, + logBuffer = logger, + dumpManager = dumpManager, + id = "0", + systemClock = { systemClock }, ) } } 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 76920e406bff..3b0057d87048 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalDreamStartableTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalDreamStartableTest.kt @@ -81,6 +81,7 @@ class CommunalDreamStartableTest : SysuiTestCase() { @Test fun startDreamWhenTransitioningToHub() = testScope.runTest { + keyguardRepository.setKeyguardShowing(true) keyguardRepository.setDreaming(false) powerRepository.setScreenPowerState(ScreenPowerState.SCREEN_ON) whenever(dreamManager.canStartDreaming(/* isScreenOn= */ true)).thenReturn(true) diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalSmartspaceRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalSmartspaceRepositoryImplTest.kt index d2515858dc0b..1a426d6ebce2 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalSmartspaceRepositoryImplTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalSmartspaceRepositoryImplTest.kt @@ -28,6 +28,7 @@ import com.android.systemui.communal.smartspace.CommunalSmartspaceController import com.android.systemui.concurrency.fakeExecutor import com.android.systemui.coroutines.collectLastValue import com.android.systemui.kosmos.testScope +import com.android.systemui.log.logcatLogBuffer import com.android.systemui.plugins.BcSmartspaceDataPlugin.SmartspaceTargetListener import com.android.systemui.testKosmos import com.android.systemui.util.time.fakeSystemClock @@ -67,6 +68,7 @@ class CommunalSmartspaceRepositoryImplTest : SysuiTestCase() { smartspaceController, fakeExecutor, systemClock, + logcatLogBuffer("CommunalSmartspaceRepositoryImplTest"), ) } 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 1d03ced19c72..99fcbb854e3a 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 @@ -352,7 +352,7 @@ class CommunalInteractorTest : SysuiTestCase() { smartspaceRepository.setTimers(targets) - val smartspaceContent by collectLastValue(underTest.getOngoingContent(true)) + val smartspaceContent by collectLastValue(underTest.ongoingContent) assertThat(smartspaceContent?.size).isEqualTo(totalTargets) for (index in 0 until totalTargets) { assertThat(smartspaceContent?.get(index)?.size).isEqualTo(expectedSizes[index]) @@ -368,7 +368,7 @@ class CommunalInteractorTest : SysuiTestCase() { // Media is playing. mediaRepository.mediaActive() - val umoContent by collectLastValue(underTest.getOngoingContent(true)) + val umoContent by collectLastValue(underTest.ongoingContent) assertThat(umoContent?.size).isEqualTo(1) assertThat(umoContent?.get(0)).isInstanceOf(CommunalContentModel.Umo::class.java) @@ -376,20 +376,6 @@ 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. @@ -412,7 +398,7 @@ class CommunalInteractorTest : SysuiTestCase() { val timer3 = smartspaceTimer("timer3", timestamp = 4L) smartspaceRepository.setTimers(listOf(timer1, timer2, timer3)) - val ongoingContent by collectLastValue(underTest.getOngoingContent(true)) + val ongoingContent by collectLastValue(underTest.ongoingContent) assertThat(ongoingContent?.size).isEqualTo(4) assertThat(ongoingContent?.get(0)?.key) .isEqualTo(CommunalContentModel.KEY.smartspace("timer3")) 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 82181788e1be..179ba2256442 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 @@ -20,9 +20,7 @@ import android.appwidget.AppWidgetProviderInfo import android.content.ActivityNotFoundException import android.content.ComponentName import android.content.Intent -import android.content.pm.ActivityInfo import android.content.pm.PackageManager -import android.content.pm.ResolveInfo import android.content.pm.UserInfo import android.provider.Settings import android.view.accessibility.AccessibilityEvent @@ -72,7 +70,6 @@ import org.junit.Assert.assertTrue import org.junit.Before import org.junit.Test import org.junit.runner.RunWith -import org.mockito.ArgumentMatchers.anyInt import org.mockito.Mock import org.mockito.Mockito import org.mockito.Mockito.never @@ -141,6 +138,7 @@ class CommunalEditModeViewModelTest : SysuiTestCase() { context, accessibilityManager, packageManager, + WIDGET_PICKER_PACKAGE_NAME, ) } @@ -259,18 +257,8 @@ class CommunalEditModeViewModelTest : SysuiTestCase() { @Test fun onOpenWidgetPicker_launchesWidgetPickerActivity() { testScope.runTest { - whenever(packageManager.resolveActivity(any(), anyInt())).then { - ResolveInfo().apply { - activityInfo = ActivityInfo().apply { packageName = WIDGET_PICKER_PACKAGE_NAME } - } - } - val success = - underTest.onOpenWidgetPicker( - testableResources.resources, - packageManager, - activityResultLauncher - ) + underTest.onOpenWidgetPicker(testableResources.resources, activityResultLauncher) verify(activityResultLauncher).launch(any()) assertTrue(success) @@ -278,38 +266,14 @@ class CommunalEditModeViewModelTest : SysuiTestCase() { } @Test - fun onOpenWidgetPicker_launcherActivityNotResolved_doesNotLaunchWidgetPickerActivity() { - testScope.runTest { - whenever(packageManager.resolveActivity(any(), anyInt())).thenReturn(null) - - val success = - underTest.onOpenWidgetPicker( - testableResources.resources, - packageManager, - activityResultLauncher - ) - - verify(activityResultLauncher, never()).launch(any()) - assertFalse(success) - } - } - - @Test fun onOpenWidgetPicker_activityLaunchThrowsException_failure() { testScope.runTest { - whenever(packageManager.resolveActivity(any(), anyInt())).then { - ResolveInfo().apply { - activityInfo = ActivityInfo().apply { packageName = WIDGET_PICKER_PACKAGE_NAME } - } - } - whenever(activityResultLauncher.launch(any())) .thenThrow(ActivityNotFoundException::class.java) val success = underTest.onOpenWidgetPicker( testableResources.resources, - packageManager, activityResultLauncher, ) 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 f6f5bc038209..780d3576c5e4 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 @@ -97,6 +97,7 @@ import org.mockito.Mock import org.mockito.Mockito import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations +import org.mockito.kotlin.atLeastOnce import org.mockito.kotlin.eq import org.mockito.kotlin.mock import org.mockito.kotlin.never @@ -757,7 +758,7 @@ class CommunalViewModelTest(flags: FlagsParameterization) : SysuiTestCase() { // updateViewVisibility is called when the flow is collected. assertThat(communalContent).isNotNull() - verify(mediaHost).updateViewVisibility() + verify(mediaHost, atLeastOnce()).updateViewVisibility() } @Test diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayServiceTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayServiceTest.kt index eda9039c748e..d21a8270ae54 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayServiceTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayServiceTest.kt @@ -15,6 +15,7 @@ */ package com.android.systemui.dreams +import android.app.WindowConfiguration import android.content.ComponentName import android.content.Intent import android.os.RemoteException @@ -65,6 +66,8 @@ import com.android.systemui.keyguard.domain.interactor.keyguardInteractor import com.android.systemui.keyguard.gesture.domain.gestureInteractor import com.android.systemui.kosmos.testScope import com.android.systemui.navigationbar.gestural.domain.GestureInteractor +import com.android.systemui.navigationbar.gestural.domain.TaskInfo +import com.android.systemui.navigationbar.gestural.domain.TaskMatcher import com.android.systemui.testKosmos import com.android.systemui.touch.TouchInsetManager import com.android.systemui.util.concurrency.FakeExecutor @@ -83,13 +86,17 @@ import org.mockito.Mock import org.mockito.Mockito import org.mockito.Mockito.clearInvocations import org.mockito.Mockito.isNull +import org.mockito.Mockito.never import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations import org.mockito.kotlin.any import org.mockito.kotlin.argumentCaptor import org.mockito.kotlin.eq +import org.mockito.kotlin.firstValue import org.mockito.kotlin.mock import org.mockito.kotlin.spy +import org.mockito.kotlin.times +import org.mockito.kotlin.verifyZeroInteractions import org.mockito.kotlin.whenever @OptIn(ExperimentalCoroutinesApi::class) @@ -315,6 +322,7 @@ class DreamOverlayServiceTest : SysuiTestCase() { mWindowParams, mDreamOverlayCallback, DREAM_COMPONENT, + false /*isPreview*/, false /*shouldShowComplication*/ ) mMainExecutor.runAllReady() @@ -332,6 +340,7 @@ class DreamOverlayServiceTest : SysuiTestCase() { mWindowParams, mDreamOverlayCallback, DREAM_COMPONENT, + false /*isPreview*/, false /*shouldShowComplication*/ ) mMainExecutor.runAllReady() @@ -351,6 +360,7 @@ class DreamOverlayServiceTest : SysuiTestCase() { mWindowParams, mDreamOverlayCallback, DREAM_COMPONENT, + false /*isPreview*/, false /*shouldShowComplication*/ ) mMainExecutor.runAllReady() @@ -373,6 +383,7 @@ class DreamOverlayServiceTest : SysuiTestCase() { mWindowParams, mDreamOverlayCallback, DREAM_COMPONENT, + false /*isPreview*/, false /*shouldShowComplication*/ ) mMainExecutor.runAllReady() @@ -391,6 +402,7 @@ class DreamOverlayServiceTest : SysuiTestCase() { mWindowParams, mDreamOverlayCallback, DREAM_COMPONENT, + false /*isPreview*/, false /*shouldShowComplication*/ ) mMainExecutor.runAllReady() @@ -406,6 +418,7 @@ class DreamOverlayServiceTest : SysuiTestCase() { mWindowParams, mDreamOverlayCallback, DREAM_COMPONENT, + false /*isPreview*/, true /*shouldShowComplication*/ ) mMainExecutor.runAllReady() @@ -413,6 +426,47 @@ class DreamOverlayServiceTest : SysuiTestCase() { } @Test + fun testDeferredResetRespondsToAnimationEnd() { + val client = client + + // Inform the overlay service of dream starting. + client.startDream( + mWindowParams, + mDreamOverlayCallback, + DREAM_COMPONENT, + false /*isPreview*/, + true /*shouldShowComplication*/ + ) + mMainExecutor.runAllReady() + + whenever(mStateController.areExitAnimationsRunning()).thenReturn(true) + clearInvocations(mStateController, mTouchMonitor) + + // Starting a dream will cause it to end first. + client.startDream( + mWindowParams, + mDreamOverlayCallback, + DREAM_COMPONENT, + false /*isPreview*/, + true /*shouldShowComplication*/ + ) + + mMainExecutor.runAllReady() + + verifyZeroInteractions(mTouchMonitor) + + val captor = ArgumentCaptor.forClass(DreamOverlayStateController.Callback::class.java) + verify(mStateController).addCallback(captor.capture()) + + whenever(mStateController.areExitAnimationsRunning()).thenReturn(false) + + captor.firstValue.onStateChanged() + + // Should only be called once since it should be null during the second reset. + verify(mTouchMonitor).destroy() + } + + @Test fun testLowLightSetByStartDream() { val client = client @@ -421,6 +475,7 @@ class DreamOverlayServiceTest : SysuiTestCase() { mWindowParams, mDreamOverlayCallback, LOW_LIGHT_COMPONENT.flattenToString(), + false /*isPreview*/, false /*shouldShowComplication*/ ) mMainExecutor.runAllReady() @@ -437,6 +492,7 @@ class DreamOverlayServiceTest : SysuiTestCase() { mWindowParams, mDreamOverlayCallback, HOME_CONTROL_PANEL_DREAM_COMPONENT.flattenToString(), + false /*isPreview*/, false /*shouldShowComplication*/ ) mMainExecutor.runAllReady() @@ -453,6 +509,7 @@ class DreamOverlayServiceTest : SysuiTestCase() { mWindowParams, mDreamOverlayCallback, LOW_LIGHT_COMPONENT.flattenToString(), + false /*isPreview*/, false /*shouldShowComplication*/ ) mMainExecutor.runAllReady() @@ -487,6 +544,7 @@ class DreamOverlayServiceTest : SysuiTestCase() { mWindowParams, mDreamOverlayCallback, DREAM_COMPONENT, + false /*isPreview*/, false /*shouldShowComplication*/ ) // Immediately end the dream. @@ -518,6 +576,7 @@ class DreamOverlayServiceTest : SysuiTestCase() { mWindowParams, mDreamOverlayCallback, DREAM_COMPONENT, + false /*isPreview*/, false /*shouldShowComplication*/ ) mMainExecutor.runAllReady() @@ -537,6 +596,7 @@ class DreamOverlayServiceTest : SysuiTestCase() { mWindowParams, mDreamOverlayCallback, LOW_LIGHT_COMPONENT.flattenToString(), + false /*isPreview*/, false /*shouldShowComplication*/ ) mMainExecutor.runAllReady() @@ -588,6 +648,7 @@ class DreamOverlayServiceTest : SysuiTestCase() { mWindowParams, mDreamOverlayCallback, DREAM_COMPONENT, + false /*isPreview*/, false /*shouldShowComplication*/ ) mMainExecutor.runAllReady() @@ -611,6 +672,7 @@ class DreamOverlayServiceTest : SysuiTestCase() { mWindowParams, mDreamOverlayCallback, DREAM_COMPONENT, + false /*isPreview*/, false /*shouldShowComplication*/ ) mMainExecutor.runAllReady() @@ -631,6 +693,7 @@ class DreamOverlayServiceTest : SysuiTestCase() { mWindowParams, mDreamOverlayCallback, DREAM_COMPONENT, + false /*isPreview*/, true /*shouldShowComplication*/ ) mMainExecutor.runAllReady() @@ -660,6 +723,7 @@ class DreamOverlayServiceTest : SysuiTestCase() { mWindowParams, mDreamOverlayCallback, DREAM_COMPONENT, + false /*isPreview*/, true /*shouldShowComplication*/ ) mMainExecutor.runAllReady() @@ -683,6 +747,7 @@ class DreamOverlayServiceTest : SysuiTestCase() { mWindowParams, mDreamOverlayCallback, DREAM_COMPONENT, + false /*isPreview*/, false /*shouldShowComplication*/ ) mMainExecutor.runAllReady() @@ -708,6 +773,7 @@ class DreamOverlayServiceTest : SysuiTestCase() { mWindowParams, mDreamOverlayCallback, DREAM_COMPONENT, + false /*isPreview*/, false /*shouldShowComplication*/ ) mMainExecutor.runAllReady() @@ -733,6 +799,7 @@ class DreamOverlayServiceTest : SysuiTestCase() { mWindowParams, mDreamOverlayCallback, DREAM_COMPONENT, + false /*isPreview*/, false /*shouldShowComplication*/ ) // Set communal available, verify that overlay callback is informed. @@ -761,6 +828,7 @@ class DreamOverlayServiceTest : SysuiTestCase() { mWindowParams, mDreamOverlayCallback, DREAM_COMPONENT, + false /*isPreview*/, true /*shouldShowComplication*/ ) mMainExecutor.runAllReady() @@ -781,6 +849,7 @@ class DreamOverlayServiceTest : SysuiTestCase() { mWindowParams, mDreamOverlayCallback, DREAM_COMPONENT, + false /*isPreview*/, true /*shouldShowComplication*/ ) mMainExecutor.runAllReady() @@ -800,6 +869,7 @@ class DreamOverlayServiceTest : SysuiTestCase() { mWindowParams, mDreamOverlayCallback, DREAM_COMPONENT, + false /*isPreview*/, true /*shouldShowComplication*/ ) mMainExecutor.runAllReady() @@ -823,6 +893,7 @@ class DreamOverlayServiceTest : SysuiTestCase() { mWindowParams, mDreamOverlayCallback, DREAM_COMPONENT, + false /*isPreview*/, false /*shouldShowComplication*/ ) mMainExecutor.runAllReady() @@ -853,6 +924,7 @@ class DreamOverlayServiceTest : SysuiTestCase() { mWindowParams, mDreamOverlayCallback, DREAM_COMPONENT, + false /*isPreview*/, false /*shouldShowComplication*/ ) testScope.runCurrent() @@ -869,6 +941,7 @@ class DreamOverlayServiceTest : SysuiTestCase() { mWindowParams, mDreamOverlayCallback, DREAM_COMPONENT, + false /*isPreview*/, false /*shouldShowComplication*/ ) mMainExecutor.runAllReady() @@ -892,6 +965,7 @@ class DreamOverlayServiceTest : SysuiTestCase() { mWindowParams, mDreamOverlayCallback, DREAM_COMPONENT, + false /*isPreview*/, false /*shouldShowComplication*/ ) mMainExecutor.runAllReady() @@ -923,6 +997,7 @@ class DreamOverlayServiceTest : SysuiTestCase() { mWindowParams, mDreamOverlayCallback, DREAM_COMPONENT, + false /*isPreview*/, false /*shouldShowComplication*/ ) mMainExecutor.runAllReady() @@ -954,6 +1029,7 @@ class DreamOverlayServiceTest : SysuiTestCase() { mWindowParams, mDreamOverlayCallback, DREAM_COMPONENT, + false /*isPreview*/, false /*shouldShowComplication*/ ) mMainExecutor.runAllReady() @@ -989,6 +1065,7 @@ class DreamOverlayServiceTest : SysuiTestCase() { mWindowParams, mDreamOverlayCallback, DREAM_COMPONENT, + false /*isPreview*/, false /*shouldShowComplication*/ ) mMainExecutor.runAllReady() @@ -1015,7 +1092,7 @@ class DreamOverlayServiceTest : SysuiTestCase() { } @Test - fun testDreamActivityGesturesBlockedOnStart() { + fun testDreamActivityGesturesBlockedWhenDreaming() { val client = client // Inform the overlay service of dream starting. @@ -1023,18 +1100,28 @@ class DreamOverlayServiceTest : SysuiTestCase() { mWindowParams, mDreamOverlayCallback, DREAM_COMPONENT, + false /*isPreview*/, false /*shouldShowComplication*/ ) mMainExecutor.runAllReady() - val captor = argumentCaptor<ComponentName>() + + val matcherCaptor = argumentCaptor<TaskMatcher>() verify(gestureInteractor) - .addGestureBlockedActivity(captor.capture(), eq(GestureInteractor.Scope.Global)) - assertThat(captor.firstValue.packageName) - .isEqualTo(ComponentName.unflattenFromString(DREAM_COMPONENT)?.packageName) + .addGestureBlockedMatcher(matcherCaptor.capture(), eq(GestureInteractor.Scope.Global)) + val matcher = matcherCaptor.firstValue + + val dreamTaskInfo = TaskInfo(mock<ComponentName>(), WindowConfiguration.ACTIVITY_TYPE_DREAM) + assertThat(matcher.matches(dreamTaskInfo)).isTrue() + + client.endDream() + mMainExecutor.runAllReady() + + verify(gestureInteractor) + .removeGestureBlockedMatcher(eq(matcher), eq(GestureInteractor.Scope.Global)) } @Test - fun testDreamActivityGesturesUnblockedOnEnd() { + fun testDreamActivityGesturesNotBlockedWhenPreview() { val client = client // Inform the overlay service of dream starting. @@ -1042,17 +1129,13 @@ class DreamOverlayServiceTest : SysuiTestCase() { mWindowParams, mDreamOverlayCallback, DREAM_COMPONENT, + true /*isPreview*/, false /*shouldShowComplication*/ ) mMainExecutor.runAllReady() - client.endDream() - mMainExecutor.runAllReady() - val captor = argumentCaptor<ComponentName>() - verify(gestureInteractor) - .removeGestureBlockedActivity(captor.capture(), eq(GestureInteractor.Scope.Global)) - assertThat(captor.firstValue.packageName) - .isEqualTo(ComponentName.unflattenFromString(DREAM_COMPONENT)?.packageName) + verify(gestureInteractor, never()) + .addGestureBlockedMatcher(any(), eq(GestureInteractor.Scope.Global)) } @Test @@ -1077,6 +1160,7 @@ class DreamOverlayServiceTest : SysuiTestCase() { mWindowParams, mDreamOverlayCallback, DREAM_COMPONENT, + false /*isPreview*/, false /*shouldShowComplication*/ ) mMainExecutor.runAllReady() diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduInteractorTest.kt index 3aed79f5915f..ca15eff4610b 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduInteractorTest.kt @@ -17,6 +17,9 @@ package com.android.systemui.education.domain.interactor import android.content.pm.UserInfo +import android.hardware.input.InputManager +import android.hardware.input.KeyGestureEvent +import android.view.KeyEvent import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase @@ -41,6 +44,9 @@ import kotlinx.coroutines.test.runTest import org.junit.Before import org.junit.Test import org.junit.runner.RunWith +import org.mockito.ArgumentCaptor +import org.mockito.kotlin.any +import org.mockito.kotlin.verify @SmallTest @RunWith(AndroidJUnit4::class) @@ -203,6 +209,31 @@ class KeyboardTouchpadEduInteractorTest : SysuiTestCase() { assertThat(model.keyboardFirstConnectionTime).isEqualTo(newUserFirstConnectionTime) } + @Test + fun updateShortcutTimeOnKeyboardShortcutTriggered() = + testScope.runTest { + // runCurrent() to trigger inputManager#registerKeyGestureEventListener in the + // interactor + runCurrent() + val listenerCaptor = + ArgumentCaptor.forClass(InputManager.KeyGestureEventListener::class.java) + verify(kosmos.mockEduInputManager) + .registerKeyGestureEventListener(any(), listenerCaptor.capture()) + + val backGestureEvent = + KeyGestureEvent( + /* deviceId= */ 1, + intArrayOf(KeyEvent.KEYCODE_ESCAPE), + KeyEvent.META_META_ON, + KeyGestureEvent.KEY_GESTURE_TYPE_BACK + ) + listenerCaptor.value.onKeyGestureEvent(backGestureEvent) + + val model by + collectLastValue(kosmos.contextualEducationRepository.readGestureEduModelFlow(BACK)) + assertThat(model?.lastShortcutTriggeredTime).isEqualTo(eduClock.instant()) + } + private suspend fun triggerMaxEducationSignals(gestureType: GestureType) { // Increment max number of signal to try triggering education for (i in 1..KeyboardTouchpadEduInteractor.MAX_SIGNAL_COUNT) { diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/gesture/data/GestureRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/gesture/data/GestureRepositoryTest.kt index 91d37cf1816a..a764256df9a3 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/gesture/data/GestureRepositoryTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/gesture/data/GestureRepositoryTest.kt @@ -16,12 +16,14 @@ package com.android.systemui.gesture.data +import android.app.WindowConfiguration import android.content.ComponentName 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.navigationbar.gestural.data.respository.GestureRepositoryImpl +import com.android.systemui.navigationbar.gestural.domain.TaskMatcher import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.test.StandardTestDispatcher import kotlinx.coroutines.test.TestScope @@ -40,14 +42,36 @@ class GestureRepositoryTest : SysuiTestCase() { @Test fun addRemoveComponentToBlock_updatesBlockedComponentSet() = testScope.runTest { - val component = mock<ComponentName>() + val matcher = TaskMatcher.TopActivityComponent(mock<ComponentName>()) - underTest.addGestureBlockedActivity(component) - val addedBlockedComponents by collectLastValue(underTest.gestureBlockedActivities) - assertThat(addedBlockedComponents).contains(component) + kotlin.run { + underTest.addGestureBlockedMatcher(matcher) + val blockedMatchers by collectLastValue(underTest.gestureBlockedMatchers) + assertThat(blockedMatchers).contains(matcher) + } - underTest.removeGestureBlockedActivity(component) - val removedBlockedComponents by collectLastValue(underTest.gestureBlockedActivities) - assertThat(removedBlockedComponents).isEmpty() + kotlin.run { + underTest.removeGestureBlockedMatcher(matcher) + val blockedMatchers by collectLastValue(underTest.gestureBlockedMatchers) + assertThat(blockedMatchers).doesNotContain(matcher) + } + } + + @Test + fun addRemoveActivityTypeToBlock_updatesBlockedActivityTypesSet() = + testScope.runTest { + val matcher = TaskMatcher.TopActivityType(WindowConfiguration.ACTIVITY_TYPE_STANDARD) + + kotlin.run { + underTest.addGestureBlockedMatcher(matcher) + val blockedMatchers by collectLastValue(underTest.gestureBlockedMatchers) + assertThat(blockedMatchers).contains(matcher) + } + + kotlin.run { + underTest.removeGestureBlockedMatcher(matcher) + val blockedMatchers by collectLastValue(underTest.gestureBlockedMatchers) + assertThat(blockedMatchers).doesNotContain(matcher) + } } } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/gesture/domain/GestureInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/gesture/domain/GestureInteractorTest.kt index 639544889c2a..0ce0d934c381 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/gesture/domain/GestureInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/gesture/domain/GestureInteractorTest.kt @@ -26,6 +26,7 @@ import com.android.systemui.kosmos.backgroundCoroutineContext import com.android.systemui.kosmos.testDispatcher import com.android.systemui.navigationbar.gestural.data.gestureRepository import com.android.systemui.navigationbar.gestural.domain.GestureInteractor +import com.android.systemui.navigationbar.gestural.domain.TaskMatcher import com.android.systemui.shared.system.activityManagerWrapper import com.android.systemui.shared.system.taskStackChangeListeners import com.android.systemui.testKosmos @@ -76,13 +77,16 @@ class GestureInteractorTest : SysuiTestCase() { fun addBlockedActivity_testCombination() = testScope.runTest { val globalComponent = mock<ComponentName>() - repository.addGestureBlockedActivity(globalComponent) + repository.addGestureBlockedMatcher(TaskMatcher.TopActivityComponent(globalComponent)) val localComponent = mock<ComponentName>() val blocked by collectLastValue(underTest.topActivityBlocked) - underTest.addGestureBlockedActivity(localComponent, GestureInteractor.Scope.Local) + underTest.addGestureBlockedMatcher( + TaskMatcher.TopActivityComponent(localComponent), + GestureInteractor.Scope.Local + ) assertThat(blocked).isFalse() @@ -95,7 +99,7 @@ class GestureInteractorTest : SysuiTestCase() { fun initialization_testEmit() = testScope.runTest { val globalComponent = mock<ComponentName>() - repository.addGestureBlockedActivity(globalComponent) + repository.addGestureBlockedMatcher(TaskMatcher.TopActivityComponent(globalComponent)) setTopActivity(globalComponent) val interactor = createInteractor() @@ -114,10 +118,36 @@ class GestureInteractorTest : SysuiTestCase() { val localComponent = mock<ComponentName>() - interactor1.addGestureBlockedActivity(localComponent, GestureInteractor.Scope.Local) + interactor1.addGestureBlockedMatcher( + TaskMatcher.TopActivityComponent(localComponent), + GestureInteractor.Scope.Local + ) setTopActivity(localComponent) assertThat(interactor1Blocked).isTrue() assertThat(interactor2Blocked).isFalse() } + + @Test + fun matchingBlockers_separatelyManaged() = + testScope.runTest { + val interactor = createInteractor() + val interactorBlocked by collectLastValue(interactor.topActivityBlocked) + + val localComponent = mock<ComponentName>() + + val matcher1 = TaskMatcher.TopActivityComponent(localComponent) + val matcher2 = TaskMatcher.TopActivityComponent(localComponent) + + interactor.addGestureBlockedMatcher(matcher1, GestureInteractor.Scope.Local) + interactor.addGestureBlockedMatcher(matcher2, GestureInteractor.Scope.Local) + setTopActivity(localComponent) + assertThat(interactorBlocked).isTrue() + + interactor.removeGestureBlockedMatcher(matcher1, GestureInteractor.Scope.Local) + assertThat(interactorBlocked).isTrue() + + interactor.removeGestureBlockedMatcher(matcher2, GestureInteractor.Scope.Local) + assertThat(interactorBlocked).isFalse() + } } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/gesture/domain/TaskMatcherTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/gesture/domain/TaskMatcherTest.kt new file mode 100644 index 000000000000..a246270cf413 --- /dev/null +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/gesture/domain/TaskMatcherTest.kt @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.gesture.domain + +import android.app.WindowConfiguration +import android.content.ComponentName +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.navigationbar.gestural.domain.TaskInfo +import com.android.systemui.navigationbar.gestural.domain.TaskMatcher +import com.google.common.truth.Truth.assertThat +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.kotlin.mock + +@RunWith(AndroidJUnit4::class) +@SmallTest +class TaskMatcherTest : SysuiTestCase() { + @Test + fun activityMatcher_matchesComponentName() { + val componentName = ComponentName.unflattenFromString("com.foo/.bar")!! + val matcher = TaskMatcher.TopActivityComponent(componentName) + + val taskInfo = TaskInfo(componentName, WindowConfiguration.ACTIVITY_TYPE_STANDARD) + assertThat(matcher.matches(taskInfo)).isTrue() + } + + @Test + fun activityMatcher_doesNotMatchComponentName() { + val componentName = ComponentName.unflattenFromString("com.foo/.bar")!! + val matcher = TaskMatcher.TopActivityComponent(componentName) + + val taskInfo = + TaskInfo( + ComponentName.unflattenFromString("com.bar/.baz"), + WindowConfiguration.ACTIVITY_TYPE_STANDARD + ) + assertThat(matcher.matches(taskInfo)).isFalse() + } + + @Test + fun activityMatcher_matchesActivityType() { + val activityType = WindowConfiguration.ACTIVITY_TYPE_HOME + val matcher = TaskMatcher.TopActivityType(activityType) + + val taskInfo = TaskInfo(mock<ComponentName>(), activityType) + assertThat(matcher.matches(taskInfo)).isTrue() + } + + @Test + fun activityMatcher_doesNotMatchEmptyActivityType() { + val activityType = WindowConfiguration.ACTIVITY_TYPE_HOME + val matcher = TaskMatcher.TopActivityType(activityType) + + val taskInfo = TaskInfo(null, activityType) + assertThat(matcher.matches(taskInfo)).isFalse() + } + + @Test + fun activityMatcher_doesNotMatchActivityType() { + val activityType = WindowConfiguration.ACTIVITY_TYPE_HOME + val matcher = TaskMatcher.TopActivityType(activityType) + + val taskInfo = TaskInfo(mock<ComponentName>(), WindowConfiguration.ACTIVITY_TYPE_STANDARD) + assertThat(matcher.matches(taskInfo)).isFalse() + } + + @Test + fun activityMatcher_equivalentMatchersAreNotEqual() { + val activityType = WindowConfiguration.ACTIVITY_TYPE_HOME + val matcher1 = TaskMatcher.TopActivityType(activityType) + val matcher2 = TaskMatcher.TopActivityType(activityType) + + assertThat(matcher1).isNotEqualTo(matcher2) + } +} 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 3b8ffcd91c48..17e3006812ca 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 @@ -116,7 +116,7 @@ class FromAlternateBouncerTransitionInteractorTest : SysuiTestCase() { reset(transitionRepository) // Authentication results in calling startDismissKeyguardTransition. - kosmos.keyguardTransitionInteractor.startDismissKeyguardTransition() + kosmos.keyguardDismissTransitionInteractor.startDismissKeyguardTransition() runCurrent() assertThat(transitionRepository) 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 6eb9862fb4f1..33f3cd4ac167 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 @@ -39,12 +39,15 @@ import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.Flags import com.android.systemui.SysuiTestCase +import com.android.systemui.coroutines.collectLastValue import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository import com.android.systemui.keyguard.data.repository.keyguardOcclusionRepository import com.android.systemui.keyguard.shared.model.BiometricUnlockMode import com.android.systemui.keyguard.shared.model.KeyguardState +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.TransitionState import com.android.systemui.keyguard.shared.model.TransitionStep import com.android.systemui.keyguard.util.KeyguardTransitionRepositorySpySubject.Companion.assertThat @@ -53,6 +56,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.scene.shared.model.Scenes import com.android.systemui.statusbar.domain.interactor.keyguardOcclusionInteractor import com.android.systemui.testKosmos import junit.framework.Assert.assertEquals @@ -166,6 +170,10 @@ class FromAodTransitionInteractorTest : SysuiTestCase() { @EnableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR) fun testTransitionToGone_onWakeUp_ifPowerButtonGestureDetected_fromGone() = testScope.runTest { + val isGone by + collectLastValue( + kosmos.keyguardTransitionInteractor.isFinishedIn(Scenes.Gone, GONE) + ) powerInteractor.setAwakeForTest() transitionRepository.sendTransitionSteps( from = KeyguardState.AOD, @@ -175,7 +183,7 @@ class FromAodTransitionInteractorTest : SysuiTestCase() { runCurrent() // Make sure we're GONE. - assertEquals(KeyguardState.GONE, kosmos.keyguardTransitionInteractor.getFinishedState()) + assertEquals(true, isGone) // Get part way to AOD. powerInteractor.onStartedGoingToSleep(PowerManager.GO_TO_SLEEP_REASON_MIN) @@ -204,6 +212,10 @@ class FromAodTransitionInteractorTest : SysuiTestCase() { @EnableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR) fun testTransitionToOccluded_onWakeUp_ifPowerButtonGestureDetectedAfterFinishedInAod_fromGone() = testScope.runTest { + val isGone by + collectLastValue( + kosmos.keyguardTransitionInteractor.isFinishedIn(Scenes.Gone, GONE) + ) powerInteractor.setAwakeForTest() transitionRepository.sendTransitionSteps( from = KeyguardState.AOD, @@ -213,7 +225,7 @@ class FromAodTransitionInteractorTest : SysuiTestCase() { runCurrent() // Make sure we're GONE. - assertEquals(KeyguardState.GONE, kosmos.keyguardTransitionInteractor.getFinishedState()) + assertEquals(true, isGone) // Get all the way to AOD powerInteractor.onStartedGoingToSleep(PowerManager.GO_TO_SLEEP_REASON_MIN) @@ -239,6 +251,10 @@ class FromAodTransitionInteractorTest : SysuiTestCase() { @EnableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR) fun testTransitionToOccluded_onWakeUp_ifPowerButtonGestureDetected_fromLockscreen() = testScope.runTest { + val isLockscreen by + collectLastValue( + kosmos.keyguardTransitionInteractor.isFinishedIn(Scenes.Lockscreen, LOCKSCREEN) + ) powerInteractor.setAwakeForTest() transitionRepository.sendTransitionSteps( from = KeyguardState.AOD, @@ -248,10 +264,7 @@ class FromAodTransitionInteractorTest : SysuiTestCase() { runCurrent() // Make sure we're in LOCKSCREEN. - assertEquals( - KeyguardState.LOCKSCREEN, - kosmos.keyguardTransitionInteractor.getFinishedState() - ) + assertEquals(true, isLockscreen) // Get part way to AOD. powerInteractor.onStartedGoingToSleep(PowerManager.GO_TO_SLEEP_REASON_MIN) @@ -314,7 +327,7 @@ class FromAodTransitionInteractorTest : SysuiTestCase() { testScope.runTest { kosmos.fakeKeyguardRepository.setKeyguardShowing(false) kosmos.fakeKeyguardRepository.setKeyguardDismissible(true) - kosmos.keyguardTransitionInteractor.startDismissKeyguardTransition() + kosmos.keyguardDismissTransitionInteractor.startDismissKeyguardTransition() powerInteractor.setAwakeForTest() advanceTimeBy(100) // account for debouncing @@ -327,6 +340,10 @@ class FromAodTransitionInteractorTest : SysuiTestCase() { @DisableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR) fun testTransitionToGone_onWakeUp_ifPowerButtonGestureDetectedInAod_fromGone() = testScope.runTest { + val isGone by + collectLastValue( + kosmos.keyguardTransitionInteractor.isFinishedIn(Scenes.Gone, GONE) + ) powerInteractor.setAwakeForTest() transitionRepository.sendTransitionSteps( from = KeyguardState.AOD, @@ -336,7 +353,7 @@ class FromAodTransitionInteractorTest : SysuiTestCase() { runCurrent() // Make sure we're GONE. - assertEquals(KeyguardState.GONE, kosmos.keyguardTransitionInteractor.getFinishedState()) + assertEquals(true, isGone) // Start going to AOD on first button push powerInteractor.onStartedGoingToSleep(PowerManager.GO_TO_SLEEP_REASON_MIN) diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractorTest.kt index ee4a0d2d4e75..c18deb134075 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractorTest.kt @@ -48,12 +48,15 @@ import com.android.systemui.communal.data.repository.fakeCommunalSceneRepository import com.android.systemui.communal.domain.interactor.setCommunalAvailable import com.android.systemui.communal.shared.model.CommunalScenes import com.android.systemui.communal.shared.model.CommunalTransitionKeys +import com.android.systemui.coroutines.collectLastValue import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository import com.android.systemui.keyguard.data.repository.keyguardOcclusionRepository import com.android.systemui.keyguard.shared.model.BiometricUnlockMode import com.android.systemui.keyguard.shared.model.KeyguardState +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.TransitionState import com.android.systemui.keyguard.util.KeyguardTransitionRepositorySpySubject.Companion.assertThat import com.android.systemui.kosmos.applicationCoroutineScope @@ -62,6 +65,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.scene.shared.model.Scenes import com.android.systemui.testKosmos import junit.framework.Assert.assertEquals import kotlinx.coroutines.ExperimentalCoroutinesApi @@ -316,6 +320,10 @@ class FromDozingTransitionInteractorTest(flags: FlagsParameterization?) : SysuiT @EnableFlags(FLAG_KEYGUARD_WM_STATE_REFACTOR) fun testTransitionToGone_onWakeUp_ifPowerButtonGestureDetected_fromGone() = testScope.runTest { + val isGone by + collectLastValue( + kosmos.keyguardTransitionInteractor.isFinishedIn(Scenes.Gone, GONE) + ) powerInteractor.setAwakeForTest() transitionRepository.sendTransitionSteps( from = KeyguardState.DOZING, @@ -325,7 +333,7 @@ class FromDozingTransitionInteractorTest(flags: FlagsParameterization?) : SysuiT runCurrent() // Make sure we're GONE. - assertEquals(KeyguardState.GONE, kosmos.keyguardTransitionInteractor.getFinishedState()) + assertEquals(true, isGone) // Get part way to AOD. powerInteractor.onStartedGoingToSleep(PowerManager.GO_TO_SLEEP_REASON_MIN) @@ -355,6 +363,10 @@ class FromDozingTransitionInteractorTest(flags: FlagsParameterization?) : SysuiT @Suppress("ktlint:standard:max-line-length") fun testTransitionToOccluded_onWakeUp_ifPowerButtonGestureDetectedAfterFinishedInAod_fromGone() = testScope.runTest { + val isGone by + collectLastValue( + kosmos.keyguardTransitionInteractor.isFinishedIn(Scenes.Gone, GONE) + ) powerInteractor.setAwakeForTest() transitionRepository.sendTransitionSteps( from = KeyguardState.DOZING, @@ -364,7 +376,7 @@ class FromDozingTransitionInteractorTest(flags: FlagsParameterization?) : SysuiT runCurrent() // Make sure we're GONE. - assertEquals(KeyguardState.GONE, kosmos.keyguardTransitionInteractor.getFinishedState()) + assertEquals(true, isGone) // Get all the way to AOD powerInteractor.onStartedGoingToSleep(PowerManager.GO_TO_SLEEP_REASON_MIN) @@ -390,6 +402,10 @@ class FromDozingTransitionInteractorTest(flags: FlagsParameterization?) : SysuiT @EnableFlags(FLAG_KEYGUARD_WM_STATE_REFACTOR) fun testTransitionToOccluded_onWakeUp_ifPowerButtonGestureDetected_fromLockscreen() = testScope.runTest { + val isLockscreen by + collectLastValue( + kosmos.keyguardTransitionInteractor.isFinishedIn(Scenes.Lockscreen, LOCKSCREEN) + ) powerInteractor.setAwakeForTest() transitionRepository.sendTransitionSteps( from = KeyguardState.DOZING, @@ -399,10 +415,7 @@ class FromDozingTransitionInteractorTest(flags: FlagsParameterization?) : SysuiT runCurrent() // Make sure we're in LOCKSCREEN. - assertEquals( - KeyguardState.LOCKSCREEN, - kosmos.keyguardTransitionInteractor.getFinishedState() - ) + assertEquals(true, isLockscreen) // Get part way to AOD. powerInteractor.onStartedGoingToSleep(PowerManager.GO_TO_SLEEP_REASON_MIN) 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 6e76cbce95f1..6708ffa2a091 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 @@ -90,52 +90,6 @@ class KeyguardTransitionInteractorTest : SysuiTestCase() { } @Test - fun finishedKeyguardStateTests() = - testScope.runTest { - val finishedSteps by collectValues(underTest.finishedKeyguardState) - runCurrent() - val steps = mutableListOf<TransitionStep>() - - steps.add(TransitionStep(AOD, PRIMARY_BOUNCER, 0f, STARTED)) - steps.add(TransitionStep(AOD, PRIMARY_BOUNCER, 0.5f, RUNNING)) - steps.add(TransitionStep(AOD, PRIMARY_BOUNCER, 1f, FINISHED)) - steps.add(TransitionStep(PRIMARY_BOUNCER, AOD, 0f, STARTED)) - steps.add(TransitionStep(PRIMARY_BOUNCER, AOD, 0.9f, RUNNING)) - steps.add(TransitionStep(PRIMARY_BOUNCER, AOD, 1f, FINISHED)) - steps.add(TransitionStep(AOD, GONE, 1f, STARTED)) - - steps.forEach { - repository.sendTransitionStep(it) - runCurrent() - } - - assertThat(finishedSteps).isEqualTo(listOf(LOCKSCREEN, PRIMARY_BOUNCER, AOD)) - } - - @Test - fun startedKeyguardStateTests() = - testScope.runTest { - val startedStates by collectValues(underTest.startedKeyguardState) - runCurrent() - val steps = mutableListOf<TransitionStep>() - - steps.add(TransitionStep(AOD, PRIMARY_BOUNCER, 0f, STARTED)) - steps.add(TransitionStep(AOD, PRIMARY_BOUNCER, 0.5f, RUNNING)) - steps.add(TransitionStep(AOD, PRIMARY_BOUNCER, 1f, FINISHED)) - steps.add(TransitionStep(PRIMARY_BOUNCER, AOD, 0f, STARTED)) - steps.add(TransitionStep(PRIMARY_BOUNCER, AOD, 0.9f, RUNNING)) - steps.add(TransitionStep(PRIMARY_BOUNCER, AOD, 1f, FINISHED)) - steps.add(TransitionStep(AOD, GONE, 1f, STARTED)) - - steps.forEach { - repository.sendTransitionStep(it) - runCurrent() - } - - assertThat(startedStates).isEqualTo(listOf(LOCKSCREEN, PRIMARY_BOUNCER, AOD, GONE)) - } - - @Test fun startedKeyguardTransitionStepTests() = testScope.runTest { val startedSteps by collectValues(underTest.startedKeyguardTransitionStep) @@ -1206,95 +1160,6 @@ class KeyguardTransitionInteractorTest : SysuiTestCase() { } @Test - fun finishedKeyguardState_emitsAgainIfCancelledAndReversed() = - testScope.runTest { - val finishedStates by collectValues(underTest.finishedKeyguardState) - - // We default FINISHED in LOCKSCREEN. - assertEquals(listOf(LOCKSCREEN), finishedStates) - - sendSteps( - TransitionStep(LOCKSCREEN, AOD, 0f, STARTED), - TransitionStep(LOCKSCREEN, AOD, 0.5f, RUNNING), - TransitionStep(LOCKSCREEN, AOD, 1f, FINISHED), - ) - - // We're FINISHED in AOD. - assertEquals( - listOf( - LOCKSCREEN, - AOD, - ), - finishedStates - ) - - // Transition back to LOCKSCREEN. - sendSteps( - TransitionStep(AOD, LOCKSCREEN, 0f, STARTED), - TransitionStep(AOD, LOCKSCREEN, 0.5f, RUNNING), - TransitionStep(AOD, LOCKSCREEN, 1f, FINISHED), - ) - - // We're FINISHED in LOCKSCREEN. - assertEquals( - listOf( - LOCKSCREEN, - AOD, - LOCKSCREEN, - ), - finishedStates - ) - - sendSteps( - TransitionStep(LOCKSCREEN, GONE, 0f, STARTED), - TransitionStep(LOCKSCREEN, GONE, 0.5f, RUNNING), - ) - - // We've STARTED a transition to GONE but not yet finished it so we're still FINISHED in - // LOCKSCREEN. - assertEquals( - listOf( - LOCKSCREEN, - AOD, - LOCKSCREEN, - ), - finishedStates - ) - - sendSteps( - TransitionStep(LOCKSCREEN, GONE, 0.6f, CANCELED), - ) - - // We've CANCELED a transition to GONE, we're still FINISHED in LOCKSCREEN. - assertEquals( - listOf( - LOCKSCREEN, - AOD, - LOCKSCREEN, - ), - finishedStates - ) - - sendSteps( - TransitionStep(GONE, LOCKSCREEN, 0.6f, STARTED), - TransitionStep(GONE, LOCKSCREEN, 0.9f, RUNNING), - TransitionStep(GONE, LOCKSCREEN, 1f, FINISHED), - ) - - // Expect another emission of LOCKSCREEN, as we have FINISHED a second transition to - // LOCKSCREEN after the cancellation. - assertEquals( - listOf( - LOCKSCREEN, - AOD, - LOCKSCREEN, - LOCKSCREEN, - ), - finishedStates - ) - } - - @Test fun testCurrentState() = testScope.runTest { val currentStates by collectValues(underTest.currentKeyguardState) diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractorTest.kt index 3e0a1f31302d..073ed61a949b 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractorTest.kt @@ -19,18 +19,20 @@ package com.android.systemui.keyguard.domain.interactor import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase +import com.android.systemui.coroutines.collectValues import com.android.systemui.keyguard.data.fakeLightRevealScrimRepository +import com.android.systemui.keyguard.data.repository.DEFAULT_REVEAL_EFFECT import com.android.systemui.keyguard.data.repository.FakeLightRevealScrimRepository 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.keyguard.shared.model.TransitionStep +import com.android.systemui.kosmos.testScope import com.android.systemui.statusbar.LightRevealEffect import com.android.systemui.statusbar.LightRevealScrim import com.android.systemui.testKosmos import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.flow.launchIn -import kotlinx.coroutines.flow.onEach -import kotlinx.coroutines.test.UnconfinedTestDispatcher +import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest import org.junit.Assert.assertEquals import org.junit.Test @@ -64,35 +66,45 @@ class LightRevealScrimInteractorTest : SysuiTestCase() { @Test fun lightRevealEffect_doesNotChangeDuringKeyguardTransition() = - runTest(UnconfinedTestDispatcher()) { - val values = mutableListOf<LightRevealEffect>() - val job = underTest.lightRevealEffect.onEach(values::add).launchIn(this) + kosmos.testScope.runTest { + val values by collectValues(underTest.lightRevealEffect) + runCurrent() + assertEquals(listOf(DEFAULT_REVEAL_EFFECT), values) fakeLightRevealScrimRepository.setRevealEffect(reveal1) - + runCurrent() // The reveal effect shouldn't emit anything until a keyguard transition starts. - assertEquals(values.size, 0) + assertEquals(listOf(DEFAULT_REVEAL_EFFECT), values) // Once it starts, it should emit reveal1. fakeKeyguardTransitionRepository.sendTransitionStep( - TransitionStep(transitionState = TransitionState.STARTED) + TransitionStep(to = KeyguardState.AOD, transitionState = TransitionState.STARTED) ) - assertEquals(values, listOf(reveal1)) + runCurrent() + assertEquals(listOf(DEFAULT_REVEAL_EFFECT, reveal1), values) // Until the next transition starts, reveal2 should not be emitted. fakeLightRevealScrimRepository.setRevealEffect(reveal2) + runCurrent() fakeKeyguardTransitionRepository.sendTransitionStep( - TransitionStep(transitionState = TransitionState.RUNNING) + TransitionStep( + to = KeyguardState.LOCKSCREEN, + transitionState = TransitionState.RUNNING + ) ) + runCurrent() fakeKeyguardTransitionRepository.sendTransitionStep( - TransitionStep(transitionState = TransitionState.FINISHED) + TransitionStep(to = KeyguardState.AOD, transitionState = TransitionState.FINISHED) ) - assertEquals(values, listOf(reveal1)) + runCurrent() + assertEquals(listOf(DEFAULT_REVEAL_EFFECT, reveal1), values) fakeKeyguardTransitionRepository.sendTransitionStep( - TransitionStep(transitionState = TransitionState.STARTED) + TransitionStep( + to = KeyguardState.LOCKSCREEN, + transitionState = TransitionState.STARTED + ) ) - assertEquals(values, listOf(reveal1, reveal2)) - - job.cancel() + runCurrent() + assertEquals(listOf(DEFAULT_REVEAL_EFFECT, reveal1, reveal2), values) } } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneActionsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneActionsViewModelTest.kt index b3ea03ec2fb5..c66ebf3a31e0 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneActionsViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneActionsViewModelTest.kt @@ -26,6 +26,7 @@ import com.android.compose.animation.scene.SceneKey import com.android.compose.animation.scene.Swipe import com.android.compose.animation.scene.SwipeDirection import com.android.compose.animation.scene.TransitionKey +import com.android.compose.animation.scene.UserActionResult import com.android.systemui.Flags import com.android.systemui.SysuiTestCase import com.android.systemui.authentication.data.repository.fakeAuthenticationRepository @@ -200,7 +201,7 @@ class LockscreenSceneActionsViewModelTest : SysuiTestCase() { fromSource = Edge.Top.takeIf { downFromEdge }, pointerCount = if (downWithTwoPointers) 2 else 1, ) - ) + ) as? UserActionResult.ChangeScene val downScene by collectLastValue( downDestination?.let { @@ -226,9 +227,11 @@ class LockscreenSceneActionsViewModelTest : SysuiTestCase() { val upScene by collectLastValue( - destinationScenes?.get(Swipe(SwipeDirection.Up))?.toScene?.let { scene -> - kosmos.sceneInteractor.resolveSceneFamily(scene) - } ?: flowOf(null) + (destinationScenes?.get(Swipe(SwipeDirection.Up)) + as? UserActionResult.ChangeScene) + ?.toScene + ?.let { scene -> kosmos.sceneInteractor.resolveSceneFamily(scene) } + ?: flowOf(null) ) assertThat(upScene) @@ -241,9 +244,11 @@ class LockscreenSceneActionsViewModelTest : SysuiTestCase() { val leftScene by collectLastValue( - destinationScenes?.get(Swipe(SwipeDirection.Left))?.toScene?.let { scene -> - kosmos.sceneInteractor.resolveSceneFamily(scene) - } ?: flowOf(null) + (destinationScenes?.get(Swipe(SwipeDirection.Left)) + as? UserActionResult.ChangeScene) + ?.toScene + ?.let { scene -> kosmos.sceneInteractor.resolveSceneFamily(scene) } + ?: flowOf(null) ) assertThat(leftScene) diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/lifecycle/HydratorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/lifecycle/HydratorTest.kt index 8c9c527bd6fd..ec6045ca0a68 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/lifecycle/HydratorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/lifecycle/HydratorTest.kt @@ -48,12 +48,13 @@ class HydratorTest : SysuiTestCase() { composeRule.setContent { val keepAlive by keepAliveMutable if (keepAlive) { - val viewModel = rememberViewModel { - FakeSysUiViewModel( - upstreamFlow = upstreamFlow, - upstreamStateFlow = upstreamStateFlow, - ) - } + val viewModel = + rememberViewModel("test") { + FakeSysUiViewModel( + upstreamFlow = upstreamFlow, + upstreamStateFlow = upstreamStateFlow, + ) + } Column { Text( diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeSceneActionsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeSceneActionsViewModelTest.kt index 8f925d52e649..0505e1996927 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeSceneActionsViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeSceneActionsViewModelTest.kt @@ -20,6 +20,7 @@ import android.testing.TestableLooper import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.compose.animation.scene.Swipe +import com.android.compose.animation.scene.UserActionResult import com.android.systemui.SysuiTestCase import com.android.systemui.authentication.data.repository.fakeAuthenticationRepository import com.android.systemui.authentication.shared.model.AuthenticationMethodModel @@ -68,7 +69,8 @@ class NotificationsShadeSceneActionsViewModelTest : SysuiTestCase() { lockDevice() underTest.activateIn(this) - assertThat(actions?.get(Swipe.Up)?.toScene).isEqualTo(SceneFamilies.Home) + assertThat((actions?.get(Swipe.Up) as? UserActionResult.ChangeScene)?.toScene) + .isEqualTo(SceneFamilies.Home) assertThat(actions?.get(Swipe.Down)).isNull() assertThat(kosmos.homeSceneFamilyResolver.resolvedScene.value) .isEqualTo(Scenes.Lockscreen) @@ -82,7 +84,8 @@ class NotificationsShadeSceneActionsViewModelTest : SysuiTestCase() { kosmos.keyguardEnabledInteractor.notifyKeyguardEnabled(false) underTest.activateIn(this) - assertThat(actions?.get(Swipe.Up)?.toScene).isEqualTo(SceneFamilies.Home) + assertThat((actions?.get(Swipe.Up) as? UserActionResult.ChangeScene)?.toScene) + .isEqualTo(SceneFamilies.Home) assertThat(kosmos.homeSceneFamilyResolver.resolvedScene.value).isEqualTo(Scenes.Gone) } @@ -94,7 +97,8 @@ class NotificationsShadeSceneActionsViewModelTest : SysuiTestCase() { unlockDevice() underTest.activateIn(this) - assertThat(actions?.get(Swipe.Up)?.toScene).isEqualTo(SceneFamilies.Home) + assertThat((actions?.get(Swipe.Up) as? UserActionResult.ChangeScene)?.toScene) + .isEqualTo(SceneFamilies.Home) assertThat(actions?.get(Swipe.Down)).isNull() assertThat(sceneInteractor.currentScene.value).isEqualTo(Scenes.Gone) } @@ -107,7 +111,8 @@ class NotificationsShadeSceneActionsViewModelTest : SysuiTestCase() { lockDevice() underTest.activateIn(this) - assertThat(actions?.get(Swipe.Down)?.toScene).isEqualTo(SceneFamilies.Home) + assertThat((actions?.get(Swipe.Down) as? UserActionResult.ChangeScene)?.toScene) + .isEqualTo(SceneFamilies.Home) assertThat(actions?.get(Swipe.Up)).isNull() assertThat(kosmos.homeSceneFamilyResolver.resolvedScene.value) .isEqualTo(Scenes.Lockscreen) @@ -122,7 +127,8 @@ class NotificationsShadeSceneActionsViewModelTest : SysuiTestCase() { unlockDevice() underTest.activateIn(this) - assertThat(actions?.get(Swipe.Down)?.toScene).isEqualTo(SceneFamilies.Home) + assertThat((actions?.get(Swipe.Down) as? UserActionResult.ChangeScene)?.toScene) + .isEqualTo(SceneFamilies.Home) assertThat(actions?.get(Swipe.Up)).isNull() assertThat(sceneInteractor.currentScene.value).isEqualTo(Scenes.Gone) } @@ -138,7 +144,8 @@ class NotificationsShadeSceneActionsViewModelTest : SysuiTestCase() { sceneInteractor.changeScene(Scenes.Lockscreen, "reason") underTest.activateIn(this) - assertThat(actions?.get(Swipe.Up)?.toScene).isEqualTo(SceneFamilies.Home) + assertThat((actions?.get(Swipe.Up) as? UserActionResult.ChangeScene)?.toScene) + .isEqualTo(SceneFamilies.Home) assertThat(kosmos.homeSceneFamilyResolver.resolvedScene.value) .isEqualTo(Scenes.Lockscreen) } @@ -156,7 +163,8 @@ class NotificationsShadeSceneActionsViewModelTest : SysuiTestCase() { sceneInteractor.changeScene(Scenes.Gone, "reason") underTest.activateIn(this) - assertThat(actions?.get(Swipe.Up)?.toScene).isEqualTo(SceneFamilies.Home) + assertThat((actions?.get(Swipe.Up) as? UserActionResult.ChangeScene)?.toScene) + .isEqualTo(SceneFamilies.Home) assertThat(kosmos.homeSceneFamilyResolver.resolvedScene.value).isEqualTo(Scenes.Gone) } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModelTest.kt index 768fbca90b56..7203b61ecc9f 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModelTest.kt @@ -16,6 +16,7 @@ package com.android.systemui.qs.composefragment.viewmodel +import android.app.StatusBarManager import android.content.testableContext import android.testing.TestableLooper.RunWithLooper import androidx.lifecycle.Lifecycle @@ -32,6 +33,8 @@ import com.android.systemui.qs.fgsManagerController import com.android.systemui.res.R import com.android.systemui.shade.largeScreenHeaderHelper import com.android.systemui.statusbar.StatusBarState +import com.android.systemui.statusbar.disableflags.data.model.DisableFlagsModel +import com.android.systemui.statusbar.disableflags.data.repository.fakeDisableFlagsRepository import com.android.systemui.statusbar.sysuiStatusBarStateController import com.android.systemui.testKosmos import com.google.common.truth.Truth.assertThat @@ -178,6 +181,23 @@ class QSFragmentComposeViewModelTest : SysuiTestCase() { } } + @Test + fun qsEnabled_followsRepository() = + with(kosmos) { + testScope.testWithinLifecycle { + val qsEnabled by collectLastValue(underTest.qsEnabled) + + fakeDisableFlagsRepository.disableFlags.value = + DisableFlagsModel(disable2 = QS_DISABLE_FLAG) + + assertThat(qsEnabled).isFalse() + + fakeDisableFlagsRepository.disableFlags.value = DisableFlagsModel() + + assertThat(qsEnabled).isTrue() + } + } + private inline fun TestScope.testWithinLifecycle( crossinline block: suspend TestScope.() -> TestResult ): TestResult { @@ -186,4 +206,8 @@ class QSFragmentComposeViewModelTest : SysuiTestCase() { block().also { lifecycleOwner.setCurrentState(Lifecycle.State.DESTROYED) } } } + + companion object { + private const val QS_DISABLE_FLAG = StatusBarManager.DISABLE2_QUICK_SETTINGS + } } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileDataInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileDataInteractorTest.kt index 5925819f27a7..5fd3a242e195 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileDataInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileDataInteractorTest.kt @@ -24,9 +24,10 @@ 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.settingslib.notification.modes.ZenIconLoader +import com.android.internal.R import com.android.systemui.SysuiTestCase import com.android.systemui.common.shared.model.asIcon +import com.android.systemui.coroutines.collectLastValue import com.android.systemui.coroutines.collectValues import com.android.systemui.kosmos.testDispatcher import com.android.systemui.kosmos.testScope @@ -36,7 +37,6 @@ import com.android.systemui.statusbar.policy.data.repository.fakeZenModeReposito import com.android.systemui.statusbar.policy.domain.interactor.zenModeInteractor import com.android.systemui.testKosmos import com.google.common.truth.Truth.assertThat -import com.google.common.util.concurrent.MoreExecutors import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.toCollection @@ -60,10 +60,10 @@ class ModesTileDataInteractorTest : SysuiTestCase() { @Before fun setUp() { context.orCreateTestableResources.apply { - addOverride(com.android.internal.R.drawable.ic_zen_mode_type_bedtime, BEDTIME_DRAWABLE) - addOverride(com.android.internal.R.drawable.ic_zen_mode_type_driving, DRIVING_DRAWABLE) + addOverride(MODES_DRAWABLE_ID, MODES_DRAWABLE) + addOverride(R.drawable.ic_zen_mode_type_bedtime, BEDTIME_DRAWABLE) + addOverride(R.drawable.ic_zen_mode_type_driving, DRIVING_DRAWABLE) } - ZenIconLoader.setInstance(ZenIconLoader(MoreExecutors.newDirectExecutorService())) } @EnableFlags(Flags.FLAG_MODES_UI) @@ -128,28 +128,34 @@ class ModesTileDataInteractorTest : SysuiTestCase() { @Test @EnableFlags(Flags.FLAG_MODES_UI, Flags.FLAG_MODES_UI_ICONS) - fun changesIconWhenActiveModesChange() = + fun tileData_iconsFlagEnabled_changesIconWhenActiveModesChange() = testScope.runTest { - val dataList: List<ModesTileModel> by - collectValues( + val tileData by + collectLastValue( underTest.tileData(TEST_USER, flowOf(DataUpdateTrigger.InitialRequest)) ) + + // Tile starts with the generic Modes icon. runCurrent() - assertThat(dataList.map { it.icon }).containsExactly(null).inOrder() + assertThat(tileData?.icon).isEqualTo(MODES_ICON) + assertThat(tileData?.iconResId).isEqualTo(MODES_DRAWABLE_ID) - // Add an inactive mode: state hasn't changed, so this shouldn't cause another emission + // Add an inactive mode -> Still modes icon zenModeRepository.addMode(id = "Mode", active = false) runCurrent() - assertThat(dataList.map { it.icon }).containsExactly(null).inOrder() + assertThat(tileData?.icon).isEqualTo(MODES_ICON) + assertThat(tileData?.iconResId).isEqualTo(MODES_DRAWABLE_ID) - // Add an active mode: icon should be the mode icon + // Add an active mode: icon should be the mode icon. No iconResId, because we don't + // really know that it's a system icon. zenModeRepository.addMode( id = "Bedtime", type = AutomaticZenRule.TYPE_BEDTIME, active = true ) runCurrent() - assertThat(dataList.map { it.icon }).containsExactly(null, BEDTIME_ICON).inOrder() + assertThat(tileData?.icon).isEqualTo(BEDTIME_ICON) + assertThat(tileData?.iconResId).isNull() // Add another, less-prioritized mode: icon should remain the first mode icon zenModeRepository.addMode( @@ -158,29 +164,58 @@ class ModesTileDataInteractorTest : SysuiTestCase() { active = true ) runCurrent() - assertThat(dataList.map { it.icon }) - .containsExactly(null, BEDTIME_ICON, BEDTIME_ICON) - .inOrder() + assertThat(tileData?.icon).isEqualTo(BEDTIME_ICON) + assertThat(tileData?.iconResId).isNull() - // Deactivate more important mode: icon should be the less important, still active mode. + // Deactivate more important mode: icon should be the less important, still active mode zenModeRepository.deactivateMode("Bedtime") runCurrent() - assertThat(dataList.map { it.icon }) - .containsExactly(null, BEDTIME_ICON, BEDTIME_ICON, DRIVING_ICON) - .inOrder() + assertThat(tileData?.icon).isEqualTo(DRIVING_ICON) + assertThat(tileData?.iconResId).isNull() - // Deactivate remaining mode: no icon + // Deactivate remaining mode: back to the default modes icon zenModeRepository.deactivateMode("Driving") runCurrent() - assertThat(dataList.map { it.icon }) - .containsExactly(null, BEDTIME_ICON, BEDTIME_ICON, DRIVING_ICON, null) - .inOrder() + assertThat(tileData?.icon).isEqualTo(MODES_ICON) + assertThat(tileData?.iconResId).isEqualTo(MODES_DRAWABLE_ID) + } + + @Test + @EnableFlags(Flags.FLAG_MODES_UI) + @DisableFlags(Flags.FLAG_MODES_UI_ICONS) + fun tileData_iconsFlagDisabled_hasPriorityModesIcon() = + testScope.runTest { + val tileData by + collectLastValue( + underTest.tileData(TEST_USER, flowOf(DataUpdateTrigger.InitialRequest)) + ) + + runCurrent() + assertThat(tileData?.icon).isEqualTo(MODES_ICON) + assertThat(tileData?.iconResId).isEqualTo(MODES_DRAWABLE_ID) + + // Activate a Mode -> Icon doesn't change. + zenModeRepository.addMode(id = "Mode", active = true) + runCurrent() + assertThat(tileData?.icon).isEqualTo(MODES_ICON) + assertThat(tileData?.iconResId).isEqualTo(MODES_DRAWABLE_ID) + + zenModeRepository.deactivateMode(id = "Mode") + runCurrent() + assertThat(tileData?.icon).isEqualTo(MODES_ICON) + assertThat(tileData?.iconResId).isEqualTo(MODES_DRAWABLE_ID) } private companion object { val TEST_USER = UserHandle.of(1)!! + + val MODES_DRAWABLE_ID = com.android.systemui.res.R.drawable.qs_dnd_icon_off + + val MODES_DRAWABLE = TestStubDrawable("modes_icon") val BEDTIME_DRAWABLE = TestStubDrawable("bedtime") val DRIVING_DRAWABLE = TestStubDrawable("driving") + + val MODES_ICON = MODES_DRAWABLE.asIcon() val BEDTIME_ICON = BEDTIME_DRAWABLE.asIcon() val DRIVING_ICON = DRIVING_DRAWABLE.asIcon() } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileUserActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileUserActionInteractorTest.kt index 4b7564981855..cd5812710292 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileUserActionInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileUserActionInteractorTest.kt @@ -16,12 +16,14 @@ package com.android.systemui.qs.tiles.impl.modes.domain.interactor +import android.graphics.drawable.TestStubDrawable import android.platform.test.annotations.EnableFlags 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.animation.Expandable +import com.android.systemui.common.shared.model.asIcon import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandlerSubject import com.android.systemui.qs.tiles.base.actions.qsTileIntentUserInputHandler import com.android.systemui.qs.tiles.base.interactor.QSTileInputTestKtx @@ -54,10 +56,7 @@ class ModesTileUserActionInteractorTest : SysuiTestCase() { fun handleClick_active() = runTest { val expandable = mock<Expandable>() underTest.handleInput( - QSTileInputTestKtx.click( - data = ModesTileModel(true, listOf("DND")), - expandable = expandable - ) + QSTileInputTestKtx.click(data = modelOf(true, listOf("DND")), expandable = expandable) ) verify(mockDialogDelegate).showDialog(eq(expandable)) @@ -67,10 +66,7 @@ class ModesTileUserActionInteractorTest : SysuiTestCase() { fun handleClick_inactive() = runTest { val expandable = mock<Expandable>() underTest.handleInput( - QSTileInputTestKtx.click( - data = ModesTileModel(false, emptyList()), - expandable = expandable - ) + QSTileInputTestKtx.click(data = modelOf(false, emptyList()), expandable = expandable) ) verify(mockDialogDelegate).showDialog(eq(expandable)) @@ -78,7 +74,7 @@ class ModesTileUserActionInteractorTest : SysuiTestCase() { @Test fun handleLongClick_active() = runTest { - underTest.handleInput(QSTileInputTestKtx.longClick(ModesTileModel(true, listOf("DND")))) + underTest.handleInput(QSTileInputTestKtx.longClick(modelOf(true, listOf("DND")))) QSTileIntentUserInputHandlerSubject.assertThat(inputHandler).handledOneIntentInput { assertThat(it.intent.action).isEqualTo(Settings.ACTION_ZEN_MODE_SETTINGS) @@ -87,10 +83,14 @@ class ModesTileUserActionInteractorTest : SysuiTestCase() { @Test fun handleLongClick_inactive() = runTest { - underTest.handleInput(QSTileInputTestKtx.longClick(ModesTileModel(false, emptyList()))) + underTest.handleInput(QSTileInputTestKtx.longClick(modelOf(false, emptyList()))) QSTileIntentUserInputHandlerSubject.assertThat(inputHandler).handledOneIntentInput { assertThat(it.intent.action).isEqualTo(Settings.ACTION_ZEN_MODE_SETTINGS) } } + + private fun modelOf(isActivated: Boolean, activeModeNames: List<String>): ModesTileModel { + return ModesTileModel(isActivated, activeModeNames, TestStubDrawable("icon").asIcon(), 123) + } } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/ui/ModesTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/ui/ModesTileMapperTest.kt index a41f15d8ff82..f7bdcb8086ef 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/ui/ModesTileMapperTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/ui/ModesTileMapperTest.kt @@ -18,11 +18,12 @@ package com.android.systemui.qs.tiles.impl.modes.ui import android.app.Flags import android.graphics.drawable.TestStubDrawable +import android.platform.test.annotations.DisableFlags import android.platform.test.annotations.EnableFlags import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase -import com.android.systemui.common.shared.model.Icon +import com.android.systemui.common.shared.model.asIcon import com.android.systemui.qs.tiles.impl.modes.domain.model.ModesTileModel import com.android.systemui.qs.tiles.viewmodel.QSTileConfigTestBuilder import com.android.systemui.qs.tiles.viewmodel.QSTileState @@ -58,47 +59,88 @@ class ModesTileMapperTest : SysuiTestCase() { @Test fun inactiveState() { - val model = ModesTileModel(isActivated = false, activeModes = emptyList()) + val icon = TestStubDrawable("res123").asIcon() + val model = + ModesTileModel( + isActivated = false, + activeModes = emptyList(), + icon = icon, + ) val state = underTest.map(config, model) assertThat(state.activationState).isEqualTo(QSTileState.ActivationState.INACTIVE) - assertThat(state.iconRes).isEqualTo(R.drawable.qs_dnd_icon_off) + assertThat(state.icon()).isEqualTo(icon) assertThat(state.secondaryLabel).isEqualTo("No active modes") } @Test fun activeState_oneMode() { - val model = ModesTileModel(isActivated = true, activeModes = listOf("DND")) + val icon = TestStubDrawable("res123").asIcon() + val model = + ModesTileModel( + isActivated = true, + activeModes = listOf("DND"), + icon = icon, + ) val state = underTest.map(config, model) assertThat(state.activationState).isEqualTo(QSTileState.ActivationState.ACTIVE) - assertThat(state.iconRes).isEqualTo(R.drawable.qs_dnd_icon_on) + assertThat(state.icon()).isEqualTo(icon) assertThat(state.secondaryLabel).isEqualTo("DND is active") } @Test fun activeState_multipleModes() { + val icon = TestStubDrawable("res123").asIcon() val model = - ModesTileModel(isActivated = true, activeModes = listOf("Mode 1", "Mode 2", "Mode 3")) + ModesTileModel( + isActivated = true, + activeModes = listOf("Mode 1", "Mode 2", "Mode 3"), + icon = icon, + ) val state = underTest.map(config, model) assertThat(state.activationState).isEqualTo(QSTileState.ActivationState.ACTIVE) - assertThat(state.iconRes).isEqualTo(R.drawable.qs_dnd_icon_on) + assertThat(state.icon()).isEqualTo(icon) assertThat(state.secondaryLabel).isEqualTo("3 modes are active") } @Test @EnableFlags(Flags.FLAG_MODES_UI_ICONS) - fun activeState_withIcon() { - val icon = Icon.Resource(1234, contentDescription = null) - val model = ModesTileModel(isActivated = true, activeModes = listOf("DND"), icon = icon) + fun state_withEnabledFlag_noIconResId() { + val icon = TestStubDrawable("res123").asIcon() + val model = + ModesTileModel( + isActivated = false, + activeModes = emptyList(), + icon = icon, + iconResId = 123 // Should not be populated, but is ignored even if present + ) val state = underTest.map(config, model) + assertThat(state.icon()).isEqualTo(icon) assertThat(state.iconRes).isNull() + } + + @Test + @DisableFlags(Flags.FLAG_MODES_UI_ICONS) + fun state_withDisabledFlag_includesIconResId() { + val icon = TestStubDrawable("res123").asIcon() + val model = + ModesTileModel( + isActivated = false, + activeModes = emptyList(), + icon = icon, + iconResId = 123 + ) + + val state = underTest.map(config, model) + assertThat(state.icon()).isEqualTo(icon) + assertThat(state.iconRes).isEqualTo(123) } } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeSceneActionsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeSceneActionsViewModelTest.kt index 647fdf6d6931..db58c8500768 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeSceneActionsViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeSceneActionsViewModelTest.kt @@ -21,6 +21,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.compose.animation.scene.Back import com.android.compose.animation.scene.Swipe +import com.android.compose.animation.scene.UserActionResult import com.android.systemui.SysuiTestCase import com.android.systemui.authentication.data.repository.fakeAuthenticationRepository import com.android.systemui.authentication.shared.model.AuthenticationMethodModel @@ -45,7 +46,6 @@ 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 @@ -63,19 +63,16 @@ class QuickSettingsShadeSceneActionsViewModelTest : SysuiTestCase() { private val underTest by lazy { kosmos.quickSettingsShadeSceneActionsViewModel } - @Before - fun setUp() { - underTest.activateIn(testScope) - } - @Test fun upTransitionSceneKey_deviceLocked_lockscreen() = testScope.runTest { + underTest.activateIn(this) val actions by collectLastValue(underTest.actions) val homeScene by collectLastValue(kosmos.homeSceneFamilyResolver.resolvedScene) lockDevice() - assertThat(actions?.get(Swipe.Up)?.toScene).isEqualTo(SceneFamilies.Home) + assertThat((actions?.get(Swipe.Up) as? UserActionResult.ChangeScene)?.toScene) + .isEqualTo(SceneFamilies.Home) assertThat(actions?.get(Swipe.Down)).isNull() assertThat(homeScene).isEqualTo(Scenes.Lockscreen) } @@ -83,24 +80,28 @@ class QuickSettingsShadeSceneActionsViewModelTest : SysuiTestCase() { @Test fun upTransitionSceneKey_deviceLocked_keyguardDisabled_gone() = testScope.runTest { + underTest.activateIn(this) val actions by collectLastValue(underTest.actions) val homeScene by collectLastValue(kosmos.homeSceneFamilyResolver.resolvedScene) lockDevice() kosmos.keyguardEnabledInteractor.notifyKeyguardEnabled(false) - assertThat(actions?.get(Swipe.Up)?.toScene).isEqualTo(SceneFamilies.Home) + assertThat((actions?.get(Swipe.Up) as? UserActionResult.ChangeScene)?.toScene) + .isEqualTo(SceneFamilies.Home) assertThat(homeScene).isEqualTo(Scenes.Gone) } @Test fun upTransitionSceneKey_deviceUnlocked_gone() = testScope.runTest { + underTest.activateIn(this) val actions by collectLastValue(underTest.actions) val homeScene by collectLastValue(kosmos.homeSceneFamilyResolver.resolvedScene) lockDevice() unlockDevice() - assertThat(actions?.get(Swipe.Up)?.toScene).isEqualTo(SceneFamilies.Home) + assertThat((actions?.get(Swipe.Up) as? UserActionResult.ChangeScene)?.toScene) + .isEqualTo(SceneFamilies.Home) assertThat(actions?.get(Swipe.Down)).isNull() assertThat(homeScene).isEqualTo(Scenes.Gone) } @@ -109,11 +110,13 @@ class QuickSettingsShadeSceneActionsViewModelTest : SysuiTestCase() { fun downTransitionSceneKey_deviceLocked_bottomAligned_lockscreen() = testScope.runTest { kosmos.fakeShadeRepository.setDualShadeAlignedToBottom(true) + underTest.activateIn(this) val actions by collectLastValue(underTest.actions) val homeScene by collectLastValue(kosmos.homeSceneFamilyResolver.resolvedScene) lockDevice() - assertThat(actions?.get(Swipe.Down)?.toScene).isEqualTo(SceneFamilies.Home) + assertThat((actions?.get(Swipe.Down) as? UserActionResult.ChangeScene)?.toScene) + .isEqualTo(SceneFamilies.Home) assertThat(actions?.get(Swipe.Up)).isNull() assertThat(homeScene).isEqualTo(Scenes.Lockscreen) } @@ -122,12 +125,14 @@ class QuickSettingsShadeSceneActionsViewModelTest : SysuiTestCase() { fun downTransitionSceneKey_deviceUnlocked_bottomAligned_gone() = testScope.runTest { kosmos.fakeShadeRepository.setDualShadeAlignedToBottom(true) + underTest.activateIn(this) val actions by collectLastValue(underTest.actions) val homeScene by collectLastValue(kosmos.homeSceneFamilyResolver.resolvedScene) lockDevice() unlockDevice() - assertThat(actions?.get(Swipe.Down)?.toScene).isEqualTo(SceneFamilies.Home) + assertThat((actions?.get(Swipe.Down) as? UserActionResult.ChangeScene)?.toScene) + .isEqualTo(SceneFamilies.Home) assertThat(actions?.get(Swipe.Up)).isNull() assertThat(homeScene).isEqualTo(Scenes.Gone) } @@ -135,6 +140,7 @@ class QuickSettingsShadeSceneActionsViewModelTest : SysuiTestCase() { @Test fun upTransitionSceneKey_authMethodSwipe_lockscreenNotDismissed_goesToLockscreen() = testScope.runTest { + underTest.activateIn(this) val actions by collectLastValue(underTest.actions) val homeScene by collectLastValue(kosmos.homeSceneFamilyResolver.resolvedScene) kosmos.fakeDeviceEntryRepository.setLockscreenEnabled(true) @@ -143,13 +149,15 @@ class QuickSettingsShadeSceneActionsViewModelTest : SysuiTestCase() { ) sceneInteractor.changeScene(Scenes.Lockscreen, "reason") - assertThat(actions?.get(Swipe.Up)?.toScene).isEqualTo(SceneFamilies.Home) + assertThat((actions?.get(Swipe.Up) as? UserActionResult.ChangeScene)?.toScene) + .isEqualTo(SceneFamilies.Home) assertThat(homeScene).isEqualTo(Scenes.Lockscreen) } @Test fun upTransitionSceneKey_authMethodSwipe_lockscreenDismissed_goesToGone() = testScope.runTest { + underTest.activateIn(this) val actions by collectLastValue(underTest.actions) val homeScene by collectLastValue(kosmos.homeSceneFamilyResolver.resolvedScene) kosmos.fakeDeviceEntryRepository.setLockscreenEnabled(true) @@ -159,21 +167,25 @@ class QuickSettingsShadeSceneActionsViewModelTest : SysuiTestCase() { runCurrent() sceneInteractor.changeScene(Scenes.Gone, "reason") - assertThat(actions?.get(Swipe.Up)?.toScene).isEqualTo(SceneFamilies.Home) + assertThat((actions?.get(Swipe.Up) as? UserActionResult.ChangeScene)?.toScene) + .isEqualTo(SceneFamilies.Home) assertThat(homeScene).isEqualTo(Scenes.Gone) } @Test fun backTransitionSceneKey_notEditing_Home() = testScope.runTest { + underTest.activateIn(this) val actions by collectLastValue(underTest.actions) - assertThat(actions?.get(Back)?.toScene).isEqualTo(SceneFamilies.Home) + assertThat((actions?.get(Back) as? UserActionResult.ChangeScene)?.toScene) + .isEqualTo(SceneFamilies.Home) } @Test fun backTransition_editing_noDestination() = testScope.runTest { + underTest.activateIn(this) val actions by collectLastValue(underTest.actions) kosmos.editModeViewModel.startEditing() 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 5b987b309b6b..f365afbfcc06 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt @@ -26,6 +26,7 @@ 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.UserActionResult import com.android.internal.R import com.android.internal.util.EmergencyAffordanceManager import com.android.internal.util.emergencyAffordanceManager @@ -229,7 +230,8 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() { fun swipeUpOnLockscreen_enterCorrectPin_unlocksDevice() = testScope.runTest { val actions by collectLastValue(lockscreenSceneActionsViewModel.actions) - val upDestinationSceneKey = actions?.get(Swipe.Up)?.toScene + val upDestinationSceneKey = + (actions?.get(Swipe.Up) as? UserActionResult.ChangeScene)?.toScene assertThat(upDestinationSceneKey).isEqualTo(Scenes.Bouncer) emulateUserDrivenTransition( to = upDestinationSceneKey, @@ -249,7 +251,8 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() { setAuthMethod(AuthenticationMethodModel.None, enableLockscreen = true) val actions by collectLastValue(lockscreenSceneActionsViewModel.actions) - val upDestinationSceneKey = actions?.get(Swipe.Up)?.toScene + val upDestinationSceneKey = + (actions?.get(Swipe.Up) as? UserActionResult.ChangeScene)?.toScene assertThat(upDestinationSceneKey).isEqualTo(Scenes.Gone) emulateUserDrivenTransition( to = upDestinationSceneKey, @@ -268,7 +271,8 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() { emulateUserDrivenTransition(to = Scenes.Shade) assertCurrentScene(Scenes.Shade) - val upDestinationSceneKey = actions?.get(Swipe.Up)?.toScene + val upDestinationSceneKey = + (actions?.get(Swipe.Up) as? UserActionResult.ChangeScene)?.toScene assertThat(upDestinationSceneKey).isEqualTo(SceneFamilies.Home) assertThat(homeScene).isEqualTo(Scenes.Lockscreen) emulateUserDrivenTransition( @@ -296,7 +300,8 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() { emulateUserDrivenTransition(to = Scenes.Shade) assertCurrentScene(Scenes.Shade) - val upDestinationSceneKey = actions?.get(Swipe.Up)?.toScene + val upDestinationSceneKey = + (actions?.get(Swipe.Up) as? UserActionResult.ChangeScene)?.toScene assertThat(upDestinationSceneKey).isEqualTo(SceneFamilies.Home) assertThat(homeScene).isEqualTo(Scenes.Gone) emulateUserDrivenTransition( @@ -365,7 +370,8 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() { testScope.runTest { unlockDevice() val actions by collectLastValue(lockscreenSceneActionsViewModel.actions) - val upDestinationSceneKey = actions?.get(Swipe.Up)?.toScene + val upDestinationSceneKey = + (actions?.get(Swipe.Up) as? UserActionResult.ChangeScene)?.toScene assertThat(upDestinationSceneKey).isEqualTo(Scenes.Gone) } @@ -387,7 +393,8 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() { testScope.runTest { setAuthMethod(AuthenticationMethodModel.Password) val actions by collectLastValue(lockscreenSceneActionsViewModel.actions) - val upDestinationSceneKey = actions?.get(Swipe.Up)?.toScene + val upDestinationSceneKey = + (actions?.get(Swipe.Up) as? UserActionResult.ChangeScene)?.toScene assertThat(upDestinationSceneKey).isEqualTo(Scenes.Bouncer) emulateUserDrivenTransition( to = upDestinationSceneKey, @@ -405,7 +412,8 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() { testScope.runTest { setAuthMethod(AuthenticationMethodModel.Password) val actions by collectLastValue(lockscreenSceneActionsViewModel.actions) - val upDestinationSceneKey = actions?.get(Swipe.Up)?.toScene + val upDestinationSceneKey = + (actions?.get(Swipe.Up) as? UserActionResult.ChangeScene)?.toScene assertThat(upDestinationSceneKey).isEqualTo(Scenes.Bouncer) emulateUserDrivenTransition(to = upDestinationSceneKey) @@ -425,7 +433,8 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() { setAuthMethod(AuthenticationMethodModel.Password) startPhoneCall() val actions by collectLastValue(lockscreenSceneActionsViewModel.actions) - val upDestinationSceneKey = actions?.get(Swipe.Up)?.toScene + val upDestinationSceneKey = + (actions?.get(Swipe.Up) as? UserActionResult.ChangeScene)?.toScene assertThat(upDestinationSceneKey).isEqualTo(Scenes.Bouncer) emulateUserDrivenTransition(to = upDestinationSceneKey) @@ -492,7 +501,7 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() { private fun getCurrentSceneInUi(): SceneKey { return when (val state = transitionState.value) { is ObservableTransitionState.Idle -> state.currentScene - is ObservableTransitionState.Transition.ChangeCurrentScene -> state.fromScene + is ObservableTransitionState.Transition.ChangeScene -> state.fromScene is ObservableTransitionState.Transition.ShowOrHideOverlay -> state.currentScene is ObservableTransitionState.Transition.ReplaceOverlay -> state.currentScene } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/data/repository/SceneContainerRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/data/repository/SceneContainerRepositoryTest.kt index df30c4bf1b5a..227b3a610188 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/data/repository/SceneContainerRepositoryTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/data/repository/SceneContainerRepositoryTest.kt @@ -25,6 +25,7 @@ import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.collectLastValue import com.android.systemui.flags.EnableSceneContainer import com.android.systemui.kosmos.testScope +import com.android.systemui.scene.overlayKeys import com.android.systemui.scene.sceneContainerConfig import com.android.systemui.scene.sceneKeys import com.android.systemui.scene.shared.model.Scenes @@ -46,19 +47,9 @@ class SceneContainerRepositoryTest : SysuiTestCase() { private val testScope = kosmos.testScope @Test - fun allSceneKeys() { + fun allContentKeys() { val underTest = kosmos.sceneContainerRepository - assertThat(underTest.allSceneKeys()) - .isEqualTo( - listOf( - Scenes.QuickSettings, - Scenes.Shade, - Scenes.Lockscreen, - Scenes.Bouncer, - Scenes.Gone, - Scenes.Communal, - ) - ) + assertThat(underTest.allContentKeys).isEqualTo(kosmos.sceneKeys + kosmos.overlayKeys) } @Test @@ -75,6 +66,18 @@ class SceneContainerRepositoryTest : SysuiTestCase() { assertThat(currentScene).isEqualTo(Scenes.QuickSettings) } + // TODO(b/356596436): Add tests for showing, hiding, and replacing overlays after we've defined + // them. + @Test + fun currentOverlays() = + testScope.runTest { + val underTest = kosmos.sceneContainerRepository + val currentOverlays by collectLastValue(underTest.currentOverlays) + assertThat(currentOverlays).isEmpty() + + // TODO(b/356596436): When we have a first overlay, add it here and assert contains. + } + @Test(expected = IllegalStateException::class) fun changeScene_noSuchSceneInContainer_throws() { kosmos.sceneKeys = listOf(Scenes.QuickSettings, Scenes.Lockscreen) 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 35cefa6b58df..4a7d8b0f8287 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 @@ -35,6 +35,7 @@ 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.overlayKeys import com.android.systemui.scene.sceneContainerConfig import com.android.systemui.scene.sceneKeys import com.android.systemui.scene.shared.model.SceneFamilies @@ -72,9 +73,11 @@ class SceneInteractorTest : SysuiTestCase() { kosmos.keyguardEnabledInteractor } + // TODO(b/356596436): Add tests for showing, hiding, and replacing overlays after we've defined + // them. @Test - fun allSceneKeys() { - assertThat(underTest.allSceneKeys()).isEqualTo(kosmos.sceneKeys) + fun allContentKeys() { + assertThat(underTest.allContentKeys).isEqualTo(kosmos.sceneKeys + kosmos.overlayKeys) } @Test 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 8f8d2e22e161..d3b51d1d17f7 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 @@ -51,7 +51,7 @@ import com.android.systemui.keyguard.data.repository.keyguardRepository import com.android.systemui.keyguard.data.repository.keyguardTransitionRepository import com.android.systemui.keyguard.dismissCallbackRegistry import com.android.systemui.keyguard.domain.interactor.keyguardEnabledInteractor -import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor +import com.android.systemui.keyguard.domain.interactor.keyguardInteractor import com.android.systemui.keyguard.domain.interactor.scenetransition.lockscreenSceneTransitionInteractor import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.shared.model.SuccessFingerprintAuthenticationStatus @@ -551,8 +551,7 @@ class SceneContainerStartableTest : SysuiTestCase() { fun switchToAOD_whenAvailable_whenDeviceSleepsLocked() = testScope.runTest { kosmos.lockscreenSceneTransitionInteractor.start() - val asleepState by - collectLastValue(kosmos.keyguardTransitionInteractor.asleepKeyguardState) + val asleepState by collectLastValue(kosmos.keyguardInteractor.asleepKeyguardState) val currentTransitionInfo by collectLastValue(kosmos.keyguardTransitionRepository.currentTransitionInfoInternal) val transitionState = @@ -584,8 +583,7 @@ class SceneContainerStartableTest : SysuiTestCase() { fun switchToDozing_whenAodUnavailable_whenDeviceSleepsLocked() = testScope.runTest { kosmos.lockscreenSceneTransitionInteractor.start() - val asleepState by - collectLastValue(kosmos.keyguardTransitionInteractor.asleepKeyguardState) + val asleepState by collectLastValue(kosmos.keyguardInteractor.asleepKeyguardState) val currentTransitionInfo by collectLastValue(kosmos.keyguardTransitionRepository.currentTransitionInfoInternal) val transitionState = diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/shared/model/SceneDataSourceDelegatorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/shared/model/SceneDataSourceDelegatorTest.kt index 32c0172071f6..2720c5722376 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/shared/model/SceneDataSourceDelegatorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/shared/model/SceneDataSourceDelegatorTest.kt @@ -37,6 +37,8 @@ class SceneDataSourceDelegatorTest : SysuiTestCase() { private val initialSceneKey = kosmos.initialSceneKey private val fakeSceneDataSource = kosmos.fakeSceneDataSource + // TODO(b/356596436): Add tests for showing, hiding, and replacing overlays after we've defined + // them. private val underTest = kosmos.sceneDataSourceDelegator @Test diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/SceneActionsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/SceneActionsViewModelTest.kt index dd4432dd9797..900f2a464588 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/SceneActionsViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/SceneActionsViewModelTest.kt @@ -35,7 +35,6 @@ import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.Job import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest import org.junit.Test @@ -123,7 +122,7 @@ class SceneActionsViewModelTest : SysuiTestCase() { override suspend fun hydrateActions( setActions: (Map<UserAction, UserActionResult>) -> Unit, ) { - upstream.collectLatest { setActions(it) } + upstream.collect { setActions(it) } } } } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModelTest.kt index 832e7b1bcc0c..3558f178b5b4 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModelTest.kt @@ -32,7 +32,6 @@ import com.android.systemui.power.data.repository.fakePowerRepository import com.android.systemui.power.domain.interactor.powerInteractor import com.android.systemui.scene.domain.interactor.sceneInteractor import com.android.systemui.scene.sceneContainerConfig -import com.android.systemui.scene.sceneKeys import com.android.systemui.scene.shared.logger.sceneLogger import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.scene.shared.model.fakeSceneDataSource @@ -49,6 +48,7 @@ import org.junit.Before import org.junit.Test import org.junit.runner.RunWith +@OptIn(ExperimentalCoroutinesApi::class) @SmallTest @RunWith(AndroidJUnit4::class) @EnableSceneContainer @@ -113,11 +113,6 @@ class SceneContainerViewModelTest : SysuiTestCase() { } @Test - fun allSceneKeys() { - assertThat(underTest.allSceneKeys).isEqualTo(kosmos.sceneKeys) - } - - @Test fun sceneTransition() = testScope.runTest { val currentScene by collectLastValue(underTest.currentScene) diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneActionsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneActionsViewModelTest.kt index 06a02c65bc64..a931e656c3c6 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneActionsViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneActionsViewModelTest.kt @@ -24,6 +24,7 @@ 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.compose.animation.scene.UserActionResult import com.android.systemui.SysuiTestCase import com.android.systemui.authentication.data.repository.fakeAuthenticationRepository import com.android.systemui.authentication.shared.model.AuthenticationMethodModel @@ -87,7 +88,10 @@ class ShadeSceneActionsViewModelTest : SysuiTestCase() { AuthenticationMethodModel.Pin ) - assertThat(actions?.get(Swipe(SwipeDirection.Up))?.toScene) + assertThat( + (actions?.get(Swipe(SwipeDirection.Up)) as? UserActionResult.ChangeScene) + ?.toScene + ) .isEqualTo(SceneFamilies.Home) assertThat(homeScene).isEqualTo(Scenes.Lockscreen) } @@ -102,7 +106,10 @@ class ShadeSceneActionsViewModelTest : SysuiTestCase() { ) setDeviceEntered(true) - assertThat(actions?.get(Swipe(SwipeDirection.Up))?.toScene) + assertThat( + (actions?.get(Swipe(SwipeDirection.Up)) as? UserActionResult.ChangeScene) + ?.toScene + ) .isEqualTo(SceneFamilies.Home) assertThat(homeScene).isEqualTo(Scenes.Gone) } @@ -117,7 +124,10 @@ class ShadeSceneActionsViewModelTest : SysuiTestCase() { ) kosmos.keyguardEnabledInteractor.notifyKeyguardEnabled(false) - assertThat(actions?.get(Swipe(SwipeDirection.Up))?.toScene) + assertThat( + (actions?.get(Swipe(SwipeDirection.Up)) as? UserActionResult.ChangeScene) + ?.toScene + ) .isEqualTo(SceneFamilies.Home) assertThat(homeScene).isEqualTo(Scenes.Gone) } @@ -133,7 +143,10 @@ class ShadeSceneActionsViewModelTest : SysuiTestCase() { ) sceneInteractor.changeScene(Scenes.Lockscreen, "reason") - assertThat(actions?.get(Swipe(SwipeDirection.Up))?.toScene) + assertThat( + (actions?.get(Swipe(SwipeDirection.Up)) as? UserActionResult.ChangeScene) + ?.toScene + ) .isEqualTo(SceneFamilies.Home) assertThat(homeScene).isEqualTo(Scenes.Lockscreen) } @@ -150,7 +163,10 @@ class ShadeSceneActionsViewModelTest : SysuiTestCase() { runCurrent() sceneInteractor.changeScene(Scenes.Gone, "reason") - assertThat(actions?.get(Swipe(SwipeDirection.Up))?.toScene) + assertThat( + (actions?.get(Swipe(SwipeDirection.Up)) as? UserActionResult.ChangeScene) + ?.toScene + ) .isEqualTo(SceneFamilies.Home) assertThat(homeScene).isEqualTo(Scenes.Gone) } @@ -182,7 +198,11 @@ class ShadeSceneActionsViewModelTest : SysuiTestCase() { overrideResource(R.bool.config_use_split_notification_shade, true) kosmos.shadeStartable.start() val actions by collectLastValue(underTest.actions) - assertThat(actions?.get(Swipe(SwipeDirection.Down))?.toScene).isNull() + assertThat( + (actions?.get(Swipe(SwipeDirection.Down)) as? UserActionResult.ChangeScene) + ?.toScene + ) + .isNull() } @Test @@ -191,7 +211,10 @@ class ShadeSceneActionsViewModelTest : SysuiTestCase() { overrideResource(R.bool.config_use_split_notification_shade, false) kosmos.shadeStartable.start() val actions by collectLastValue(underTest.actions) - assertThat(actions?.get(Swipe(SwipeDirection.Down))?.toScene) + assertThat( + (actions?.get(Swipe(SwipeDirection.Down)) as? UserActionResult.ChangeScene) + ?.toScene + ) .isEqualTo(Scenes.QuickSettings) } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/domain/interactor/HeadsUpNotificationInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/domain/interactor/HeadsUpNotificationInteractorTest.kt index 14134ccc34d0..cea8857c01bf 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/domain/interactor/HeadsUpNotificationInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/domain/interactor/HeadsUpNotificationInteractorTest.kt @@ -18,11 +18,11 @@ package com.android.systemui.statusbar.notification.domain.interactor -import android.platform.test.annotations.EnableFlags import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.collectLastValue +import com.android.systemui.flags.EnableSceneContainer import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository import com.android.systemui.keyguard.data.repository.fakeDeviceEntryFaceAuthRepository import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository @@ -31,7 +31,6 @@ import com.android.systemui.kosmos.testScope import com.android.systemui.shade.shadeTestUtil import com.android.systemui.statusbar.notification.data.repository.FakeHeadsUpRowRepository import com.android.systemui.statusbar.notification.data.repository.notificationsKeyguardViewStateRepository -import com.android.systemui.statusbar.notification.shared.NotificationsHeadsUpRefactor import com.android.systemui.statusbar.notification.stack.data.repository.headsUpNotificationRepository import com.android.systemui.statusbar.notification.stack.domain.interactor.headsUpNotificationInteractor import com.android.systemui.testKosmos @@ -44,7 +43,7 @@ import org.junit.runner.RunWith @SmallTest @RunWith(AndroidJUnit4::class) -@EnableFlags(NotificationsHeadsUpRefactor.FLAG_NAME) +@EnableSceneContainer class HeadsUpNotificationInteractorTest : SysuiTestCase() { private val kosmos = testKosmos().apply { diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModelTest.kt index f96cf1011fb8..840aa92548c8 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModelTest.kt @@ -26,6 +26,7 @@ import androidx.test.filters.SmallTest import com.android.settingslib.notification.data.repository.updateNotificationPolicy import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.collectLastValue +import com.android.systemui.flags.EnableSceneContainer import com.android.systemui.flags.Flags import com.android.systemui.flags.andSceneContainer import com.android.systemui.flags.fakeFeatureFlagsClassic @@ -41,7 +42,6 @@ import com.android.systemui.statusbar.notification.data.repository.FakeHeadsUpRo import com.android.systemui.statusbar.notification.data.repository.activeNotificationListRepository import com.android.systemui.statusbar.notification.data.repository.setActiveNotifs import com.android.systemui.statusbar.notification.footer.shared.FooterViewRefactor -import com.android.systemui.statusbar.notification.shared.NotificationsHeadsUpRefactor import com.android.systemui.statusbar.notification.stack.data.repository.headsUpNotificationRepository import com.android.systemui.statusbar.policy.data.repository.fakeUserSetupRepository import com.android.systemui.statusbar.policy.data.repository.zenModeRepository @@ -535,7 +535,7 @@ class NotificationListViewModelTest(flags: FlagsParameterization) : SysuiTestCas } @Test - @EnableFlags(NotificationsHeadsUpRefactor.FLAG_NAME) + @EnableSceneContainer fun pinnedHeadsUpRows_filtersForPinnedItems() = testScope.runTest { val pinnedHeadsUpRows by collectLastValue(underTest.pinnedHeadsUpRows) @@ -576,7 +576,7 @@ class NotificationListViewModelTest(flags: FlagsParameterization) : SysuiTestCas } @Test - @EnableFlags(NotificationsHeadsUpRefactor.FLAG_NAME) + @EnableSceneContainer fun hasPinnedHeadsUpRows_true() = testScope.runTest { val hasPinnedHeadsUpRow by collectLastValue(underTest.hasPinnedHeadsUpRow) @@ -591,7 +591,7 @@ class NotificationListViewModelTest(flags: FlagsParameterization) : SysuiTestCas } @Test - @EnableFlags(NotificationsHeadsUpRefactor.FLAG_NAME) + @EnableSceneContainer fun hasPinnedHeadsUpRows_false() = testScope.runTest { val hasPinnedHeadsUpRow by collectLastValue(underTest.hasPinnedHeadsUpRow) @@ -606,7 +606,7 @@ class NotificationListViewModelTest(flags: FlagsParameterization) : SysuiTestCas } @Test - @EnableFlags(NotificationsHeadsUpRefactor.FLAG_NAME) + @EnableSceneContainer fun topHeadsUpRow_emptyList_null() = testScope.runTest { val topHeadsUpRow by collectLastValue(underTest.topHeadsUpRow) @@ -618,7 +618,7 @@ class NotificationListViewModelTest(flags: FlagsParameterization) : SysuiTestCas } @Test - @EnableFlags(NotificationsHeadsUpRefactor.FLAG_NAME) + @EnableSceneContainer fun headsUpAnimationsEnabled_true() = testScope.runTest { val animationsEnabled by collectLastValue(underTest.headsUpAnimationsEnabled) @@ -631,7 +631,7 @@ class NotificationListViewModelTest(flags: FlagsParameterization) : SysuiTestCas } @Test - @EnableFlags(NotificationsHeadsUpRefactor.FLAG_NAME) + @EnableSceneContainer fun headsUpAnimationsEnabled_keyguardShowing_true() = testScope.runTest { val animationsEnabled by collectLastValue(underTest.headsUpAnimationsEnabled) 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 733cac99f4ec..3f97f0b7a67d 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,10 +42,14 @@ import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository 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.ALTERNATE_BOUNCER import com.android.systemui.keyguard.shared.model.KeyguardState.AOD +import com.android.systemui.keyguard.shared.model.KeyguardState.DREAMING +import com.android.systemui.keyguard.shared.model.KeyguardState.GLANCEABLE_HUB 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.KeyguardState.OCCLUDED +import com.android.systemui.keyguard.shared.model.KeyguardState.PRIMARY_BOUNCER import com.android.systemui.keyguard.shared.model.StatusBarState import com.android.systemui.keyguard.shared.model.TransitionState import com.android.systemui.keyguard.shared.model.TransitionStep @@ -56,6 +60,10 @@ import com.android.systemui.keyguard.ui.viewmodel.aodBurnInViewModel import com.android.systemui.keyguard.ui.viewmodel.keyguardRootViewModel import com.android.systemui.kosmos.testScope import com.android.systemui.res.R +import com.android.systemui.scene.data.repository.Idle +import com.android.systemui.scene.data.repository.Transition +import com.android.systemui.scene.data.repository.setTransition +import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.shade.data.repository.fakeShadeRepository import com.android.systemui.shade.mockLargeScreenHeaderHelper import com.android.systemui.shade.shadeTestUtil @@ -66,6 +74,7 @@ import com.google.common.collect.Range 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.TestScope import kotlinx.coroutines.test.advanceTimeBy import kotlinx.coroutines.test.runCurrent @@ -295,34 +304,47 @@ class SharedNotificationContainerViewModelTest(flags: FlagsParameterization) : S // Start transitioning to glanceable hub val progress = 0.6f - keyguardTransitionRepository.sendTransitionStep( - TransitionStep( - transitionState = TransitionState.STARTED, - from = KeyguardState.LOCKSCREEN, - to = KeyguardState.GLANCEABLE_HUB, - value = 0f, - ) + kosmos.setTransition( + sceneTransition = Transition(from = Scenes.Lockscreen, to = Scenes.Communal), + stateTransition = + TransitionStep( + transitionState = TransitionState.STARTED, + from = LOCKSCREEN, + to = GLANCEABLE_HUB, + value = 0f, + ) ) + runCurrent() - keyguardTransitionRepository.sendTransitionStep( - TransitionStep( - transitionState = TransitionState.RUNNING, - from = KeyguardState.LOCKSCREEN, - to = KeyguardState.GLANCEABLE_HUB, - value = progress, - ) + kosmos.setTransition( + sceneTransition = + Transition( + from = Scenes.Lockscreen, + to = Scenes.Communal, + progress = flowOf(progress) + ), + stateTransition = + TransitionStep( + transitionState = TransitionState.RUNNING, + from = LOCKSCREEN, + to = GLANCEABLE_HUB, + value = progress, + ) ) + runCurrent() assertThat(alpha).isIn(Range.closed(0f, 1f)) // Finish transition to glanceable hub - keyguardTransitionRepository.sendTransitionStep( - TransitionStep( - transitionState = TransitionState.FINISHED, - from = KeyguardState.LOCKSCREEN, - to = KeyguardState.GLANCEABLE_HUB, - value = 1f, - ) + kosmos.setTransition( + sceneTransition = Idle(Scenes.Communal), + stateTransition = + TransitionStep( + transitionState = TransitionState.FINISHED, + from = LOCKSCREEN, + to = GLANCEABLE_HUB, + value = 1f, + ) ) assertThat(alpha).isEqualTo(0f) @@ -348,35 +370,46 @@ class SharedNotificationContainerViewModelTest(flags: FlagsParameterization) : S // Start transitioning to glanceable hub val progress = 0.6f - keyguardTransitionRepository.sendTransitionStep( - TransitionStep( - transitionState = TransitionState.STARTED, - from = KeyguardState.DREAMING, - to = KeyguardState.GLANCEABLE_HUB, - value = 0f, - ) + kosmos.setTransition( + sceneTransition = Transition(from = Scenes.Lockscreen, to = Scenes.Communal), + stateTransition = + TransitionStep( + transitionState = TransitionState.STARTED, + from = DREAMING, + to = GLANCEABLE_HUB, + value = 0f, + ) ) runCurrent() - keyguardTransitionRepository.sendTransitionStep( - TransitionStep( - transitionState = TransitionState.RUNNING, - from = KeyguardState.DREAMING, - to = KeyguardState.GLANCEABLE_HUB, - value = progress, - ) + kosmos.setTransition( + sceneTransition = + Transition( + from = Scenes.Lockscreen, + to = Scenes.Communal, + progress = flowOf(progress) + ), + stateTransition = + TransitionStep( + transitionState = TransitionState.RUNNING, + from = DREAMING, + to = GLANCEABLE_HUB, + value = progress, + ) ) runCurrent() // Keep notifications hidden during the transition from dream to hub assertThat(alpha).isEqualTo(0) // Finish transition to glanceable hub - keyguardTransitionRepository.sendTransitionStep( - TransitionStep( - transitionState = TransitionState.FINISHED, - from = KeyguardState.DREAMING, - to = KeyguardState.GLANCEABLE_HUB, - value = 1f, - ) + kosmos.setTransition( + sceneTransition = Idle(Scenes.Communal), + stateTransition = + TransitionStep( + transitionState = TransitionState.FINISHED, + from = DREAMING, + to = GLANCEABLE_HUB, + value = 1f, + ) ) assertThat(alpha).isEqualTo(0f) } @@ -400,35 +433,47 @@ class SharedNotificationContainerViewModelTest(flags: FlagsParameterization) : S testScope.runTest { val isOnLockscreen by collectLastValue(underTest.isOnLockscreen) - keyguardTransitionRepository.sendTransitionSteps( - from = KeyguardState.LOCKSCREEN, - to = KeyguardState.GONE, - testScope, + kosmos.setTransition( + sceneTransition = Idle(Scenes.Gone), + stateTransition = TransitionStep(from = LOCKSCREEN, to = GONE) ) assertThat(isOnLockscreen).isFalse() + kosmos.setTransition( + sceneTransition = Idle(Scenes.Lockscreen), + stateTransition = TransitionStep(from = GONE, to = LOCKSCREEN) + ) + assertThat(isOnLockscreen).isTrue() // While progressing from lockscreen, should still be true - keyguardTransitionRepository.sendTransitionStep( - TransitionStep( - from = KeyguardState.LOCKSCREEN, - to = KeyguardState.GONE, - value = 0.8f, - transitionState = TransitionState.RUNNING - ) + kosmos.setTransition( + sceneTransition = Transition(from = Scenes.Lockscreen, to = Scenes.Gone), + stateTransition = + TransitionStep( + from = LOCKSCREEN, + to = GONE, + value = 0.8f, + transitionState = TransitionState.RUNNING + ) ) assertThat(isOnLockscreen).isTrue() - keyguardTransitionRepository.sendTransitionSteps( - from = KeyguardState.GONE, - to = KeyguardState.LOCKSCREEN, - testScope, + kosmos.setTransition( + sceneTransition = Idle(Scenes.Lockscreen), + stateTransition = + TransitionStep( + from = GONE, + to = LOCKSCREEN, + ) ) assertThat(isOnLockscreen).isTrue() - keyguardTransitionRepository.sendTransitionSteps( - from = KeyguardState.LOCKSCREEN, - to = KeyguardState.PRIMARY_BOUNCER, - testScope, + kosmos.setTransition( + sceneTransition = Idle(Scenes.Bouncer), + stateTransition = + TransitionStep( + from = LOCKSCREEN, + to = PRIMARY_BOUNCER, + ) ) assertThat(isOnLockscreen).isTrue() } @@ -442,8 +487,8 @@ class SharedNotificationContainerViewModelTest(flags: FlagsParameterization) : S shadeTestUtil.setLockscreenShadeExpansion(0f) shadeTestUtil.setQsExpansion(0f) keyguardTransitionRepository.sendTransitionSteps( - from = KeyguardState.LOCKSCREEN, - to = KeyguardState.OCCLUDED, + from = LOCKSCREEN, + to = OCCLUDED, testScope, ) assertThat(isOnLockscreenWithoutShade).isFalse() @@ -480,11 +525,15 @@ class SharedNotificationContainerViewModelTest(flags: FlagsParameterization) : S assertThat(isOnGlanceableHubWithoutShade).isFalse() // Move to glanceable hub - keyguardTransitionRepository.sendTransitionSteps( - from = KeyguardState.LOCKSCREEN, - to = KeyguardState.GLANCEABLE_HUB, - testScope = this + kosmos.setTransition( + sceneTransition = Idle(Scenes.Communal), + stateTransition = + TransitionStep( + from = LOCKSCREEN, + to = GLANCEABLE_HUB, + ) ) + assertThat(isOnGlanceableHubWithoutShade).isTrue() // While state is GLANCEABLE_HUB, validate variations of both shade and qs expansion @@ -502,6 +551,14 @@ class SharedNotificationContainerViewModelTest(flags: FlagsParameterization) : S shadeTestUtil.setQsExpansion(0f) shadeTestUtil.setLockscreenShadeExpansion(0f) + kosmos.setTransition( + sceneTransition = Idle(Scenes.Communal), + stateTransition = + TransitionStep( + from = LOCKSCREEN, + to = GLANCEABLE_HUB, + ) + ) assertThat(isOnGlanceableHubWithoutShade).isTrue() } @@ -808,8 +865,8 @@ class SharedNotificationContainerViewModelTest(flags: FlagsParameterization) : S // GONE transition gets to 90% complete keyguardTransitionRepository.sendTransitionStep( TransitionStep( - from = KeyguardState.LOCKSCREEN, - to = KeyguardState.GONE, + from = LOCKSCREEN, + to = GONE, transitionState = TransitionState.STARTED, value = 0f, ) @@ -817,8 +874,8 @@ class SharedNotificationContainerViewModelTest(flags: FlagsParameterization) : S runCurrent() keyguardTransitionRepository.sendTransitionStep( TransitionStep( - from = KeyguardState.LOCKSCREEN, - to = KeyguardState.GONE, + from = LOCKSCREEN, + to = GONE, transitionState = TransitionState.RUNNING, value = 0.9f, ) @@ -843,8 +900,8 @@ class SharedNotificationContainerViewModelTest(flags: FlagsParameterization) : S // OCCLUDED transition gets to 90% complete keyguardTransitionRepository.sendTransitionStep( TransitionStep( - from = KeyguardState.LOCKSCREEN, - to = KeyguardState.OCCLUDED, + from = LOCKSCREEN, + to = OCCLUDED, transitionState = TransitionState.STARTED, value = 0f, ) @@ -852,8 +909,8 @@ class SharedNotificationContainerViewModelTest(flags: FlagsParameterization) : S runCurrent() keyguardTransitionRepository.sendTransitionStep( TransitionStep( - from = KeyguardState.LOCKSCREEN, - to = KeyguardState.OCCLUDED, + from = LOCKSCREEN, + to = OCCLUDED, transitionState = TransitionState.RUNNING, value = 0.9f, ) @@ -877,8 +934,8 @@ class SharedNotificationContainerViewModelTest(flags: FlagsParameterization) : S showLockscreen() keyguardTransitionRepository.sendTransitionSteps( - from = KeyguardState.LOCKSCREEN, - to = KeyguardState.GONE, + from = LOCKSCREEN, + to = GONE, testScope ) keyguardRepository.setStatusBarState(StatusBarState.SHADE) @@ -922,8 +979,8 @@ class SharedNotificationContainerViewModelTest(flags: FlagsParameterization) : S // ... then user hits power to go to AOD keyguardTransitionRepository.sendTransitionSteps( - from = KeyguardState.LOCKSCREEN, - to = KeyguardState.AOD, + from = LOCKSCREEN, + to = AOD, testScope, ) // ... followed by a shade collapse @@ -945,7 +1002,7 @@ class SharedNotificationContainerViewModelTest(flags: FlagsParameterization) : S // PRIMARY_BOUNCER->GONE transition is started keyguardTransitionRepository.sendTransitionStep( TransitionStep( - from = KeyguardState.PRIMARY_BOUNCER, + from = PRIMARY_BOUNCER, to = GONE, transitionState = TransitionState.STARTED, value = 0f, @@ -956,7 +1013,7 @@ class SharedNotificationContainerViewModelTest(flags: FlagsParameterization) : S // PRIMARY_BOUNCER->GONE transition running keyguardTransitionRepository.sendTransitionStep( TransitionStep( - from = KeyguardState.PRIMARY_BOUNCER, + from = PRIMARY_BOUNCER, to = GONE, transitionState = TransitionState.RUNNING, value = 0.1f, @@ -967,7 +1024,7 @@ class SharedNotificationContainerViewModelTest(flags: FlagsParameterization) : S keyguardTransitionRepository.sendTransitionStep( TransitionStep( - from = KeyguardState.PRIMARY_BOUNCER, + from = PRIMARY_BOUNCER, to = GONE, transitionState = TransitionState.RUNNING, value = 0.9f, @@ -979,7 +1036,7 @@ class SharedNotificationContainerViewModelTest(flags: FlagsParameterization) : S hideCommunalScene() keyguardTransitionRepository.sendTransitionStep( TransitionStep( - from = KeyguardState.PRIMARY_BOUNCER, + from = PRIMARY_BOUNCER, to = GONE, transitionState = TransitionState.FINISHED, value = 1f @@ -1003,7 +1060,7 @@ class SharedNotificationContainerViewModelTest(flags: FlagsParameterization) : S // PRIMARY_BOUNCER->GONE transition is started keyguardTransitionRepository.sendTransitionStep( TransitionStep( - from = KeyguardState.PRIMARY_BOUNCER, + from = PRIMARY_BOUNCER, to = GONE, transitionState = TransitionState.STARTED, ) @@ -1013,7 +1070,7 @@ class SharedNotificationContainerViewModelTest(flags: FlagsParameterization) : S // PRIMARY_BOUNCER->GONE transition running keyguardTransitionRepository.sendTransitionStep( TransitionStep( - from = KeyguardState.PRIMARY_BOUNCER, + from = PRIMARY_BOUNCER, to = GONE, transitionState = TransitionState.RUNNING, value = 0.1f, @@ -1024,7 +1081,7 @@ class SharedNotificationContainerViewModelTest(flags: FlagsParameterization) : S keyguardTransitionRepository.sendTransitionStep( TransitionStep( - from = KeyguardState.PRIMARY_BOUNCER, + from = PRIMARY_BOUNCER, to = GONE, transitionState = TransitionState.RUNNING, value = 0.9f, @@ -1035,7 +1092,7 @@ class SharedNotificationContainerViewModelTest(flags: FlagsParameterization) : S keyguardTransitionRepository.sendTransitionStep( TransitionStep( - from = KeyguardState.PRIMARY_BOUNCER, + from = PRIMARY_BOUNCER, to = GONE, transitionState = TransitionState.FINISHED, value = 1f @@ -1058,7 +1115,7 @@ class SharedNotificationContainerViewModelTest(flags: FlagsParameterization) : S // ALTERNATE_BOUNCER->GONE transition is started keyguardTransitionRepository.sendTransitionStep( TransitionStep( - from = KeyguardState.ALTERNATE_BOUNCER, + from = ALTERNATE_BOUNCER, to = GONE, transitionState = TransitionState.STARTED, value = 0f, @@ -1069,7 +1126,7 @@ class SharedNotificationContainerViewModelTest(flags: FlagsParameterization) : S // ALTERNATE_BOUNCER->GONE transition running keyguardTransitionRepository.sendTransitionStep( TransitionStep( - from = KeyguardState.ALTERNATE_BOUNCER, + from = ALTERNATE_BOUNCER, to = GONE, transitionState = TransitionState.RUNNING, value = 0.1f, @@ -1080,7 +1137,7 @@ class SharedNotificationContainerViewModelTest(flags: FlagsParameterization) : S keyguardTransitionRepository.sendTransitionStep( TransitionStep( - from = KeyguardState.ALTERNATE_BOUNCER, + from = ALTERNATE_BOUNCER, to = GONE, transitionState = TransitionState.RUNNING, value = 0.9f, @@ -1092,7 +1149,7 @@ class SharedNotificationContainerViewModelTest(flags: FlagsParameterization) : S hideCommunalScene() keyguardTransitionRepository.sendTransitionStep( TransitionStep( - from = KeyguardState.ALTERNATE_BOUNCER, + from = ALTERNATE_BOUNCER, to = GONE, transitionState = TransitionState.FINISHED, value = 1f @@ -1116,7 +1173,7 @@ class SharedNotificationContainerViewModelTest(flags: FlagsParameterization) : S // ALTERNATE_BOUNCER->GONE transition is started keyguardTransitionRepository.sendTransitionStep( TransitionStep( - from = KeyguardState.ALTERNATE_BOUNCER, + from = ALTERNATE_BOUNCER, to = GONE, transitionState = TransitionState.STARTED, ) @@ -1126,7 +1183,7 @@ class SharedNotificationContainerViewModelTest(flags: FlagsParameterization) : S // ALTERNATE_BOUNCER->GONE transition running keyguardTransitionRepository.sendTransitionStep( TransitionStep( - from = KeyguardState.ALTERNATE_BOUNCER, + from = ALTERNATE_BOUNCER, to = GONE, transitionState = TransitionState.RUNNING, value = 0.1f, @@ -1137,7 +1194,7 @@ class SharedNotificationContainerViewModelTest(flags: FlagsParameterization) : S keyguardTransitionRepository.sendTransitionStep( TransitionStep( - from = KeyguardState.ALTERNATE_BOUNCER, + from = ALTERNATE_BOUNCER, to = GONE, transitionState = TransitionState.RUNNING, value = 0.9f, @@ -1148,7 +1205,7 @@ class SharedNotificationContainerViewModelTest(flags: FlagsParameterization) : S keyguardTransitionRepository.sendTransitionStep( TransitionStep( - from = KeyguardState.ALTERNATE_BOUNCER, + from = ALTERNATE_BOUNCER, to = GONE, transitionState = TransitionState.FINISHED, value = 1f @@ -1165,8 +1222,8 @@ class SharedNotificationContainerViewModelTest(flags: FlagsParameterization) : S keyguardRepository.setStatusBarState(StatusBarState.KEYGUARD) runCurrent() keyguardTransitionRepository.sendTransitionSteps( - from = KeyguardState.AOD, - to = KeyguardState.LOCKSCREEN, + from = AOD, + to = LOCKSCREEN, testScope, ) } @@ -1178,8 +1235,8 @@ class SharedNotificationContainerViewModelTest(flags: FlagsParameterization) : S keyguardRepository.setDreaming(true) runCurrent() keyguardTransitionRepository.sendTransitionSteps( - from = KeyguardState.LOCKSCREEN, - to = KeyguardState.DREAMING, + from = LOCKSCREEN, + to = DREAMING, testScope, ) } @@ -1191,8 +1248,8 @@ class SharedNotificationContainerViewModelTest(flags: FlagsParameterization) : S keyguardRepository.setStatusBarState(StatusBarState.SHADE_LOCKED) runCurrent() keyguardTransitionRepository.sendTransitionSteps( - from = KeyguardState.AOD, - to = KeyguardState.LOCKSCREEN, + from = AOD, + to = LOCKSCREEN, testScope, ) } @@ -1204,8 +1261,8 @@ class SharedNotificationContainerViewModelTest(flags: FlagsParameterization) : S keyguardRepository.setStatusBarState(StatusBarState.SHADE_LOCKED) runCurrent() keyguardTransitionRepository.sendTransitionSteps( - from = KeyguardState.AOD, - to = KeyguardState.LOCKSCREEN, + from = AOD, + to = LOCKSCREEN, testScope, ) } @@ -1219,8 +1276,8 @@ class SharedNotificationContainerViewModelTest(flags: FlagsParameterization) : S kosmos.keyguardBouncerRepository.setPrimaryShow(true) runCurrent() keyguardTransitionRepository.sendTransitionSteps( - from = KeyguardState.GLANCEABLE_HUB, - to = KeyguardState.PRIMARY_BOUNCER, + from = GLANCEABLE_HUB, + to = PRIMARY_BOUNCER, testScope, ) } @@ -1234,8 +1291,8 @@ class SharedNotificationContainerViewModelTest(flags: FlagsParameterization) : S kosmos.keyguardBouncerRepository.setPrimaryShow(false) runCurrent() keyguardTransitionRepository.sendTransitionSteps( - from = KeyguardState.GLANCEABLE_HUB, - to = KeyguardState.ALTERNATE_BOUNCER, + from = GLANCEABLE_HUB, + to = ALTERNATE_BOUNCER, testScope, ) } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/HeadsUpManagerPhoneTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/HeadsUpManagerPhoneTest.kt index ca106fa01f9a..469a7bc6d9fb 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/HeadsUpManagerPhoneTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/HeadsUpManagerPhoneTest.kt @@ -23,6 +23,7 @@ import android.testing.TestableLooper.RunWithLooper import androidx.test.filters.SmallTest import com.android.internal.logging.UiEventLogger import com.android.systemui.dump.DumpManager +import com.android.systemui.flags.andSceneContainer import com.android.systemui.kosmos.testScope import com.android.systemui.log.logcatLogBuffer import com.android.systemui.plugins.statusbar.StatusBarStateController @@ -36,7 +37,6 @@ import com.android.systemui.statusbar.notification.collection.provider.VisualSta import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow import com.android.systemui.statusbar.notification.shared.NotificationThrottleHun -import com.android.systemui.statusbar.notification.shared.NotificationsHeadsUpRefactor import com.android.systemui.statusbar.phone.ConfigurationControllerImpl import com.android.systemui.statusbar.phone.HeadsUpManagerPhone import com.android.systemui.statusbar.phone.KeyguardBypassController @@ -469,9 +469,9 @@ class HeadsUpManagerPhoneTest(flags: FlagsParameterization) : BaseHeadsUpManager @get:Parameters(name = "{0}") val flags: List<FlagsParameterization> get() = buildList { - addAll(FlagsParameterization.allCombinationsOf(NotificationThrottleHun.FLAG_NAME)) addAll( - FlagsParameterization.allCombinationsOf(NotificationsHeadsUpRefactor.FLAG_NAME) + FlagsParameterization.allCombinationsOf(NotificationThrottleHun.FLAG_NAME) + .andSceneContainer() ) } } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/domain/interactor/ZenModeInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/domain/interactor/ZenModeInteractorTest.kt index 20d3a7b6b7b5..639d34d5e74d 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/domain/interactor/ZenModeInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/domain/interactor/ZenModeInteractorTest.kt @@ -22,8 +22,10 @@ import android.provider.Settings import android.provider.Settings.Secure.ZEN_DURATION import android.provider.Settings.Secure.ZEN_DURATION_FOREVER import android.provider.Settings.Secure.ZEN_DURATION_PROMPT +import android.service.notification.SystemZenRules import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest +import com.android.internal.R import com.android.settingslib.notification.data.repository.updateNotificationPolicy import com.android.settingslib.notification.modes.TestModeBuilder import com.android.systemui.SysuiTestCase @@ -220,30 +222,86 @@ class ZenModeInteractorTest : SysuiTestCase() { } @Test - fun mainActiveMode_returnsMainActiveMode() = + fun activeModes_computesMainActiveMode() = testScope.runTest { - val mainActiveMode by collectLastValue(underTest.mainActiveMode) + val activeModes by collectLastValue(underTest.activeModes) zenModeRepository.addMode(id = "Bedtime", type = AutomaticZenRule.TYPE_BEDTIME) zenModeRepository.addMode(id = "Other", type = AutomaticZenRule.TYPE_OTHER) runCurrent() + assertThat(activeModes?.modeNames).hasSize(0) + assertThat(activeModes?.mainMode).isNull() + + zenModeRepository.activateMode("Other") + runCurrent() + assertThat(activeModes?.modeNames).containsExactly("Mode Other") + assertThat(activeModes?.mainMode?.name).isEqualTo("Mode Other") + + zenModeRepository.activateMode("Bedtime") + runCurrent() + assertThat(activeModes?.modeNames) + .containsExactly("Mode Bedtime", "Mode Other") + .inOrder() + assertThat(activeModes?.mainMode?.name).isEqualTo("Mode Bedtime") + + zenModeRepository.deactivateMode("Other") + runCurrent() + assertThat(activeModes?.modeNames).containsExactly("Mode Bedtime") + assertThat(activeModes?.mainMode?.name).isEqualTo("Mode Bedtime") + + zenModeRepository.deactivateMode("Bedtime") + runCurrent() + assertThat(activeModes?.modeNames).hasSize(0) + assertThat(activeModes?.mainMode).isNull() + } + + @Test + fun mainActiveMode_flows() = + testScope.runTest { + val mainActiveMode by collectLastValue(underTest.mainActiveMode) + + zenModeRepository.addModes( + listOf( + TestModeBuilder() + .setId("Bedtime") + .setName("Mode Bedtime") + .setType(AutomaticZenRule.TYPE_BEDTIME) + .setActive(false) + .setPackage(mContext.packageName) + .setIconResId(R.drawable.ic_zen_mode_type_bedtime) + .build(), + TestModeBuilder() + .setId("Other") + .setName("Mode Other") + .setType(AutomaticZenRule.TYPE_OTHER) + .setActive(false) + .setPackage(SystemZenRules.PACKAGE_ANDROID) + .setIconResId(R.drawable.ic_zen_mode_type_other) + .build(), + ) + ) + + runCurrent() assertThat(mainActiveMode).isNull() zenModeRepository.activateMode("Other") runCurrent() - assertThat(mainActiveMode).isNotNull() - assertThat(mainActiveMode!!.id).isEqualTo("Other") + assertThat(mainActiveMode?.name).isEqualTo("Mode Other") + assertThat(mainActiveMode?.icon?.key?.resId) + .isEqualTo(R.drawable.ic_zen_mode_type_other) zenModeRepository.activateMode("Bedtime") runCurrent() - assertThat(mainActiveMode).isNotNull() - assertThat(mainActiveMode!!.id).isEqualTo("Bedtime") + assertThat(mainActiveMode?.name).isEqualTo("Mode Bedtime") + assertThat(mainActiveMode?.icon?.key?.resId) + .isEqualTo(R.drawable.ic_zen_mode_type_bedtime) zenModeRepository.deactivateMode("Other") runCurrent() - assertThat(mainActiveMode).isNotNull() - assertThat(mainActiveMode!!.id).isEqualTo("Bedtime") + assertThat(mainActiveMode?.name).isEqualTo("Mode Bedtime") + assertThat(mainActiveMode?.icon?.key?.resId) + .isEqualTo(R.drawable.ic_zen_mode_type_bedtime) zenModeRepository.deactivateMode("Bedtime") runCurrent() diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/ui/dialog/viewmodel/ModesDialogViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/ui/dialog/viewmodel/ModesDialogViewModelTest.kt index 33f379d020b7..d2bc54e09944 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/ui/dialog/viewmodel/ModesDialogViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/ui/dialog/viewmodel/ModesDialogViewModelTest.kt @@ -23,7 +23,6 @@ import android.provider.Settings import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.settingslib.notification.modes.TestModeBuilder -import com.android.settingslib.notification.modes.ZenIconLoader import com.android.settingslib.notification.modes.ZenMode import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.collectLastValue @@ -35,12 +34,10 @@ import com.android.systemui.statusbar.policy.ui.dialog.mockModesDialogDelegate import com.android.systemui.statusbar.policy.ui.dialog.mockModesDialogEventLogger import com.android.systemui.testKosmos import com.google.common.truth.Truth.assertThat -import com.google.common.util.concurrent.MoreExecutors import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.Job import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest -import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mockito.clearInvocations @@ -67,11 +64,6 @@ class ModesDialogViewModelTest : SysuiTestCase() { mockDialogEventLogger ) - @Before - fun setUp() { - ZenIconLoader.setInstance(ZenIconLoader(MoreExecutors.newDirectExecutorService())) - } - @Test fun tiles_filtersOutUserDisabledModes() = testScope.runTest { diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/ui/viewmodel/VolumePanelViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/ui/viewmodel/VolumePanelViewModelTest.kt index 51a70bda6034..fe6c7417032f 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/ui/viewmodel/VolumePanelViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/ui/viewmodel/VolumePanelViewModelTest.kt @@ -28,6 +28,7 @@ import com.android.systemui.dump.DumpManager import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.applicationCoroutineScope import com.android.systemui.kosmos.testScope +import com.android.systemui.res.R import com.android.systemui.statusbar.policy.configurationController import com.android.systemui.statusbar.policy.fakeConfigurationController import com.android.systemui.testKosmos @@ -157,6 +158,10 @@ class VolumePanelViewModelTest : SysuiTestCase() { @Test fun testDumpingState() = test({ + testableResources.addOverride(R.bool.volume_panel_is_large_screen, false) + testableResources.overrideConfiguration( + Configuration().apply { orientation = Configuration.ORIENTATION_PORTRAIT } + ) componentByKey = mapOf( COMPONENT_1 to mockVolumePanelUiComponentProvider, diff --git a/packages/SystemUI/res/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml index dfdb15d07d4f..7251f03024e3 100644 --- a/packages/SystemUI/res/values-af/strings.xml +++ b/packages/SystemUI/res/values-af/strings.xml @@ -308,8 +308,7 @@ <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="7545274861795853838">"Deel oudio"</string> <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Deel tans oudio"</string> - <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_accessibility (7604615019302091708) --> - <skip /> + <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"voer instellings vir oudiodeling in"</string> <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> @@ -436,6 +435,7 @@ <string name="zen_modes_dialog_done" msgid="6654130880256438950">"Klaar"</string> <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Instellings"</string> <string name="zen_mode_on" msgid="9085304934016242591">"Aan"</string> + <string name="zen_mode_on_with_details" msgid="7416143430557895497">"Op • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string> <string name="zen_mode_off" msgid="1736604456618147306">"Af"</string> <string name="zen_mode_set_up" msgid="7457957033034460064">"Stel op"</string> <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"Bestuur in instellings"</string> @@ -718,6 +718,8 @@ <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Satelliet, goeie toestand"</string> <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Satelliet, verbinding is beskikbaar"</string> <string name="satellite_connected_carrier_text" msgid="118524195198532589">"Satelliet-SOS"</string> + <!-- no translation found for satellite_emergency_only_carrier_text (828510231597991206) --> + <skip /> <string name="accessibility_managed_profile" msgid="4703836746209377356">"Werkprofiel"</string> <string name="tuner_warning_title" msgid="7721976098452135267">"Pret vir party mense, maar nie vir almal nie"</string> <string name="tuner_warning" msgid="1861736288458481650">"Stelsel-UI-ontvanger gee jou ekstra maniere om die Android-gebruikerkoppelvlak in te stel en te pasmaak. Hierdie eksperimentele kenmerke kan in toekomstige uitreikings verander, breek of verdwyn. Gaan versigtig voort."</string> diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml index 9babced1c4e0..54fb216d99ae 100644 --- a/packages/SystemUI/res/values-am/strings.xml +++ b/packages/SystemUI/res/values-am/strings.xml @@ -308,8 +308,7 @@ <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"ብሉቱዝ ነገ ጠዋት ይበራል"</string> <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"ኦዲዮ አጋራ"</string> <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"ኦዲዮ በማጋራት ላይ"</string> - <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_accessibility (7604615019302091708) --> - <skip /> + <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"የድምፅ ማጋሪያ ቅንብሮች አስገባ"</string> <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> @@ -436,6 +435,7 @@ <string name="zen_modes_dialog_done" msgid="6654130880256438950">"ተከናውኗል"</string> <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"ቅንብሮች"</string> <string name="zen_mode_on" msgid="9085304934016242591">"በርቷል"</string> + <string name="zen_mode_on_with_details" msgid="7416143430557895497">"በርቷል • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string> <string name="zen_mode_off" msgid="1736604456618147306">"ጠፍቷል"</string> <string name="zen_mode_set_up" msgid="7457957033034460064">"አዋቅር"</string> <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"በቅንብሮች ውስጥ አስተዳድር"</string> @@ -718,6 +718,8 @@ <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"ሳተላይት፣ ጥሩ ግንኙነት"</string> <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"ሳተላይት፣ ግንኙነት አለ"</string> <string name="satellite_connected_carrier_text" msgid="118524195198532589">"ሳተላይት ኤስኦኤስ"</string> + <!-- no translation found for satellite_emergency_only_carrier_text (828510231597991206) --> + <skip /> <string name="accessibility_managed_profile" msgid="4703836746209377356">"የስራ መገለጫ"</string> <string name="tuner_warning_title" msgid="7721976098452135267">"ለአንዳንዶች አስደሳች ቢሆንም ለሁሉም አይደለም"</string> <string name="tuner_warning" msgid="1861736288458481650">"የስርዓት በይነገጽ መቃኛ የAndroid ተጠቃሚ በይነገጹን የሚነካኩበት እና የሚያበጁበት ተጨማሪ መንገዶች ይሰጠዎታል። እነዚህ የሙከራ ባህሪዎች ወደፊት በሚኖሩ ልቀቶች ላይ ሊለወጡ፣ ሊሰበሩ ወይም ሊጠፉ ይችላሉ። ከጥንቃቄ ጋር ወደፊት ይቀጥሉ።"</string> diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml index 3b867be0e3dd..6866ed717d2b 100644 --- a/packages/SystemUI/res/values-ar/strings.xml +++ b/packages/SystemUI/res/values-ar/strings.xml @@ -308,8 +308,7 @@ <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"سيتم تفعيل البلوتوث صباح الغد"</string> <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"مشاركة الصوت"</string> <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"جارٍ مشاركة الصوت"</string> - <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_accessibility (7604615019302091708) --> - <skip /> + <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"أدخِل إعدادات ميزة \"مشاركة الصوت\""</string> <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> @@ -436,6 +435,7 @@ <string name="zen_modes_dialog_done" msgid="6654130880256438950">"تم"</string> <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"الإعدادات"</string> <string name="zen_mode_on" msgid="9085304934016242591">"مفعَّل"</string> + <string name="zen_mode_on_with_details" msgid="7416143430557895497">"مفعّل • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string> <string name="zen_mode_off" msgid="1736604456618147306">"غير مفعَّل"</string> <string name="zen_mode_set_up" msgid="7457957033034460064">"إعداد"</string> <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"الإدارة في الإعدادات"</string> @@ -718,6 +718,8 @@ <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"قمر صناعي، الاتصال جيد"</string> <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"قمر صناعي، الاتصال متوفّر"</string> <string name="satellite_connected_carrier_text" msgid="118524195198532589">"اتصالات الطوارئ بالقمر الصناعي"</string> + <!-- no translation found for satellite_emergency_only_carrier_text (828510231597991206) --> + <skip /> <string name="accessibility_managed_profile" msgid="4703836746209377356">"ملف العمل"</string> <string name="tuner_warning_title" msgid="7721976098452135267">"متعة للبعض وليس للجميع"</string> <string name="tuner_warning" msgid="1861736288458481650">"توفر لك أداة ضبط واجهة مستخدم النظام طرقًا إضافية لتعديل واجهة مستخدم Android وتخصيصها. ويمكن أن تطرأ تغييرات على هذه الميزات التجريبية أو يمكن أن تتعطل هذه الميزات أو تختفي في الإصدارات المستقبلية. عليك متابعة الاستخدام مع توخي الحذر."</string> @@ -845,7 +847,7 @@ <string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"الإدخال"</string> <string name="input_switch_input_language_next" msgid="3782155659868227855">"التبديل إلى اللغة التالية"</string> <string name="input_switch_input_language_previous" msgid="6043341362202336623">"التبديل إلى اللغة السابقة"</string> - <string name="input_access_emoji" msgid="8105642858900406351">"الوصول إلى الرموز التعبيرية"</string> + <string name="input_access_emoji" msgid="8105642858900406351">"الوصول إلى رموز الإيموجي"</string> <string name="input_access_voice_typing" msgid="7291201476395326141">"الوصول إلى ميزة \"الكتابة بالصوت\""</string> <string name="keyboard_shortcut_group_applications" msgid="7386239431100651266">"التطبيقات"</string> <string name="keyboard_shortcut_group_applications_assist" msgid="6772492350416591448">"مساعد Google"</string> diff --git a/packages/SystemUI/res/values-as/strings.xml b/packages/SystemUI/res/values-as/strings.xml index 6dbfe673aabd..0c8ef431fff9 100644 --- a/packages/SystemUI/res/values-as/strings.xml +++ b/packages/SystemUI/res/values-as/strings.xml @@ -308,8 +308,7 @@ <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"কাইলৈ পুৱা ব্লুটুথ অন হ’ব"</string> <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"অডিঅ’ শ্বেয়াৰ কৰক"</string> <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"অডিঅ’ শ্বেয়াৰ কৰি থকা হৈছে"</string> - <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_accessibility (7604615019302091708) --> - <skip /> + <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"অডিঅ’ শ্বেয়াৰ কৰাৰ ছেটিঙলৈ যাওক"</string> <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> @@ -436,6 +435,7 @@ <string name="zen_modes_dialog_done" msgid="6654130880256438950">"কৰা হ’ল"</string> <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"ছেটিং"</string> <string name="zen_mode_on" msgid="9085304934016242591">"অন আছে"</string> + <string name="zen_mode_on_with_details" msgid="7416143430557895497">"অন আছে • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string> <string name="zen_mode_off" msgid="1736604456618147306">"অফ আছে"</string> <string name="zen_mode_set_up" msgid="7457957033034460064">"ছেট আপ কৰক"</string> <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"ছেটিঙত পৰিচালনা কৰক"</string> @@ -718,6 +718,8 @@ <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"উপগ্ৰহ, ভাল সংযোগ"</string> <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"উপগ্ৰহ, সংযোগ উপলব্ধ"</string> <string name="satellite_connected_carrier_text" msgid="118524195198532589">"উপগ্ৰহ SOS"</string> + <!-- no translation found for satellite_emergency_only_carrier_text (828510231597991206) --> + <skip /> <string name="accessibility_managed_profile" msgid="4703836746209377356">"কৰ্মস্থানৰ প্ৰ\'ফাইল"</string> <string name="tuner_warning_title" msgid="7721976098452135267">"কিছুমানৰ বাবে আমোদজনক হয় কিন্তু সকলোৰে বাবে নহয়"</string> <string name="tuner_warning" msgid="1861736288458481650">"System UI Tunerএ আপোনাক Android ব্যৱহাৰকাৰী ইণ্টাৰফেইচ সলনি কৰিবলৈ আৰু নিজৰ উপযোগিতা অনুসৰি ব্যৱহাৰ কৰিবলৈ অতিৰিক্ত সুবিধা প্ৰদান কৰে। এই পৰীক্ষামূলক সুবিধাসমূহ সলনি হ\'ব পাৰে, সেইবোৰে কাম নকৰিব পাৰে বা আগন্তুক সংস্কৰণসমূহত সেইবোৰ অন্তৰ্ভুক্ত কৰা নহ’ব পাৰে। সাৱধানেৰে আগবাঢ়ক।"</string> diff --git a/packages/SystemUI/res/values-az/strings.xml b/packages/SystemUI/res/values-az/strings.xml index ee7872e65002..df3ecf7d8b3c 100644 --- a/packages/SystemUI/res/values-az/strings.xml +++ b/packages/SystemUI/res/values-az/strings.xml @@ -308,8 +308,7 @@ <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="7545274861795853838">"Audio paylaşın"</string> <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Audio paylaşılır"</string> - <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_accessibility (7604615019302091708) --> - <skip /> + <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"audio paylaşma ayarlarına daxil olun"</string> <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> @@ -436,6 +435,7 @@ <string name="zen_modes_dialog_done" msgid="6654130880256438950">"Hazırdır"</string> <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Ayarlar"</string> <string name="zen_mode_on" msgid="9085304934016242591">"Aktiv"</string> + <string name="zen_mode_on_with_details" msgid="7416143430557895497">"Aktiv • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string> <string name="zen_mode_off" msgid="1736604456618147306">"Deaktiv"</string> <string name="zen_mode_set_up" msgid="7457957033034460064">"Ayarlayın"</string> <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"Ayarlarda idarə edin"</string> @@ -718,6 +718,8 @@ <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Peyk, bağlantı yaxşıdır"</string> <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Peyk, bağlantı var"</string> <string name="satellite_connected_carrier_text" msgid="118524195198532589">"Təcili peyk bağlantısı"</string> + <!-- no translation found for satellite_emergency_only_carrier_text (828510231597991206) --> + <skip /> <string name="accessibility_managed_profile" msgid="4703836746209377356">"İş profili"</string> <string name="tuner_warning_title" msgid="7721976098452135267">"Hamı üçün deyil, bəziləri üçün əyləncəli"</string> <string name="tuner_warning" msgid="1861736288458481650">"System UI Tuner Android istifadəçi interfeysini dəyişdirmək və fərdiləşdirmək üçün Sizə ekstra yollar təklif edir."</string> diff --git a/packages/SystemUI/res/values-b+sr+Latn/strings.xml b/packages/SystemUI/res/values-b+sr+Latn/strings.xml index 0d6504f86b4e..9198710eed09 100644 --- a/packages/SystemUI/res/values-b+sr+Latn/strings.xml +++ b/packages/SystemUI/res/values-b+sr+Latn/strings.xml @@ -308,8 +308,7 @@ <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="7545274861795853838">"Deli zvuk"</string> <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Deli se zvuk"</string> - <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_accessibility (7604615019302091708) --> - <skip /> + <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"uđite u podešavanja deljenja zvuka"</string> <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> @@ -436,6 +435,7 @@ <string name="zen_modes_dialog_done" msgid="6654130880256438950">"Gotovo"</string> <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Podešavanja"</string> <string name="zen_mode_on" msgid="9085304934016242591">"Uključeno"</string> + <string name="zen_mode_on_with_details" msgid="7416143430557895497">"Uklj. • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string> <string name="zen_mode_off" msgid="1736604456618147306">"Isključeno"</string> <string name="zen_mode_set_up" msgid="7457957033034460064">"Podesi"</string> <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"Upravljajte u podešavanjima"</string> @@ -718,6 +718,8 @@ <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Satelit, veza je dobra"</string> <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Satelit, veza je dostupna"</string> <string name="satellite_connected_carrier_text" msgid="118524195198532589">"Hitna pomoć preko satelita"</string> + <!-- no translation found for satellite_emergency_only_carrier_text (828510231597991206) --> + <skip /> <string name="accessibility_managed_profile" msgid="4703836746209377356">"Poslovni profil"</string> <string name="tuner_warning_title" msgid="7721976098452135267">"Zabava za neke, ali ne za sve"</string> <string name="tuner_warning" msgid="1861736288458481650">"Tjuner za korisnički interfejs sistema vam pruža dodatne načine za podešavanje i prilagođavanje Android korisničkog interfejsa. Ove eksperimentalne funkcije mogu da se promene, otkažu ili nestanu u budućim izdanjima. Budite oprezni."</string> diff --git a/packages/SystemUI/res/values-be/strings.xml b/packages/SystemUI/res/values-be/strings.xml index 4fae4f5b84f4..c5c2912f8b3e 100644 --- a/packages/SystemUI/res/values-be/strings.xml +++ b/packages/SystemUI/res/values-be/strings.xml @@ -308,8 +308,7 @@ <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth уключыцца заўтра раніцай"</string> <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Абагуліць аўдыя"</string> <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Ідзе абагульванне аўдыя"</string> - <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_accessibility (7604615019302091708) --> - <skip /> + <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"адкрыць налады абагульвання аўдыя"</string> <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> @@ -436,6 +435,7 @@ <string name="zen_modes_dialog_done" msgid="6654130880256438950">"Гатова"</string> <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Налады"</string> <string name="zen_mode_on" msgid="9085304934016242591">"Уключана"</string> + <string name="zen_mode_on_with_details" msgid="7416143430557895497">"Уключана • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string> <string name="zen_mode_off" msgid="1736604456618147306">"Выключана"</string> <string name="zen_mode_set_up" msgid="7457957033034460064">"Наладзіць"</string> <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"Адкрыць налады"</string> @@ -718,6 +718,8 @@ <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Спадарожнікавая сувязь, добрае падключэнне"</string> <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Спадарожнікавая сувязь, падключэнне даступнае"</string> <string name="satellite_connected_carrier_text" msgid="118524195198532589">"Экстраннае спадарожнікавае падключэнне"</string> + <!-- no translation found for satellite_emergency_only_carrier_text (828510231597991206) --> + <skip /> <string name="accessibility_managed_profile" msgid="4703836746209377356">"Працоўны профіль"</string> <string name="tuner_warning_title" msgid="7721976098452135267">"Цікава для некаторых, але не для ўсіх"</string> <string name="tuner_warning" msgid="1861736288458481650">"Наладка сістэмнага інтэрфейсу карыстальніка дае вам дадатковыя спосабы наладжвання і дапасоўвання карыстальніцкага інтэрфейсу Android. Гэтыя эксперыментальныя функцыі могуць змяніцца, перастаць працаваць або знікнуць у будучых версіях. Карыстайцеся з асцярожнасцю."</string> diff --git a/packages/SystemUI/res/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml index dee6682ec555..58f492e55b9d 100644 --- a/packages/SystemUI/res/values-bg/strings.xml +++ b/packages/SystemUI/res/values-bg/strings.xml @@ -308,8 +308,7 @@ <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth ще се включи утре сутрин"</string> <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Споделяне на звука"</string> <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Звукът се споделя"</string> - <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_accessibility (7604615019302091708) --> - <skip /> + <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"отваряне на настройките за споделяне на звука"</string> <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> @@ -390,7 +389,7 @@ <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Запис на екрана"</string> <string name="performance" msgid="6552785217174378320">"Ефективност"</string> <string name="user_interface" msgid="3712869377953950887">"Потребителски интерфейс"</string> - <string name="thermal" msgid="6758074791325414831">"Термално"</string> + <string name="thermal" msgid="6758074791325414831">"Температура"</string> <string name="custom" msgid="3337456985275158299">"Персонализирано"</string> <string name="custom_trace_settings_dialog_title" msgid="2608570500144830554">"Настройки за персонализираната следа"</string> <string name="restore_default" msgid="5259420807486239755">"Възстановяване на стандартната настройка"</string> @@ -436,6 +435,7 @@ <string name="zen_modes_dialog_done" msgid="6654130880256438950">"Готово"</string> <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Настройки"</string> <string name="zen_mode_on" msgid="9085304934016242591">"Вкл."</string> + <string name="zen_mode_on_with_details" msgid="7416143430557895497">"Вкл. • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string> <string name="zen_mode_off" msgid="1736604456618147306">"Изкл."</string> <string name="zen_mode_set_up" msgid="7457957033034460064">"Настройване"</string> <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"Управление от настройките"</string> @@ -718,6 +718,8 @@ <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Сателит, добра връзка"</string> <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Сателит, налице е връзка"</string> <string name="satellite_connected_carrier_text" msgid="118524195198532589">"SOS чрез сателит"</string> + <!-- no translation found for satellite_emergency_only_carrier_text (828510231597991206) --> + <skip /> <string name="accessibility_managed_profile" msgid="4703836746209377356">"Потребителски профил в Work"</string> <string name="tuner_warning_title" msgid="7721976098452135267">"Забавно – но не за всички"</string> <string name="tuner_warning" msgid="1861736288458481650">"Тунерът на системния потребителски интерфейс ви предоставя допълнителни възможности за прецизиране и персонализиране на практическата работа с Android. Тези експериментални функции може да се променят, повредят или да изчезнат в бъдещите версии. Действайте внимателно."</string> diff --git a/packages/SystemUI/res/values-bn/strings.xml b/packages/SystemUI/res/values-bn/strings.xml index ebef35a5bc84..c9e24f0bb126 100644 --- a/packages/SystemUI/res/values-bn/strings.xml +++ b/packages/SystemUI/res/values-bn/strings.xml @@ -308,8 +308,7 @@ <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"ব্লুটুথ আগামীকাল সকালে চালু হয়ে যাবে"</string> <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"অডিও শেয়ার করুন"</string> <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"অডিও শেয়ার করা হচ্ছে"</string> - <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_accessibility (7604615019302091708) --> - <skip /> + <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"অডিও শেয়ার করার সেটিংসে যান"</string> <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> @@ -436,6 +435,7 @@ <string name="zen_modes_dialog_done" msgid="6654130880256438950">"হয়ে গেছে"</string> <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"সেটিংস"</string> <string name="zen_mode_on" msgid="9085304934016242591">"চালু আছে"</string> + <string name="zen_mode_on_with_details" msgid="7416143430557895497">"চালু আছে • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string> <string name="zen_mode_off" msgid="1736604456618147306">"বন্ধ আছে"</string> <string name="zen_mode_set_up" msgid="7457957033034460064">"সেট-আপ করুন"</string> <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"সেটিংসে গিয়ে ম্যানেজ করুন"</string> @@ -718,6 +718,8 @@ <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"স্যাটেলাইট, ভালো কানেকশন"</string> <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"স্যাটেলাইট, কানেকশন উপলভ্য আছে"</string> <string name="satellite_connected_carrier_text" msgid="118524195198532589">"স্যাটেলাইট SOS"</string> + <!-- no translation found for satellite_emergency_only_carrier_text (828510231597991206) --> + <skip /> <string name="accessibility_managed_profile" msgid="4703836746209377356">"কাজের প্রোফাইল"</string> <string name="tuner_warning_title" msgid="7721976098452135267">"কিছু ব্যক্তির জন্য মজাদার কিন্তু সকলের জন্য নয়"</string> <string name="tuner_warning" msgid="1861736288458481650">"এই পরীক্ষামূলক বৈশিষ্ট্যগুলি ভবিষ্যতের সংস্করণগুলির মধ্যে পরিবর্তিত, বিভাজিত এবং অদৃশ্য হয়ে যেতে পারে৷ সাবধানতার সাথে এগিয়ে যান৷ সিস্টেম UI টিউনার আপনাকে Android ব্যবহারকারী ইন্টারফেসের সূক্ষ্ম সমন্বয় এবং কাস্টমাইজ করার অতিরিক্ত উপায়গুলি প্রদান করে৷"</string> diff --git a/packages/SystemUI/res/values-bs/strings.xml b/packages/SystemUI/res/values-bs/strings.xml index e1a632afaab4..894956703753 100644 --- a/packages/SystemUI/res/values-bs/strings.xml +++ b/packages/SystemUI/res/values-bs/strings.xml @@ -308,8 +308,7 @@ <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="7545274861795853838">"Dijeli zvuk"</string> <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Dijeljenje zvuka"</string> - <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_accessibility (7604615019302091708) --> - <skip /> + <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"ulazak u postavke dijeljenja zvuka"</string> <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> @@ -381,8 +380,8 @@ <string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Snimanje ekrana"</string> <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Započnite"</string> <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Zaustavite"</string> - <string name="qs_record_issue_label" msgid="8166290137285529059">"Zabilježite problem"</string> - <string name="qs_record_issue_start" msgid="2979831312582567056">"Pokrenite"</string> + <string name="qs_record_issue_label" msgid="8166290137285529059">"Snimite problem"</string> + <string name="qs_record_issue_start" msgid="2979831312582567056">"Pokreni"</string> <string name="qs_record_issue_stop" msgid="3531747965741982657">"Zaustavite"</string> <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Izvještaj o grešci"</string> <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Koji dio uređaja je imao problem?"</string> @@ -436,6 +435,7 @@ <string name="zen_modes_dialog_done" msgid="6654130880256438950">"Gotovo"</string> <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Postavke"</string> <string name="zen_mode_on" msgid="9085304934016242591">"Uključeno"</string> + <string name="zen_mode_on_with_details" msgid="7416143430557895497">"Uključeno • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string> <string name="zen_mode_off" msgid="1736604456618147306">"Isključeno"</string> <string name="zen_mode_set_up" msgid="7457957033034460064">"Postavite"</string> <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"Upravljajte opcijom u postavkama"</string> @@ -673,7 +673,7 @@ <string name="volume_panel_spatial_audio_title" msgid="3367048857932040660">"Prostorni zvuk"</string> <string name="volume_panel_spatial_audio_off" msgid="4177490084606772989">"Isključi"</string> <string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"Fiksno"</string> - <string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Praćenje glave"</string> + <string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Praćenje položaja glave"</string> <string name="volume_ringer_change" msgid="3574969197796055532">"Dodirnite da promijenite način rada zvuka zvona"</string> <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"isključite zvuk"</string> <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"uključite zvuk"</string> @@ -718,6 +718,8 @@ <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Satelit, dobra veza"</string> <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Satelit, veza je dostupna"</string> <string name="satellite_connected_carrier_text" msgid="118524195198532589">"Hitna pomoć putem satelita"</string> + <!-- no translation found for satellite_emergency_only_carrier_text (828510231597991206) --> + <skip /> <string name="accessibility_managed_profile" msgid="4703836746209377356">"Radni profil"</string> <string name="tuner_warning_title" msgid="7721976098452135267">"Zabava za neke, ali ne za sve"</string> <string name="tuner_warning" msgid="1861736288458481650">"Podešavač za korisnički interfejs sistema vam omogućava dodatne načine da podesite i prilagodite Androidov interfejs. Ove eksperimentalne funkcije se u budućim verzijama mogu mijenjati, kvariti ili nestati. Budite oprezni."</string> diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml index e985e655d8ed..74cce98dde5b 100644 --- a/packages/SystemUI/res/values-ca/strings.xml +++ b/packages/SystemUI/res/values-ca/strings.xml @@ -308,8 +308,7 @@ <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="7545274861795853838">"Comparteix l\'àudio"</string> <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"S\'està compartint l\'àudio"</string> - <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_accessibility (7604615019302091708) --> - <skip /> + <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"introduir la configuració de compartició d\'àudio"</string> <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> @@ -436,6 +435,7 @@ <string name="zen_modes_dialog_done" msgid="6654130880256438950">"Fet"</string> <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Configuració"</string> <string name="zen_mode_on" msgid="9085304934016242591">"Activat"</string> + <string name="zen_mode_on_with_details" msgid="7416143430557895497">"Activat • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string> <string name="zen_mode_off" msgid="1736604456618147306">"Desactivat"</string> <string name="zen_mode_set_up" msgid="7457957033034460064">"Configura"</string> <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"Gestiona a la configuració"</string> @@ -506,7 +506,7 @@ <string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"suprimeix el widget"</string> <string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"col·loca el widget seleccionat"</string> <string name="communal_widget_picker_title" msgid="1953369090475731663">"Widgets de la pantalla de bloqueig"</string> - <string name="communal_widget_picker_description" msgid="490515450110487871">"Tothom pot veure widgets a la pantalla de bloqueig, fins i tot amb la tauleta bloquejada."</string> + <string name="communal_widget_picker_description" msgid="490515450110487871">"Tothom pot veure els widgets de la teva pantalla de bloqueig, fins i tot quan la tauleta està bloquejada."</string> <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"desselecciona el widget"</string> <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Widgets de la pantalla de bloqueig"</string> <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Per obrir una aplicació utilitzant un widget, necessitaràs verificar la teva identitat. També has de tenir en compte que qualsevol persona pot veure els widgets, fins i tot quan la tauleta està bloquejada. És possible que alguns widgets no estiguin pensats per a la pantalla de bloqueig i que no sigui segur afegir-los-hi."</string> @@ -718,6 +718,8 @@ <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Satèl·lit, bona connexió"</string> <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Satèl·lit, connexió disponible"</string> <string name="satellite_connected_carrier_text" msgid="118524195198532589">"SOS per satèl·lit"</string> + <!-- no translation found for satellite_emergency_only_carrier_text (828510231597991206) --> + <skip /> <string name="accessibility_managed_profile" msgid="4703836746209377356">"Perfil de treball"</string> <string name="tuner_warning_title" msgid="7721976098452135267">"Diversió per a uns quants, però no per a tothom"</string> <string name="tuner_warning" msgid="1861736288458481650">"El Personalitzador d\'interfície d\'usuari presenta opcions addicionals per canviar i personalitzar la interfície d\'usuari d\'Android. És possible que aquestes funcions experimentals canviïn, deixin de funcionar o desapareguin en versions futures. Continua amb precaució."</string> diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml index df5447e1b218..8c0571ef5dc9 100644 --- a/packages/SystemUI/res/values-cs/strings.xml +++ b/packages/SystemUI/res/values-cs/strings.xml @@ -308,8 +308,7 @@ <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="7545274861795853838">"Sdílet zvuk"</string> <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Zvuk se sdílí"</string> - <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_accessibility (7604615019302091708) --> - <skip /> + <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"přejdete do nastavení sdílení zvuku"</string> <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> @@ -436,6 +435,7 @@ <string name="zen_modes_dialog_done" msgid="6654130880256438950">"Hotovo"</string> <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Nastavení"</string> <string name="zen_mode_on" msgid="9085304934016242591">"Zapnuto"</string> + <string name="zen_mode_on_with_details" msgid="7416143430557895497">"Zapnuto • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string> <string name="zen_mode_off" msgid="1736604456618147306">"Vypnuto"</string> <string name="zen_mode_set_up" msgid="7457957033034460064">"Nastavit"</string> <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"Spravovat v nastavení"</string> @@ -718,6 +718,8 @@ <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Satelit, dobré připojení"</string> <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Satelit, připojení je k dispozici"</string> <string name="satellite_connected_carrier_text" msgid="118524195198532589">"SOS přes satelit"</string> + <!-- no translation found for satellite_emergency_only_carrier_text (828510231597991206) --> + <skip /> <string name="accessibility_managed_profile" msgid="4703836746209377356">"Pracovní profil"</string> <string name="tuner_warning_title" msgid="7721976098452135267">"Zábava, která není pro každého"</string> <string name="tuner_warning" msgid="1861736288458481650">"Nástroj na ladění uživatelského rozhraní systému vám nabízí další způsoby, jak si vyladit a přizpůsobit uživatelské rozhraní Android. Tyto experimentální funkce mohou v dalších verzích chybět, nefungovat nebo být změněny. Postupujte proto prosím opatrně."</string> diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml index 813329bb5fc1..9b72478b7259 100644 --- a/packages/SystemUI/res/values-da/strings.xml +++ b/packages/SystemUI/res/values-da/strings.xml @@ -308,8 +308,7 @@ <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="7545274861795853838">"Del lyd"</string> <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Deler lyd"</string> - <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_accessibility (7604615019302091708) --> - <skip /> + <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"angive indstillinger for lyddeling"</string> <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> @@ -436,6 +435,7 @@ <string name="zen_modes_dialog_done" msgid="6654130880256438950">"Udfør"</string> <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Indstillinger"</string> <string name="zen_mode_on" msgid="9085304934016242591">"Til"</string> + <string name="zen_mode_on_with_details" msgid="7416143430557895497">"Til • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string> <string name="zen_mode_off" msgid="1736604456618147306">"Fra"</string> <string name="zen_mode_set_up" msgid="7457957033034460064">"Konfigurer"</string> <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"Administrer i indstillingerne"</string> @@ -718,6 +718,8 @@ <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Satellit – god forbindelse"</string> <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Satellit – forbindelsen er tilgængelig"</string> <string name="satellite_connected_carrier_text" msgid="118524195198532589">"SOS-meldinger via satellit"</string> + <!-- no translation found for satellite_emergency_only_carrier_text (828510231597991206) --> + <skip /> <string name="accessibility_managed_profile" msgid="4703836746209377356">"Arbejdsprofil"</string> <string name="tuner_warning_title" msgid="7721976098452135267">"Sjovt for nogle, men ikke for alle"</string> <string name="tuner_warning" msgid="1861736288458481650">"System UI Tuner giver dig flere muligheder for at justere og tilpasse Android-brugerfladen. Disse eksperimentelle funktioner kan ændres, gå i stykker eller forsvinde i fremtidige udgivelser. Vær forsigtig, hvis du fortsætter."</string> diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml index 4b87dd0d59fa..7bfdf6704551 100644 --- a/packages/SystemUI/res/values-de/strings.xml +++ b/packages/SystemUI/res/values-de/strings.xml @@ -308,8 +308,7 @@ <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="7545274861795853838">"Audioinhalte freigeben"</string> <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Audioinhalte werden freigegeben"</string> - <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_accessibility (7604615019302091708) --> - <skip /> + <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"Einstellungen für die Audiofreigabe eingeben"</string> <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> @@ -381,7 +380,7 @@ <string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Bildschirmaufzeichnung"</string> <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Starten"</string> <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Beenden"</string> - <string name="qs_record_issue_label" msgid="8166290137285529059">"Problem aufnehmen"</string> + <string name="qs_record_issue_label" msgid="8166290137285529059">"Problem aufzeichnen"</string> <string name="qs_record_issue_start" msgid="2979831312582567056">"Starten"</string> <string name="qs_record_issue_stop" msgid="3531747965741982657">"Aufnahme beenden"</string> <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Fehlerbericht"</string> @@ -436,6 +435,7 @@ <string name="zen_modes_dialog_done" msgid="6654130880256438950">"Fertig"</string> <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Einstellungen"</string> <string name="zen_mode_on" msgid="9085304934016242591">"An"</string> + <string name="zen_mode_on_with_details" msgid="7416143430557895497">"An • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string> <string name="zen_mode_off" msgid="1736604456618147306">"Aus"</string> <string name="zen_mode_set_up" msgid="7457957033034460064">"Einrichten"</string> <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"In den Einstellungen verwalten"</string> @@ -718,6 +718,8 @@ <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Satellit, Verbindung gut"</string> <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Satellit, Verbindung verfügbar"</string> <string name="satellite_connected_carrier_text" msgid="118524195198532589">"Notruf über Satellit"</string> + <!-- no translation found for satellite_emergency_only_carrier_text (828510231597991206) --> + <skip /> <string name="accessibility_managed_profile" msgid="4703836746209377356">"Arbeitsprofil"</string> <string name="tuner_warning_title" msgid="7721976098452135267">"Für einige ein Vergnügen, aber nicht für alle"</string> <string name="tuner_warning" msgid="1861736288458481650">"Mit System UI Tuner erhältst du zusätzliche Möglichkeiten, die Android-Benutzeroberfläche anzupassen. Achtung: Diese Testfunktionen können sich ändern, abstürzen oder in zukünftigen Versionen verschwinden."</string> diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml index 3cddb8b29fdb..ed17959ea8f0 100644 --- a/packages/SystemUI/res/values-el/strings.xml +++ b/packages/SystemUI/res/values-el/strings.xml @@ -308,8 +308,7 @@ <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Το Bluetooth θα ενεργοποιηθεί αύριο το πρωί"</string> <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Κοινή χρήση ήχου"</string> <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Κοινή χρήση ήχου σε εξέλιξη"</string> - <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_accessibility (7604615019302091708) --> - <skip /> + <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"είσοδο στις ρυθμίσεις κοινής χρήσης ήχου"</string> <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> @@ -381,7 +380,7 @@ <string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Εγγραφή οθόνης"</string> <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Έναρξη"</string> <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Διακοπή"</string> - <string name="qs_record_issue_label" msgid="8166290137285529059">"Εγγραφή προβλήματος"</string> + <string name="qs_record_issue_label" msgid="8166290137285529059">"Εγγραφή προβλ."</string> <string name="qs_record_issue_start" msgid="2979831312582567056">"Έναρξη"</string> <string name="qs_record_issue_stop" msgid="3531747965741982657">"Διακοπή"</string> <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Αναφορά σφάλματος"</string> @@ -436,6 +435,7 @@ <string name="zen_modes_dialog_done" msgid="6654130880256438950">"Τέλος"</string> <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Ρυθμίσεις"</string> <string name="zen_mode_on" msgid="9085304934016242591">"Ενεργό"</string> + <string name="zen_mode_on_with_details" msgid="7416143430557895497">"Ενεργή • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string> <string name="zen_mode_off" msgid="1736604456618147306">"Ανενεργό"</string> <string name="zen_mode_set_up" msgid="7457957033034460064">"Ρύθμιση"</string> <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"Διαχείριση στις ρυθμίσεις"</string> @@ -718,6 +718,8 @@ <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Δορυφορική, καλή σύνδεση"</string> <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Δορυφορική, διαθέσιμη σύνδεση"</string> <string name="satellite_connected_carrier_text" msgid="118524195198532589">"Δορυφορικό SOS"</string> + <!-- no translation found for satellite_emergency_only_carrier_text (828510231597991206) --> + <skip /> <string name="accessibility_managed_profile" msgid="4703836746209377356">"Προφίλ εργασίας"</string> <string name="tuner_warning_title" msgid="7721976098452135267">"Διασκέδαση για ορισμένους, αλλά όχι για όλους"</string> <string name="tuner_warning" msgid="1861736288458481650">"Το System UI Tuner σάς προσφέρει επιπλέον τρόπους για να τροποποιήσετε και να προσαρμόσετε τη διεπαφή χρήστη Android. Αυτές οι πειραματικές λειτουργίες ενδέχεται να τροποποιηθούν, να παρουσιάσουν σφάλματα ή να καταργηθούν σε μελλοντικές εκδόσεις. Συνεχίστε με προσοχή."</string> diff --git a/packages/SystemUI/res/values-en-rAU/strings.xml b/packages/SystemUI/res/values-en-rAU/strings.xml index 90442b61bfff..0565be88e855 100644 --- a/packages/SystemUI/res/values-en-rAU/strings.xml +++ b/packages/SystemUI/res/values-en-rAU/strings.xml @@ -308,8 +308,7 @@ <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="7545274861795853838">"Share audio"</string> <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Sharing audio"</string> - <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_accessibility (7604615019302091708) --> - <skip /> + <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"enter audio sharing settings"</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> @@ -436,6 +435,7 @@ <string name="zen_modes_dialog_done" msgid="6654130880256438950">"Done"</string> <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Settings"</string> <string name="zen_mode_on" msgid="9085304934016242591">"On"</string> + <string name="zen_mode_on_with_details" msgid="7416143430557895497">"On • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string> <string name="zen_mode_off" msgid="1736604456618147306">"Off"</string> <string name="zen_mode_set_up" msgid="7457957033034460064">"Set up"</string> <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"Manage in settings"</string> @@ -718,6 +718,8 @@ <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Satellite, good connection"</string> <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Satellite, connection available"</string> <string name="satellite_connected_carrier_text" msgid="118524195198532589">"Satellite SOS"</string> + <!-- no translation found for satellite_emergency_only_carrier_text (828510231597991206) --> + <skip /> <string name="accessibility_managed_profile" msgid="4703836746209377356">"Work profile"</string> <string name="tuner_warning_title" msgid="7721976098452135267">"Fun for some but not for all"</string> <string name="tuner_warning" msgid="1861736288458481650">"System UI Tuner gives you extra ways to tweak and customise the Android user interface. These experimental features may change, break or disappear in future releases. Proceed with caution."</string> diff --git a/packages/SystemUI/res/values-en-rCA/strings.xml b/packages/SystemUI/res/values-en-rCA/strings.xml index d985b43edacb..17642f7b3b58 100644 --- a/packages/SystemUI/res/values-en-rCA/strings.xml +++ b/packages/SystemUI/res/values-en-rCA/strings.xml @@ -435,6 +435,7 @@ <string name="zen_modes_dialog_done" msgid="6654130880256438950">"Done"</string> <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Settings"</string> <string name="zen_mode_on" msgid="9085304934016242591">"On"</string> + <string name="zen_mode_on_with_details" msgid="7416143430557895497">"On • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string> <string name="zen_mode_off" msgid="1736604456618147306">"Off"</string> <string name="zen_mode_set_up" msgid="7457957033034460064">"Set up"</string> <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"Manage in settings"</string> @@ -717,6 +718,7 @@ <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Satellite, good connection"</string> <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Satellite, connection available"</string> <string name="satellite_connected_carrier_text" msgid="118524195198532589">"Satellite SOS"</string> + <string name="satellite_emergency_only_carrier_text" msgid="828510231597991206">"Emergency calls or SOS"</string> <string name="accessibility_managed_profile" msgid="4703836746209377356">"Work profile"</string> <string name="tuner_warning_title" msgid="7721976098452135267">"Fun for some but not for all"</string> <string name="tuner_warning" msgid="1861736288458481650">"System UI Tuner gives you extra ways to tweak and customize the Android user interface. These experimental features may change, break, or disappear in future releases. Proceed with caution."</string> diff --git a/packages/SystemUI/res/values-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml index 90442b61bfff..0565be88e855 100644 --- a/packages/SystemUI/res/values-en-rGB/strings.xml +++ b/packages/SystemUI/res/values-en-rGB/strings.xml @@ -308,8 +308,7 @@ <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="7545274861795853838">"Share audio"</string> <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Sharing audio"</string> - <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_accessibility (7604615019302091708) --> - <skip /> + <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"enter audio sharing settings"</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> @@ -436,6 +435,7 @@ <string name="zen_modes_dialog_done" msgid="6654130880256438950">"Done"</string> <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Settings"</string> <string name="zen_mode_on" msgid="9085304934016242591">"On"</string> + <string name="zen_mode_on_with_details" msgid="7416143430557895497">"On • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string> <string name="zen_mode_off" msgid="1736604456618147306">"Off"</string> <string name="zen_mode_set_up" msgid="7457957033034460064">"Set up"</string> <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"Manage in settings"</string> @@ -718,6 +718,8 @@ <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Satellite, good connection"</string> <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Satellite, connection available"</string> <string name="satellite_connected_carrier_text" msgid="118524195198532589">"Satellite SOS"</string> + <!-- no translation found for satellite_emergency_only_carrier_text (828510231597991206) --> + <skip /> <string name="accessibility_managed_profile" msgid="4703836746209377356">"Work profile"</string> <string name="tuner_warning_title" msgid="7721976098452135267">"Fun for some but not for all"</string> <string name="tuner_warning" msgid="1861736288458481650">"System UI Tuner gives you extra ways to tweak and customise the Android user interface. These experimental features may change, break or disappear in future releases. Proceed with caution."</string> diff --git a/packages/SystemUI/res/values-en-rIN/strings.xml b/packages/SystemUI/res/values-en-rIN/strings.xml index 90442b61bfff..0565be88e855 100644 --- a/packages/SystemUI/res/values-en-rIN/strings.xml +++ b/packages/SystemUI/res/values-en-rIN/strings.xml @@ -308,8 +308,7 @@ <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="7545274861795853838">"Share audio"</string> <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Sharing audio"</string> - <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_accessibility (7604615019302091708) --> - <skip /> + <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"enter audio sharing settings"</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> @@ -436,6 +435,7 @@ <string name="zen_modes_dialog_done" msgid="6654130880256438950">"Done"</string> <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Settings"</string> <string name="zen_mode_on" msgid="9085304934016242591">"On"</string> + <string name="zen_mode_on_with_details" msgid="7416143430557895497">"On • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string> <string name="zen_mode_off" msgid="1736604456618147306">"Off"</string> <string name="zen_mode_set_up" msgid="7457957033034460064">"Set up"</string> <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"Manage in settings"</string> @@ -718,6 +718,8 @@ <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Satellite, good connection"</string> <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Satellite, connection available"</string> <string name="satellite_connected_carrier_text" msgid="118524195198532589">"Satellite SOS"</string> + <!-- no translation found for satellite_emergency_only_carrier_text (828510231597991206) --> + <skip /> <string name="accessibility_managed_profile" msgid="4703836746209377356">"Work profile"</string> <string name="tuner_warning_title" msgid="7721976098452135267">"Fun for some but not for all"</string> <string name="tuner_warning" msgid="1861736288458481650">"System UI Tuner gives you extra ways to tweak and customise the Android user interface. These experimental features may change, break or disappear in future releases. Proceed with caution."</string> diff --git a/packages/SystemUI/res/values-en-rXC/strings.xml b/packages/SystemUI/res/values-en-rXC/strings.xml index 1d29f65dd931..d31d328b4816 100644 --- a/packages/SystemUI/res/values-en-rXC/strings.xml +++ b/packages/SystemUI/res/values-en-rXC/strings.xml @@ -435,6 +435,7 @@ <string name="zen_modes_dialog_done" msgid="6654130880256438950">"Done"</string> <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Settings"</string> <string name="zen_mode_on" msgid="9085304934016242591">"On"</string> + <string name="zen_mode_on_with_details" msgid="7416143430557895497">"On • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string> <string name="zen_mode_off" msgid="1736604456618147306">"Off"</string> <string name="zen_mode_set_up" msgid="7457957033034460064">"Set up"</string> <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"Manage in settings"</string> @@ -717,6 +718,7 @@ <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Satellite, good connection"</string> <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Satellite, connection available"</string> <string name="satellite_connected_carrier_text" msgid="118524195198532589">"Satellite SOS"</string> + <string name="satellite_emergency_only_carrier_text" msgid="828510231597991206">"Emergency calls or SOS"</string> <string name="accessibility_managed_profile" msgid="4703836746209377356">"Work profile"</string> <string name="tuner_warning_title" msgid="7721976098452135267">"Fun for some but not for all"</string> <string name="tuner_warning" msgid="1861736288458481650">"System UI Tuner gives you extra ways to tweak and customize the Android user interface. These experimental features may change, break, or disappear in future releases. Proceed with caution."</string> diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml index a61f55a15081..53c38e2d2eef 100644 --- a/packages/SystemUI/res/values-es-rUS/strings.xml +++ b/packages/SystemUI/res/values-es-rUS/strings.xml @@ -308,8 +308,7 @@ <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="7545274861795853838">"Compartir audio"</string> <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Compartiendo audio"</string> - <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_accessibility (7604615019302091708) --> - <skip /> + <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"ingresar la configuración de uso compartido de audio"</string> <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> @@ -436,6 +435,7 @@ <string name="zen_modes_dialog_done" msgid="6654130880256438950">"Listo"</string> <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Configuración"</string> <string name="zen_mode_on" msgid="9085304934016242591">"Activado"</string> + <string name="zen_mode_on_with_details" msgid="7416143430557895497">"Sí • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string> <string name="zen_mode_off" msgid="1736604456618147306">"Desactivado"</string> <string name="zen_mode_set_up" msgid="7457957033034460064">"Configurar"</string> <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"Administrar en configuración"</string> @@ -718,6 +718,8 @@ <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Satélite, buena conexión"</string> <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Satélite, conexión disponible"</string> <string name="satellite_connected_carrier_text" msgid="118524195198532589">"SOS por satélite"</string> + <!-- no translation found for satellite_emergency_only_carrier_text (828510231597991206) --> + <skip /> <string name="accessibility_managed_profile" msgid="4703836746209377356">"Perfil de trabajo"</string> <string name="tuner_warning_title" msgid="7721976098452135267">"Diversión solo para algunas personas"</string> <string name="tuner_warning" msgid="1861736288458481650">"El sintonizador de IU del sistema te brinda más formas para editar y personalizar la interfaz de usuario de Android. Estas funciones experimentales pueden cambiar, dejar de funcionar o no incluirse en futuras versiones. Procede con precaución."</string> diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml index 07fec5e23374..0bebccf9b199 100644 --- a/packages/SystemUI/res/values-es/strings.xml +++ b/packages/SystemUI/res/values-es/strings.xml @@ -308,8 +308,7 @@ <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="7545274861795853838">"Compartir audio"</string> <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Compartiendo audio"</string> - <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_accessibility (7604615019302091708) --> - <skip /> + <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"acceder a las opciones para compartir audio"</string> <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> @@ -381,18 +380,18 @@ <string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Grabar pantalla"</string> <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Iniciar"</string> <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Detener"</string> - <string name="qs_record_issue_label" msgid="8166290137285529059">"Problema de grabación"</string> + <string name="qs_record_issue_label" msgid="8166290137285529059">"Grabar problema"</string> <string name="qs_record_issue_start" msgid="2979831312582567056">"Iniciar"</string> <string name="qs_record_issue_stop" msgid="3531747965741982657">"Detener"</string> - <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Informe errores"</string> + <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Informe de errores"</string> <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 name="performance" msgid="6552785217174378320">"Rendimiento"</string> <string name="user_interface" msgid="3712869377953950887">"Interfaz de usuario"</string> <string name="thermal" msgid="6758074791325414831">"Temperatura"</string> - <string name="custom" msgid="3337456985275158299">"Personalizado"</string> - <string name="custom_trace_settings_dialog_title" msgid="2608570500144830554">"Ajustes de rastreo personalizados"</string> + <string name="custom" msgid="3337456985275158299">"Otro"</string> + <string name="custom_trace_settings_dialog_title" msgid="2608570500144830554">"Ajustes de traza personalizados"</string> <string name="restore_default" msgid="5259420807486239755">"Restaurar ajustes predeterminados"</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> @@ -436,6 +435,7 @@ <string name="zen_modes_dialog_done" msgid="6654130880256438950">"Hecho"</string> <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Ajustes"</string> <string name="zen_mode_on" msgid="9085304934016242591">"Activado"</string> + <string name="zen_mode_on_with_details" msgid="7416143430557895497">"Activado • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string> <string name="zen_mode_off" msgid="1736604456618147306">"Desactivado"</string> <string name="zen_mode_set_up" msgid="7457957033034460064">"Configurar"</string> <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"Gestionar en los ajustes"</string> @@ -718,6 +718,8 @@ <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Satélite, buena conexión"</string> <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Satélite, conexión disponible"</string> <string name="satellite_connected_carrier_text" msgid="118524195198532589">"SOS por satélite"</string> + <!-- no translation found for satellite_emergency_only_carrier_text (828510231597991206) --> + <skip /> <string name="accessibility_managed_profile" msgid="4703836746209377356">"Perfil de trabajo"</string> <string name="tuner_warning_title" msgid="7721976098452135267">"Diversión solo para algunos"</string> <string name="tuner_warning" msgid="1861736288458481650">"El configurador de UI del sistema te ofrece otras formas de modificar y personalizar la interfaz de usuario de Android. Estas funciones experimentales pueden cambiar, fallar o desaparecer en futuras versiones. Te recomendamos que tengas cuidado."</string> diff --git a/packages/SystemUI/res/values-et/strings.xml b/packages/SystemUI/res/values-et/strings.xml index 67b2bd081c29..b2ffa80f6ad3 100644 --- a/packages/SystemUI/res/values-et/strings.xml +++ b/packages/SystemUI/res/values-et/strings.xml @@ -308,8 +308,7 @@ <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="7545274861795853838">"Jaga heli"</string> <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Heli jagamine"</string> - <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_accessibility (7604615019302091708) --> - <skip /> + <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"heli jagamise seadete sisestamiseks"</string> <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> @@ -436,6 +435,7 @@ <string name="zen_modes_dialog_done" msgid="6654130880256438950">"Valmis"</string> <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Seaded"</string> <string name="zen_mode_on" msgid="9085304934016242591">"Sees"</string> + <string name="zen_mode_on_with_details" msgid="7416143430557895497">"Sees • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string> <string name="zen_mode_off" msgid="1736604456618147306">"Väljas"</string> <string name="zen_mode_set_up" msgid="7457957033034460064">"Seadistamine"</string> <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"Seadetes halamine"</string> @@ -718,6 +718,8 @@ <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Satelliit, hea ühendus"</string> <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Satelliit, ühendus on saadaval"</string> <string name="satellite_connected_carrier_text" msgid="118524195198532589">"Satelliit-SOS"</string> + <!-- no translation found for satellite_emergency_only_carrier_text (828510231597991206) --> + <skip /> <string name="accessibility_managed_profile" msgid="4703836746209377356">"Tööprofiil"</string> <string name="tuner_warning_title" msgid="7721976098452135267">"Kõik ei pruugi sellest rõõmu tunda"</string> <string name="tuner_warning" msgid="1861736288458481650">"Süsteemi kasutajaliidese tuuner pakub täiendavaid võimalusi Androidi kasutajaliidese muutmiseks ja kohandamiseks. Need katselised funktsioonid võivad muutuda, rikki minna või tulevastest versioonidest kaduda. Olge jätkamisel ettevaatlik."</string> diff --git a/packages/SystemUI/res/values-eu/strings.xml b/packages/SystemUI/res/values-eu/strings.xml index 0d6c1783e699..d8cff19f5887 100644 --- a/packages/SystemUI/res/values-eu/strings.xml +++ b/packages/SystemUI/res/values-eu/strings.xml @@ -308,8 +308,7 @@ <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="7545274861795853838">"Partekatu audioa"</string> <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Audioa partekatzen"</string> - <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_accessibility (7604615019302091708) --> - <skip /> + <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"audioa partekatzeko ezarpenetan sartzeko"</string> <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> @@ -381,7 +380,7 @@ <string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Pantaila-grabaketa"</string> <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Hasi"</string> <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Gelditu"</string> - <string name="qs_record_issue_label" msgid="8166290137285529059">"Arazo bat dago grabaketarekin"</string> + <string name="qs_record_issue_label" msgid="8166290137285529059">"Grabatu arazoa"</string> <string name="qs_record_issue_start" msgid="2979831312582567056">"Hasi"</string> <string name="qs_record_issue_stop" msgid="3531747965741982657">"Gelditu"</string> <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Akatsen txostena"</string> @@ -392,7 +391,7 @@ <string name="user_interface" msgid="3712869377953950887">"Erabiltzaile-interfazea"</string> <string name="thermal" msgid="6758074791325414831">"Termikoa"</string> <string name="custom" msgid="3337456985275158299">"Pertsonalizatua"</string> - <string name="custom_trace_settings_dialog_title" msgid="2608570500144830554">"Arrasto pertsonalizatuen ezarpenak"</string> + <string name="custom_trace_settings_dialog_title" msgid="2608570500144830554">"Arrastoaren ezarpen pertsonalizatuak"</string> <string name="restore_default" msgid="5259420807486239755">"Leheneratu balio lehenetsia"</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> @@ -436,6 +435,7 @@ <string name="zen_modes_dialog_done" msgid="6654130880256438950">"Eginda"</string> <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Ezarpenak"</string> <string name="zen_mode_on" msgid="9085304934016242591">"Aktibatuta"</string> + <string name="zen_mode_on_with_details" msgid="7416143430557895497">"Aktibo • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string> <string name="zen_mode_off" msgid="1736604456618147306">"Desaktibatuta"</string> <string name="zen_mode_set_up" msgid="7457957033034460064">"Konfiguratu"</string> <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"Kudeatu ezarpenetan"</string> @@ -718,6 +718,8 @@ <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Satelitea, konexio ona"</string> <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Satelitea, konexioa erabilgarri"</string> <string name="satellite_connected_carrier_text" msgid="118524195198532589">"Satelite bidezko SOS komunikazioa"</string> + <!-- no translation found for satellite_emergency_only_carrier_text (828510231597991206) --> + <skip /> <string name="accessibility_managed_profile" msgid="4703836746209377356">"Laneko profila"</string> <string name="tuner_warning_title" msgid="7721976098452135267">"Dibertsioa batzuentzat, baina ez guztientzat"</string> <string name="tuner_warning" msgid="1861736288458481650">"Sistemaren erabiltzaile-interfazearen konfiguratzaileak Android erabiltzaile-interfazea moldatzeko eta pertsonalizatzeko modu gehiago eskaintzen dizkizu. Baliteke eginbide esperimental horiek hurrengo kaleratzeetan aldatuta, etenda edo desagertuta egotea. Kontuz erabili."</string> diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml index 1cfb3af0dd8f..5b17c4447d52 100644 --- a/packages/SystemUI/res/values-fa/strings.xml +++ b/packages/SystemUI/res/values-fa/strings.xml @@ -308,8 +308,7 @@ <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"بلوتوث فردا صبح روشن خواهد شد"</string> <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"همرسانی صدا"</string> <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"درحال همرسانی صدا"</string> - <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_accessibility (7604615019302091708) --> - <skip /> + <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"وارد شدن به تنظیمات «اشتراک صدا»"</string> <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> @@ -436,6 +435,7 @@ <string name="zen_modes_dialog_done" msgid="6654130880256438950">"تمام"</string> <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"تنظیمات"</string> <string name="zen_mode_on" msgid="9085304934016242591">"روشن"</string> + <string name="zen_mode_on_with_details" msgid="7416143430557895497">"روشن • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string> <string name="zen_mode_off" msgid="1736604456618147306">"خاموش"</string> <string name="zen_mode_set_up" msgid="7457957033034460064">"راهاندازی"</string> <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"مدیریت در تنظیمات"</string> @@ -718,6 +718,8 @@ <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"ماهواره، اتصال خوب است"</string> <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"ماهواره، اتصال دردسترس است"</string> <string name="satellite_connected_carrier_text" msgid="118524195198532589">"درخواست کمک ماهوارهای"</string> + <!-- no translation found for satellite_emergency_only_carrier_text (828510231597991206) --> + <skip /> <string name="accessibility_managed_profile" msgid="4703836746209377356">"نمایه کاری"</string> <string name="tuner_warning_title" msgid="7721976098452135267">"برای بعضی افراد سرگرمکننده است اما نه برای همه"</string> <string name="tuner_warning" msgid="1861736288458481650">"«تنظیمکننده واسط کاربری سیستم» روشهای بیشتری برای تنظیم دقیق و سفارشی کردن واسط کاربری Android در اختیار شما قرار میدهد. ممکن است این ویژگیهای آزمایشی تغییر کنند، خراب شوند یا در نسخههای آینده جود نداشته باشند. با احتیاط ادامه دهید."</string> diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml index b7467e6ccb59..9244becd8338 100644 --- a/packages/SystemUI/res/values-fi/strings.xml +++ b/packages/SystemUI/res/values-fi/strings.xml @@ -108,7 +108,7 @@ <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Näytön tallennusta käsitellään"</string> <string name="screenrecord_channel_description" msgid="4147077128486138351">"Pysyvä ilmoitus näytön tallentamisesta"</string> <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Tallennetaanko näytön toimintaa?"</string> - <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Tallenna yksi sovellus"</string> + <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Tallenna yhdestä sovelluksesta"</string> <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Tallenna koko näyttö"</string> <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Kun tallennat koko näyttöä, kaikki näytöllä näkyvä sisältö tallennetaan. Ole siis varovainen, kun lisäät salasanoja, maksutietoja, viestejä, kuvia, audiota tai videoita."</string> <string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Kun tallennat sovellusta, kaikki sovelluksessa näkyvä tai toistettu sisältö tallennetaan. Ole siis varovainen, kun lisäät salasanoja, maksutietoja, viestejä, kuvia, audiota tai videoita."</string> @@ -153,7 +153,7 @@ <string name="issuerecord_title" msgid="286627115110121849">"Ongelman tallentaja"</string> <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"Käsittely: Ongelman tallennus"</string> <string name="issuerecord_channel_description" msgid="6142326363431474632">"Ongelmankeräykseen liittyvä ilmoitus taustalla jatkuvasta toiminnasta"</string> - <string name="issuerecord_ongoing_screen_only" msgid="6248206059935015722">"Tallennusongelma"</string> + <string name="issuerecord_ongoing_screen_only" msgid="6248206059935015722">"Tallennetaan ongelmaa"</string> <string name="issuerecord_share_label" msgid="3992657993619876199">"Jaa"</string> <string name="issuerecord_save_title" msgid="4161043023696751591">"Ongelman tallennus tallennettiin"</string> <string name="issuerecord_save_text" msgid="1205985304551521495">"Näytä napauttamalla"</string> @@ -308,8 +308,7 @@ <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="7545274861795853838">"Jaa audio"</string> <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Audiota jaetaan"</string> - <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_accessibility (7604615019302091708) --> - <skip /> + <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"lisätäksesi audion jakamisasetukset"</string> <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> @@ -436,6 +435,7 @@ <string name="zen_modes_dialog_done" msgid="6654130880256438950">"Valmis"</string> <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Asetukset"</string> <string name="zen_mode_on" msgid="9085304934016242591">"Päällä"</string> + <string name="zen_mode_on_with_details" msgid="7416143430557895497">"Päällä • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string> <string name="zen_mode_off" msgid="1736604456618147306">"Pois päältä"</string> <string name="zen_mode_set_up" msgid="7457957033034460064">"Ota käyttöön"</string> <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"Muuta asetuksista"</string> @@ -718,6 +718,8 @@ <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Satelliitti, hyvä yhteys"</string> <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Satelliitti, yhteys saatavilla"</string> <string name="satellite_connected_carrier_text" msgid="118524195198532589">"Satellite SOS"</string> + <!-- no translation found for satellite_emergency_only_carrier_text (828510231597991206) --> + <skip /> <string name="accessibility_managed_profile" msgid="4703836746209377356">"Työprofiili"</string> <string name="tuner_warning_title" msgid="7721976098452135267">"Ei sovellu kaikkien käyttöön"</string> <string name="tuner_warning" msgid="1861736288458481650">"System UI Tuner antaa lisämahdollisuuksia Android-käyttöliittymän muokkaamiseen. Nämä kokeelliset ominaisuudet voivat muuttua, lakata toimimasta tai kadota milloin tahansa. Jatka omalla vastuullasi."</string> diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml index a45c9ae26a6c..41305af8d455 100644 --- a/packages/SystemUI/res/values-fr-rCA/strings.xml +++ b/packages/SystemUI/res/values-fr-rCA/strings.xml @@ -126,7 +126,7 @@ <string name="screenrecord_stop_label" msgid="72699670052087989">"Arrêter"</string> <string name="screenrecord_share_label" msgid="5025590804030086930">"Partager"</string> <string name="screenrecord_save_title" msgid="1886652605520893850">"Enregistrement sauvegardé"</string> - <string name="screenrecord_save_text" msgid="3008973099800840163">"Touchez pour afficher"</string> + <string name="screenrecord_save_text" msgid="3008973099800840163">"Touchez ceci pour afficher"</string> <string name="screenrecord_save_error" msgid="5862648532560118815">"Erreur d\'enregistrement de l\'écran"</string> <string name="screenrecord_start_error" msgid="2200660692479682368">"Une erreur s\'est produite lors du démarrage de l\'enregistrement d\'écran"</string> <string name="screenrecord_stop_dialog_title" msgid="8716193661764511095">"Arrêter l\'enregistrement?"</string> @@ -155,8 +155,8 @@ <string name="issuerecord_channel_description" msgid="6142326363431474632">"Notification continue pour une session de collecte d\'un problème"</string> <string name="issuerecord_ongoing_screen_only" msgid="6248206059935015722">"Enregistrement du problème"</string> <string name="issuerecord_share_label" msgid="3992657993619876199">"Partager"</string> - <string name="issuerecord_save_title" msgid="4161043023696751591">"L\'enregistrement du problème a été enregistré"</string> - <string name="issuerecord_save_text" msgid="1205985304551521495">"Touchez ici pour afficher l\'enregistrement"</string> + <string name="issuerecord_save_title" msgid="4161043023696751591">"Le problème a été enregistré"</string> + <string name="issuerecord_save_text" msgid="1205985304551521495">"Touchez ceci pour afficher l\'enregistrement"</string> <string name="issuerecord_save_error" msgid="6913040083446722726">"Erreur lors de l\'enregistrement du problème"</string> <string name="issuerecord_start_error" msgid="3402782952722871190">"Erreur lors du démarrage de l\'enregistrement du problème"</string> <string name="immersive_cling_title" msgid="8372056499315585941">"Affichage plein écran"</string> @@ -308,8 +308,7 @@ <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="7545274861795853838">"Partager l\'audio"</string> <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Partage de l\'audio en cours…"</string> - <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_accessibility (7604615019302091708) --> - <skip /> + <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"entrer les paramètres de partage audio"</string> <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> @@ -381,7 +380,7 @@ <string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Enregistrement d\'écran"</string> <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Démarrer"</string> <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Arrêter"</string> - <string name="qs_record_issue_label" msgid="8166290137285529059">"Rapporter le problème"</string> + <string name="qs_record_issue_label" msgid="8166290137285529059">"Enregistrer le problème"</string> <string name="qs_record_issue_start" msgid="2979831312582567056">"Commencer"</string> <string name="qs_record_issue_stop" msgid="3531747965741982657">"Arrêter"</string> <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Rapport de bogue"</string> @@ -390,9 +389,9 @@ <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Enregistrement écran"</string> <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="custom" msgid="3337456985275158299">"Personnalisé"</string> - <string name="custom_trace_settings_dialog_title" msgid="2608570500144830554">"Paramètres personnalisés de la trace"</string> + <string name="thermal" msgid="6758074791325414831">"Chaleur"</string> + <string name="custom" msgid="3337456985275158299">"Type personnalisé"</string> + <string name="custom_trace_settings_dialog_title" msgid="2608570500144830554">"Paramètres de traçage personnalisés"</string> <string name="restore_default" msgid="5259420807486239755">"Restaurer la valeur par défaut"</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> @@ -436,6 +435,7 @@ <string name="zen_modes_dialog_done" msgid="6654130880256438950">"OK"</string> <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Paramètres"</string> <string name="zen_mode_on" msgid="9085304934016242591">"Activé"</string> + <string name="zen_mode_on_with_details" msgid="7416143430557895497">"Activé • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string> <string name="zen_mode_off" msgid="1736604456618147306">"Désactivé"</string> <string name="zen_mode_set_up" msgid="7457957033034460064">"Configurer"</string> <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"Gérer dans les paramètres"</string> @@ -718,6 +718,8 @@ <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Bonne connexion satellite"</string> <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Connexion satellite accessible"</string> <string name="satellite_connected_carrier_text" msgid="118524195198532589">"SOS par satellite"</string> + <!-- no translation found for satellite_emergency_only_carrier_text (828510231597991206) --> + <skip /> <string name="accessibility_managed_profile" msgid="4703836746209377356">"Profil professionnel"</string> <string name="tuner_warning_title" msgid="7721976098452135267">"Divertissant pour certains, mais pas pour tous"</string> <string name="tuner_warning" msgid="1861736288458481650">"System UI Tuner vous propose de nouvelles manières d\'adapter et de personnaliser l\'interface utilisateur d\'Android. Ces fonctionnalités expérimentales peuvent être modifiées, cesser de fonctionner ou disparaître dans les versions futures. À utiliser avec prudence."</string> @@ -1270,7 +1272,7 @@ <string name="clipboard_edit_text_description" msgid="805254383912962103">"Modifier le texte copié"</string> <string name="clipboard_edit_image_description" msgid="8904857948976041306">"Modifier l\'image copiée"</string> <string name="clipboard_send_nearby_description" msgid="4629769637846717650">"Envoyer à un appareil à proximité"</string> - <string name="clipboard_text_hidden" msgid="7926899867471812305">"Touchez pour afficher"</string> + <string name="clipboard_text_hidden" msgid="7926899867471812305">"Touchez ceci pour afficher"</string> <string name="clipboard_text_copied" msgid="5100836834278976679">"Texte copié"</string> <string name="clipboard_image_copied" msgid="3793365360174328722">"Image copiée"</string> <string name="clipboard_content_copied" msgid="144452398567828145">"Contenu copié"</string> diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml index 12073b23693e..4df98cf1864d 100644 --- a/packages/SystemUI/res/values-fr/strings.xml +++ b/packages/SystemUI/res/values-fr/strings.xml @@ -153,7 +153,7 @@ <string name="issuerecord_title" msgid="286627115110121849">"Enregistreur de problèmes"</string> <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"Enregistrement du problème"</string> <string name="issuerecord_channel_description" msgid="6142326363431474632">"Notification d\'activité en cours concernant la session d\'enregistrement d\'un problème"</string> - <string name="issuerecord_ongoing_screen_only" msgid="6248206059935015722">"Problème d\'enregistrement"</string> + <string name="issuerecord_ongoing_screen_only" msgid="6248206059935015722">"Enregistrement du problème"</string> <string name="issuerecord_share_label" msgid="3992657993619876199">"Partager"</string> <string name="issuerecord_save_title" msgid="4161043023696751591">"Problème enregistré"</string> <string name="issuerecord_save_text" msgid="1205985304551521495">"Appuyez pour afficher"</string> @@ -308,8 +308,7 @@ <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="7545274861795853838">"Partager le contenu audio"</string> <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Audio partagé"</string> - <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_accessibility (7604615019302091708) --> - <skip /> + <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"accéder aux paramètres de partage audio"</string> <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> @@ -381,7 +380,7 @@ <string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Enregistr. écran"</string> <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Démarrer"</string> <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Arrêter"</string> - <string name="qs_record_issue_label" msgid="8166290137285529059">"Enregistrer le problème"</string> + <string name="qs_record_issue_label" msgid="8166290137285529059">"Enreg. le problème"</string> <string name="qs_record_issue_start" msgid="2979831312582567056">"Lancer"</string> <string name="qs_record_issue_stop" msgid="3531747965741982657">"Arrêter"</string> <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Rapport de bug"</string> @@ -436,6 +435,7 @@ <string name="zen_modes_dialog_done" msgid="6654130880256438950">"OK"</string> <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Paramètres"</string> <string name="zen_mode_on" msgid="9085304934016242591">"Activé"</string> + <string name="zen_mode_on_with_details" msgid="7416143430557895497">"Activé • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string> <string name="zen_mode_off" msgid="1736604456618147306">"Désactivé"</string> <string name="zen_mode_set_up" msgid="7457957033034460064">"Configurer"</string> <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"Gérer dans les paramètres"</string> @@ -718,6 +718,8 @@ <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Bonne connexion satellite"</string> <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Connexion satellite disponible"</string> <string name="satellite_connected_carrier_text" msgid="118524195198532589">"SOS par satellite"</string> + <!-- no translation found for satellite_emergency_only_carrier_text (828510231597991206) --> + <skip /> <string name="accessibility_managed_profile" msgid="4703836746209377356">"Profil professionnel"</string> <string name="tuner_warning_title" msgid="7721976098452135267">"Divertissant pour certains, mais pas pour tous"</string> <string name="tuner_warning" msgid="1861736288458481650">"System UI Tuner vous propose de nouvelles manières d\'adapter et de personnaliser l\'interface utilisateur Android. Ces fonctionnalités expérimentales peuvent être modifiées, cesser de fonctionner ou disparaître dans les versions futures. À utiliser avec prudence."</string> diff --git a/packages/SystemUI/res/values-gl/strings.xml b/packages/SystemUI/res/values-gl/strings.xml index b0f4c4182840..1fe1242bf466 100644 --- a/packages/SystemUI/res/values-gl/strings.xml +++ b/packages/SystemUI/res/values-gl/strings.xml @@ -308,8 +308,7 @@ <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="7545274861795853838">"Compartir audio"</string> <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Compartindo audio"</string> - <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_accessibility (7604615019302091708) --> - <skip /> + <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"configurar o uso compartido de audio"</string> <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> @@ -381,7 +380,7 @@ <string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Gravar pantalla"</string> <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Iniciar"</string> <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Deter"</string> - <string name="qs_record_issue_label" msgid="8166290137285529059">"Rexistrar problema"</string> + <string name="qs_record_issue_label" msgid="8166290137285529059">"Gravar problema"</string> <string name="qs_record_issue_start" msgid="2979831312582567056">"Iniciar"</string> <string name="qs_record_issue_stop" msgid="3531747965741982657">"Deter"</string> <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Informe de erros"</string> @@ -391,7 +390,7 @@ <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="custom" msgid="3337456985275158299">"Personalizada"</string> + <string name="custom" msgid="3337456985275158299">"Personalizado"</string> <string name="custom_trace_settings_dialog_title" msgid="2608570500144830554">"Configuración de rastrexo personalizada"</string> <string name="restore_default" msgid="5259420807486239755">"Restaurar configuración predeterminada"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Modo dunha soa man"</string> @@ -436,6 +435,7 @@ <string name="zen_modes_dialog_done" msgid="6654130880256438950">"Feito"</string> <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Configuración"</string> <string name="zen_mode_on" msgid="9085304934016242591">"Activado"</string> + <string name="zen_mode_on_with_details" msgid="7416143430557895497">"Activo • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string> <string name="zen_mode_off" msgid="1736604456618147306">"Desactivado"</string> <string name="zen_mode_set_up" msgid="7457957033034460064">"Configurar"</string> <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"Xestionar na configuración"</string> @@ -718,6 +718,8 @@ <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Satélite, boa conexión"</string> <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Satélite, conexión dispoñible"</string> <string name="satellite_connected_carrier_text" msgid="118524195198532589">"SOS por satélite"</string> + <!-- no translation found for satellite_emergency_only_carrier_text (828510231597991206) --> + <skip /> <string name="accessibility_managed_profile" msgid="4703836746209377356">"Perfil de traballo"</string> <string name="tuner_warning_title" msgid="7721976098452135267">"Diversión só para algúns"</string> <string name="tuner_warning" msgid="1861736288458481650">"O configurador da IU do sistema ofréceche formas adicionais de modificar e personalizar a interface de usuario de Android. Estas funcións experimentais poden cambiar, interromperse ou desaparecer en futuras versións. Continúa con precaución."</string> diff --git a/packages/SystemUI/res/values-gu/strings.xml b/packages/SystemUI/res/values-gu/strings.xml index 0f36b0e8e482..7ecc0576f913 100644 --- a/packages/SystemUI/res/values-gu/strings.xml +++ b/packages/SystemUI/res/values-gu/strings.xml @@ -308,8 +308,7 @@ <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"બ્લૂટૂથ આવતીકાલે સવારે ચાલુ થશે"</string> <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"ઑડિયો શેર કરો"</string> <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"ઑડિયો શેર કરી રહ્યાં છીએ"</string> - <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_accessibility (7604615019302091708) --> - <skip /> + <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"ઑડિયો શેરિંગ સેટિંગ દાખલ કરો"</string> <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> @@ -378,7 +377,7 @@ <string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string> <string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC અક્ષમ કરેલ છે"</string> <string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC સક્ષમ કરેલ છે"</string> - <string name="quick_settings_screen_record_label" msgid="8650355346742003694">"સ્ક્રીન રેકૉર્ડ"</string> + <string name="quick_settings_screen_record_label" msgid="8650355346742003694">"સ્ક્રીન રેકોર્ડ"</string> <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"શરૂ કરો"</string> <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"રોકો"</string> <string name="qs_record_issue_label" msgid="8166290137285529059">"રેકોર્ડિંગમાં સમસ્યા"</string> @@ -436,6 +435,7 @@ <string name="zen_modes_dialog_done" msgid="6654130880256438950">"થઈ ગયું"</string> <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"સેટિંગ"</string> <string name="zen_mode_on" msgid="9085304934016242591">"ચાલુ"</string> + <string name="zen_mode_on_with_details" msgid="7416143430557895497">"ચાલુ • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string> <string name="zen_mode_off" msgid="1736604456618147306">"બંધ"</string> <string name="zen_mode_set_up" msgid="7457957033034460064">"સેટઅપ કરો"</string> <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"સેટિંગમાં જઈને મેનેજ કરો"</string> @@ -718,6 +718,8 @@ <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"સૅટલાઇટ, સારું કનેક્શન"</string> <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"સૅટલાઇટ, કનેક્શન ઉપલબ્ધ છે"</string> <string name="satellite_connected_carrier_text" msgid="118524195198532589">"ઇમર્જન્સી સૅટલાઇટ સહાય"</string> + <!-- no translation found for satellite_emergency_only_carrier_text (828510231597991206) --> + <skip /> <string name="accessibility_managed_profile" msgid="4703836746209377356">"ઑફિસની પ્રોફાઇલ"</string> <string name="tuner_warning_title" msgid="7721976098452135267">"કેટલાક માટે મજા પરંતુ બધા માટે નહીં"</string> <string name="tuner_warning" msgid="1861736288458481650">"સિસ્ટમ UI ટ્યૂનર તમને Android વપરાશકર્તા ઇન્ટરફેસને ટ્વીક અને કસ્ટમાઇઝ કરવાની વધારાની રીતો આપે છે. ભાવિ રીલિઝેસમાં આ પ્રાયોગિક સુવિધાઓ બદલાઈ, ભંગ અથવા અદૃશ્ય થઈ શકે છે. સાવધાની સાથે આગળ વધો."</string> diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml index e7cfe53d4059..c6a5e572e106 100644 --- a/packages/SystemUI/res/values-hi/strings.xml +++ b/packages/SystemUI/res/values-hi/strings.xml @@ -150,10 +150,10 @@ <string name="cast_to_other_device_stop_dialog_message_generic" msgid="4100272100480415076">"फ़िलहाल, आस-पास मौजूद किसी डिवाइस पर कास्ट किया जा रहा है"</string> <string name="cast_to_other_device_stop_dialog_button" msgid="6420183747435521834">"कास्ट करना बंद करें"</string> <string name="close_dialog_button" msgid="4749497706540104133">"बंद करें"</string> - <string name="issuerecord_title" msgid="286627115110121849">"समस्या का डेटा सेव करने वाला टूल"</string> + <string name="issuerecord_title" msgid="286627115110121849">"समस्या रिकॉर्ड करने वाला टूल"</string> <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"समस्या का डेटा प्रोसेस हो रहा"</string> <string name="issuerecord_channel_description" msgid="6142326363431474632">"समस्या का डेटा इकट्ठा करने के लिए बैकग्राउंड में जारी गतिविधि की सूचना"</string> - <string name="issuerecord_ongoing_screen_only" msgid="6248206059935015722">"समस्या का डेटा इकट्ठा किया जा रहा है"</string> + <string name="issuerecord_ongoing_screen_only" msgid="6248206059935015722">"समस्या रिकॉर्ड की जा रही है"</string> <string name="issuerecord_share_label" msgid="3992657993619876199">"शेयर करें"</string> <string name="issuerecord_save_title" msgid="4161043023696751591">"समस्या का डेटा सेव किया गया"</string> <string name="issuerecord_save_text" msgid="1205985304551521495">"देखने के लिए टैप करें"</string> @@ -308,8 +308,7 @@ <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"ब्लूटूथ कल सुबह चालू होगा"</string> <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"ऑडियो शेयर करें"</string> <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"ऑडियो शेयर किया जा रहा है"</string> - <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_accessibility (7604615019302091708) --> - <skip /> + <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"ऑडियो शेयर करने की सेटिंग जोड़ें"</string> <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> @@ -392,7 +391,7 @@ <string name="user_interface" msgid="3712869377953950887">"यूज़र इंटरफ़ेस"</string> <string name="thermal" msgid="6758074791325414831">"थर्मल"</string> <string name="custom" msgid="3337456985275158299">"कस्टम"</string> - <string name="custom_trace_settings_dialog_title" msgid="2608570500144830554">"कस्टम ट्रेस सेटिंग"</string> + <string name="custom_trace_settings_dialog_title" msgid="2608570500144830554">"ट्रेस करने से जुड़ी कस्टम सेटिंग"</string> <string name="restore_default" msgid="5259420807486239755">"डिफ़ॉल्ट सेटिंग वापस लाएं"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"वन-हैंडेड मोड"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"कान की मशीनें"</string> @@ -436,6 +435,7 @@ <string name="zen_modes_dialog_done" msgid="6654130880256438950">"हो गया"</string> <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"सेटिंग"</string> <string name="zen_mode_on" msgid="9085304934016242591">"चालू है"</string> + <string name="zen_mode_on_with_details" msgid="7416143430557895497">"<xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g> • पर"</string> <string name="zen_mode_off" msgid="1736604456618147306">"बंद है"</string> <string name="zen_mode_set_up" msgid="7457957033034460064">"सेट अप करें"</string> <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"सेटिंग में जाकर मैनेज करें"</string> @@ -509,7 +509,7 @@ <string name="communal_widget_picker_description" msgid="490515450110487871">"टैबलेट लॉक होने के बावजूद, कोई भी व्यक्ति इसकी लॉक स्क्रीन पर विजेट देख सकता है."</string> <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"विजेट से चुने हुए का निशान हटाएं"</string> <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"लॉक स्क्रीन विजेट"</string> - <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"किसी विजेट से कोई ऐप्लिकेशन खोलने के लिए, आपको अपनी पहचान की पुष्टि करनी होगी. ध्यान रखें कि टैबलेट के लॉक होने पर भी कोई व्यक्ति विजेट देख सकता है. ऐसा हो सकता है कि कुछ विजेट लॉक स्क्रीन पर दिखाने के लिए न बने हों. इन्हें लॉक स्क्रीन पर जोड़ना असुरक्षित हो सकता है."</string> + <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"किसी विजेट से कोई ऐप्लिकेशन खोलने के लिए, आपको अपनी पहचान की पुष्टि करनी होगी. ध्यान रखें कि आपके टैबलेट के लॉक होने पर भी, कोई व्यक्ति विजेट देख सकता है. ऐसा हो सकता है कि कुछ विजेट, लॉक स्क्रीन पर दिखाने के लिए न बने हों. इन्हें लॉक स्क्रीन पर जोड़ना असुरक्षित हो सकता है."</string> <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"ठीक है"</string> <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"उपयोगकर्ता बदलें"</string> <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"पुलडाउन मेन्यू"</string> @@ -718,6 +718,8 @@ <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"सैटलाइट कनेक्शन अच्छा है"</string> <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"सैटलाइट कनेक्शन उपलब्ध है"</string> <string name="satellite_connected_carrier_text" msgid="118524195198532589">"सैटलाइट एसओएस"</string> + <!-- no translation found for satellite_emergency_only_carrier_text (828510231597991206) --> + <skip /> <string name="accessibility_managed_profile" msgid="4703836746209377356">"वर्क प्रोफ़ाइल"</string> <string name="tuner_warning_title" msgid="7721976098452135267">"कुछ के लिए मज़ेदार लेकिन सबके लिए नहीं"</string> <string name="tuner_warning" msgid="1861736288458481650">"सिस्टम यूज़र इंटरफ़ेस (यूआई) ट्यूनर, आपको Android यूज़र इंटरफ़ेस में सुधार लाने और उसे अपनी पसंद के हिसाब से बदलने के कुछ और तरीके देता है. प्रयोग के तौर पर इस्तेमाल हो रहीं ये सुविधाएं आगे चल कर रिलीज़ की जा सकती हैं, रोकी जा सकती हैं या दिखाई देना बंद हो सकती हैं. सावधानी से आगे बढ़ें."</string> diff --git a/packages/SystemUI/res/values-hr/strings.xml b/packages/SystemUI/res/values-hr/strings.xml index 196ae31f9d01..2edf138cc217 100644 --- a/packages/SystemUI/res/values-hr/strings.xml +++ b/packages/SystemUI/res/values-hr/strings.xml @@ -308,8 +308,7 @@ <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="7545274861795853838">"Dijeli zvuk"</string> <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Zajedničko slušanje"</string> - <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_accessibility (7604615019302091708) --> - <skip /> + <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"unesite postavke zajedničkog slušanja"</string> <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> @@ -381,12 +380,12 @@ <string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Snimanje zaslona"</string> <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Početak"</string> <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Zaustavi"</string> - <string name="qs_record_issue_label" msgid="8166290137285529059">"Zabilježite poteškoću"</string> + <string name="qs_record_issue_label" msgid="8166290137285529059">"Snimite poteškoću"</string> <string name="qs_record_issue_start" msgid="2979831312582567056">"Pokreni"</string> <string name="qs_record_issue_stop" msgid="3531747965741982657">"Zaustavi"</string> <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Izvješće o programskim pogreškama"</string> <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Na koji je 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_prompt" msgid="2526949919167046219">"Odaberite vrstu poteškoće"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Snimanje zaslona"</string> <string name="performance" msgid="6552785217174378320">"Izvedba"</string> <string name="user_interface" msgid="3712869377953950887">"Korisničko sučelje"</string> @@ -436,6 +435,7 @@ <string name="zen_modes_dialog_done" msgid="6654130880256438950">"Gotovo"</string> <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Postavke"</string> <string name="zen_mode_on" msgid="9085304934016242591">"Uključeno"</string> + <string name="zen_mode_on_with_details" msgid="7416143430557895497">"Uklj. • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string> <string name="zen_mode_off" msgid="1736604456618147306">"Isključeno"</string> <string name="zen_mode_set_up" msgid="7457957033034460064">"Postavi"</string> <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"Upravljajte u postavkama"</string> @@ -718,6 +718,8 @@ <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Satelit, dobra veza"</string> <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Satelit, veza je dostupna"</string> <string name="satellite_connected_carrier_text" msgid="118524195198532589">"SOS putem satelita"</string> + <!-- no translation found for satellite_emergency_only_carrier_text (828510231597991206) --> + <skip /> <string name="accessibility_managed_profile" msgid="4703836746209377356">"Poslovni profil"</string> <string name="tuner_warning_title" msgid="7721976098452135267">"Zabava za neke, ali ne za sve"</string> <string name="tuner_warning" msgid="1861736288458481650">"Ugađanje korisničkog sučelja sustava pruža vam dodatne načine za prilagodbu korisničkog sučelja Androida. Te se eksperimentalne značajke mogu promijeniti, prekinuti ili nestati u budućim izdanjima. Nastavite uz oprez."</string> diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml index c3922d7bac51..36bae57c5239 100644 --- a/packages/SystemUI/res/values-hu/strings.xml +++ b/packages/SystemUI/res/values-hu/strings.xml @@ -153,7 +153,7 @@ <string name="issuerecord_title" msgid="286627115110121849">"Problémafelvevő"</string> <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"Problémafelvétel feldolgozása…"</string> <string name="issuerecord_channel_description" msgid="6142326363431474632">"Folyamatban lévő értesítés egy problémagyűjtési munkamenethez"</string> - <string name="issuerecord_ongoing_screen_only" msgid="6248206059935015722">"Problémafelvétel folyamatban…"</string> + <string name="issuerecord_ongoing_screen_only" msgid="6248206059935015722">"Probléma rögzítése folyamatban…"</string> <string name="issuerecord_share_label" msgid="3992657993619876199">"Megosztás"</string> <string name="issuerecord_save_title" msgid="4161043023696751591">"Problémafelvétel mentve"</string> <string name="issuerecord_save_text" msgid="1205985304551521495">"Koppintson a megtekintéshez"</string> @@ -308,8 +308,7 @@ <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="7545274861795853838">"Hang megosztása"</string> <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Hang megosztása…"</string> - <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_accessibility (7604615019302091708) --> - <skip /> + <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"a hangmegosztási beállítások megadásához"</string> <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> @@ -436,6 +435,7 @@ <string name="zen_modes_dialog_done" msgid="6654130880256438950">"Kész"</string> <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Beállítások"</string> <string name="zen_mode_on" msgid="9085304934016242591">"Be"</string> + <string name="zen_mode_on_with_details" msgid="7416143430557895497">"Be • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string> <string name="zen_mode_off" msgid="1736604456618147306">"Ki"</string> <string name="zen_mode_set_up" msgid="7457957033034460064">"Beállítás"</string> <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"A Beállítások között kezelheti"</string> @@ -718,6 +718,8 @@ <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Műhold, jó kapcsolat"</string> <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Műhold, van rendelkezésre álló kapcsolat"</string> <string name="satellite_connected_carrier_text" msgid="118524195198532589">"Műholdas SOS"</string> + <!-- no translation found for satellite_emergency_only_carrier_text (828510231597991206) --> + <skip /> <string name="accessibility_managed_profile" msgid="4703836746209377356">"Munkaprofil"</string> <string name="tuner_warning_title" msgid="7721976098452135267">"Egyeseknek tetszik, másoknak nem"</string> <string name="tuner_warning" msgid="1861736288458481650">"A Kezelőfelület-hangoló az Android felhasználói felületének szerkesztéséhez és testreszabásához nyújt további megoldásokat. Ezek a kísérleti funkciók változhatnak vagy megsérülhetnek a későbbi kiadásokban, illetve eltűnhetnek azokból. Körültekintően járjon el."</string> diff --git a/packages/SystemUI/res/values-hy/strings.xml b/packages/SystemUI/res/values-hy/strings.xml index 1499bcac099e..24e90653fa96 100644 --- a/packages/SystemUI/res/values-hy/strings.xml +++ b/packages/SystemUI/res/values-hy/strings.xml @@ -126,7 +126,7 @@ <string name="screenrecord_stop_label" msgid="72699670052087989">"Կանգնեցնել"</string> <string name="screenrecord_share_label" msgid="5025590804030086930">"Կիսվել"</string> <string name="screenrecord_save_title" msgid="1886652605520893850">"Էկրանի տեսագրությունը պահվեց"</string> - <string name="screenrecord_save_text" msgid="3008973099800840163">"Հպեք՝ դիտելու համար"</string> + <string name="screenrecord_save_text" msgid="3008973099800840163">"Հպեք դիտելու համար"</string> <string name="screenrecord_save_error" msgid="5862648532560118815">"Չհաջողվեց պահել էկրանի տեսագրությունը"</string> <string name="screenrecord_start_error" msgid="2200660692479682368">"Չհաջողվեց սկսել տեսագրումը"</string> <string name="screenrecord_stop_dialog_title" msgid="8716193661764511095">"Կանգնեցնե՞լ տեսագրումը"</string> @@ -156,7 +156,7 @@ <string name="issuerecord_ongoing_screen_only" msgid="6248206059935015722">"Տեսագրում ենք խնդիրը"</string> <string name="issuerecord_share_label" msgid="3992657993619876199">"Կիսվել"</string> <string name="issuerecord_save_title" msgid="4161043023696751591">"Տեսագրությունը պահվեց"</string> - <string name="issuerecord_save_text" msgid="1205985304551521495">"Հպեք՝ դիտելու համար"</string> + <string name="issuerecord_save_text" msgid="1205985304551521495">"Հպեք դիտելու համար"</string> <string name="issuerecord_save_error" msgid="6913040083446722726">"Չհաջողվեց պահել տեսագրությունը"</string> <string name="issuerecord_start_error" msgid="3402782952722871190">"Չհաջողվեց սկսել տեսագրումը"</string> <string name="immersive_cling_title" msgid="8372056499315585941">"Լիաէկրան դիտակերպ"</string> @@ -308,8 +308,7 @@ <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth-ը կմիանա վաղն առավոտյան"</string> <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Փոխանցել աուդիո"</string> <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Աուդիոյի փոխանցում"</string> - <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_accessibility (7604615019302091708) --> - <skip /> + <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"անցնել աուդիոյի փոխանցման կարգավորումներ"</string> <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> @@ -381,7 +380,7 @@ <string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Էկրանի տեսագրում"</string> <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Սկսել"</string> <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Կանգնեցնել"</string> - <string name="qs_record_issue_label" msgid="8166290137285529059">"Ձայնագրել"</string> + <string name="qs_record_issue_label" msgid="8166290137285529059">"Խնդրի տեսագրում"</string> <string name="qs_record_issue_start" msgid="2979831312582567056">"Սկսել"</string> <string name="qs_record_issue_stop" msgid="3531747965741982657">"Կանգնեցնել"</string> <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Հաղորդում սխալի մասին"</string> @@ -390,7 +389,7 @@ <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Էկրանի տեսագրում"</string> <string name="performance" msgid="6552785217174378320">"Արդյունավետություն"</string> <string name="user_interface" msgid="3712869377953950887">"Օգտատիրական ինտերֆեյս"</string> - <string name="thermal" msgid="6758074791325414831">"Ջերմատեսիլ"</string> + <string name="thermal" msgid="6758074791325414831">"Տաքացում"</string> <string name="custom" msgid="3337456985275158299">"Հատուկ"</string> <string name="custom_trace_settings_dialog_title" msgid="2608570500144830554">"Հետագծման հատուկ կարգավորումներ"</string> <string name="restore_default" msgid="5259420807486239755">"Վերականգնել կանխադրված կարգավորումները"</string> @@ -436,6 +435,7 @@ <string name="zen_modes_dialog_done" msgid="6654130880256438950">"Պատրաստ է"</string> <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Կարգավորումներ"</string> <string name="zen_mode_on" msgid="9085304934016242591">"Միացված է"</string> + <string name="zen_mode_on_with_details" msgid="7416143430557895497">"Միաց․ • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string> <string name="zen_mode_off" msgid="1736604456618147306">"Անջատված է"</string> <string name="zen_mode_set_up" msgid="7457957033034460064">"Կարգավորել"</string> <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"Կառավարել կարգավորումներում"</string> @@ -665,7 +665,7 @@ <string name="stream_alarm_unavailable" msgid="4059817189292197839">"Հասանելի չէ․ «Չանհանգստացնել» ռեժիմը միացված է"</string> <string name="stream_media_unavailable" msgid="6823020894438959853">"Հասանելի չէ․ «Չանհանգստացնել» ռեժիմը միացված է"</string> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s: Հպեք՝ ձայնը միացնելու համար:"</string> - <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s: Հպեք՝ թրթռումը միացնելու համար: Մատչելիության ծառայությունների ձայնը կարող է անջատվել:"</string> + <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s: Հպեք՝ թրթռոցը միացնելու համար: Մատչելիության ծառայությունների ձայնը կարող է անջատվել:"</string> <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> @@ -718,6 +718,8 @@ <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Արբանյակային լավ կապ"</string> <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Հասանելի է արբանյակային կապ"</string> <string name="satellite_connected_carrier_text" msgid="118524195198532589">"Satellite SOS"</string> + <!-- no translation found for satellite_emergency_only_carrier_text (828510231597991206) --> + <skip /> <string name="accessibility_managed_profile" msgid="4703836746209377356">"Աշխատանքային պրոֆիլ"</string> <string name="tuner_warning_title" msgid="7721976098452135267">"Զվարճանք մեկ՝ որոշակի մարդու համար"</string> <string name="tuner_warning" msgid="1861736288458481650">"Համակարգի ՕՄ-ի կարգավորիչը հնարավորություն է տալիս հարմարեցնել Android-ի օգտատիրոջ միջերեսը: Այս փորձնական գործառույթները կարող են հետագա թողարկումների մեջ փոփոխվել, խափանվել կամ ընդհանրապես չհայտնվել: Եթե շարունակում եք, զգուշացեք:"</string> diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml index b580c81996ca..d5f766b09e82 100644 --- a/packages/SystemUI/res/values-in/strings.xml +++ b/packages/SystemUI/res/values-in/strings.xml @@ -123,7 +123,7 @@ <string name="screenrecord_ongoing_screen_only" msgid="4459670242451527727">"Merekam layar"</string> <string name="screenrecord_ongoing_screen_and_audio" msgid="5351133763125180920">"Merekam layar dan audio"</string> <string name="screenrecord_taps_label" msgid="1595690528298857649">"Tampilkan lokasi sentuhan pada layar"</string> - <string name="screenrecord_stop_label" msgid="72699670052087989">"Stop"</string> + <string name="screenrecord_stop_label" msgid="72699670052087989">"Berhenti"</string> <string name="screenrecord_share_label" msgid="5025590804030086930">"Bagikan"</string> <string name="screenrecord_save_title" msgid="1886652605520893850">"Rekaman layar disimpan"</string> <string name="screenrecord_save_text" msgid="3008973099800840163">"Ketuk untuk melihat"</string> @@ -308,8 +308,7 @@ <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="7545274861795853838">"Bagikan audio"</string> <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Berbagi audio"</string> - <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_accessibility (7604615019302091708) --> - <skip /> + <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"masuk ke setelan berbagi audio"</string> <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> @@ -381,16 +380,16 @@ <string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Perekam layar"</string> <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Mulai"</string> <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Berhenti"</string> - <string name="qs_record_issue_label" msgid="8166290137285529059">"Mencatat Masalah"</string> + <string name="qs_record_issue_label" msgid="8166290137285529059">"Rekam Masalah"</string> <string name="qs_record_issue_start" msgid="2979831312582567056">"Mulai"</string> <string name="qs_record_issue_stop" msgid="3531747965741982657">"Berhenti"</string> <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Laporan Bug"</string> - <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Hal apa yang terpengaruh?"</string> + <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Apa jenis masalah yang Anda alami pada perangkat?"</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 name="performance" msgid="6552785217174378320">"Performa"</string> <string name="user_interface" msgid="3712869377953950887">"Antarmuka Pengguna"</string> - <string name="thermal" msgid="6758074791325414831">"Termal"</string> + <string name="thermal" msgid="6758074791325414831">"Panas"</string> <string name="custom" msgid="3337456985275158299">"Kustom"</string> <string name="custom_trace_settings_dialog_title" msgid="2608570500144830554">"Setelan Rekaman Aktivitas Kustom"</string> <string name="restore_default" msgid="5259420807486239755">"Pulihkan Default"</string> @@ -436,6 +435,7 @@ <string name="zen_modes_dialog_done" msgid="6654130880256438950">"Selesai"</string> <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Setelan"</string> <string name="zen_mode_on" msgid="9085304934016242591">"Aktif"</string> + <string name="zen_mode_on_with_details" msgid="7416143430557895497">"Aktif • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string> <string name="zen_mode_off" msgid="1736604456618147306">"Nonaktif"</string> <string name="zen_mode_set_up" msgid="7457957033034460064">"Siapkan"</string> <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"Kelola di setelan"</string> @@ -718,6 +718,8 @@ <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Satelit, koneksi baik"</string> <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Satelit, koneksi tersedia"</string> <string name="satellite_connected_carrier_text" msgid="118524195198532589">"SOS via Satelit"</string> + <!-- no translation found for satellite_emergency_only_carrier_text (828510231597991206) --> + <skip /> <string name="accessibility_managed_profile" msgid="4703836746209377356">"Profil kerja"</string> <string name="tuner_warning_title" msgid="7721976098452135267">"Tidak semua orang menganggapnya baik"</string> <string name="tuner_warning" msgid="1861736288458481650">"Penyetel Antarmuka Pengguna Sistem memberikan cara tambahan untuk mengubah dan menyesuaikan antarmuka pengguna Android. Fitur eksperimental ini dapat berubah, rusak, atau menghilang dalam rilis di masa mendatang. Lanjutkan dengan hati-hati."</string> @@ -786,7 +788,7 @@ <string name="keyboard_key_enter" msgid="8633362970109751646">"Enter"</string> <string name="keyboard_key_backspace" msgid="4095278312039628074">"Backspace"</string> <string name="keyboard_key_media_play_pause" msgid="8389984232732277478">"Play/Pause"</string> - <string name="keyboard_key_media_stop" msgid="1509943745250377699">"Stop"</string> + <string name="keyboard_key_media_stop" msgid="1509943745250377699">"Berhenti"</string> <string name="keyboard_key_media_next" msgid="8502476691227914952">"Next"</string> <string name="keyboard_key_media_previous" msgid="5637875709190955351">"Previous"</string> <string name="keyboard_key_media_rewind" msgid="3450387734224327577">"Rewind"</string> diff --git a/packages/SystemUI/res/values-is/strings.xml b/packages/SystemUI/res/values-is/strings.xml index 25c98ade3b4e..b3291bb93b35 100644 --- a/packages/SystemUI/res/values-is/strings.xml +++ b/packages/SystemUI/res/values-is/strings.xml @@ -308,8 +308,7 @@ <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="7545274861795853838">"Deila hljóði"</string> <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Deilir hljóði"</string> - <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_accessibility (7604615019302091708) --> - <skip /> + <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"slá inn stillingar hljóðdeilingar"</string> <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> @@ -436,6 +435,7 @@ <string name="zen_modes_dialog_done" msgid="6654130880256438950">"Lokið"</string> <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Stillingar"</string> <string name="zen_mode_on" msgid="9085304934016242591">"Kveikt"</string> + <string name="zen_mode_on_with_details" msgid="7416143430557895497">"Kveikt • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string> <string name="zen_mode_off" msgid="1736604456618147306">"Slökkt"</string> <string name="zen_mode_set_up" msgid="7457957033034460064">"Setja upp"</string> <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"Stjórna í stillingum"</string> @@ -718,6 +718,8 @@ <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Gervihnöttur, góð tenging"</string> <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Gervihnöttur, tenging tiltæk"</string> <string name="satellite_connected_carrier_text" msgid="118524195198532589">"Gervihnattar-SOS"</string> + <!-- no translation found for satellite_emergency_only_carrier_text (828510231597991206) --> + <skip /> <string name="accessibility_managed_profile" msgid="4703836746209377356">"Vinnusnið"</string> <string name="tuner_warning_title" msgid="7721976098452135267">"Þetta er ekki allra"</string> <string name="tuner_warning" msgid="1861736288458481650">"Fínstillingar kerfisviðmóts gera þér kleift að fínstilla og sérsníða notendaviðmót Android. Þessir tilraunaeiginleikar geta breyst, bilað eða horfið í síðari útgáfum. Gakktu því hægt um gleðinnar dyr."</string> diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml index eb941db77537..b37eff908415 100644 --- a/packages/SystemUI/res/values-it/strings.xml +++ b/packages/SystemUI/res/values-it/strings.xml @@ -308,8 +308,7 @@ <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="7545274861795853838">"Condividi audio"</string> <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Condivisione audio in corso…"</string> - <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_accessibility (7604615019302091708) --> - <skip /> + <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"inserisci le impostazioni di condivisione audio"</string> <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> @@ -385,14 +384,14 @@ <string name="qs_record_issue_start" msgid="2979831312582567056">"Avvia"</string> <string name="qs_record_issue_stop" msgid="3531747965741982657">"Interrompi"</string> <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Segnalazione di bug"</string> - <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Quali problemi ha l\'esperienza del dispositivo?"</string> + <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Quali problemi ha l\'esperienza con il 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">"Registrazione schermo"</string> <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="custom" msgid="3337456985275158299">"Personalizzate"</string> - <string name="custom_trace_settings_dialog_title" msgid="2608570500144830554">"Impostazioni monitoraggio personalizzate"</string> + <string name="custom_trace_settings_dialog_title" msgid="2608570500144830554">"Impostazioni di traccia personalizzate"</string> <string name="restore_default" msgid="5259420807486239755">"Ripristina predefinite"</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> @@ -436,6 +435,7 @@ <string name="zen_modes_dialog_done" msgid="6654130880256438950">"Fine"</string> <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Impostazioni"</string> <string name="zen_mode_on" msgid="9085304934016242591">"On"</string> + <string name="zen_mode_on_with_details" msgid="7416143430557895497">"On • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string> <string name="zen_mode_off" msgid="1736604456618147306">"Off"</string> <string name="zen_mode_set_up" msgid="7457957033034460064">"Configura"</string> <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"Gestisci nelle impostazioni"</string> @@ -718,6 +718,8 @@ <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Satellitare, connessione buona"</string> <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Satellitare, connessione disponibile"</string> <string name="satellite_connected_carrier_text" msgid="118524195198532589">"SOS satellitare"</string> + <!-- no translation found for satellite_emergency_only_carrier_text (828510231597991206) --> + <skip /> <string name="accessibility_managed_profile" msgid="4703836746209377356">"Profilo di lavoro"</string> <string name="tuner_warning_title" msgid="7721976098452135267">"Il divertimento riservato a pochi eletti"</string> <string name="tuner_warning" msgid="1861736288458481650">"L\'Ottimizzatore UI di sistema mette a disposizione altri metodi per modificare e personalizzare l\'interfaccia utente di Android. Queste funzioni sperimentali potrebbero cambiare, interrompersi o scomparire nelle versioni successive. Procedi con cautela."</string> diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml index be64a37e2d3b..ecb840947cf9 100644 --- a/packages/SystemUI/res/values-iw/strings.xml +++ b/packages/SystemUI/res/values-iw/strings.xml @@ -150,12 +150,12 @@ <string name="cast_to_other_device_stop_dialog_message_generic" msgid="4100272100480415076">"מתבצעת כרגע פעולת Cast למכשיר בקרבת מקום"</string> <string name="cast_to_other_device_stop_dialog_button" msgid="6420183747435521834">"הפסקת ה-Cast"</string> <string name="close_dialog_button" msgid="4749497706540104133">"סגירה"</string> - <string name="issuerecord_title" msgid="286627115110121849">"בעיה במכשיר ההקלטה"</string> + <string name="issuerecord_title" msgid="286627115110121849">"תיעוד של בעיה"</string> <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"מתבצע עיבוד של בעיית ההקלטה"</string> <string name="issuerecord_channel_description" msgid="6142326363431474632">"התראה מתמשכת לסשן איסוף הבעיה"</string> - <string name="issuerecord_ongoing_screen_only" msgid="6248206059935015722">"בעיית הקלטה"</string> + <string name="issuerecord_ongoing_screen_only" msgid="6248206059935015722">"הבעיה בתהליך הקלטה"</string> <string name="issuerecord_share_label" msgid="3992657993619876199">"שיתוף"</string> - <string name="issuerecord_save_title" msgid="4161043023696751591">"בעיית ההקלטה נשמרה"</string> + <string name="issuerecord_save_title" msgid="4161043023696751591">"הקלטת הבעיה נשמרה"</string> <string name="issuerecord_save_text" msgid="1205985304551521495">"אפשר להקיש כדי להציג"</string> <string name="issuerecord_save_error" msgid="6913040083446722726">"שגיאה בשמירה של בעיית ההקלטה"</string> <string name="issuerecord_start_error" msgid="3402782952722871190">"שגיאה בהפעלה של בעיית ההקלטה"</string> @@ -308,8 +308,7 @@ <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"חיבור ה-Bluetooth יופעל מחר בבוקר"</string> <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"שיתוף האודיו"</string> <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"מתבצע שיתוף של האודיו"</string> - <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_accessibility (7604615019302091708) --> - <skip /> + <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"להזנת הרשאות השיתוף של האודיו"</string> <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> @@ -436,6 +435,7 @@ <string name="zen_modes_dialog_done" msgid="6654130880256438950">"סיום"</string> <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"הגדרות"</string> <string name="zen_mode_on" msgid="9085304934016242591">"מצב מופעל"</string> + <string name="zen_mode_on_with_details" msgid="7416143430557895497">"פועל • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string> <string name="zen_mode_off" msgid="1736604456618147306">"מצב מושבת"</string> <string name="zen_mode_set_up" msgid="7457957033034460064">"הגדרה"</string> <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"שינוי ב\'הגדרות\'"</string> @@ -718,6 +718,8 @@ <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"לוויין, חיבור באיכות טובה"</string> <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"לוויין, יש חיבור זמין"</string> <string name="satellite_connected_carrier_text" msgid="118524195198532589">"תקשורת לוויינית למצב חירום"</string> + <!-- no translation found for satellite_emergency_only_carrier_text (828510231597991206) --> + <skip /> <string name="accessibility_managed_profile" msgid="4703836746209377356">"פרופיל עבודה"</string> <string name="tuner_warning_title" msgid="7721976098452135267">"מהנה בשביל חלק מהאנשים, אבל לא בשביל כולם"</string> <string name="tuner_warning" msgid="1861736288458481650">"התכונה System UI Tuner מספקת לך דרכים נוספות להתאים אישית את ממשק המשתמש של Android. התכונות הניסיוניות האלה עשויות להשתנות, לא לעבוד כראוי או להיעלם בגרסאות עתידיות. יש להמשיך בזהירות."</string> diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml index 1ad16d4a186e..98d320de2189 100644 --- a/packages/SystemUI/res/values-ja/strings.xml +++ b/packages/SystemUI/res/values-ja/strings.xml @@ -308,8 +308,7 @@ <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"明日の朝に Bluetooth が ON になります"</string> <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"音声を共有"</string> <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"音声を共有中"</string> - <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_accessibility (7604615019302091708) --> - <skip /> + <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"音声の共有設定を開く"</string> <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> @@ -436,6 +435,7 @@ <string name="zen_modes_dialog_done" msgid="6654130880256438950">"完了"</string> <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"設定"</string> <string name="zen_mode_on" msgid="9085304934016242591">"ON"</string> + <string name="zen_mode_on_with_details" msgid="7416143430557895497">"ON • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string> <string name="zen_mode_off" msgid="1736604456618147306">"OFF"</string> <string name="zen_mode_set_up" msgid="7457957033034460064">"設定"</string> <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"設定で管理"</string> @@ -718,6 +718,8 @@ <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"衛生、接続状態良好"</string> <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"衛生、接続利用可能"</string> <string name="satellite_connected_carrier_text" msgid="118524195198532589">"衛星 SOS"</string> + <!-- no translation found for satellite_emergency_only_carrier_text (828510231597991206) --> + <skip /> <string name="accessibility_managed_profile" msgid="4703836746209377356">"仕事用プロファイル"</string> <string name="tuner_warning_title" msgid="7721976098452135267">"一部の方のみお楽しみいただける限定公開ツール"</string> <string name="tuner_warning" msgid="1861736288458481650">"システムUI調整ツールでは、Androidユーザーインターフェースの調整やカスタマイズを行えます。これらの試験運用機能は今後のリリースで変更となったり、中止となったり、削除されたりする可能性がありますのでご注意ください。"</string> @@ -1315,7 +1317,7 @@ <string name="keyguard_affordance_enablement_dialog_notes_app_action" msgid="6821710209675089470">"アプリを選択"</string> <string name="keyguard_affordance_press_too_short" msgid="8145437175134998864">"ショートカットの長押しが必要です"</string> <string name="rear_display_bottom_sheet_cancel" msgid="3461468855493357248">"キャンセル"</string> - <string name="rear_display_bottom_sheet_confirm" msgid="1507591562761552899">"画面を切り替えましょう"</string> + <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> diff --git a/packages/SystemUI/res/values-ka/strings.xml b/packages/SystemUI/res/values-ka/strings.xml index 6e11261bfe1e..d85d7c1c26a7 100644 --- a/packages/SystemUI/res/values-ka/strings.xml +++ b/packages/SystemUI/res/values-ka/strings.xml @@ -308,8 +308,7 @@ <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth ჩაირთვება ხვალ დილით"</string> <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"აუდიოს გაზიარება"</string> <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"მიმდინარებოს აუდიოს გაზიარება"</string> - <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_accessibility (7604615019302091708) --> - <skip /> + <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"აუდიო გაზიარების პარამეტრების შეყვანა"</string> <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> @@ -436,6 +435,7 @@ <string name="zen_modes_dialog_done" msgid="6654130880256438950">"მზადაა"</string> <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"პარამეტრები"</string> <string name="zen_mode_on" msgid="9085304934016242591">"ჩართული"</string> + <string name="zen_mode_on_with_details" msgid="7416143430557895497">"ჩართულია • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string> <string name="zen_mode_off" msgid="1736604456618147306">"გამორთული"</string> <string name="zen_mode_set_up" msgid="7457957033034460064">"დაყენება"</string> <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"პარამეტრებში მართვა"</string> @@ -718,6 +718,8 @@ <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"კარგი სატელიტური კავშირი"</string> <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"ხელმისაწვდომია სატელიტური კავშირი"</string> <string name="satellite_connected_carrier_text" msgid="118524195198532589">"სატელიტური SOS"</string> + <!-- no translation found for satellite_emergency_only_carrier_text (828510231597991206) --> + <skip /> <string name="accessibility_managed_profile" msgid="4703836746209377356">"სამსახურის პროფილი"</string> <string name="tuner_warning_title" msgid="7721976098452135267">"ზოგისთვის გასართობია, მაგრამ არა ყველასთვის"</string> <string name="tuner_warning" msgid="1861736288458481650">"სისტემის UI ტუნერი გაძლევთ დამატებით გზებს Android-ის სამომხმარებლო ინტერფეისის პარამეტრების დაყენებისთვის. ეს ექსპერიმენტული მახასიათებლები შეიძლება შეიცვალოს, შეწყდეს ან გაქრეს მომავალ ვერსიებში. სიფრთხილით გააგრძელეთ."</string> diff --git a/packages/SystemUI/res/values-kk/strings.xml b/packages/SystemUI/res/values-kk/strings.xml index d111ff14a1dc..7a43b4b2395c 100644 --- a/packages/SystemUI/res/values-kk/strings.xml +++ b/packages/SystemUI/res/values-kk/strings.xml @@ -150,12 +150,12 @@ <string name="cast_to_other_device_stop_dialog_message_generic" msgid="4100272100480415076">"Қазір маңайдағы құрылғыға трансляциялап жатырсыз."</string> <string name="cast_to_other_device_stop_dialog_button" msgid="6420183747435521834">"Трансляцияны тоқтату"</string> <string name="close_dialog_button" msgid="4749497706540104133">"Жабу"</string> - <string name="issuerecord_title" msgid="286627115110121849">"Мәселені жазу құралы"</string> + <string name="issuerecord_title" msgid="286627115110121849">"Ақау жазу құралы"</string> <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"Мәселе жазбасы өңделіп жатыр"</string> <string name="issuerecord_channel_description" msgid="6142326363431474632">"Мәселе туралы дерек жинау сеансына арналған ағымдағы хабарландыру"</string> - <string name="issuerecord_ongoing_screen_only" msgid="6248206059935015722">"Мәселе жазылып жатыр"</string> + <string name="issuerecord_ongoing_screen_only" msgid="6248206059935015722">"Ақау жазылып жатыр"</string> <string name="issuerecord_share_label" msgid="3992657993619876199">"Бөлісу"</string> - <string name="issuerecord_save_title" msgid="4161043023696751591">"Мәселе жазбасы сақталды"</string> + <string name="issuerecord_save_title" msgid="4161043023696751591">"Ақау жазбасы сақталды."</string> <string name="issuerecord_save_text" msgid="1205985304551521495">"Көру үшін түртіңіз."</string> <string name="issuerecord_save_error" msgid="6913040083446722726">"Мәселе жазбасын сақтау кезінде қате шықты."</string> <string name="issuerecord_start_error" msgid="3402782952722871190">"Мәселені жазуды бастау кезінде қате шықты."</string> @@ -308,8 +308,7 @@ <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth ертең таңертең қосылады."</string> <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Аудионы бөлісу"</string> <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Аудио беріліп жатыр"</string> - <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_accessibility (7604615019302091708) --> - <skip /> + <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"аудио бөлісу параметрлерін енгізу"</string> <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> @@ -436,6 +435,7 @@ <string name="zen_modes_dialog_done" msgid="6654130880256438950">"Дайын"</string> <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Параметрлер"</string> <string name="zen_mode_on" msgid="9085304934016242591">"Қосулы"</string> + <string name="zen_mode_on_with_details" msgid="7416143430557895497">"Қосулы • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string> <string name="zen_mode_off" msgid="1736604456618147306">"Өшірулі"</string> <string name="zen_mode_set_up" msgid="7457957033034460064">"Реттеу"</string> <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"\"Параметрлер\" бөлімінде реттеу"</string> @@ -718,6 +718,8 @@ <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Жерсерік, байланыс жақсы."</string> <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Жерсерік, байланыс бар."</string> <string name="satellite_connected_carrier_text" msgid="118524195198532589">"Satellite SOS"</string> + <!-- no translation found for satellite_emergency_only_carrier_text (828510231597991206) --> + <skip /> <string name="accessibility_managed_profile" msgid="4703836746209377356">"Жұмыс профилі"</string> <string name="tuner_warning_title" msgid="7721976098452135267">"Кейбіреулерге қызық, бірақ барлығына емес"</string> <string name="tuner_warning" msgid="1861736288458481650">"Жүйелік пайдаланушылық интерфейс тюнері Android пайдаланушылық интерфейсін реттеудің қосымша жолдарын береді. Бұл эксперименттік мүмкіндіктер болашақ шығарылымдарда өзгеруі, бұзылуы немесе жоғалуы мүмкін. Сақтықпен жалғастырыңыз."</string> diff --git a/packages/SystemUI/res/values-km/strings.xml b/packages/SystemUI/res/values-km/strings.xml index c686855e0105..2319623a80ef 100644 --- a/packages/SystemUI/res/values-km/strings.xml +++ b/packages/SystemUI/res/values-km/strings.xml @@ -308,8 +308,7 @@ <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"ប៊្លូធូសនឹងបើកនៅព្រឹកស្អែក"</string> <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"ស្ដាប់សំឡេងរួមគ្នា"</string> <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"កំពុងស្ដាប់សំឡេងរួមគ្នា"</string> - <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_accessibility (7604615019302091708) --> - <skip /> + <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"បញ្ចូលការកំណត់ការស្ដាប់សំឡេងរួមគ្នា"</string> <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> @@ -436,6 +435,7 @@ <string name="zen_modes_dialog_done" msgid="6654130880256438950">"រួចរាល់"</string> <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"ការកំណត់"</string> <string name="zen_mode_on" msgid="9085304934016242591">"បើក"</string> + <string name="zen_mode_on_with_details" msgid="7416143430557895497">"បើក • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string> <string name="zen_mode_off" msgid="1736604456618147306">"បិទ"</string> <string name="zen_mode_set_up" msgid="7457957033034460064">"រៀបចំ"</string> <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"គ្រប់គ្រងនៅក្នុងការកំណត់"</string> @@ -718,6 +718,8 @@ <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"ផ្កាយរណប មានការតភ្ជាប់ល្អ"</string> <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"ផ្កាយរណប អាចតភ្ជាប់បាន"</string> <string name="satellite_connected_carrier_text" msgid="118524195198532589">"ការប្រកាសអាសន្នតាមផ្កាយរណប"</string> + <!-- no translation found for satellite_emergency_only_carrier_text (828510231597991206) --> + <skip /> <string name="accessibility_managed_profile" msgid="4703836746209377356">"កម្រងព័ត៌មានការងារ"</string> <string name="tuner_warning_title" msgid="7721976098452135267">"ល្អសម្រាប់អ្នកប្រើមួយចំនួន តែមិនសម្រាប់គ្រប់គ្នាទេ"</string> <string name="tuner_warning" msgid="1861736288458481650">"កម្មវិធីសម្រួល UI ប្រព័ន្ធផ្តល់ជូនអ្នកនូវមធ្យោបាយបន្ថែមទៀតដើម្បីកែសម្រួល និងប្តូរចំណុចប្រទាក់អ្នកប្រើ Android តាមបំណង។ លក្ខណៈពិសេសសាកល្បងនេះអាចនឹងផ្លាស់ប្តូរ បំបែក ឬបាត់បង់បន្ទាប់ពីការចេញផ្សាយនាពេលអនាគត។ សូមបន្តដោយប្រុងប្រយ័ត្ន។"</string> diff --git a/packages/SystemUI/res/values-kn/strings.xml b/packages/SystemUI/res/values-kn/strings.xml index 9e22f64bca1e..aeb3b1ef0078 100644 --- a/packages/SystemUI/res/values-kn/strings.xml +++ b/packages/SystemUI/res/values-kn/strings.xml @@ -308,8 +308,7 @@ <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"ಬ್ಲೂಟೂತ್ ನಾಳೆ ಬೆಳಗ್ಗೆ ಆನ್ ಆಗುತ್ತದೆ"</string> <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"ಆಡಿಯೋ ಹಂಚಿಕೊಳ್ಳಿ"</string> <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"ಆಡಿಯೋವನ್ನು ಹಂಚಿಕೊಳ್ಳಲಾಗುತ್ತಿದೆ"</string> - <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_accessibility (7604615019302091708) --> - <skip /> + <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"ಆಡಿಯೋ ಹಂಚಿಕೊಳ್ಳುವಿಕೆ ಸೆಟ್ಟಿಂಗ್ಗಳನ್ನು ನಮೂದಿಸಿ"</string> <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> @@ -436,6 +435,7 @@ <string name="zen_modes_dialog_done" msgid="6654130880256438950">"ಮುಗಿದಿದೆ"</string> <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"ಸೆಟ್ಟಿಂಗ್ಗಳು"</string> <string name="zen_mode_on" msgid="9085304934016242591">"ಆನ್ ಆಗಿದೆ"</string> + <string name="zen_mode_on_with_details" msgid="7416143430557895497">"<xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g> • ನಲ್ಲಿ"</string> <string name="zen_mode_off" msgid="1736604456618147306">"ಆಫ್ ಆಗಿದೆ"</string> <string name="zen_mode_set_up" msgid="7457957033034460064">"ಸೆಟಪ್ ಮಾಡಿ"</string> <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"ಸೆಟ್ಟಿಂಗ್ಗಳಲ್ಲಿ ನಿರ್ವಹಿಸಿ"</string> @@ -465,7 +465,7 @@ <string name="phone_hint" msgid="6682125338461375925">"ಫೋನ್ಗಾಗಿ ಐಕಾನ್ನಿಂದ ಸ್ವೈಪ್ ಮಾಡಿ"</string> <string name="voice_hint" msgid="7476017460191291417">"ಧ್ವನಿ ಸಹಾಯಕ್ಕಾಗಿ ಐಕಾನ್ನಿಂದ ಸ್ವೈಪ್ ಮಾಡಿ"</string> <string name="camera_hint" msgid="4519495795000658637">"ಕ್ಯಾಮರಾಗಾಗಿ ಐಕಾನ್ನಿಂದ ಸ್ವೈಪ್ ಮಾಡಿ"</string> - <string name="interruption_level_none_with_warning" msgid="8394434073508145437">"ಒಟ್ಟು ಮೌನ. ಇದು ಪರದೆ ರೀಡರ್ ಅನ್ನು ಮೌನವಾಗಿರಿಸುತ್ತದೆ."</string> + <string name="interruption_level_none_with_warning" msgid="8394434073508145437">"ಒಟ್ಟು ಮೌನ. ಇದು ಸ್ಕ್ರೀನ್ ರೀಡರ್ ಅನ್ನು ಮೌನವಾಗಿರಿಸುತ್ತದೆ."</string> <string name="interruption_level_none" msgid="219484038314193379">"ಸಂಪೂರ್ಣ ನಿಶ್ಯಬ್ಧ"</string> <string name="interruption_level_priority" msgid="661294280016622209">"ಆದ್ಯತೆ ಮಾತ್ರ"</string> <string name="interruption_level_alarms" msgid="2457850481335846959">"ಅಲಾರಮ್ಗಳು ಮಾತ್ರ"</string> @@ -718,6 +718,8 @@ <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"ಸ್ಯಾಟಲೈಟ್, ಕನೆಕ್ಷನ್ ಉತ್ತಮವಾಗಿದೆ"</string> <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"ಸ್ಯಾಟಲೈಟ್, ಕನೆಕ್ಷನ್ ಲಭ್ಯವಿದೆ"</string> <string name="satellite_connected_carrier_text" msgid="118524195198532589">"ಸ್ಯಾಟಲೈಟ್ SOS"</string> + <!-- no translation found for satellite_emergency_only_carrier_text (828510231597991206) --> + <skip /> <string name="accessibility_managed_profile" msgid="4703836746209377356">"ಕೆಲಸದ ಪ್ರೊಫೈಲ್"</string> <string name="tuner_warning_title" msgid="7721976098452135267">"ಕೆಲವರಿಗೆ ಮೋಜು ಆಗಿದೆ ಎಲ್ಲರಿಗೆ ಇಲ್ಲ"</string> <string name="tuner_warning" msgid="1861736288458481650">"ಸಿಸ್ಟಂ UI ಟ್ಯೂನರ್ ನಿಮಗೆ Android ಬಳಕೆದಾರ ಅಂತರಸಂಪರ್ಕವನ್ನು ಸರಿಪಡಿಸಲು ಮತ್ತು ಕಸ್ಟಮೈಸ್ ಮಾಡಲು ಹೆಚ್ಚುವರಿ ಮಾರ್ಗಗಳನ್ನು ನೀಡುತ್ತದೆ. ಈ ಪ್ರಾಯೋಗಿಕ ವೈಶಿಷ್ಟ್ಯಗಳು ಭವಿಷ್ಯದ ಬಿಡುಗಡೆಗಳಲ್ಲಿ ಬದಲಾಗಬಹುದು, ವಿರಾಮವಾಗಬಹುದು ಅಥವಾ ಕಾಣಿಸಿಕೊಳ್ಳದಿರಬಹುದು. ಎಚ್ಚರಿಕೆಯಿಂದ ಮುಂದುವರಿಯಿರಿ."</string> diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml index 70a994f392bb..277582c75200 100644 --- a/packages/SystemUI/res/values-ko/strings.xml +++ b/packages/SystemUI/res/values-ko/strings.xml @@ -308,8 +308,7 @@ <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"블루투스가 내일 아침에 켜집니다."</string> <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"오디오 공유"</string> <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"오디오 공유 중"</string> - <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_accessibility (7604615019302091708) --> - <skip /> + <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"오디오 공유 설정으로 이동"</string> <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> @@ -436,6 +435,7 @@ <string name="zen_modes_dialog_done" msgid="6654130880256438950">"완료"</string> <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"설정"</string> <string name="zen_mode_on" msgid="9085304934016242591">"사용"</string> + <string name="zen_mode_on_with_details" msgid="7416143430557895497">"켜짐 • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string> <string name="zen_mode_off" msgid="1736604456618147306">"사용 안함"</string> <string name="zen_mode_set_up" msgid="7457957033034460064">"설정"</string> <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"설정에서 관리"</string> @@ -718,6 +718,8 @@ <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"위성, 연결 상태 양호"</string> <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"위성, 연결 가능"</string> <string name="satellite_connected_carrier_text" msgid="118524195198532589">"위성 긴급 SOS"</string> + <!-- no translation found for satellite_emergency_only_carrier_text (828510231597991206) --> + <skip /> <string name="accessibility_managed_profile" msgid="4703836746209377356">"직장 프로필"</string> <string name="tuner_warning_title" msgid="7721976098452135267">"마음에 들지 않을 수도 있음"</string> <string name="tuner_warning" msgid="1861736288458481650">"시스템 UI 튜너를 사용하면 Android 사용자 인터페이스를 변경 및 맞춤설정할 수 있습니다. 이러한 실험실 기능은 향후 출시 버전에서는 변경되거나 다운되거나 사라질 수 있습니다. 신중하게 진행하시기 바랍니다."</string> diff --git a/packages/SystemUI/res/values-ky/strings.xml b/packages/SystemUI/res/values-ky/strings.xml index 0643033e796f..4680236c39fb 100644 --- a/packages/SystemUI/res/values-ky/strings.xml +++ b/packages/SystemUI/res/values-ky/strings.xml @@ -153,9 +153,9 @@ <string name="issuerecord_title" msgid="286627115110121849">"Маселе жаздыргыч"</string> <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"Маселе жаздырылууда"</string> <string name="issuerecord_channel_description" msgid="6142326363431474632">"Маселе тууралуу маалымат чогултулуп жатканы жөнүндө учурдагы билдирме"</string> - <string name="issuerecord_ongoing_screen_only" msgid="6248206059935015722">"Жаздыруу маселеси"</string> + <string name="issuerecord_ongoing_screen_only" msgid="6248206059935015722">"Маселе жазылууда"</string> <string name="issuerecord_share_label" msgid="3992657993619876199">"Бөлүшүү"</string> - <string name="issuerecord_save_title" msgid="4161043023696751591">"Жаздырылган маселе сакталды"</string> + <string name="issuerecord_save_title" msgid="4161043023696751591">"Маселе жаздырылды"</string> <string name="issuerecord_save_text" msgid="1205985304551521495">"Көрүү үчүн таптаңыз"</string> <string name="issuerecord_save_error" msgid="6913040083446722726">"Жаздырылган маселе сакталган жок"</string> <string name="issuerecord_start_error" msgid="3402782952722871190">"Башталбай койду"</string> @@ -308,8 +308,7 @@ <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth эртең таңда күйөт"</string> <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Чогуу угуу"</string> <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Чогуу угулууда"</string> - <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_accessibility (7604615019302091708) --> - <skip /> + <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"чогуу угуу параметрлерин киргизүү"</string> <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> @@ -436,6 +435,7 @@ <string name="zen_modes_dialog_done" msgid="6654130880256438950">"Бүттү"</string> <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Параметрлер"</string> <string name="zen_mode_on" msgid="9085304934016242591">"Күйүк"</string> + <string name="zen_mode_on_with_details" msgid="7416143430557895497">"Күйүк • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string> <string name="zen_mode_off" msgid="1736604456618147306">"Өчүк"</string> <string name="zen_mode_set_up" msgid="7457957033034460064">"Тууралоо"</string> <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"Параметрлерден тескөө"</string> @@ -718,6 +718,8 @@ <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Спутник, байланыш жакшы"</string> <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Спутник, байланыш бар"</string> <string name="satellite_connected_carrier_text" msgid="118524195198532589">"Спутник SOS"</string> + <!-- no translation found for satellite_emergency_only_carrier_text (828510231597991206) --> + <skip /> <string name="accessibility_managed_profile" msgid="4703836746209377356">"Жумуш профили"</string> <string name="tuner_warning_title" msgid="7721976098452135267">"Баарына эле жага бербейт"</string> <string name="tuner_warning" msgid="1861736288458481650">"System UI Tuner Android колдонуучу интерфейсин жөнгө салып жана ыңгайлаштыруунун кошумча ыкмаларын сунуштайт. Бул сынамык функциялар кийинки чыгарылыштарда өзгөрүлүп, бузулуп же жоголуп кетиши мүмкүн. Абайлап колдонуңуз."</string> diff --git a/packages/SystemUI/res/values-lo/strings.xml b/packages/SystemUI/res/values-lo/strings.xml index c9c136b61fa6..edb8fa338d7a 100644 --- a/packages/SystemUI/res/values-lo/strings.xml +++ b/packages/SystemUI/res/values-lo/strings.xml @@ -308,8 +308,7 @@ <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth ຈະເປີດມື້ອື່ນເຊົ້າ"</string> <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"ແບ່ງປັນສຽງ"</string> <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"ກຳລັງແບ່ງປັນສຽງ"</string> - <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_accessibility (7604615019302091708) --> - <skip /> + <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"ເຂົ້າສູ່ການຕັ້ງຄ່າການແບ່ງປັນສຽງ"</string> <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> @@ -436,6 +435,7 @@ <string name="zen_modes_dialog_done" msgid="6654130880256438950">"ແລ້ວໆ"</string> <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"ການຕັ້ງຄ່າ"</string> <string name="zen_mode_on" msgid="9085304934016242591">"ເປີດ"</string> + <string name="zen_mode_on_with_details" msgid="7416143430557895497">"ເປີດ • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string> <string name="zen_mode_off" msgid="1736604456618147306">"ປິດ"</string> <string name="zen_mode_set_up" msgid="7457957033034460064">"ຕັ້ງຄ່າ"</string> <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"ຈັດການໃນການຕັ້ງຄ່າ"</string> @@ -718,6 +718,8 @@ <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"ດາວທຽມ, ການເຊື່ອມຕໍ່ດີ"</string> <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"ດາວທຽມ, ການເຊື່ອມຕໍ່ທີ່ພ້ອມນຳໃຊ້"</string> <string name="satellite_connected_carrier_text" msgid="118524195198532589">"SOS ດາວທຽມ"</string> + <!-- no translation found for satellite_emergency_only_carrier_text (828510231597991206) --> + <skip /> <string name="accessibility_managed_profile" msgid="4703836746209377356">"ໂປຣໄຟລ໌ບ່ອນເຮັດວຽກ"</string> <string name="tuner_warning_title" msgid="7721976098452135267">"ມ່ວນຊື່ນສຳລັບບາງຄົນ ແຕ່ບໍ່ແມ່ນສຳລັບທຸກຄົນ"</string> <string name="tuner_warning" msgid="1861736288458481650">"System UI Tuner ໃຫ້ທ່ານມີວິທີພິເສດຕື່ມອີກໃນການປັບປ່ຽນ ແລະຕົບແຕ່ງສ່ວນຕໍ່ປະສານຜູ້ໃຊ້ຂອງ Android. ຄຸນສົມບັດທົດລອງໃຊ້ເຫຼົ່ານີ້ອາດຈະປ່ຽນແປງ, ຢຸດເຊົາ ຫຼືຫາຍໄປໃນການວາງຈຳໜ່າຍໃນອະນາຄົດ. ຈົ່ງດຳເນີນຕໍ່ດ້ວຍຄວາມລະມັດລະວັງ."</string> diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml index fdffbfc6cd1e..a9a0e73c4322 100644 --- a/packages/SystemUI/res/values-lt/strings.xml +++ b/packages/SystemUI/res/values-lt/strings.xml @@ -308,8 +308,7 @@ <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="7545274861795853838">"Bendrinti garsą"</string> <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Bendrinamas garsas"</string> - <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_accessibility (7604615019302091708) --> - <skip /> + <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"įvesti garso įrašų bendrinimo nustatymus"</string> <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> @@ -436,6 +435,7 @@ <string name="zen_modes_dialog_done" msgid="6654130880256438950">"Atlikta"</string> <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Nustatymai"</string> <string name="zen_mode_on" msgid="9085304934016242591">"Įjungta"</string> + <string name="zen_mode_on_with_details" msgid="7416143430557895497">"Įjungta • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string> <string name="zen_mode_off" msgid="1736604456618147306">"Išjungta"</string> <string name="zen_mode_set_up" msgid="7457957033034460064">"Nustatyti"</string> <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"Tvarkyti skiltyje „Nustatymai“"</string> @@ -718,6 +718,8 @@ <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Palydovas, geras ryšys"</string> <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Palydovas, pasiekiamas ryšys"</string> <string name="satellite_connected_carrier_text" msgid="118524195198532589">"Prisijungimas prie palydovo kritiniu atveju"</string> + <!-- no translation found for satellite_emergency_only_carrier_text (828510231597991206) --> + <skip /> <string name="accessibility_managed_profile" msgid="4703836746209377356">"Darbo profilis"</string> <string name="tuner_warning_title" msgid="7721976098452135267">"Smagu, bet ne visada"</string> <string name="tuner_warning" msgid="1861736288458481650">"Sistemos naudotojo sąsajos derinimo priemonė suteikia papildomų galimybių pagerinti ir tinkinti „Android“ naudotojo sąsają. Šios eksperimentinės funkcijos gali pasikeisti, nutrūkti ar išnykti iš būsimų laidų. Tęskite atsargiai."</string> diff --git a/packages/SystemUI/res/values-lv/strings.xml b/packages/SystemUI/res/values-lv/strings.xml index dfef9ccabcd5..d1e97a65e159 100644 --- a/packages/SystemUI/res/values-lv/strings.xml +++ b/packages/SystemUI/res/values-lv/strings.xml @@ -308,8 +308,7 @@ <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="7545274861795853838">"Kopīgot audio"</string> <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Notiek audio kopīgošana…"</string> - <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_accessibility (7604615019302091708) --> - <skip /> + <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"atvērt audio kopīgošanas iestatījumus"</string> <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> @@ -436,6 +435,7 @@ <string name="zen_modes_dialog_done" msgid="6654130880256438950">"Gatavs"</string> <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Iestatījumi"</string> <string name="zen_mode_on" msgid="9085304934016242591">"Ieslēgts"</string> + <string name="zen_mode_on_with_details" msgid="7416143430557895497">"Ieslēgts • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string> <string name="zen_mode_off" msgid="1736604456618147306">"Izslēgts"</string> <string name="zen_mode_set_up" msgid="7457957033034460064">"Iestatīt"</string> <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"Pārvaldīt iestatījumos"</string> @@ -718,6 +718,8 @@ <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Satelīts, labs savienojums"</string> <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Satelīts, ir pieejams savienojums"</string> <string name="satellite_connected_carrier_text" msgid="118524195198532589">"Satelīta SOS"</string> + <!-- no translation found for satellite_emergency_only_carrier_text (828510231597991206) --> + <skip /> <string name="accessibility_managed_profile" msgid="4703836746209377356">"Darba profils"</string> <string name="tuner_warning_title" msgid="7721976098452135267">"Jautri dažiem, bet ne visiem"</string> <string name="tuner_warning" msgid="1861736288458481650">"Sistēmas saskarnes regulators sniedz papildu veidus, kā mainīt un pielāgot Android lietotāja saskarni. Nākamajās versijās šīs eksperimentālās funkcijas var tikt mainītas, bojātas vai to darbība var tikt pārtraukta. Turpinot esiet uzmanīgs."</string> diff --git a/packages/SystemUI/res/values-mk/strings.xml b/packages/SystemUI/res/values-mk/strings.xml index e90ec8fe6a04..7691ae6181a2 100644 --- a/packages/SystemUI/res/values-mk/strings.xml +++ b/packages/SystemUI/res/values-mk/strings.xml @@ -308,8 +308,7 @@ <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth ќе се вклучи утре наутро"</string> <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Споделувај аудио"</string> <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Се споделува аудио"</string> - <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_accessibility (7604615019302091708) --> - <skip /> + <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"внесете ги поставките за „Споделување аудио“"</string> <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> @@ -436,6 +435,7 @@ <string name="zen_modes_dialog_done" msgid="6654130880256438950">"Готово"</string> <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Поставки"</string> <string name="zen_mode_on" msgid="9085304934016242591">"Вклучено"</string> + <string name="zen_mode_on_with_details" msgid="7416143430557895497">"Вклучено: <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string> <string name="zen_mode_off" msgid="1736604456618147306">"Исклучено"</string> <string name="zen_mode_set_up" msgid="7457957033034460064">"Поставете"</string> <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"Управувајте во поставките"</string> @@ -718,6 +718,8 @@ <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Добра сателитска врска"</string> <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Достапна е сателитска врска"</string> <string name="satellite_connected_carrier_text" msgid="118524195198532589">"Сателитски SOS"</string> + <!-- no translation found for satellite_emergency_only_carrier_text (828510231597991206) --> + <skip /> <string name="accessibility_managed_profile" msgid="4703836746209377356">"Работен профил"</string> <string name="tuner_warning_title" msgid="7721976098452135267">"Забава за некои, но не за сите"</string> <string name="tuner_warning" msgid="1861736288458481650">"Адаптерот на УИ на системот ви дава дополнителни начини за дотерување и приспособување на корисничкиот интерфејс на Android. Овие експериментални функции можеби ќе се изменат, расипат или ќе исчезнат во следните изданија. Продолжете со претпазливост."</string> diff --git a/packages/SystemUI/res/values-ml/strings.xml b/packages/SystemUI/res/values-ml/strings.xml index bb8a91edfb6f..8c0378a6bd6b 100644 --- a/packages/SystemUI/res/values-ml/strings.xml +++ b/packages/SystemUI/res/values-ml/strings.xml @@ -308,8 +308,7 @@ <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth നാളെ രാവിലെ ഓണാകും"</string> <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"ഓഡിയോ പങ്കിടുക"</string> <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"ഓഡിയോ പങ്കിടുന്നു"</string> - <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_accessibility (7604615019302091708) --> - <skip /> + <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"ഓഡിയോ പങ്കിടൽ ക്രമീകരണം നൽകുക"</string> <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> @@ -436,6 +435,7 @@ <string name="zen_modes_dialog_done" msgid="6654130880256438950">"ശരി"</string> <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"ക്രമീകരണം"</string> <string name="zen_mode_on" msgid="9085304934016242591">"ഓണാണ്"</string> + <string name="zen_mode_on_with_details" msgid="7416143430557895497">"ഓണാണ് • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string> <string name="zen_mode_off" msgid="1736604456618147306">"ഓഫാണ്"</string> <string name="zen_mode_set_up" msgid="7457957033034460064">"സജ്ജീകരിക്കുക"</string> <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"ക്രമീകരണത്തിൽ മാനേജ് ചെയ്യുക"</string> @@ -718,6 +718,8 @@ <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"സാറ്റലൈറ്റ്, മികച്ച കണക്ഷൻ"</string> <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"സാറ്റലൈറ്റ്, കണക്ഷൻ ലഭ്യമാണ്"</string> <string name="satellite_connected_carrier_text" msgid="118524195198532589">"സാറ്റലൈറ്റ് SOS"</string> + <!-- no translation found for satellite_emergency_only_carrier_text (828510231597991206) --> + <skip /> <string name="accessibility_managed_profile" msgid="4703836746209377356">"ഔദ്യോഗിക പ്രൊഫൈൽ"</string> <string name="tuner_warning_title" msgid="7721976098452135267">"ചിലർക്ക് വിനോദം, എന്നാൽ എല്ലാവർക്കുമില്ല"</string> <string name="tuner_warning" msgid="1861736288458481650">"Android ഉപയോക്തൃ ഇന്റർഫേസ് ആവശ്യമുള്ള രീതിയിൽ മാറ്റുന്നതിനും ഇഷ്ടാനുസൃതമാക്കുന്നതിനും സിസ്റ്റം UI ട്യൂണർ നിങ്ങൾക്ക് അധിക വഴികൾ നൽകുന്നു. ഭാവി റിലീസുകളിൽ ഈ പരീക്ഷണാത്മക ഫീച്ചറുകൾ മാറ്റുകയോ നിർത്തുകയോ അപ്രത്യക്ഷമാവുകയോ ചെയ്തേക്കാം. ശ്രദ്ധയോടെ മുന്നോട്ടുപോകുക."</string> diff --git a/packages/SystemUI/res/values-mn/strings.xml b/packages/SystemUI/res/values-mn/strings.xml index 526e39bd1b7b..bfd48a45a629 100644 --- a/packages/SystemUI/res/values-mn/strings.xml +++ b/packages/SystemUI/res/values-mn/strings.xml @@ -308,8 +308,7 @@ <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth-г маргааш өглөө асаана"</string> <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Аудио хуваалцах"</string> <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Аудио хуваалцаж байна"</string> - <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_accessibility (7604615019302091708) --> - <skip /> + <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"аудио хуваалцах тохиргоог оруулах"</string> <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> @@ -436,6 +435,7 @@ <string name="zen_modes_dialog_done" msgid="6654130880256438950">"Болсон"</string> <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Тохиргоо"</string> <string name="zen_mode_on" msgid="9085304934016242591">"Асаалттай"</string> + <string name="zen_mode_on_with_details" msgid="7416143430557895497">"Асаасан • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string> <string name="zen_mode_off" msgid="1736604456618147306">"Унтраалттай"</string> <string name="zen_mode_set_up" msgid="7457957033034460064">"Тохируулах"</string> <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"Тохиргоонд удирдах"</string> @@ -718,6 +718,8 @@ <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Хиймэл дагуул, холболт сайн байна"</string> <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Хиймэл дагуул, холболт боломжтой"</string> <string name="satellite_connected_carrier_text" msgid="118524195198532589">"Хиймэл дагуул SOS"</string> + <!-- no translation found for satellite_emergency_only_carrier_text (828510231597991206) --> + <skip /> <string name="accessibility_managed_profile" msgid="4703836746209377356">"Ажлын профайл"</string> <string name="tuner_warning_title" msgid="7721976098452135267">"Зарим хүнд хөгжилтэй байж болох ч бүх хүнд тийм биш"</string> <string name="tuner_warning" msgid="1861736288458481650">"Системийн UI Tохируулагч нь Android хэрэглэгчийн интерфэйсийг тааруулах, өөрчлөх нэмэлт аргыг зааж өгөх болно. Эдгээр туршилтын тохиргоо нь цаашид өөрчлөгдөх, эвдрэх, алга болох магадлалтай. Үйлдлийг болгоомжтой хийнэ үү."</string> diff --git a/packages/SystemUI/res/values-mr/strings.xml b/packages/SystemUI/res/values-mr/strings.xml index 8cf4855483f8..d124ce366bb9 100644 --- a/packages/SystemUI/res/values-mr/strings.xml +++ b/packages/SystemUI/res/values-mr/strings.xml @@ -308,8 +308,7 @@ <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"ब्लूटूथ उद्या सकाळी सुरू होईल"</string> <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"ऑडिओ शेअर करा"</string> <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"ऑडिओ शेअर करत आहे"</string> - <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_accessibility (7604615019302091708) --> - <skip /> + <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"ऑडिओ शेअरिंग सेटिंग्ज एंटर करा"</string> <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> @@ -392,7 +391,7 @@ <string name="user_interface" msgid="3712869377953950887">"यूझर इंटरफेस"</string> <string name="thermal" msgid="6758074791325414831">"थर्मल"</string> <string name="custom" msgid="3337456985275158299">"कस्टम"</string> - <string name="custom_trace_settings_dialog_title" msgid="2608570500144830554">"मागाच्या कस्टम सेटिंग्ज"</string> + <string name="custom_trace_settings_dialog_title" msgid="2608570500144830554">"ट्रेससाठी कस्टम सेटिंग्ज"</string> <string name="restore_default" msgid="5259420807486239755">"डीफॉल्ट रिस्टोअर करा"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"एकहाती मोड"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"श्रवणयंत्रे"</string> @@ -436,6 +435,7 @@ <string name="zen_modes_dialog_done" msgid="6654130880256438950">"पूर्ण झाले"</string> <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"सेटिंग्ज"</string> <string name="zen_mode_on" msgid="9085304934016242591">"सुरू आहे"</string> + <string name="zen_mode_on_with_details" msgid="7416143430557895497">"सुरू • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string> <string name="zen_mode_off" msgid="1736604456618147306">"बंद आहे"</string> <string name="zen_mode_set_up" msgid="7457957033034460064">"सेट करा"</string> <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"सेटिंग्जमध्ये व्यवस्थापित करा"</string> @@ -718,6 +718,8 @@ <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"सॅटेलाइट, चांगले कनेक्शन"</string> <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"सॅटेलाइट, कनेक्शन उपलब्ध"</string> <string name="satellite_connected_carrier_text" msgid="118524195198532589">"सॅटेलाइट SOS"</string> + <!-- no translation found for satellite_emergency_only_carrier_text (828510231597991206) --> + <skip /> <string name="accessibility_managed_profile" msgid="4703836746209377356">"कार्य प्रोफाईल"</string> <string name="tuner_warning_title" msgid="7721976098452135267">"सर्वांसाठी नाही तर काहींसाठी मजेदार असू शकते"</string> <string name="tuner_warning" msgid="1861736288458481650">"सिस्टम UI ट्युनर आपल्याला Android यूझर इंटरफेस ट्विक आणि कस्टमाइझ करण्याचे अनेक प्रकार देते. ही प्रयोगात्मक वैशिष्ट्ये बदलू शकतात, खंडित होऊ शकतात किंवा भविष्यातील रिलीज मध्ये कदाचित दिसणार नाहीत. सावधगिरी बाळगून पुढे सुरू ठेवा."</string> diff --git a/packages/SystemUI/res/values-ms/strings.xml b/packages/SystemUI/res/values-ms/strings.xml index 9b6781a27107..8385682e6939 100644 --- a/packages/SystemUI/res/values-ms/strings.xml +++ b/packages/SystemUI/res/values-ms/strings.xml @@ -308,8 +308,7 @@ <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="7545274861795853838">"Kongsi audio"</string> <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Perkongsian audio"</string> - <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_accessibility (7604615019302091708) --> - <skip /> + <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"masukkan tetapan perkongsian audio"</string> <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> @@ -381,7 +380,7 @@ <string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Rakam skrin"</string> <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Mula"</string> <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Berhenti"</string> - <string name="qs_record_issue_label" msgid="8166290137285529059">"Rekodkan Masalah"</string> + <string name="qs_record_issue_label" msgid="8166290137285529059">"Rakam Masalah"</string> <string name="qs_record_issue_start" msgid="2979831312582567056">"Mula"</string> <string name="qs_record_issue_stop" msgid="3531747965741982657">"Hentikan"</string> <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Laporan Pepijat"</string> @@ -436,6 +435,7 @@ <string name="zen_modes_dialog_done" msgid="6654130880256438950">"Selesai"</string> <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Tetapan"</string> <string name="zen_mode_on" msgid="9085304934016242591">"Hidup"</string> + <string name="zen_mode_on_with_details" msgid="7416143430557895497">"Pada • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string> <string name="zen_mode_off" msgid="1736604456618147306">"Mati"</string> <string name="zen_mode_set_up" msgid="7457957033034460064">"Sediakan"</string> <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"Urus dalam tetapan"</string> @@ -718,6 +718,8 @@ <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Satelit, sambungan yang baik"</string> <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Satelit, sambungan tersedia"</string> <string name="satellite_connected_carrier_text" msgid="118524195198532589">"SOS via Satelit"</string> + <!-- no translation found for satellite_emergency_only_carrier_text (828510231597991206) --> + <skip /> <string name="accessibility_managed_profile" msgid="4703836746209377356">"Profil kerja"</string> <string name="tuner_warning_title" msgid="7721976098452135267">"Menarik untuk sesetengah orang tetapi bukan untuk semua"</string> <string name="tuner_warning" msgid="1861736288458481650">"Penala UI Sistem memberi anda cara tambahan untuk mengolah dan menyesuaikan antara muka Android. Ciri eksperimen ini boleh berubah, rosak atau hilang dalam keluaran masa hadapan. Teruskan dengan berhati-hati."</string> diff --git a/packages/SystemUI/res/values-my/strings.xml b/packages/SystemUI/res/values-my/strings.xml index e9f07b93a3e7..60c74c1ca148 100644 --- a/packages/SystemUI/res/values-my/strings.xml +++ b/packages/SystemUI/res/values-my/strings.xml @@ -308,8 +308,7 @@ <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"မနက်ဖြန်နံနက်တွင် ဘလူးတုသ် ပွင့်ပါမည်"</string> <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"အသံမျှဝေရန်"</string> <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"အသံမျှဝေနေသည်"</string> - <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_accessibility (7604615019302091708) --> - <skip /> + <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"အော်ဒီယို မျှဝေခြင်း ဆက်တင်များ ထည့်ရန်"</string> <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> @@ -436,6 +435,7 @@ <string name="zen_modes_dialog_done" msgid="6654130880256438950">"ပြီးပြီ"</string> <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"ဆက်တင်များ"</string> <string name="zen_mode_on" msgid="9085304934016242591">"ဖွင့်"</string> + <string name="zen_mode_on_with_details" msgid="7416143430557895497">"ဖွင့် • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string> <string name="zen_mode_off" msgid="1736604456618147306">"ပိတ်"</string> <string name="zen_mode_set_up" msgid="7457957033034460064">"စနစ်ထည့်သွင်းရန်"</string> <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"ဆက်တင်များတွင် စီမံရန်"</string> @@ -718,6 +718,8 @@ <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"ဂြိုဟ်တု၊ ချိတ်ဆက်မှု ကောင်းသည်"</string> <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"ဂြိုဟ်တု၊ ချိတ်ဆက်မှု ရနိုင်သည်"</string> <string name="satellite_connected_carrier_text" msgid="118524195198532589">"Satellite SOS"</string> + <!-- no translation found for satellite_emergency_only_carrier_text (828510231597991206) --> + <skip /> <string name="accessibility_managed_profile" msgid="4703836746209377356">"အလုပ် ပရိုဖိုင်"</string> <string name="tuner_warning_title" msgid="7721976098452135267">"အချို့သူများ အတွက် ပျော်စရာ ဖြစ်ပေမဲ့ အားလုံး အတွက် မဟုတ်ပါ"</string> <string name="tuner_warning" msgid="1861736288458481650">"စနစ် UI Tuner က သင့်အတွက် Android အသုံးပြုသူ အင်တာဖေ့စ်ကို ပြောင်းရန်နှင့် စိတ်ကြိုက်ပြုလုပ်ရန် နည်းလမ်း အပိုများကို သင့်အတွက် စီစဉ်ပေးသည်။ အနာဂတ်ဗားရှင်းများတွင် ဤစမ်းသပ်အင်္ဂါရပ်များမှာ ပြောင်းလဲ၊ ပျက်စီး သို့မဟုတ် ပျောက်ကွယ်သွားနိုင်သည်။ သတိဖြင့် ရှေ့ဆက်ပါ။"</string> @@ -1391,7 +1393,7 @@ <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"ပြန်သွားရန်"</string> <string name="touchpad_back_gesture_guidance" msgid="6263750214998421587">"နောက်ပြန်သွားရန် တာ့ချ်ပက်ပေါ်ရှိ မည်သည့်နေရာ၌မဆို လက်သုံးချောင်းဖြင့် ဘယ် (သို့) ညာသို့ ပွတ်ဆွဲပါ။\n\n၎င်းအတွက် လက်ကွက်ဖြတ်လမ်း Action + ESC ကိုလည်း သုံးနိုင်သည်။"</string> <string name="touchpad_back_gesture_success_title" msgid="7240576648330612171">"တော်ပါပေသည်။"</string> - <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"နောက်ဆုတ်လက်ဟန် အပြီးသတ်လိုက်ပါပြီ။"</string> + <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"နောက်သို့လက်ဟန် အပြီးသတ်လိုက်ပါပြီ"</string> <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"ပင်မစာမျက်နှာသို့ သွားရန်"</string> <string name="touchpad_home_gesture_guidance" msgid="3043931356096731966">"ပင်မစာမျက်နှာသို့ အချိန်မရွေးသွားရန် စခရင်အောက်ခြေမှ အပေါ်သို့ လက်သုံးချောင်းဖြင့် ပွတ်ဆွဲပါ။"</string> <string name="touchpad_home_gesture_success_title" msgid="3778407003948209795">"ကောင်းသည်။"</string> diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml index bc172de858b1..72ccd2c4137f 100644 --- a/packages/SystemUI/res/values-nb/strings.xml +++ b/packages/SystemUI/res/values-nb/strings.xml @@ -308,8 +308,7 @@ <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="7545274861795853838">"Del lyd"</string> <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Deler lyd"</string> - <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_accessibility (7604615019302091708) --> - <skip /> + <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"åpne innstillingene for lyddeling"</string> <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> @@ -436,6 +435,7 @@ <string name="zen_modes_dialog_done" msgid="6654130880256438950">"Ferdig"</string> <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Innstillinger"</string> <string name="zen_mode_on" msgid="9085304934016242591">"På"</string> + <string name="zen_mode_on_with_details" msgid="7416143430557895497">"På • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string> <string name="zen_mode_off" msgid="1736604456618147306">"Av"</string> <string name="zen_mode_set_up" msgid="7457957033034460064">"Konfigurer"</string> <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"Administrer i innstillingene"</string> @@ -718,6 +718,8 @@ <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Satellitt – god tilkobling"</string> <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Satellitt – tilkobling tilgjengelig"</string> <string name="satellite_connected_carrier_text" msgid="118524195198532589">"SOS-alarm via satellitt"</string> + <!-- no translation found for satellite_emergency_only_carrier_text (828510231597991206) --> + <skip /> <string name="accessibility_managed_profile" msgid="4703836746209377356">"Work-profil"</string> <string name="tuner_warning_title" msgid="7721976098452135267">"Gøy for noen – ikke for alle"</string> <string name="tuner_warning" msgid="1861736288458481650">"Med System UI Tuner har du flere måter å justere og tilpasse Android-brukergrensesnittet på. Disse eksperimentelle funksjonene kan endres, avbrytes eller fjernes i fremtidige utgivelser. Fortsett med forbehold."</string> diff --git a/packages/SystemUI/res/values-ne/strings.xml b/packages/SystemUI/res/values-ne/strings.xml index 49bf32ff7424..d6806456e54a 100644 --- a/packages/SystemUI/res/values-ne/strings.xml +++ b/packages/SystemUI/res/values-ne/strings.xml @@ -308,8 +308,7 @@ <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"ब्लुटुथ भोलि बिहान अन हुने छ"</string> <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"अडियो सेयर गर्नुहोस्"</string> <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"अडियो सेयर गरिँदै छ"</string> - <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_accessibility (7604615019302091708) --> - <skip /> + <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"अडियो सेयर गर्ने सुविधासम्बन्धी सेटिङ हाल्न"</string> <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> @@ -436,6 +435,7 @@ <string name="zen_modes_dialog_done" msgid="6654130880256438950">"सम्पन्न भयो"</string> <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"सेटिङ"</string> <string name="zen_mode_on" msgid="9085304934016242591">"अन छ"</string> + <string name="zen_mode_on_with_details" msgid="7416143430557895497">"अन छ • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string> <string name="zen_mode_off" msgid="1736604456618147306">"अफ छ"</string> <string name="zen_mode_set_up" msgid="7457957033034460064">"सेटअप गर्नुहोस्"</string> <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"सेटिङमा गई व्यवस्थापन गर्नुहोस्"</string> @@ -718,6 +718,8 @@ <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"स्याटलाइट, राम्रो कनेक्सन"</string> <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"स्याटलाइट, कनेक्सन उपलब्ध छ"</string> <string name="satellite_connected_carrier_text" msgid="118524195198532589">"स्याटलाइट SOS"</string> + <!-- no translation found for satellite_emergency_only_carrier_text (828510231597991206) --> + <skip /> <string name="accessibility_managed_profile" msgid="4703836746209377356">"कार्य प्रोफाइल"</string> <string name="tuner_warning_title" msgid="7721976098452135267">"केहीका लागि रमाइलो हुन्छ तर सबैका लागि होइन"</string> <string name="tuner_warning" msgid="1861736288458481650">"सिस्टम UI ट्युनरले तपाईँलाई Android प्रयोगकर्ता इन्टरफेस कस्टम गर्न र ट्विक गर्न थप तरिकाहरू प्रदान गर्छ। यी प्रयोगात्मक सुविधाहरू भावी विमोचनमा परिवर्तन हुन, बिग्रिन वा हराउन सक्ने छन्। सावधानीपूर्वक अगाडि बढ्नुहोस्।"</string> diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml index 36f88a04dbfe..6a675b8890c2 100644 --- a/packages/SystemUI/res/values-nl/strings.xml +++ b/packages/SystemUI/res/values-nl/strings.xml @@ -308,8 +308,7 @@ <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth wordt morgenochtend aangezet"</string> <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Audio delen"</string> <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Audio delen"</string> - <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_accessibility (7604615019302091708) --> - <skip /> + <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"instellingen voor audio delen openen"</string> <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> @@ -381,7 +380,7 @@ <string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Schermopname"</string> <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Starten"</string> <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Stoppen"</string> - <string name="qs_record_issue_label" msgid="8166290137285529059">"Probleem vastleggen"</string> + <string name="qs_record_issue_label" msgid="8166290137285529059">"Probleem opnemen"</string> <string name="qs_record_issue_start" msgid="2979831312582567056">"Starten"</string> <string name="qs_record_issue_stop" msgid="3531747965741982657">"Stoppen"</string> <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Bugrapport"</string> @@ -436,6 +435,7 @@ <string name="zen_modes_dialog_done" msgid="6654130880256438950">"Klaar"</string> <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Instellingen"</string> <string name="zen_mode_on" msgid="9085304934016242591">"Aan"</string> + <string name="zen_mode_on_with_details" msgid="7416143430557895497">"Aan • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string> <string name="zen_mode_off" msgid="1736604456618147306">"Uit"</string> <string name="zen_mode_set_up" msgid="7457957033034460064">"Instellen"</string> <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"Beheren via instellingen"</string> @@ -718,6 +718,8 @@ <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Satelliet, goede verbinding"</string> <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Satelliet, verbinding beschikbaar"</string> <string name="satellite_connected_carrier_text" msgid="118524195198532589">"SOS via satelliet"</string> + <!-- no translation found for satellite_emergency_only_carrier_text (828510231597991206) --> + <skip /> <string name="accessibility_managed_profile" msgid="4703836746209377356">"Werkprofiel"</string> <string name="tuner_warning_title" msgid="7721976098452135267">"Leuk voor sommige gebruikers, maar niet voor iedereen"</string> <string name="tuner_warning" msgid="1861736288458481650">"Met Systeem-UI-tuner beschikt u over extra manieren om de Android-gebruikersinterface aan te passen. Deze experimentele functies kunnen veranderen, vastlopen of verdwijnen in toekomstige releases. Ga voorzichtig verder."</string> diff --git a/packages/SystemUI/res/values-or/strings.xml b/packages/SystemUI/res/values-or/strings.xml index 44eeb43bfce5..6e6364302546 100644 --- a/packages/SystemUI/res/values-or/strings.xml +++ b/packages/SystemUI/res/values-or/strings.xml @@ -156,7 +156,7 @@ <string name="issuerecord_ongoing_screen_only" msgid="6248206059935015722">"ରେକର୍ଡିଂରେ ସମସ୍ୟା"</string> <string name="issuerecord_share_label" msgid="3992657993619876199">"ସେୟାର କରନ୍ତୁ"</string> <string name="issuerecord_save_title" msgid="4161043023696751591">"ସମସ୍ୟାର ରେକର୍ଡିଂକୁ ସେଭ କରାଯାଇଛି"</string> - <string name="issuerecord_save_text" msgid="1205985304551521495">"ଦେଖିବାକୁ ଟାପ୍ କରନ୍ତୁ"</string> + <string name="issuerecord_save_text" msgid="1205985304551521495">"ଭ୍ୟୁ କରିବାକୁ ଟାପ କରନ୍ତୁ"</string> <string name="issuerecord_save_error" msgid="6913040083446722726">"ସମସ୍ୟାର ରେକର୍ଡିଂ କରିବାରେ ତ୍ରୁଟି"</string> <string name="issuerecord_start_error" msgid="3402782952722871190">"ସମସ୍ୟାର ରେକର୍ଡିଂ ଆରମ୍ଭ କରିବାରେ ତ୍ରୁଟି"</string> <string name="immersive_cling_title" msgid="8372056499315585941">"ପୂର୍ଣ୍ଣ ସ୍କ୍ରିନରେ ଦେଖିବା"</string> @@ -308,8 +308,7 @@ <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"ବ୍ଲୁଟୁଥ ଆସନ୍ତା କାଲି ସକାଳେ ଚାଲୁ ହେବ"</string> <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"ଅଡିଓ ସେୟାର କରନ୍ତୁ"</string> <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"ଅଡିଓ ସେୟାର କରାଯାଉଛି"</string> - <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_accessibility (7604615019302091708) --> - <skip /> + <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"ଅଡିଓ ସେୟାରିଂ ସେଟିଂସରେ ପ୍ରବେଶ କରନ୍ତୁ"</string> <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> @@ -384,7 +383,7 @@ <string name="qs_record_issue_label" msgid="8166290137285529059">"ସମସ୍ୟାର ରେକର୍ଡ କରନ୍ତୁ"</string> <string name="qs_record_issue_start" msgid="2979831312582567056">"ଆରମ୍ଭ କରନ୍ତୁ"</string> <string name="qs_record_issue_stop" msgid="3531747965741982657">"ବନ୍ଦ କରନ୍ତୁ"</string> - <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"ବଗ୍ ରିପୋର୍ଟ୍"</string> + <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"ବଗ ରିପୋର୍ଟ"</string> <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> @@ -436,6 +435,7 @@ <string name="zen_modes_dialog_done" msgid="6654130880256438950">"ହୋଇଗଲା"</string> <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"ସେଟିଂସ"</string> <string name="zen_mode_on" msgid="9085304934016242591">"ଚାଲୁ ଅଛି"</string> + <string name="zen_mode_on_with_details" msgid="7416143430557895497">"ଚାଲୁ ଅଛି • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string> <string name="zen_mode_off" msgid="1736604456618147306">"ବନ୍ଦ ଅଛି"</string> <string name="zen_mode_set_up" msgid="7457957033034460064">"ସେଟ ଅପ କରନ୍ତୁ"</string> <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"ସେଟିଂସରେ ପରିଚାଳନା କରନ୍ତୁ"</string> @@ -718,6 +718,8 @@ <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"ସାଟେଲାଇଟ, ଭଲ କନେକ୍ସନ"</string> <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"ସାଟେଲାଇଟ, କନେକ୍ସନ ଉପଲବ୍ଧ"</string> <string name="satellite_connected_carrier_text" msgid="118524195198532589">"ସେଟେଲାଇଟ SOS"</string> + <!-- no translation found for satellite_emergency_only_carrier_text (828510231597991206) --> + <skip /> <string name="accessibility_managed_profile" msgid="4703836746209377356">"ୱର୍କ ପ୍ରୋଫାଇଲ୍"</string> <string name="tuner_warning_title" msgid="7721976098452135267">"କେତେକଙ୍କ ପାଇଁ ମଜାଦାର, କିନ୍ତୁ ସମସ୍ତଙ୍କ ପାଇଁ ନୁହେଁ"</string> <string name="tuner_warning" msgid="1861736288458481650">"Android ୟୁଜର୍ ଇଣ୍ଟରଫେସ୍ ବଦଳାଇବାକୁ ତଥା ନିଜ ପସନ୍ଦ ଅନୁଯାୟୀ କରିବାକୁ ସିଷ୍ଟମ୍ UI ଟ୍ୟୁନର୍ ଆପଣଙ୍କୁ ଅତିରିକ୍ତ ଉପାୟ ପ୍ରଦାନ କରେ। ଏହି ପରୀକ୍ଷାମୂଳକ ସୁବିଧାମାନ ବଦଳିପାରେ, ଭାଙ୍ଗିପାରେ କିମ୍ବା ଭବିଷ୍ୟତର ରିଲିଜ୍ଗୁଡ଼ିକରେ ନଦେଖାଯାଇପାରେ। ସତର୍କତାର ସହ ଆଗକୁ ବଢ଼ନ୍ତୁ।"</string> diff --git a/packages/SystemUI/res/values-pa/strings.xml b/packages/SystemUI/res/values-pa/strings.xml index caa98ead2a09..cd7790fd4be1 100644 --- a/packages/SystemUI/res/values-pa/strings.xml +++ b/packages/SystemUI/res/values-pa/strings.xml @@ -308,8 +308,7 @@ <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"ਬਲੂਟੁੱਥ ਕੱਲ੍ਹ ਸਵੇਰੇ ਚਾਲੂ ਹੋ ਜਾਵੇਗਾ"</string> <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"ਆਡੀਓ ਨੂੰ ਸਾਂਝਾ ਕਰੋ"</string> <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"ਆਡੀਓ ਨੂੰ ਸਾਂਝਾ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ"</string> - <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_accessibility (7604615019302091708) --> - <skip /> + <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"ਆਡੀਓ ਸਾਂਝਾਕਰਨ ਸੈਟਿੰਗਾਂ ਦਾਖਲ ਕਰੋ"</string> <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> @@ -436,6 +435,7 @@ <string name="zen_modes_dialog_done" msgid="6654130880256438950">"ਹੋ ਗਿਆ"</string> <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"ਸੈਟਿੰਗਾਂ"</string> <string name="zen_mode_on" msgid="9085304934016242591">"ਚਾਲੂ"</string> + <string name="zen_mode_on_with_details" msgid="7416143430557895497">"<xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g> • \'ਤੇ"</string> <string name="zen_mode_off" msgid="1736604456618147306">"ਬੰਦ"</string> <string name="zen_mode_set_up" msgid="7457957033034460064">"ਸੈੱਟਅੱਪ ਕਰੋ"</string> <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"ਸੈਟਿੰਗਾਂ ਵਿੱਚ ਪ੍ਰਬੰਧਨ ਕਰੋ"</string> @@ -718,6 +718,8 @@ <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"ਸੈਟੇਲਾਈਟ, ਕਨੈਕਸ਼ਨ ਵਧੀਆ ਹੈ"</string> <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"ਸੈਟੇਲਾਈਟ, ਕਨੈਕਸ਼ਨ ਉਪਲਬਧ ਹੈ"</string> <string name="satellite_connected_carrier_text" msgid="118524195198532589">"ਸੈਟੇਲਾਈਟ SOS"</string> + <!-- no translation found for satellite_emergency_only_carrier_text (828510231597991206) --> + <skip /> <string name="accessibility_managed_profile" msgid="4703836746209377356">"ਕਾਰਜ ਪ੍ਰੋਫਾਈਲ"</string> <string name="tuner_warning_title" msgid="7721976098452135267">"ਕੁਝ ਵਾਸਤੇ ਤਾਂ ਮਜ਼ੇਦਾਰ ਹੈ ਲੇਕਿਨ ਸਾਰਿਆਂ ਵਾਸਤੇ ਨਹੀਂ"</string> <string name="tuner_warning" msgid="1861736288458481650">"ਸਿਸਟਮ UI ਟਿਊਨਰ ਤੁਹਾਨੂੰ Android ਵਰਤੋਂਕਾਰ ਇੰਟਰਫ਼ੇਸ ਤਬਦੀਲ ਕਰਨ ਅਤੇ ਵਿਉਂਤਬੱਧ ਕਰਨ ਲਈ ਵਾਧੂ ਤਰੀਕੇ ਦਿੰਦਾ ਹੈ। ਇਹ ਪ੍ਰਯੋਗਾਤਮਿਕ ਵਿਸ਼ੇਸ਼ਤਾਵਾਂ ਭਵਿੱਖ ਦੀ ਰੀਲੀਜ਼ ਵਿੱਚ ਬਦਲ ਸਕਦੀਆਂ ਹਨ, ਟੁੱਟ ਸਕਦੀਆਂ ਹਨ, ਜਾਂ ਅਲੋਪ ਹੋ ਸਕਦੀਆਂ ਹਨ। ਸਾਵਧਾਨੀ ਨਾਲ ਅੱਗੇ ਵੱਧੋ।"</string> diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml index 9a0560c0e746..cf881721bfe0 100644 --- a/packages/SystemUI/res/values-pl/strings.xml +++ b/packages/SystemUI/res/values-pl/strings.xml @@ -110,7 +110,7 @@ <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Nagrywać ekran?"</string> <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Nagrywaj jedną aplikację"</string> <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Nagrywaj cały ekran"</string> - <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Kiedy nagrywasz cały ekran, wszystko, co jest na nim widoczne, zostaje nagrane. Dlatego zachowaj ostrożność w zakresie haseł, danych do płatności, wiadomości, zdjęć, audio i filmów."</string> + <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Kiedy nagrywasz cały ekran, nagrane zostanie wszystko, co jest na nim widoczne. Dlatego uważaj na hasła, dane do płatności, wiadomości, zdjęcia, nagrania audio czy filmy."</string> <string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Kiedy nagrywasz aplikację, wszystko, co jest w niej wyświetlane lub odtwarzane, zostaje nagrane. Dlatego zachowaj ostrożność w zakresie haseł, danych do płatności, wiadomości, zdjęć, audio i filmów."</string> <string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Nagrywaj ekran"</string> <string name="screenrecord_app_selector_title" msgid="3854492366333954736">"Wybieranie aplikacji do nagrywania"</string> @@ -308,8 +308,7 @@ <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="7545274861795853838">"Udostępnij dźwięk"</string> <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Udostępnia dźwięk"</string> - <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_accessibility (7604615019302091708) --> - <skip /> + <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"aby otworzyć ustawienia udostępniania dźwięku"</string> <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> @@ -378,20 +377,20 @@ <string name="quick_settings_nfc_label" msgid="1054317416221168085">"Komunikacja NFC"</string> <string name="quick_settings_nfc_off" msgid="3465000058515424663">"Komunikacja NFC jest wyłączona"</string> <string name="quick_settings_nfc_on" msgid="1004976611203202230">"Komunikacja NFC jest włączona"</string> - <string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Nagrywanie ekranu"</string> + <string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Nagraj ekran"</string> <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Rozpocznij"</string> <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Zatrzymaj"</string> <string name="qs_record_issue_label" msgid="8166290137285529059">"Zarejestruj problem"</string> <string name="qs_record_issue_start" msgid="2979831312582567056">"Rozpocznij"</string> <string name="qs_record_issue_stop" msgid="3531747965741982657">"Zatrzymaj"</string> - <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Raport o błędzie"</string> + <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Zgłoś błąd"</string> <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 name="performance" msgid="6552785217174378320">"Wydajność"</string> <string name="user_interface" msgid="3712869377953950887">"Interfejs"</string> - <string name="thermal" msgid="6758074791325414831">"Termografia"</string> - <string name="custom" msgid="3337456985275158299">"Niestandardowe"</string> + <string name="thermal" msgid="6758074791325414831">"Temperatura"</string> + <string name="custom" msgid="3337456985275158299">"Niestandardowy"</string> <string name="custom_trace_settings_dialog_title" msgid="2608570500144830554">"Niestandardowe ustawienia śladu"</string> <string name="restore_default" msgid="5259420807486239755">"Przywróć wartości domyślne"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Tryb jednej ręki"</string> @@ -436,6 +435,7 @@ <string name="zen_modes_dialog_done" msgid="6654130880256438950">"Gotowe"</string> <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Ustawienia"</string> <string name="zen_mode_on" msgid="9085304934016242591">"Wł."</string> + <string name="zen_mode_on_with_details" msgid="7416143430557895497">"Włączone • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string> <string name="zen_mode_off" msgid="1736604456618147306">"Wył."</string> <string name="zen_mode_set_up" msgid="7457957033034460064">"Skonfiguruj"</string> <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"Zarządzaj w ustawieniach"</string> @@ -718,6 +718,8 @@ <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Satelita – połączenie dobre"</string> <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Satelita – połączenie dostępne"</string> <string name="satellite_connected_carrier_text" msgid="118524195198532589">"Satelitarne połączenie alarmowe"</string> + <!-- no translation found for satellite_emergency_only_carrier_text (828510231597991206) --> + <skip /> <string name="accessibility_managed_profile" msgid="4703836746209377356">"Profil służbowy"</string> <string name="tuner_warning_title" msgid="7721976098452135267">"Dobra zabawa, ale nie dla każdego"</string> <string name="tuner_warning" msgid="1861736288458481650">"Kalibrator System UI udostępnia dodatkowe sposoby dostrajania i dostosowywania interfejsu Androida. Te eksperymentalne funkcje mogą się zmienić, popsuć lub zniknąć w przyszłych wersjach. Zachowaj ostrożność."</string> diff --git a/packages/SystemUI/res/values-pt-rBR/strings.xml b/packages/SystemUI/res/values-pt-rBR/strings.xml index b6bb1140b66a..c2c40db52841 100644 --- a/packages/SystemUI/res/values-pt-rBR/strings.xml +++ b/packages/SystemUI/res/values-pt-rBR/strings.xml @@ -308,8 +308,7 @@ <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="7545274861795853838">"Compartilhar áudio"</string> <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Compartilhando áudio"</string> - <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_accessibility (7604615019302091708) --> - <skip /> + <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"acessar configurações de compartilhamento de áudio"</string> <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> @@ -390,7 +389,7 @@ <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Gravação de tela"</string> <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="thermal" msgid="6758074791325414831">"Temperatura"</string> <string name="custom" msgid="3337456985275158299">"Personalizado"</string> <string name="custom_trace_settings_dialog_title" msgid="2608570500144830554">"Configurações de rastreamento personalizado"</string> <string name="restore_default" msgid="5259420807486239755">"Restaurar padrão"</string> @@ -436,6 +435,7 @@ <string name="zen_modes_dialog_done" msgid="6654130880256438950">"Concluído"</string> <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Configurações"</string> <string name="zen_mode_on" msgid="9085304934016242591">"Ativado"</string> + <string name="zen_mode_on_with_details" msgid="7416143430557895497">"Ativado • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string> <string name="zen_mode_off" msgid="1736604456618147306">"Desativado"</string> <string name="zen_mode_set_up" msgid="7457957033034460064">"Configurar"</string> <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"Gerenciar nas configurações"</string> @@ -718,6 +718,8 @@ <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Satélite, conexão boa"</string> <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Satélite, conexão disponível"</string> <string name="satellite_connected_carrier_text" msgid="118524195198532589">"SOS via satélite"</string> + <!-- no translation found for satellite_emergency_only_carrier_text (828510231597991206) --> + <skip /> <string name="accessibility_managed_profile" msgid="4703836746209377356">"Perfil de trabalho"</string> <string name="tuner_warning_title" msgid="7721976098452135267">"Diversão para alguns, mas não para todos"</string> <string name="tuner_warning" msgid="1861736288458481650">"O sintonizador System UI fornece maneiras adicionais de ajustar e personalizar a interface do usuário do Android. Esses recursos experimentais podem mudar, falhar ou desaparecer nas versões futuras. Prossiga com cuidado."</string> diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml index 68a977e609ec..c52354fb50c6 100644 --- a/packages/SystemUI/res/values-pt-rPT/strings.xml +++ b/packages/SystemUI/res/values-pt-rPT/strings.xml @@ -308,8 +308,7 @@ <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="7545274861795853838">"Partilhar áudio"</string> <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"A partilhar áudio"</string> - <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_accessibility (7604615019302091708) --> - <skip /> + <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"aceder às definições de partilha de áudio"</string> <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> @@ -385,7 +384,7 @@ <string name="qs_record_issue_start" msgid="2979831312582567056">"Iniciar"</string> <string name="qs_record_issue_stop" msgid="3531747965741982657">"Parar"</string> <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Relatório de erro"</string> - <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Que parte do dispositivo foi afetada?"</string> + <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Que experiência com o dispositivo 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 name="performance" msgid="6552785217174378320">"Desempenho"</string> @@ -436,6 +435,7 @@ <string name="zen_modes_dialog_done" msgid="6654130880256438950">"Concluir"</string> <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Definições"</string> <string name="zen_mode_on" msgid="9085304934016242591">"Ativado"</string> + <string name="zen_mode_on_with_details" msgid="7416143430557895497">"Ativado • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string> <string name="zen_mode_off" msgid="1736604456618147306">"Desativado"</string> <string name="zen_mode_set_up" msgid="7457957033034460064">"Configurar"</string> <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"Gerir nas definições"</string> @@ -718,6 +718,8 @@ <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Satélite, boa ligação"</string> <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Satélite, ligação disponível"</string> <string name="satellite_connected_carrier_text" msgid="118524195198532589">"Satélite SOS"</string> + <!-- no translation found for satellite_emergency_only_carrier_text (828510231597991206) --> + <skip /> <string name="accessibility_managed_profile" msgid="4703836746209377356">"Perfil de trabalho"</string> <string name="tuner_warning_title" msgid="7721976098452135267">"Diversão para alguns, mas não para todos"</string> <string name="tuner_warning" msgid="1861736288458481650">"O Sintonizador da interface do sistema disponibiliza-lhe formas adicionais ajustar e personalizar a interface do utilizador do Android. Estas funcionalidades experimentais podem ser alteradas, deixar de funcionar ou desaparecer em versões futuras. Prossiga com cuidado."</string> diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml index b6bb1140b66a..c2c40db52841 100644 --- a/packages/SystemUI/res/values-pt/strings.xml +++ b/packages/SystemUI/res/values-pt/strings.xml @@ -308,8 +308,7 @@ <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="7545274861795853838">"Compartilhar áudio"</string> <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Compartilhando áudio"</string> - <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_accessibility (7604615019302091708) --> - <skip /> + <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"acessar configurações de compartilhamento de áudio"</string> <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> @@ -390,7 +389,7 @@ <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Gravação de tela"</string> <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="thermal" msgid="6758074791325414831">"Temperatura"</string> <string name="custom" msgid="3337456985275158299">"Personalizado"</string> <string name="custom_trace_settings_dialog_title" msgid="2608570500144830554">"Configurações de rastreamento personalizado"</string> <string name="restore_default" msgid="5259420807486239755">"Restaurar padrão"</string> @@ -436,6 +435,7 @@ <string name="zen_modes_dialog_done" msgid="6654130880256438950">"Concluído"</string> <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Configurações"</string> <string name="zen_mode_on" msgid="9085304934016242591">"Ativado"</string> + <string name="zen_mode_on_with_details" msgid="7416143430557895497">"Ativado • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string> <string name="zen_mode_off" msgid="1736604456618147306">"Desativado"</string> <string name="zen_mode_set_up" msgid="7457957033034460064">"Configurar"</string> <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"Gerenciar nas configurações"</string> @@ -718,6 +718,8 @@ <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Satélite, conexão boa"</string> <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Satélite, conexão disponível"</string> <string name="satellite_connected_carrier_text" msgid="118524195198532589">"SOS via satélite"</string> + <!-- no translation found for satellite_emergency_only_carrier_text (828510231597991206) --> + <skip /> <string name="accessibility_managed_profile" msgid="4703836746209377356">"Perfil de trabalho"</string> <string name="tuner_warning_title" msgid="7721976098452135267">"Diversão para alguns, mas não para todos"</string> <string name="tuner_warning" msgid="1861736288458481650">"O sintonizador System UI fornece maneiras adicionais de ajustar e personalizar a interface do usuário do Android. Esses recursos experimentais podem mudar, falhar ou desaparecer nas versões futuras. Prossiga com cuidado."</string> diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml index 684b04c4855a..1f7b7164d4b2 100644 --- a/packages/SystemUI/res/values-ro/strings.xml +++ b/packages/SystemUI/res/values-ro/strings.xml @@ -308,8 +308,7 @@ <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="7545274861795853838">"Trimite audio"</string> <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Se permite accesul la conținutul audio"</string> - <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_accessibility (7604615019302091708) --> - <skip /> + <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"accesa setările de permitere a accesului la audio"</string> <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> @@ -436,6 +435,7 @@ <string name="zen_modes_dialog_done" msgid="6654130880256438950">"Gata"</string> <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Setări"</string> <string name="zen_mode_on" msgid="9085304934016242591">"Activat"</string> + <string name="zen_mode_on_with_details" msgid="7416143430557895497">"Activat • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string> <string name="zen_mode_off" msgid="1736604456618147306">"Dezactivat"</string> <string name="zen_mode_set_up" msgid="7457957033034460064">"Configurează"</string> <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"Gestionează în setări"</string> @@ -718,6 +718,8 @@ <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Satelit, conexiune bună"</string> <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Satelit, conexiune disponibilă"</string> <string name="satellite_connected_carrier_text" msgid="118524195198532589">"SOS prin satelit"</string> + <!-- no translation found for satellite_emergency_only_carrier_text (828510231597991206) --> + <skip /> <string name="accessibility_managed_profile" msgid="4703836746209377356">"Profil de serviciu"</string> <string name="tuner_warning_title" msgid="7721976098452135267">"Distractiv pentru unii, dar nu pentru toată lumea"</string> <string name="tuner_warning" msgid="1861736288458481650">"System UI Tuner oferă modalități suplimentare de a ajusta și a personaliza interfața de utilizare Android. Aceste funcții experimentale pot să se schimbe, să se blocheze sau să dispară din versiunile viitoare. Continuă cu prudență."</string> diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml index 7045163d982f..0b545ce46eed 100644 --- a/packages/SystemUI/res/values-ru/strings.xml +++ b/packages/SystemUI/res/values-ru/strings.xml @@ -308,8 +308,7 @@ <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth включится завтра утром"</string> <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Отправить аудио"</string> <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Отправка аудио"</string> - <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_accessibility (7604615019302091708) --> - <skip /> + <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"перейти в настройки передачи аудио"</string> <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> @@ -436,6 +435,7 @@ <string name="zen_modes_dialog_done" msgid="6654130880256438950">"Готово"</string> <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Настройки"</string> <string name="zen_mode_on" msgid="9085304934016242591">"Включено"</string> + <string name="zen_mode_on_with_details" msgid="7416143430557895497">"Вкл. • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string> <string name="zen_mode_off" msgid="1736604456618147306">"Отключено"</string> <string name="zen_mode_set_up" msgid="7457957033034460064">"Настроить"</string> <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"Открыть настройки"</string> @@ -718,6 +718,8 @@ <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Спутниковая связь, хорошее качество соединения"</string> <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Доступно соединение по спутниковой связи"</string> <string name="satellite_connected_carrier_text" msgid="118524195198532589">"Спутниковый SOS"</string> + <!-- no translation found for satellite_emergency_only_carrier_text (828510231597991206) --> + <skip /> <string name="accessibility_managed_profile" msgid="4703836746209377356">"Рабочий профиль"</string> <string name="tuner_warning_title" msgid="7721976098452135267">"Внимание!"</string> <string name="tuner_warning" msgid="1861736288458481650">"System UI Tuner позволяет настраивать интерфейс устройства Android по вашему вкусу. В будущем эта экспериментальная функция может измениться, перестать работать или исчезнуть."</string> diff --git a/packages/SystemUI/res/values-si/strings.xml b/packages/SystemUI/res/values-si/strings.xml index 72924e9ac85f..b34b8a38b358 100644 --- a/packages/SystemUI/res/values-si/strings.xml +++ b/packages/SystemUI/res/values-si/strings.xml @@ -308,8 +308,7 @@ <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"බ්ලූටූත් හෙට උදේ සක්රීය වෙයි"</string> <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"ශ්රව්ය බෙදා ගන්න"</string> <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"ශ්රව්ය බෙදා ගැනීම"</string> - <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_accessibility (7604615019302091708) --> - <skip /> + <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"ශ්රව්ය බෙදා ගැනීමේ සැකසීම් ඇතුළු කරන්න"</string> <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> @@ -436,6 +435,7 @@ <string name="zen_modes_dialog_done" msgid="6654130880256438950">"නිමයි"</string> <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"සැකසීම්"</string> <string name="zen_mode_on" msgid="9085304934016242591">"ක්රියාත්මකයි"</string> + <string name="zen_mode_on_with_details" msgid="7416143430557895497">"ක්රියාත්මකයි • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string> <string name="zen_mode_off" msgid="1736604456618147306">"ක්රියාවිරහිතයි"</string> <string name="zen_mode_set_up" msgid="7457957033034460064">"පිහිටුවන්න"</string> <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"සැකසීම් තුළ කළමනාකරණය කරන්න"</string> @@ -718,6 +718,8 @@ <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"චන්ද්රිකාව, හොඳ සම්බන්ධතාවයක්"</string> <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"චන්ද්රිකාව, සම්බන්ධතාවය තිබේ"</string> <string name="satellite_connected_carrier_text" msgid="118524195198532589">"චන්ද්රිකා SOS"</string> + <!-- no translation found for satellite_emergency_only_carrier_text (828510231597991206) --> + <skip /> <string name="accessibility_managed_profile" msgid="4703836746209377356">"කාර්යාල පැතිකඩ"</string> <string name="tuner_warning_title" msgid="7721976098452135267">"සමහරක් දේවල් වලට විනෝදයි, නමුත් සියල්ලටම නොවේ"</string> <string name="tuner_warning" msgid="1861736288458481650">"පද්ධති UI සුසරකය ඔබට Android පරිශීලක අතුරු මුහුණත වෙනස් කිරීමට හෝ අභිරුචිකරණය කිරීමට අමතර ක්රම ලබා දේ. මෙම පර්යේෂණාත්මක අංග ඉදිරි නිකුත් වීම් වල වෙනස් වීමට, වැඩ නොකිරීමට, හෝ නැතිවීමට හැක. ප්රවේශමෙන් ඉදිරියට යන්න."</string> diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml index 01399bd7d278..b1983b87a0b4 100644 --- a/packages/SystemUI/res/values-sk/strings.xml +++ b/packages/SystemUI/res/values-sk/strings.xml @@ -308,8 +308,7 @@ <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="7545274861795853838">"Zdieľať zvuk"</string> <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Zdieľa sa zvuk"</string> - <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_accessibility (7604615019302091708) --> - <skip /> + <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"prejsť do nastavení zdieľania zvuku"</string> <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> @@ -381,7 +380,7 @@ <string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Rekordér obrazovky"</string> <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Začať"</string> <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Ukončiť"</string> - <string name="qs_record_issue_label" msgid="8166290137285529059">"Nahrať problém"</string> + <string name="qs_record_issue_label" msgid="8166290137285529059">"Zaznamenať problém"</string> <string name="qs_record_issue_start" msgid="2979831312582567056">"Začať"</string> <string name="qs_record_issue_stop" msgid="3531747965741982657">"Zastavte"</string> <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Hlásenie chyby"</string> @@ -436,6 +435,7 @@ <string name="zen_modes_dialog_done" msgid="6654130880256438950">"Hotovo"</string> <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Nastavenia"</string> <string name="zen_mode_on" msgid="9085304934016242591">"Zapnuté"</string> + <string name="zen_mode_on_with_details" msgid="7416143430557895497">"Zapnuté • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string> <string name="zen_mode_off" msgid="1736604456618147306">"Vypnuté"</string> <string name="zen_mode_set_up" msgid="7457957033034460064">"Nastavenie"</string> <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"Správa v nastaveniach"</string> @@ -718,6 +718,8 @@ <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Satelit, dobrá kvalita pripojenia"</string> <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Satelit, pripojenie je k dispozícii"</string> <string name="satellite_connected_carrier_text" msgid="118524195198532589">"Pomoc cez satelit"</string> + <!-- no translation found for satellite_emergency_only_carrier_text (828510231597991206) --> + <skip /> <string name="accessibility_managed_profile" msgid="4703836746209377356">"Pracovný profil"</string> <string name="tuner_warning_title" msgid="7721976098452135267">"Pri používaní tuneru postupujte opatrne"</string> <string name="tuner_warning" msgid="1861736288458481650">"Tuner používateľského rozhrania systému poskytujte ďalšie spôsoby ladenia a prispôsobenia používateľského rozhrania Android. Tieto experimentálne funkcie sa môžu v budúcich verziách zmeniť, ich poskytovanie môže byť prerušené alebo môžu byť odstránené. Pokračujte opatrne."</string> diff --git a/packages/SystemUI/res/values-sl/strings.xml b/packages/SystemUI/res/values-sl/strings.xml index ad81798d5807..a3b042e0959f 100644 --- a/packages/SystemUI/res/values-sl/strings.xml +++ b/packages/SystemUI/res/values-sl/strings.xml @@ -308,8 +308,7 @@ <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="7545274861795853838">"Deli zvok"</string> <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Poteka deljenje zvoka"</string> - <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_accessibility (7604615019302091708) --> - <skip /> + <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"odpiranje nastavitev deljenja zvoka"</string> <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> @@ -390,7 +389,7 @@ <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Snemanje zaslona"</string> <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="thermal" msgid="6758074791325414831">"Toplota"</string> <string name="custom" msgid="3337456985275158299">"Po meri"</string> <string name="custom_trace_settings_dialog_title" msgid="2608570500144830554">"Nastavitve sledi po meri"</string> <string name="restore_default" msgid="5259420807486239755">"Obnovi privzeto"</string> @@ -436,6 +435,7 @@ <string name="zen_modes_dialog_done" msgid="6654130880256438950">"Končano"</string> <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Nastavitve"</string> <string name="zen_mode_on" msgid="9085304934016242591">"Vklopljeno"</string> + <string name="zen_mode_on_with_details" msgid="7416143430557895497">"Vklopljeno • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string> <string name="zen_mode_off" msgid="1736604456618147306">"Izklopljeno"</string> <string name="zen_mode_set_up" msgid="7457957033034460064">"Nastavitev"</string> <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"Upravljanje v nastavitvah"</string> @@ -673,7 +673,7 @@ <string name="volume_panel_spatial_audio_title" msgid="3367048857932040660">"Prostorski zvok"</string> <string name="volume_panel_spatial_audio_off" msgid="4177490084606772989">"Izklopljeno"</string> <string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"Fiksno"</string> - <string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Spremljanje položaja glave"</string> + <string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Spremljanje premikov glave"</string> <string name="volume_ringer_change" msgid="3574969197796055532">"Dotaknite se, če želite spremeniti način zvonjenja."</string> <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"izklop zvoka"</string> <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"vklop zvoka"</string> @@ -718,6 +718,8 @@ <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Satelit, dobra povezava"</string> <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Satelit, povezava je na voljo"</string> <string name="satellite_connected_carrier_text" msgid="118524195198532589">"SOS prek satelita"</string> + <!-- no translation found for satellite_emergency_only_carrier_text (828510231597991206) --> + <skip /> <string name="accessibility_managed_profile" msgid="4703836746209377356">"Delovni profil"</string> <string name="tuner_warning_title" msgid="7721976098452135267">"Zabavno za nekatere, a ne za vse"</string> <string name="tuner_warning" msgid="1861736288458481650">"Uglaševalnik uporabniškega vmesnika sistema vam omogoča dodatne načine za spreminjanje in prilagajanje uporabniškega vmesnika Android. Te poskusne funkcije lahko v prihodnjih izdajah kadar koli izginejo, se spremenijo ali pokvarijo. Bodite previdni."</string> diff --git a/packages/SystemUI/res/values-sq/strings.xml b/packages/SystemUI/res/values-sq/strings.xml index 3ddbea55b261..4cba52795ff6 100644 --- a/packages/SystemUI/res/values-sq/strings.xml +++ b/packages/SystemUI/res/values-sq/strings.xml @@ -308,8 +308,7 @@ <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="7545274861795853838">"Ndaj audion"</string> <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Audioja po ndahet"</string> - <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_accessibility (7604615019302091708) --> - <skip /> + <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"për të hyrë te cilësimet e ndarjes së audios"</string> <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> @@ -436,6 +435,7 @@ <string name="zen_modes_dialog_done" msgid="6654130880256438950">"U krye"</string> <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Cilësimet"</string> <string name="zen_mode_on" msgid="9085304934016242591">"Aktiv"</string> + <string name="zen_mode_on_with_details" msgid="7416143430557895497">"Aktiv • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string> <string name="zen_mode_off" msgid="1736604456618147306">"Joaktiv"</string> <string name="zen_mode_set_up" msgid="7457957033034460064">"Konfiguro"</string> <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"Menaxho te cilësimet"</string> @@ -718,6 +718,8 @@ <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Sateliti. Lidhje e mirë"</string> <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Sateliti. Ofrohet lidhje"</string> <string name="satellite_connected_carrier_text" msgid="118524195198532589">"SOS satelitor"</string> + <!-- no translation found for satellite_emergency_only_carrier_text (828510231597991206) --> + <skip /> <string name="accessibility_managed_profile" msgid="4703836746209377356">"Profili i punës"</string> <string name="tuner_warning_title" msgid="7721976098452135267">"Argëtim për disa, por jo për të gjithë!"</string> <string name="tuner_warning" msgid="1861736288458481650">"Sintonizuesi i Sistemit të Ndërfaqes së Përdoruesit të jep mënyra shtesë për të tërhequr dhe personalizuar ndërfaqen Android të përdoruesit. Këto funksione eksperimentale mund të ndryshojnë, prishen ose zhduken në versionet e ardhshme. Vazhdo me kujdes."</string> diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml index 9a3196c8a7d5..26c135b8d213 100644 --- a/packages/SystemUI/res/values-sr/strings.xml +++ b/packages/SystemUI/res/values-sr/strings.xml @@ -308,8 +308,7 @@ <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth ће се укључити сутра ујутру"</string> <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Дели звук"</string> <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Дели се звук"</string> - <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_accessibility (7604615019302091708) --> - <skip /> + <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"уђите у подешавања дељења звука"</string> <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> @@ -436,6 +435,7 @@ <string name="zen_modes_dialog_done" msgid="6654130880256438950">"Готово"</string> <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Подешавања"</string> <string name="zen_mode_on" msgid="9085304934016242591">"Укључено"</string> + <string name="zen_mode_on_with_details" msgid="7416143430557895497">"Укљ. • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string> <string name="zen_mode_off" msgid="1736604456618147306">"Искључено"</string> <string name="zen_mode_set_up" msgid="7457957033034460064">"Подеси"</string> <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"Управљајте у подешавањима"</string> @@ -718,6 +718,8 @@ <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Сателит, веза је добра"</string> <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Сателит, веза је доступна"</string> <string name="satellite_connected_carrier_text" msgid="118524195198532589">"Хитна помоћ преко сателита"</string> + <!-- no translation found for satellite_emergency_only_carrier_text (828510231597991206) --> + <skip /> <string name="accessibility_managed_profile" msgid="4703836746209377356">"Пословни профил"</string> <string name="tuner_warning_title" msgid="7721976098452135267">"Забава за неке, али не за све"</string> <string name="tuner_warning" msgid="1861736288458481650">"Тјунер за кориснички интерфејс система вам пружа додатне начине за подешавање и прилагођавање Android корисничког интерфејса. Ове експерименталне функције могу да се промене, откажу или нестану у будућим издањима. Будите опрезни."</string> diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml index cf9a2a23ae2b..56cc4427bf17 100644 --- a/packages/SystemUI/res/values-sv/strings.xml +++ b/packages/SystemUI/res/values-sv/strings.xml @@ -308,8 +308,7 @@ <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="7545274861795853838">"Dela ljud"</string> <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Delar ljud"</string> - <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_accessibility (7604615019302091708) --> - <skip /> + <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"öppna inställningarna för ljuddelning"</string> <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> @@ -381,7 +380,7 @@ <string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Skärminspelning"</string> <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Starta"</string> <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Stoppa"</string> - <string name="qs_record_issue_label" msgid="8166290137285529059">"Registrera problem"</string> + <string name="qs_record_issue_label" msgid="8166290137285529059">"Anmäl problem"</string> <string name="qs_record_issue_start" msgid="2979831312582567056">"Starta"</string> <string name="qs_record_issue_stop" msgid="3531747965741982657">"Stoppa"</string> <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Felrapport"</string> @@ -436,6 +435,7 @@ <string name="zen_modes_dialog_done" msgid="6654130880256438950">"Klar"</string> <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Inställningar"</string> <string name="zen_mode_on" msgid="9085304934016242591">"På"</string> + <string name="zen_mode_on_with_details" msgid="7416143430557895497">"På • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string> <string name="zen_mode_off" msgid="1736604456618147306">"Av"</string> <string name="zen_mode_set_up" msgid="7457957033034460064">"Ställ in"</string> <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"Hantera i inställningarna"</string> @@ -718,6 +718,8 @@ <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Satellit, bra anslutning"</string> <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Satellit, anslutning tillgänglig"</string> <string name="satellite_connected_carrier_text" msgid="118524195198532589">"SOS-larm via satellit"</string> + <!-- no translation found for satellite_emergency_only_carrier_text (828510231597991206) --> + <skip /> <string name="accessibility_managed_profile" msgid="4703836746209377356">"Jobbprofil"</string> <string name="tuner_warning_title" msgid="7721976098452135267">"Kul för vissa, inte för alla"</string> <string name="tuner_warning" msgid="1861736288458481650">"Du kan använda inställningarna för systemgränssnitt för att justera användargränssnittet i Android. Dessa experimentfunktioner kan när som helst ändras, sluta fungera eller försvinna. Använd med försiktighet."</string> diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml index 317d7f105d27..db237b501a53 100644 --- a/packages/SystemUI/res/values-sw/strings.xml +++ b/packages/SystemUI/res/values-sw/strings.xml @@ -308,8 +308,7 @@ <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth itawaka kesho asubuhi"</string> <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Sikiliza pamoja na wengine"</string> <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Mnasikiliza pamoja"</string> - <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_accessibility (7604615019302091708) --> - <skip /> + <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"uweke mipangilio ya kusikiliza pamoja"</string> <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> @@ -436,6 +435,7 @@ <string name="zen_modes_dialog_done" msgid="6654130880256438950">"Nimemaliza"</string> <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Mipangilio"</string> <string name="zen_mode_on" msgid="9085304934016242591">"Imewashwa"</string> + <string name="zen_mode_on_with_details" msgid="7416143430557895497">"Imewashwa • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string> <string name="zen_mode_off" msgid="1736604456618147306">"Imezimwa"</string> <string name="zen_mode_set_up" msgid="7457957033034460064">"Weka mipangilio"</string> <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"Dhibiti katika mipangilio"</string> @@ -718,6 +718,8 @@ <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Setilaiti, muunganisho thabiti"</string> <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Setilaiti, muunganisho unapatikana"</string> <string name="satellite_connected_carrier_text" msgid="118524195198532589">"Msaada kupitia Setilaiti"</string> + <!-- no translation found for satellite_emergency_only_carrier_text (828510231597991206) --> + <skip /> <string name="accessibility_managed_profile" msgid="4703836746209377356">"Wasifu wa kazini"</string> <string name="tuner_warning_title" msgid="7721976098452135267">"Kinafurahisha kwa baadhi ya watu lakini si wote"</string> <string name="tuner_warning" msgid="1861736288458481650">"Kirekebishi cha kiolesura cha mfumo kinakupa njia zaidi za kugeuza na kubadilisha kiolesura cha Android ili kikufae. Vipengele hivi vya majaribio vinaweza kubadilika, kuharibika au kupotea katika matoleo ya siku zijazo. Endelea kwa uangalifu."</string> diff --git a/packages/SystemUI/res/values-ta/strings.xml b/packages/SystemUI/res/values-ta/strings.xml index 7ba080b204f6..e617e88d8f1d 100644 --- a/packages/SystemUI/res/values-ta/strings.xml +++ b/packages/SystemUI/res/values-ta/strings.xml @@ -308,8 +308,7 @@ <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"நாளை காலை புளூடூத் இயக்கப்படும்"</string> <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"ஆடியோவைப் பகிர்"</string> <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"ஆடியோ பகிரப்படுகிறது"</string> - <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_accessibility (7604615019302091708) --> - <skip /> + <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"ஆடியோ பகிர்வு அமைப்புகளுக்குச் செல்லும்"</string> <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> @@ -436,6 +435,7 @@ <string name="zen_modes_dialog_done" msgid="6654130880256438950">"முடிந்தது"</string> <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"அமைப்புகள்"</string> <string name="zen_mode_on" msgid="9085304934016242591">"இயக்கப்பட்டுள்ளது"</string> + <string name="zen_mode_on_with_details" msgid="7416143430557895497">"ஆன் • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string> <string name="zen_mode_off" msgid="1736604456618147306">"முடக்கப்பட்டுள்ளது"</string> <string name="zen_mode_set_up" msgid="7457957033034460064">"அமையுங்கள்"</string> <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"அமைப்புகளில் நிர்வகியுங்கள்"</string> @@ -718,6 +718,8 @@ <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"சாட்டிலைட், நிலையான இணைப்பு"</string> <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"சாட்டிலைட், இணைப்பு கிடைக்கிறது"</string> <string name="satellite_connected_carrier_text" msgid="118524195198532589">"சாட்டிலைட் SOS"</string> + <!-- no translation found for satellite_emergency_only_carrier_text (828510231597991206) --> + <skip /> <string name="accessibility_managed_profile" msgid="4703836746209377356">"பணிக் கணக்கு"</string> <string name="tuner_warning_title" msgid="7721976098452135267">"சில வேடிக்கையாக இருந்தாலும் கவனம் தேவை"</string> <string name="tuner_warning" msgid="1861736288458481650">"System UI Tuner, Android பயனர் இடைமுகத்தை மாற்றவும் தனிப்பயனாக்கவும் கூடுதல் வழிகளை வழங்குகிறது. இந்தப் பரிசோதனைக்குரிய அம்சங்கள் எதிர்கால வெளியீடுகளில் மாற்றப்படலாம், இடைநிறுத்தப்படலாம் அல்லது தோன்றாமல் போகலாம். கவனத்துடன் தொடரவும்."</string> diff --git a/packages/SystemUI/res/values-te/strings.xml b/packages/SystemUI/res/values-te/strings.xml index de140af7fd62..b45e82b0e704 100644 --- a/packages/SystemUI/res/values-te/strings.xml +++ b/packages/SystemUI/res/values-te/strings.xml @@ -308,8 +308,7 @@ <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"బ్లూటూత్ రేపు ఉదయం ఆన్ అవుతుంది"</string> <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"ఆడియోను షేర్ చేయండి"</string> <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"ఆడియోను షేర్ చేస్తున్నారు"</string> - <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_accessibility (7604615019302091708) --> - <skip /> + <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"ఆడియో షేరింగ్ సెట్టింగ్లను ఎంటర్ చేయండి"</string> <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> @@ -436,6 +435,7 @@ <string name="zen_modes_dialog_done" msgid="6654130880256438950">"పూర్తయింది"</string> <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"సెట్టింగ్లు"</string> <string name="zen_mode_on" msgid="9085304934016242591">"ఆన్లో ఉంది"</string> + <string name="zen_mode_on_with_details" msgid="7416143430557895497">"ఆన్ • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string> <string name="zen_mode_off" msgid="1736604456618147306">"ఆఫ్లో ఉంది"</string> <string name="zen_mode_set_up" msgid="7457957033034460064">"సెటప్ చేయండి"</string> <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"సెట్టింగ్లలో మేనేజ్ చేయండి"</string> @@ -718,6 +718,8 @@ <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"శాటిలైట్, కనెక్షన్ బాగుంది"</string> <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"శాటిలైట్, కనెక్షన్ అందుబాటులో ఉంది"</string> <string name="satellite_connected_carrier_text" msgid="118524195198532589">"ఎమర్జెన్సీ శాటిలైట్ సహాయం"</string> + <!-- no translation found for satellite_emergency_only_carrier_text (828510231597991206) --> + <skip /> <string name="accessibility_managed_profile" msgid="4703836746209377356">"ఆఫీస్ ప్రొఫైల్"</string> <string name="tuner_warning_title" msgid="7721976098452135267">"కొందరికి సరదాగా ఉంటుంది కానీ అందరికీ అలాగే ఉండదు"</string> <string name="tuner_warning" msgid="1861736288458481650">"సిస్టమ్ UI ట్యూనర్ Android వినియోగదారు ఇంటర్ఫేస్ను మెరుగుపరచడానికి మరియు అనుకూలంగా మార్చడానికి మీకు మరిన్ని మార్గాలను అందిస్తుంది. ఈ ప్రయోగాత్మక లక్షణాలు భవిష్యత్తు విడుదలల్లో మార్పుకు లోనవ్వచ్చు, తాత్కాలికంగా లేదా పూర్తిగా నిలిపివేయవచ్చు. జాగ్రత్తగా కొనసాగండి."</string> diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml index e4dc392ca228..252fc05f8b6d 100644 --- a/packages/SystemUI/res/values-th/strings.xml +++ b/packages/SystemUI/res/values-th/strings.xml @@ -155,7 +155,7 @@ <string name="issuerecord_channel_description" msgid="6142326363431474632">"การแจ้งเตือนต่อเนื่องสำหรับเซสชันการรวบรวมปัญหา"</string> <string name="issuerecord_ongoing_screen_only" msgid="6248206059935015722">"กำลังบันทึกปัญหา"</string> <string name="issuerecord_share_label" msgid="3992657993619876199">"แชร์"</string> - <string name="issuerecord_save_title" msgid="4161043023696751591">"บันทึกไฟล์บันทึกปัญหาแล้ว"</string> + <string name="issuerecord_save_title" msgid="4161043023696751591">"จัดเก็บไฟล์บันทึกปัญหาแล้ว"</string> <string name="issuerecord_save_text" msgid="1205985304551521495">"แตะเพื่อดู"</string> <string name="issuerecord_save_error" msgid="6913040083446722726">"เกิดข้อผิดพลาดในการบันทึกไฟล์บันทึกปัญหา"</string> <string name="issuerecord_start_error" msgid="3402782952722871190">"เกิดข้อผิดพลาดในการเริ่มบันทึกปัญหา"</string> @@ -308,8 +308,7 @@ <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"บลูทูธจะเปิดพรุ่งนี้เช้า"</string> <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"แชร์เสียง"</string> <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"กำลังแชร์เสียง"</string> - <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_accessibility (7604615019302091708) --> - <skip /> + <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"เข้าสู่การตั้งค่าการแชร์เสียง"</string> <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> @@ -436,6 +435,7 @@ <string name="zen_modes_dialog_done" msgid="6654130880256438950">"เสร็จสิ้น"</string> <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"การตั้งค่า"</string> <string name="zen_mode_on" msgid="9085304934016242591">"เปิด"</string> + <string name="zen_mode_on_with_details" msgid="7416143430557895497">"เปิด • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string> <string name="zen_mode_off" msgid="1736604456618147306">"ปิด"</string> <string name="zen_mode_set_up" msgid="7457957033034460064">"ตั้งค่า"</string> <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"จัดการในการตั้งค่า"</string> @@ -718,6 +718,8 @@ <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"ดาวเทียม, การเชื่อมต่อดี"</string> <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"ดาวเทียม, การเชื่อมต่อที่พร้อมใช้งาน"</string> <string name="satellite_connected_carrier_text" msgid="118524195198532589">"SOS ดาวเทียม"</string> + <!-- no translation found for satellite_emergency_only_carrier_text (828510231597991206) --> + <skip /> <string name="accessibility_managed_profile" msgid="4703836746209377356">"โปรไฟล์งาน"</string> <string name="tuner_warning_title" msgid="7721976098452135267">"เพลิดเพลินกับบางส่วนแต่ไม่ใช่ทั้งหมด"</string> <string name="tuner_warning" msgid="1861736288458481650">"ตัวรับสัญญาณ UI ระบบช่วยให้คุณมีวิธีพิเศษในการปรับแต่งและกำหนดค่าส่วนติดต่อผู้ใช้ Android ฟีเจอร์รุ่นทดลองเหล่านี้อาจมีการเปลี่ยนแปลง ขัดข้อง หรือหายไปในเวอร์ชันอนาคต โปรดดำเนินการด้วยความระมัดระวัง"</string> diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml index 632bdab8dc8c..04f52ea5f659 100644 --- a/packages/SystemUI/res/values-tl/strings.xml +++ b/packages/SystemUI/res/values-tl/strings.xml @@ -308,8 +308,7 @@ <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="7545274861795853838">"Ibahagi ang audio"</string> <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Ibinabahagi ang audio"</string> - <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_accessibility (7604615019302091708) --> - <skip /> + <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"pumasok sa mga setting sa pag-share ng audio"</string> <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> @@ -436,6 +435,7 @@ <string name="zen_modes_dialog_done" msgid="6654130880256438950">"Tapos na"</string> <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Mga Setting"</string> <string name="zen_mode_on" msgid="9085304934016242591">"Naka-on"</string> + <string name="zen_mode_on_with_details" msgid="7416143430557895497">"Naka-on • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string> <string name="zen_mode_off" msgid="1736604456618147306">"Naka-off"</string> <string name="zen_mode_set_up" msgid="7457957033034460064">"I-set up"</string> <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"Pamahalaan sa mga setting"</string> @@ -718,6 +718,8 @@ <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Satellite, malakas ang koneksyon"</string> <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Satellite, may koneksyon"</string> <string name="satellite_connected_carrier_text" msgid="118524195198532589">"Satellite SOS"</string> + <!-- no translation found for satellite_emergency_only_carrier_text (828510231597991206) --> + <skip /> <string name="accessibility_managed_profile" msgid="4703836746209377356">"Profile sa trabaho"</string> <string name="tuner_warning_title" msgid="7721976098452135267">"Masaya para sa ilan ngunit hindi para sa lahat"</string> <string name="tuner_warning" msgid="1861736288458481650">"Nagbibigay sa iyo ang Tuner ng System UI ng mga karagdagang paraan upang baguhin at i-customize ang user interface ng Android. Ang mga pang-eksperimentong feature na ito ay maaaring magbago, masira o mawala sa mga pagpapalabas sa hinaharap. Magpatuloy nang may pag-iingat."</string> diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml index dc317a9c71ff..19c9e0c50987 100644 --- a/packages/SystemUI/res/values-tr/strings.xml +++ b/packages/SystemUI/res/values-tr/strings.xml @@ -155,7 +155,7 @@ <string name="issuerecord_channel_description" msgid="6142326363431474632">"Sorun toplama oturumuyla ilgili devam eden görev bildirimi"</string> <string name="issuerecord_ongoing_screen_only" msgid="6248206059935015722">"Kayıt sorunu"</string> <string name="issuerecord_share_label" msgid="3992657993619876199">"Paylaş"</string> - <string name="issuerecord_save_title" msgid="4161043023696751591">"Sorun kaydı saklandı"</string> + <string name="issuerecord_save_title" msgid="4161043023696751591">"Sorun kaydı kaydedildi"</string> <string name="issuerecord_save_text" msgid="1205985304551521495">"Görüntülemek için dokunun"</string> <string name="issuerecord_save_error" msgid="6913040083446722726">"Sorun kaydı saklanırken hata oluştu"</string> <string name="issuerecord_start_error" msgid="3402782952722871190">"Sorun kaydı başlatılırken hata oluştu"</string> @@ -308,8 +308,7 @@ <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="7545274861795853838">"Sesi paylaş"</string> <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Ses paylaşılıyor"</string> - <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_accessibility (7604615019302091708) --> - <skip /> + <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"Ses paylaşımı ayarlarına gitmek için"</string> <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> @@ -436,6 +435,7 @@ <string name="zen_modes_dialog_done" msgid="6654130880256438950">"Bitti"</string> <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Ayarlar"</string> <string name="zen_mode_on" msgid="9085304934016242591">"Açık"</string> + <string name="zen_mode_on_with_details" msgid="7416143430557895497">"Açık • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string> <string name="zen_mode_off" msgid="1736604456618147306">"Kapalı"</string> <string name="zen_mode_set_up" msgid="7457957033034460064">"Ayarla"</string> <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"Ayarlarda yönet"</string> @@ -718,6 +718,8 @@ <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Uydu, bağlantı güçlü"</string> <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Uydu, bağlantı mevcut"</string> <string name="satellite_connected_carrier_text" msgid="118524195198532589">"Acil Uydu Bağlantısı"</string> + <!-- no translation found for satellite_emergency_only_carrier_text (828510231597991206) --> + <skip /> <string name="accessibility_managed_profile" msgid="4703836746209377356">"İş profili"</string> <string name="tuner_warning_title" msgid="7721976098452135267">"Bazıları için eğlenceliyken diğerleri için olmayabilir"</string> <string name="tuner_warning" msgid="1861736288458481650">"Sistem Kullanıcı Arayüzü Ayarlayıcı, Android kullanıcı arayüzünde değişiklikler yapmanız ve arayüzü özelleştirmeniz için ekstra yollar sağlar. Bu deneysel özellikler değişebilir, bozulabilir veya gelecekteki sürümlerde yer almayabilir. Dikkatli bir şekilde devam edin."</string> diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml index 0d243b7725a6..8af4120d09d9 100644 --- a/packages/SystemUI/res/values-uk/strings.xml +++ b/packages/SystemUI/res/values-uk/strings.xml @@ -308,8 +308,7 @@ <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth увімкнеться завтра вранці"</string> <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Поділитись аудіо"</string> <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Надсилання аудіо"</string> - <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_accessibility (7604615019302091708) --> - <skip /> + <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"відкрити налаштування надсилання аудіо"</string> <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> @@ -381,7 +380,7 @@ <string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Запис екрана"</string> <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Почати"</string> <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Зупинити"</string> - <string name="qs_record_issue_label" msgid="8166290137285529059">"Запис помилки"</string> + <string name="qs_record_issue_label" msgid="8166290137285529059">"Запис проблеми"</string> <string name="qs_record_issue_start" msgid="2979831312582567056">"Почати"</string> <string name="qs_record_issue_stop" msgid="3531747965741982657">"Зупинити"</string> <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Звіт про помилку"</string> @@ -391,7 +390,7 @@ <string name="performance" msgid="6552785217174378320">"Продуктивність"</string> <string name="user_interface" msgid="3712869377953950887">"Інтерфейс користувача"</string> <string name="thermal" msgid="6758074791325414831">"Нагрівання"</string> - <string name="custom" msgid="3337456985275158299">"Власні"</string> + <string name="custom" msgid="3337456985275158299">"Указати"</string> <string name="custom_trace_settings_dialog_title" msgid="2608570500144830554">"Власні налаштування трасування"</string> <string name="restore_default" msgid="5259420807486239755">"Відновити налаштування за умовчанням"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Режим керування однією рукою"</string> @@ -436,6 +435,7 @@ <string name="zen_modes_dialog_done" msgid="6654130880256438950">"Готово"</string> <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Налаштування"</string> <string name="zen_mode_on" msgid="9085304934016242591">"Увімкнено"</string> + <string name="zen_mode_on_with_details" msgid="7416143430557895497">"Увімк. • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string> <string name="zen_mode_off" msgid="1736604456618147306">"Вимкнено"</string> <string name="zen_mode_set_up" msgid="7457957033034460064">"Налаштувати"</string> <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"Керувати в налаштуваннях"</string> @@ -718,6 +718,8 @@ <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Хороше з’єднання із супутником"</string> <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Доступне з’єднання із супутником"</string> <string name="satellite_connected_carrier_text" msgid="118524195198532589">"Супутниковий сигнал SOS"</string> + <!-- no translation found for satellite_emergency_only_carrier_text (828510231597991206) --> + <skip /> <string name="accessibility_managed_profile" msgid="4703836746209377356">"Робочий профіль"</string> <string name="tuner_warning_title" msgid="7721976098452135267">"Це цікаво, але будьте обачні"</string> <string name="tuner_warning" msgid="1861736288458481650">"System UI Tuner пропонує нові способи налаштувати та персоналізувати інтерфейс користувача Android. Ці експериментальні функції можуть змінюватися, не працювати чи зникати в майбутніх версіях. Будьте обачні."</string> diff --git a/packages/SystemUI/res/values-ur/strings.xml b/packages/SystemUI/res/values-ur/strings.xml index 6872b80df782..dd275154d448 100644 --- a/packages/SystemUI/res/values-ur/strings.xml +++ b/packages/SystemUI/res/values-ur/strings.xml @@ -153,9 +153,9 @@ <string name="issuerecord_title" msgid="286627115110121849">"ایشو ریکارڈر"</string> <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"ایشو ریکارڈنگ پروسیس ہو رہی ہے"</string> <string name="issuerecord_channel_description" msgid="6142326363431474632">"ایشو کلیکشن سیشن کے لیے جاری اطلاع"</string> - <string name="issuerecord_ongoing_screen_only" msgid="6248206059935015722">"ریکارڈنگ ایشو"</string> + <string name="issuerecord_ongoing_screen_only" msgid="6248206059935015722">"مسئلہ ریکارڈ ہو رہا ہے"</string> <string name="issuerecord_share_label" msgid="3992657993619876199">"اشتراک کریں"</string> - <string name="issuerecord_save_title" msgid="4161043023696751591">"ایشو ریکارڈنگ محفوظ ہو گئی"</string> + <string name="issuerecord_save_title" msgid="4161043023696751591">"مسئلے کی ریکارڈنگ محفوظ ہو گئی"</string> <string name="issuerecord_save_text" msgid="1205985304551521495">"دیکھنے کیلئے تھپتھپائیں"</string> <string name="issuerecord_save_error" msgid="6913040083446722726">"ایشو ریکارڈنگ محفوظ کرنے میں خرابی"</string> <string name="issuerecord_start_error" msgid="3402782952722871190">"ایشو ریکارڈنگ شروع کرنے میں خرابی"</string> @@ -308,8 +308,7 @@ <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"بلوٹوتھ کل صبح آن ہو جائے گا"</string> <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"آڈیو کا اشتراک کریں"</string> <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"آڈیو کا اشتراک ہو رہا ہے"</string> - <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_accessibility (7604615019302091708) --> - <skip /> + <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"آڈیو کے اشتراک کی ترتیبات درج کریں"</string> <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> @@ -436,6 +435,7 @@ <string name="zen_modes_dialog_done" msgid="6654130880256438950">"ہو گیا"</string> <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"ترتیبات"</string> <string name="zen_mode_on" msgid="9085304934016242591">"آن ہے"</string> + <string name="zen_mode_on_with_details" msgid="7416143430557895497">"آن ہے • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string> <string name="zen_mode_off" msgid="1736604456618147306">"آف ہے"</string> <string name="zen_mode_set_up" msgid="7457957033034460064">"سیٹ اپ کریں"</string> <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"ترتیبات میں نظم کریں"</string> @@ -718,6 +718,8 @@ <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"سیٹلائٹ، کنکشن اچھا ہے"</string> <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"سیٹلائٹ، کنکشن دستیاب ہے"</string> <string name="satellite_connected_carrier_text" msgid="118524195198532589">"سیٹلائٹ SOS"</string> + <!-- no translation found for satellite_emergency_only_carrier_text (828510231597991206) --> + <skip /> <string name="accessibility_managed_profile" msgid="4703836746209377356">"دفتری پروفائل"</string> <string name="tuner_warning_title" msgid="7721976098452135267">"کچھ کیلئے دلچسپ لیکن سبھی کیلئے نہیں"</string> <string name="tuner_warning" msgid="1861736288458481650">"سسٹم UI ٹیونر Android صارف انٹر فیس میں ردوبدل کرنے اور اسے حسب ضرورت بنانے کیلئے آپ کو اضافی طریقے دیتا ہے۔ یہ تجرباتی خصوصیات مستقبل کی ریلیزز میں تبدیل ہو سکتی، رک سکتی یا غائب ہو سکتی ہیں۔ احتیاط کے ساتھ آگے بڑھیں۔"</string> diff --git a/packages/SystemUI/res/values-uz/strings.xml b/packages/SystemUI/res/values-uz/strings.xml index a652a6d09e37..b0ba287d0364 100644 --- a/packages/SystemUI/res/values-uz/strings.xml +++ b/packages/SystemUI/res/values-uz/strings.xml @@ -308,8 +308,7 @@ <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth ertaga ertalab yoqiladi"</string> <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Audioni ulashish"</string> <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Audio ulashuvi yoniq"</string> - <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_accessibility (7604615019302091708) --> - <skip /> + <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"audio ulashuv sozlamalarini kiritish"</string> <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> @@ -381,7 +380,7 @@ <string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Ekran yozuvi"</string> <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Boshlash"</string> <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Toʻxtatish"</string> - <string name="qs_record_issue_label" msgid="8166290137285529059">"Yozib olishda xato"</string> + <string name="qs_record_issue_label" msgid="8166290137285529059">"Nosozlikni yozib olish"</string> <string name="qs_record_issue_start" msgid="2979831312582567056">"Boshlash"</string> <string name="qs_record_issue_stop" msgid="3531747965741982657">"Toʻxtatish"</string> <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Xatoliklar hisoboti"</string> @@ -436,6 +435,7 @@ <string name="zen_modes_dialog_done" msgid="6654130880256438950">"Tayyor"</string> <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Sozlamalar"</string> <string name="zen_mode_on" msgid="9085304934016242591">"Yoniq"</string> + <string name="zen_mode_on_with_details" msgid="7416143430557895497">"Yoniq • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string> <string name="zen_mode_off" msgid="1736604456618147306">"Yoqilmagan"</string> <string name="zen_mode_set_up" msgid="7457957033034460064">"Sozlash"</string> <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"Sozlamalarda boshqarish"</string> @@ -718,6 +718,8 @@ <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Sputnik, aloqa sifati yaxshi"</string> <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Sputnik, aloqa mavjud"</string> <string name="satellite_connected_carrier_text" msgid="118524195198532589">"Sputnik SOS"</string> + <!-- no translation found for satellite_emergency_only_carrier_text (828510231597991206) --> + <skip /> <string name="accessibility_managed_profile" msgid="4703836746209377356">"Ish profili"</string> <string name="tuner_warning_title" msgid="7721976098452135267">"Diqqat!"</string> <string name="tuner_warning" msgid="1861736288458481650">"System UI Tuner yordamida siz Android foydalanuvchi interfeysini tuzatish va o‘zingizga moslashtirishingiz mumkin. Ushbu tajribaviy funksiyalar o‘zgarishi, buzilishi yoki keyingi versiyalarda olib tashlanishi mumkin. Ehtiyot bo‘lib davom eting."</string> diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml index 6f55d15e4117..b6c696741318 100644 --- a/packages/SystemUI/res/values-vi/strings.xml +++ b/packages/SystemUI/res/values-vi/strings.xml @@ -153,7 +153,7 @@ <string name="issuerecord_title" msgid="286627115110121849">"Trình ghi sự cố"</string> <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"Đang xử lý bản ghi sự cố"</string> <string name="issuerecord_channel_description" msgid="6142326363431474632">"Thông báo hiển thị liên tục cho một phiên thu thập sự cố"</string> - <string name="issuerecord_ongoing_screen_only" msgid="6248206059935015722">"Ghi sự cố"</string> + <string name="issuerecord_ongoing_screen_only" msgid="6248206059935015722">"Đang ghi sự cố"</string> <string name="issuerecord_share_label" msgid="3992657993619876199">"Chia sẻ"</string> <string name="issuerecord_save_title" msgid="4161043023696751591">"Đã lưu bản ghi sự cố"</string> <string name="issuerecord_save_text" msgid="1205985304551521495">"Nhấn để xem"</string> @@ -308,8 +308,7 @@ <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="7545274861795853838">"Chia sẻ âm thanh"</string> <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Đang chia sẻ âm thanh"</string> - <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_accessibility (7604615019302091708) --> - <skip /> + <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"mở chế độ cài đặt chia sẻ âm thanh"</string> <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> @@ -381,12 +380,12 @@ <string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Ghi màn hình"</string> <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Bắt đầu"</string> <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Dừng"</string> - <string name="qs_record_issue_label" msgid="8166290137285529059">"Ghi lại vấn đề"</string> + <string name="qs_record_issue_label" msgid="8166290137285529059">"Ghi sự cố"</string> <string name="qs_record_issue_start" msgid="2979831312582567056">"Bắt đầu"</string> <string name="qs_record_issue_stop" msgid="3531747965741982657">"Dừng"</string> <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Báo cáo lỗi"</string> - <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_header" msgid="5995983175678658329">"Bạn gặp loại sự cố gì khi dùng thiết bị?"</string> + <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Chọn loại sự cố"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Ghi màn hình"</string> <string name="performance" msgid="6552785217174378320">"Hiệu suất"</string> <string name="user_interface" msgid="3712869377953950887">"Giao diện người dùng"</string> @@ -436,6 +435,7 @@ <string name="zen_modes_dialog_done" msgid="6654130880256438950">"Xong"</string> <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Cài đặt"</string> <string name="zen_mode_on" msgid="9085304934016242591">"Đang bật"</string> + <string name="zen_mode_on_with_details" msgid="7416143430557895497">"Bật • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string> <string name="zen_mode_off" msgid="1736604456618147306">"Đang tắt"</string> <string name="zen_mode_set_up" msgid="7457957033034460064">"Thiết lập"</string> <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"Quản lý trong phần cài đặt"</string> @@ -718,6 +718,8 @@ <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Kết nối vệ tinh tốt"</string> <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Hiện có kết nối vệ tinh"</string> <string name="satellite_connected_carrier_text" msgid="118524195198532589">"Liên lạc khẩn cấp qua vệ tinh"</string> + <!-- no translation found for satellite_emergency_only_carrier_text (828510231597991206) --> + <skip /> <string name="accessibility_managed_profile" msgid="4703836746209377356">"Hồ sơ công việc"</string> <string name="tuner_warning_title" msgid="7721976098452135267">"Thú vị đối với một số người nhưng không phải tất cả"</string> <string name="tuner_warning" msgid="1861736288458481650">"Bộ điều hướng giao diện người dùng hệ thống cung cấp thêm cho bạn những cách chỉnh sửa và tùy chỉnh giao diện người dùng Android. Những tính năng thử nghiệm này có thể thay đổi, hỏng hoặc biến mất trong các phiên bản tương lai. Hãy thận trọng khi tiếp tục."</string> diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml index d65bf808c380..9ce05a372a1a 100644 --- a/packages/SystemUI/res/values-zh-rCN/strings.xml +++ b/packages/SystemUI/res/values-zh-rCN/strings.xml @@ -308,8 +308,7 @@ <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"蓝牙将在明天早上开启"</string> <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"分享音频"</string> <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"正在分享音频"</string> - <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_accessibility (7604615019302091708) --> - <skip /> + <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"进入音频分享设置"</string> <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> @@ -436,6 +435,7 @@ <string name="zen_modes_dialog_done" msgid="6654130880256438950">"完成"</string> <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"设置"</string> <string name="zen_mode_on" msgid="9085304934016242591">"已开启"</string> + <string name="zen_mode_on_with_details" msgid="7416143430557895497">"已开启 • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string> <string name="zen_mode_off" msgid="1736604456618147306">"已关闭"</string> <string name="zen_mode_set_up" msgid="7457957033034460064">"设置"</string> <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"在设置中管理"</string> @@ -718,6 +718,8 @@ <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"卫星,连接质量良好"</string> <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"卫星,可连接"</string> <string name="satellite_connected_carrier_text" msgid="118524195198532589">"卫星紧急呼救"</string> + <!-- no translation found for satellite_emergency_only_carrier_text (828510231597991206) --> + <skip /> <string name="accessibility_managed_profile" msgid="4703836746209377356">"工作资料"</string> <string name="tuner_warning_title" msgid="7721976098452135267">"并不适合所有用户"</string> <string name="tuner_warning" msgid="1861736288458481650">"系统界面调节工具可让您以更多方式调整及定制 Android 界面。在日后推出的版本中,这些实验性功能可能会变更、失效或消失。操作时请务必谨慎。"</string> diff --git a/packages/SystemUI/res/values-zh-rHK/strings.xml b/packages/SystemUI/res/values-zh-rHK/strings.xml index 5c258413946e..e8532bec6d2c 100644 --- a/packages/SystemUI/res/values-zh-rHK/strings.xml +++ b/packages/SystemUI/res/values-zh-rHK/strings.xml @@ -308,8 +308,7 @@ <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"藍牙將於明天上午開啟"</string> <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"分享音訊"</string> <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"正在分享音訊"</string> - <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_accessibility (7604615019302091708) --> - <skip /> + <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"輸入音訊分享功能設定"</string> <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> @@ -436,6 +435,7 @@ <string name="zen_modes_dialog_done" msgid="6654130880256438950">"完成"</string> <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"設定"</string> <string name="zen_mode_on" msgid="9085304934016242591">"開啟"</string> + <string name="zen_mode_on_with_details" msgid="7416143430557895497">"開 • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string> <string name="zen_mode_off" msgid="1736604456618147306">"關閉"</string> <string name="zen_mode_set_up" msgid="7457957033034460064">"設定"</string> <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"在「設定」中管理"</string> @@ -718,6 +718,8 @@ <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"衛星,連線質素好"</string> <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"衛星,可以連線"</string> <string name="satellite_connected_carrier_text" msgid="118524195198532589">"緊急衛星連接"</string> + <!-- no translation found for satellite_emergency_only_carrier_text (828510231597991206) --> + <skip /> <string name="accessibility_managed_profile" msgid="4703836746209377356">"工作設定檔"</string> <string name="tuner_warning_title" msgid="7721976098452135267">"這只是測試版本,並不包含完整功能"</string> <string name="tuner_warning" msgid="1861736288458481650">"使用者介面調諧器讓你以更多方法修改和自訂 Android 使用者介面。但請小心,這些實驗功能可能會在日後發佈時更改、分拆或消失。"</string> diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml index 76019ab82042..e0493743e811 100644 --- a/packages/SystemUI/res/values-zh-rTW/strings.xml +++ b/packages/SystemUI/res/values-zh-rTW/strings.xml @@ -134,14 +134,14 @@ <string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"目前正在錄製「<xliff:g id="APP_NAME">%1$s</xliff:g>」的畫面"</string> <string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"停止錄製"</string> <string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"正在分享畫面"</string> - <string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"要停止分享畫面嗎?"</string> + <string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"停止分享?"</string> <string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"目前正在與「<xliff:g id="HOST_APP_NAME">%1$s</xliff:g>」分享整個畫面"</string> <string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"目前正在與某個應用程式分享整個畫面"</string> <string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"目前正在分享「<xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>」的畫面"</string> <string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"目前正在分享應用程式畫面"</string> <string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"停止分享"</string> <string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"正在投放畫面"</string> - <string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"要停止投放嗎?"</string> + <string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"停止投放?"</string> <string name="cast_to_other_device_stop_dialog_message_entire_screen_with_device" msgid="1474703115926205251">"目前正在將整個畫面投放到「<xliff:g id="DEVICE_NAME">%1$s</xliff:g>」"</string> <string name="cast_to_other_device_stop_dialog_message_entire_screen" msgid="8419219169553867625">"目前正在將整個畫面投放到鄰近裝置"</string> <string name="cast_to_other_device_stop_dialog_message_specific_app_with_device" msgid="2715934698604085519">"目前正在將「<xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>」投放到「<xliff:g id="DEVICE_NAME">%2$s</xliff:g>」"</string> @@ -308,8 +308,7 @@ <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"藍牙會在明天早上開啟"</string> <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"分享音訊"</string> <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"正在分享音訊"</string> - <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_accessibility (7604615019302091708) --> - <skip /> + <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"進入音訊分享設定"</string> <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> @@ -436,6 +435,7 @@ <string name="zen_modes_dialog_done" msgid="6654130880256438950">"完成"</string> <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"設定"</string> <string name="zen_mode_on" msgid="9085304934016242591">"開啟"</string> + <string name="zen_mode_on_with_details" msgid="7416143430557895497">"已開啟 • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string> <string name="zen_mode_off" msgid="1736604456618147306">"關閉"</string> <string name="zen_mode_set_up" msgid="7457957033034460064">"設定"</string> <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"在「設定」中管理"</string> @@ -718,6 +718,8 @@ <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"衛星,連線品質良好"</string> <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"衛星,可連線"</string> <string name="satellite_connected_carrier_text" msgid="118524195198532589">"緊急衛星連線"</string> + <!-- no translation found for satellite_emergency_only_carrier_text (828510231597991206) --> + <skip /> <string name="accessibility_managed_profile" msgid="4703836746209377356">"工作資料夾"</string> <string name="tuner_warning_title" msgid="7721976098452135267">"有趣與否,見仁見智"</string> <string name="tuner_warning" msgid="1861736288458481650">"系統使用者介面調整精靈可讓你透過其他方式,調整及自訂 Android 使用者介面。這些實驗性功能隨著版本更新可能會變更、損壞或消失,執行時請務必謹慎。"</string> diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml index 04fc75df87cc..d85db5572f4f 100644 --- a/packages/SystemUI/res/values-zu/strings.xml +++ b/packages/SystemUI/res/values-zu/strings.xml @@ -308,8 +308,7 @@ <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"IBluetooth izovuleka kusasa ekuseni"</string> <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Yabelana ngomsindo"</string> <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Yabelana ngomsindo"</string> - <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_accessibility (7604615019302091708) --> - <skip /> + <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"faka amasethingi okwabelana ngokuqoshiwe"</string> <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> @@ -436,6 +435,7 @@ <string name="zen_modes_dialog_done" msgid="6654130880256438950">"Kwenziwe"</string> <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Amasethingi"</string> <string name="zen_mode_on" msgid="9085304934016242591">"Vuliwe"</string> + <string name="zen_mode_on_with_details" msgid="7416143430557895497">"Vuliwe • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string> <string name="zen_mode_off" msgid="1736604456618147306">"Valiwe"</string> <string name="zen_mode_set_up" msgid="7457957033034460064">"Setha"</string> <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"Phatha kumasethingi"</string> @@ -718,6 +718,8 @@ <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Isethelayithi, uxhumano oluhle"</string> <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Isethelayithi, uxhumano luyatholakala"</string> <string name="satellite_connected_carrier_text" msgid="118524195198532589">"Isethelayithi yokuxhumana ngezimo eziphuthumayo"</string> + <!-- no translation found for satellite_emergency_only_carrier_text (828510231597991206) --> + <skip /> <string name="accessibility_managed_profile" msgid="4703836746209377356">"Iphrofayela yomsebenzi"</string> <string name="tuner_warning_title" msgid="7721976098452135267">"Kuyajabulisa kwabanye kodwa hhayi bonke"</string> <string name="tuner_warning" msgid="1861736288458481650">"Isishuni se-UI sesistimu sikunika izindlela ezingeziwe zokuhlobisa nokwenza ngezifiso isixhumanisi sokubona se-Android. Lezi zici zesilingo zingashintsha, zephuke, noma zinyamalale ekukhishweni kwangakusasa. Qhubeka ngokuqaphela."</string> diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml index 823ff9f54be3..e8fd2ef6eafa 100644 --- a/packages/SystemUI/res/values/config.xml +++ b/packages/SystemUI/res/values/config.xml @@ -727,6 +727,10 @@ .75 </item> + <!-- The last x ms of face acquired info messages to analyze to determine + whether to show a deferred face auth help message. --> + <integer name="config_face_help_msgs_defer_analyze_timeframe">500</integer> + <!-- Which face help messages to surface when fingerprint is also enrolled. Message ids correspond with the acquired ids in BiometricFaceConstants --> <integer-array name="config_face_help_msgs_when_fingerprint_enrolled"> diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index fd943d0a5414..e6cc6cf766c6 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -740,6 +740,8 @@ <string name="quick_settings_bluetooth_device_connected">Connected</string> <!-- QuickSettings: Bluetooth dialog device in audio sharing default summary [CHAR LIMIT=50]--> <string name="quick_settings_bluetooth_device_audio_sharing">Audio Sharing</string> + <!-- QuickSettings: Bluetooth dialog device summary for devices that are capable of audio sharing and switching to active[CHAR LIMIT=NONE]--> + <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active">Tap to switch or share audio</string> <!-- QuickSettings: Bluetooth dialog device saved default summary [CHAR LIMIT=NONE]--> <string name="quick_settings_bluetooth_device_saved">Saved</string> <!-- QuickSettings: Accessibility label to disconnect a device [CHAR LIMIT=NONE]--> diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java index 64fe78d9958b..7ec977a8d6aa 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java @@ -218,6 +218,7 @@ public class Task { @ViewDebug.ExportedProperty(category="recents") public String title; @ViewDebug.ExportedProperty(category="recents") + @Nullable public String titleDescription; @ViewDebug.ExportedProperty(category="recents") public int colorPrimary; diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordViewController.java index 490ad5c4136d..3ad73bc17704 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordViewController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordViewController.java @@ -19,9 +19,12 @@ package com.android.keyguard; import static com.android.systemui.flags.Flags.LOCKSCREEN_ENABLE_LANDSCAPE; import static com.android.systemui.util.kotlin.JavaAdapterKt.collectFlow; +import android.content.Context; +import android.content.pm.PackageManager; import android.content.res.Resources; import android.graphics.drawable.Drawable; import android.os.UserHandle; +import android.os.UserManager; import android.text.Editable; import android.text.InputType; import android.text.TextUtils; @@ -170,8 +173,33 @@ public class KeyguardPasswordViewController mPasswordEntry.setOnEditorActionListener(mOnEditorActionListener); mPasswordEntry.setOnKeyListener(mKeyListener); mPasswordEntry.addTextChangedListener(mTextWatcher); + // Poke the wakelock any time the text is selected or modified - mPasswordEntry.setOnClickListener(v -> mKeyguardSecurityCallback.userActivity()); + // TODO(b/362362385): Revert to the previous onClickListener implementation once this bug is + // fixed. + mPasswordEntry.setOnClickListener(new View.OnClickListener() { + + private final boolean mAutomotiveAndVisibleBackgroundUsers = + isAutomotiveAndVisibleBackgroundUsers(); + + @Override + public void onClick(View v) { + if (mAutomotiveAndVisibleBackgroundUsers) { + mInputMethodManager.restartInput(v); + } + mKeyguardSecurityCallback.userActivity(); + } + + private boolean isAutomotiveAndVisibleBackgroundUsers() { + final Context context = getContext(); + return context.getPackageManager().hasSystemFeature( + PackageManager.FEATURE_AUTOMOTIVE) + && UserManager.isVisibleBackgroundUsersEnabled() + && context.getResources().getBoolean( + android.R.bool.config_perDisplayFocusEnabled); + } + }); + mSwitchImeButton.setOnClickListener(v -> { mKeyguardSecurityCallback.userActivity(); // Leave the screen on a bit longer // Do not show auxiliary subtypes in password lock screen. diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java index afd42cbfdb25..61f9800c351b 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java @@ -81,7 +81,7 @@ import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInt import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor; import com.android.systemui.flags.FeatureFlags; import com.android.systemui.keyguard.KeyguardWmStateRefactor; -import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor; +import com.android.systemui.keyguard.domain.interactor.KeyguardDismissTransitionInteractor; import com.android.systemui.log.SessionTracker; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.plugins.FalsingManager; @@ -134,7 +134,7 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard private final DeviceEntryFaceAuthInteractor mDeviceEntryFaceAuthInteractor; private final BouncerMessageInteractor mBouncerMessageInteractor; private int mTranslationY; - private final KeyguardTransitionInteractor mKeyguardTransitionInteractor; + private final KeyguardDismissTransitionInteractor mKeyguardDismissTransitionInteractor; private final DevicePolicyManager mDevicePolicyManager; // Whether the volume keys should be handled by keyguard. If true, then // they will be handled here for specific media types such as music, otherwise @@ -321,7 +321,7 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard } if (KeyguardWmStateRefactor.isEnabled()) { - mKeyguardTransitionInteractor.startDismissKeyguardTransition( + mKeyguardDismissTransitionInteractor.startDismissKeyguardTransition( "KeyguardSecurityContainerController#finish"); } } @@ -458,7 +458,7 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard DeviceProvisionedController deviceProvisionedController, FaceAuthAccessibilityDelegate faceAuthAccessibilityDelegate, DevicePolicyManager devicePolicyManager, - KeyguardTransitionInteractor keyguardTransitionInteractor, + KeyguardDismissTransitionInteractor keyguardDismissTransitionInteractor, Lazy<PrimaryBouncerInteractor> primaryBouncerInteractor, Provider<DeviceEntryInteractor> deviceEntryInteractor ) { @@ -490,7 +490,7 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard mSelectedUserInteractor = selectedUserInteractor; mDeviceEntryInteractor = deviceEntryInteractor; mJavaAdapter = javaAdapter; - mKeyguardTransitionInteractor = keyguardTransitionInteractor; + mKeyguardDismissTransitionInteractor = keyguardDismissTransitionInteractor; mDeviceProvisionedController = deviceProvisionedController; mPrimaryBouncerInteractor = primaryBouncerInteractor; mDevicePolicyManager = devicePolicyManager; diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java index 9b45fa47cf21..f731186f6d01 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java @@ -3811,7 +3811,8 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab if (!mSimDatas.containsKey(subId)) { refreshSimState(subId, SubscriptionManager.getSlotIndex(subId)); } - return mSimDatas.get(subId).slotId; + SimData simData = mSimDatas.get(subId); + return simData != null ? simData.slotId : SubscriptionManager.INVALID_SUBSCRIPTION_ID; } private final TaskStackChangeListener mTaskStackListener = new TaskStackChangeListener() { diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationSettings.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationSettings.java index 9b6501eb1e57..2f0ca6e6bf9d 100644 --- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationSettings.java +++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationSettings.java @@ -482,6 +482,10 @@ class WindowMagnificationSettings implements MagnificationGestureDetector.OnGest } else { // mode = ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW mEditButton.setVisibility(View.VISIBLE); mAllowDiagonalScrollingView.setVisibility(View.VISIBLE); + if (Flags.saveAndRestoreMagnificationSettingsButtons()) { + selectedButtonIndex = + windowMagnificationFrameSizePrefs.getIndexForCurrentDensity(); + } } break; diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuController.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuController.java index e4b7b7e69c61..275147e6694c 100644 --- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuController.java +++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuController.java @@ -21,11 +21,13 @@ import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL; import android.content.Context; import android.hardware.display.DisplayManager; +import android.os.Handler; import android.os.UserHandle; import android.text.TextUtils; import android.view.Display; import android.view.WindowManager; import android.view.accessibility.AccessibilityManager; +import android.view.accessibility.IUserInitializationCompleteCallback; import androidx.annotation.MainThread; @@ -68,6 +70,9 @@ public class AccessibilityFloatingMenuController implements private int mBtnMode; private String mBtnTargets; private boolean mIsKeyguardVisible; + private boolean mIsUserInInitialization; + @VisibleForTesting + Handler mHandler; @VisibleForTesting final KeyguardUpdateMonitorCallback mKeyguardCallback = new KeyguardUpdateMonitorCallback() { @@ -86,18 +91,14 @@ public class AccessibilityFloatingMenuController implements @Override public void onUserSwitching(int userId) { destroyFloatingMenu(); - } - - @Override - public void onUserSwitchComplete(int userId) { - mContext = mContext.createContextAsUser(UserHandle.of(userId), /* flags= */ 0); - mBtnMode = mAccessibilityButtonModeObserver.getCurrentAccessibilityButtonMode(); - mBtnTargets = - mAccessibilityButtonTargetsObserver.getCurrentAccessibilityButtonTargets(); - handleFloatingMenuVisibility(mIsKeyguardVisible, mBtnMode, mBtnTargets); + mIsUserInInitialization = true; } }; + @VisibleForTesting + final UserInitializationCompleteCallback mUserInitializationCompleteCallback = + new UserInitializationCompleteCallback(); + @Inject public AccessibilityFloatingMenuController(Context context, WindowManager windowManager, @@ -109,7 +110,8 @@ public class AccessibilityFloatingMenuController implements KeyguardUpdateMonitor keyguardUpdateMonitor, SecureSettings secureSettings, DisplayTracker displayTracker, - NavigationModeController navigationModeController) { + NavigationModeController navigationModeController, + Handler handler) { mContext = context; mWindowManager = windowManager; mViewCaptureAwareWindowManager = viewCaptureAwareWindowManager; @@ -121,6 +123,7 @@ public class AccessibilityFloatingMenuController implements mSecureSettings = secureSettings; mDisplayTracker = displayTracker; mNavigationModeController = navigationModeController; + mHandler = handler; mIsKeyguardVisible = false; } @@ -159,6 +162,8 @@ public class AccessibilityFloatingMenuController implements mAccessibilityButtonModeObserver.addListener(this); mAccessibilityButtonTargetsObserver.addListener(this); mKeyguardUpdateMonitor.registerCallback(mKeyguardCallback); + mAccessibilityManager.registerUserInitializationCompleteCallback( + mUserInitializationCompleteCallback); } /** @@ -172,7 +177,7 @@ public class AccessibilityFloatingMenuController implements */ private void handleFloatingMenuVisibility(boolean keyguardVisible, @AccessibilityButtonMode int mode, String targets) { - if (keyguardVisible) { + if (keyguardVisible || mIsUserInInitialization) { destroyFloatingMenu(); return; } @@ -210,4 +215,18 @@ public class AccessibilityFloatingMenuController implements mFloatingMenu.hide(); mFloatingMenu = null; } + + class UserInitializationCompleteCallback + extends IUserInitializationCompleteCallback.Stub { + @Override + public void onUserInitializationComplete(int userId) { + mIsUserInInitialization = false; + mContext = mContext.createContextAsUser(UserHandle.of(userId), /* flags= */ 0); + mBtnMode = mAccessibilityButtonModeObserver.getCurrentAccessibilityButtonMode(); + mBtnTargets = + mAccessibilityButtonTargetsObserver.getCurrentAccessibilityButtonTargets(); + mHandler.post( + () -> handleFloatingMenuVisibility(mIsKeyguardVisible, mBtnMode, mBtnTargets)); + } + } } diff --git a/packages/SystemUI/src/com/android/systemui/ambient/touch/BouncerSwipeTouchHandler.kt b/packages/SystemUI/src/com/android/systemui/ambient/touch/BouncerSwipeTouchHandler.kt index a093f58b88ba..fd06bb174ea3 100644 --- a/packages/SystemUI/src/com/android/systemui/ambient/touch/BouncerSwipeTouchHandler.kt +++ b/packages/SystemUI/src/com/android/systemui/ambient/touch/BouncerSwipeTouchHandler.kt @@ -29,7 +29,6 @@ import android.view.VelocityTracker import androidx.annotation.VisibleForTesting import com.android.internal.logging.UiEvent import com.android.internal.logging.UiEventLogger -import com.android.internal.widget.LockPatternUtils import com.android.systemui.Flags import com.android.systemui.ambient.touch.TouchHandler.TouchSession import com.android.systemui.ambient.touch.dagger.BouncerSwipeModule @@ -37,8 +36,8 @@ import com.android.systemui.ambient.touch.scrim.ScrimController import com.android.systemui.ambient.touch.scrim.ScrimManager import com.android.systemui.bouncer.shared.constants.KeyguardBouncerConstants import com.android.systemui.communal.ui.viewmodel.CommunalViewModel +import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor import com.android.systemui.plugins.ActivityStarter -import com.android.systemui.settings.UserTracker import com.android.systemui.shade.ShadeExpansionChangeEvent import com.android.systemui.statusbar.NotificationShadeWindowController import com.android.systemui.statusbar.phone.CentralSurfaces @@ -63,8 +62,6 @@ constructor( private val notificationShadeWindowController: NotificationShadeWindowController, private val valueAnimatorCreator: ValueAnimatorCreator, private val velocityTrackerFactory: VelocityTrackerFactory, - private val lockPatternUtils: LockPatternUtils, - private val userTracker: UserTracker, private val communalViewModel: CommunalViewModel, @param:Named(BouncerSwipeModule.SWIPE_TO_BOUNCER_FLING_ANIMATION_UTILS_OPENING) private val flingAnimationUtils: FlingAnimationUtils, @@ -75,7 +72,8 @@ constructor( @param:Named(BouncerSwipeModule.MIN_BOUNCER_ZONE_SCREEN_PERCENTAGE) private val minBouncerZoneScreenPercentage: Float, private val uiEventLogger: UiEventLogger, - private val activityStarter: ActivityStarter + private val activityStarter: ActivityStarter, + private val keyguardInteractor: KeyguardInteractor, ) : TouchHandler { /** An interface for creating ValueAnimators. */ interface ValueAnimatorCreator { @@ -148,7 +146,7 @@ constructor( // If scrolling up and keyguard is not locked, dismiss both keyguard and the // dream since there's no bouncer to show. - if (y > e2.y && !lockPatternUtils.isSecure(userTracker.userId)) { + if (y > e2.y && keyguardInteractor.isKeyguardDismissible.value) { activityStarter.executeRunnableDismissingKeyguard( { centralSurfaces.get().awakenDreams() }, /* cancelAction= */ null, @@ -331,8 +329,8 @@ constructor( return } - // Don't set expansion if the user doesn't have a pin/password set. - if (!lockPatternUtils.isSecure(userTracker.userId)) { + // Don't set expansion if keyguard is dismissible (i.e. unlocked). + if (keyguardInteractor.isKeyguardDismissible.value) { return } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/FaceHelpMessageDebouncer.kt b/packages/SystemUI/src/com/android/systemui/biometrics/FaceHelpMessageDebouncer.kt index 1685f49e4f3e..4731ebba7124 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/FaceHelpMessageDebouncer.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/FaceHelpMessageDebouncer.kt @@ -25,11 +25,13 @@ import com.android.systemui.deviceentry.shared.model.HelpFaceAuthenticationStatu * - startWindow: Window of time on start required before showing the first help message * - shownFaceMessageFrequencyBoost: Frequency boost given to messages that are currently shown to * the user + * - threshold: minimum percentage of frames a message must appear in order to show it */ class FaceHelpMessageDebouncer( private val window: Long = DEFAULT_WINDOW_MS, private val startWindow: Long = window, private val shownFaceMessageFrequencyBoost: Int = 4, + private val threshold: Float = 0f, ) { private val TAG = "FaceHelpMessageDebouncer" private var startTime = 0L @@ -56,7 +58,7 @@ class FaceHelpMessageDebouncer( } } - private fun getMostFrequentHelpMessage(): HelpFaceAuthenticationStatus? { + private fun getMostFrequentHelpMessageSurpassingThreshold(): HelpFaceAuthenticationStatus? { // freqMap: msgId => frequency val freqMap = helpFaceAuthStatuses.groupingBy { it.msgId }.eachCount().toMutableMap() @@ -83,7 +85,25 @@ class FaceHelpMessageDebouncer( } } ?.key - return helpFaceAuthStatuses.findLast { it.msgId == msgIdWithHighestFrequency } + + if (msgIdWithHighestFrequency == null) { + return null + } + + val freq = + if (msgIdWithHighestFrequency == lastMessageIdShown) { + freqMap[msgIdWithHighestFrequency]!! - shownFaceMessageFrequencyBoost + } else { + freqMap[msgIdWithHighestFrequency]!! + } + .toFloat() + + return if ((freq / helpFaceAuthStatuses.size.toFloat()) >= threshold) { + helpFaceAuthStatuses.findLast { it.msgId == msgIdWithHighestFrequency } + } else { + Log.v(TAG, "most frequent helpFaceAuthStatus didn't make the threshold: $threshold") + null + } } fun addMessage(helpFaceAuthStatus: HelpFaceAuthenticationStatus) { @@ -98,14 +118,15 @@ class FaceHelpMessageDebouncer( return null } removeOldMessages(atTimestamp) - val messageToShow = getMostFrequentHelpMessage() + val messageToShow = getMostFrequentHelpMessageSurpassingThreshold() if (lastMessageIdShown != messageToShow?.msgId) { Log.v( TAG, "showMessage previousLastMessageId=$lastMessageIdShown" + "\n\tmessageToShow=$messageToShow " + "\n\thelpFaceAuthStatusesSize=${helpFaceAuthStatuses.size}" + - "\n\thelpFaceAuthStatuses=$helpFaceAuthStatuses" + "\n\thelpFaceAuthStatuses=$helpFaceAuthStatuses" + + "\n\tthreshold=$threshold" ) lastMessageIdShown = messageToShow?.msgId } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/FaceHelpMessageDeferral.kt b/packages/SystemUI/src/com/android/systemui/biometrics/FaceHelpMessageDeferral.kt index 90d06fb0bec1..d382adaff955 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/FaceHelpMessageDeferral.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/FaceHelpMessageDeferral.kt @@ -17,14 +17,19 @@ package com.android.systemui.biometrics import android.content.res.Resources +import android.os.SystemClock.elapsedRealtime import com.android.keyguard.logging.BiometricMessageDeferralLogger import com.android.systemui.Dumpable +import com.android.systemui.Flags.faceMessageDeferUpdate import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Main +import com.android.systemui.deviceentry.shared.model.HelpFaceAuthenticationStatus import com.android.systemui.dump.DumpManager import com.android.systemui.log.LogBuffer import com.android.systemui.log.dagger.BiometricLog import com.android.systemui.res.R +import com.android.systemui.util.time.SystemClock +import dagger.Lazy import java.io.PrintWriter import java.util.Objects import java.util.UUID @@ -36,7 +41,8 @@ class FaceHelpMessageDeferralFactory constructor( @Main private val resources: Resources, @BiometricLog private val logBuffer: LogBuffer, - private val dumpManager: DumpManager + private val dumpManager: DumpManager, + private val systemClock: Lazy<SystemClock>, ) { fun create(): FaceHelpMessageDeferral { val id = UUID.randomUUID().toString() @@ -45,6 +51,7 @@ constructor( logBuffer = BiometricMessageDeferralLogger(logBuffer, "FaceHelpMessageDeferral[$id]"), dumpManager = dumpManager, id = id, + systemClock, ) } } @@ -58,14 +65,17 @@ class FaceHelpMessageDeferral( logBuffer: BiometricMessageDeferralLogger, dumpManager: DumpManager, val id: String, + val systemClock: Lazy<SystemClock>, ) : BiometricMessageDeferral( resources.getIntArray(R.array.config_face_help_msgs_defer_until_timeout).toHashSet(), resources.getIntArray(R.array.config_face_help_msgs_ignore).toHashSet(), resources.getFloat(R.dimen.config_face_help_msgs_defer_until_timeout_threshold), + resources.getInteger(R.integer.config_face_help_msgs_defer_analyze_timeframe).toLong(), logBuffer, dumpManager, id, + systemClock, ) /** @@ -77,10 +87,24 @@ open class BiometricMessageDeferral( private val messagesToDefer: Set<Int>, private val acquiredInfoToIgnore: Set<Int>, private val threshold: Float, + private val windowToAnalyzeLastNFrames: Long, private val logBuffer: BiometricMessageDeferralLogger, dumpManager: DumpManager, id: String, + private val systemClock: Lazy<SystemClock>, ) : Dumpable { + + private val faceHelpMessageDebouncer: FaceHelpMessageDebouncer? = + if (faceMessageDeferUpdate()) { + FaceHelpMessageDebouncer( + window = windowToAnalyzeLastNFrames, + startWindow = 0L, + shownFaceMessageFrequencyBoost = 0, + threshold = threshold, + ) + } else { + null + } private val acquiredInfoToFrequency: MutableMap<Int, Int> = HashMap() private val acquiredInfoToHelpString: MutableMap<Int, String> = HashMap() private var mostFrequentAcquiredInfoToDefer: Int? = null @@ -97,13 +121,20 @@ open class BiometricMessageDeferral( pw.println("messagesToDefer=$messagesToDefer") pw.println("totalFrames=$totalFrames") pw.println("threshold=$threshold") + pw.println("faceMessageDeferUpdateFlagEnabled=${faceMessageDeferUpdate()}") + if (faceMessageDeferUpdate()) { + pw.println("windowToAnalyzeLastNFrames(ms)=$windowToAnalyzeLastNFrames") + } } /** Reset all saved counts. */ fun reset() { totalFrames = 0 - mostFrequentAcquiredInfoToDefer = null - acquiredInfoToFrequency.clear() + if (!faceMessageDeferUpdate()) { + mostFrequentAcquiredInfoToDefer = null + acquiredInfoToFrequency.clear() + } + acquiredInfoToHelpString.clear() logBuffer.reset() } @@ -137,24 +168,48 @@ open class BiometricMessageDeferral( logBuffer.logFrameIgnored(acquiredInfo) return } - totalFrames++ - val newAcquiredInfoCount = acquiredInfoToFrequency.getOrDefault(acquiredInfo, 0) + 1 - acquiredInfoToFrequency[acquiredInfo] = newAcquiredInfoCount - if ( - messagesToDefer.contains(acquiredInfo) && - (mostFrequentAcquiredInfoToDefer == null || - newAcquiredInfoCount > - acquiredInfoToFrequency.getOrDefault(mostFrequentAcquiredInfoToDefer!!, 0)) - ) { - mostFrequentAcquiredInfoToDefer = acquiredInfo + if (faceMessageDeferUpdate()) { + faceHelpMessageDebouncer?.let { + val helpFaceAuthStatus = + HelpFaceAuthenticationStatus( + msgId = acquiredInfo, + msg = null, + systemClock.get().elapsedRealtime() + ) + if (totalFrames == 1) { // first frame + it.startNewFaceAuthSession(helpFaceAuthStatus.createdAt) + } + it.addMessage(helpFaceAuthStatus) + } + } else { + val newAcquiredInfoCount = acquiredInfoToFrequency.getOrDefault(acquiredInfo, 0) + 1 + acquiredInfoToFrequency[acquiredInfo] = newAcquiredInfoCount + if ( + messagesToDefer.contains(acquiredInfo) && + (mostFrequentAcquiredInfoToDefer == null || + newAcquiredInfoCount > + acquiredInfoToFrequency.getOrDefault( + mostFrequentAcquiredInfoToDefer!!, + 0 + )) + ) { + mostFrequentAcquiredInfoToDefer = acquiredInfo + } } logBuffer.logFrameProcessed( acquiredInfo, totalFrames, - mostFrequentAcquiredInfoToDefer?.toString() + if (faceMessageDeferUpdate()) { + faceHelpMessageDebouncer + ?.getMessageToShow(systemClock.get().elapsedRealtime()) + ?.msgId + .toString() + } else { + mostFrequentAcquiredInfoToDefer?.toString() + } ) } @@ -166,9 +221,16 @@ open class BiometricMessageDeferral( * [threshold] percentage. */ fun getDeferredMessage(): CharSequence? { - mostFrequentAcquiredInfoToDefer?.let { - if (acquiredInfoToFrequency.getOrDefault(it, 0) > (threshold * totalFrames)) { - return acquiredInfoToHelpString[it] + if (faceMessageDeferUpdate()) { + faceHelpMessageDebouncer?.let { + val helpFaceAuthStatus = it.getMessageToShow(systemClock.get().elapsedRealtime()) + return acquiredInfoToHelpString[helpFaceAuthStatus?.msgId] + } + } else { + mostFrequentAcquiredInfoToDefer?.let { + if (acquiredInfoToFrequency.getOrDefault(it, 0) > (threshold * totalFrames)) { + return acquiredInfoToHelpString[it] + } } } return null diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt index cd9b9bc71f32..0b440ad81fb5 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt @@ -45,6 +45,7 @@ import androidx.lifecycle.lifecycleScope import androidx.lifecycle.repeatOnLifecycle import com.airbnb.lottie.LottieAnimationView import com.airbnb.lottie.LottieCompositionFactory +import com.android.systemui.Flags.bpIconA11y import com.android.systemui.biometrics.Utils.ellipsize import com.android.systemui.biometrics.shared.model.BiometricModalities import com.android.systemui.biometrics.shared.model.BiometricModality @@ -54,6 +55,7 @@ import com.android.systemui.biometrics.ui.viewmodel.FingerprintStartMode import com.android.systemui.biometrics.ui.viewmodel.PromptMessage import com.android.systemui.biometrics.ui.viewmodel.PromptSize import com.android.systemui.biometrics.ui.viewmodel.PromptViewModel +import com.android.systemui.common.ui.view.onTouchListener import com.android.systemui.lifecycle.repeatWhenAttached import com.android.systemui.res.R import com.android.systemui.statusbar.VibratorHelper @@ -330,17 +332,31 @@ object BiometricViewBinder { // reuse the icon as a confirm button launch { - viewModel.isIconConfirmButton - .map { isPending -> - when { - isPending && modalities.hasFaceAndFingerprint -> - View.OnTouchListener { _: View, event: MotionEvent -> - viewModel.onOverlayTouch(event) - } - else -> null + if (bpIconA11y()) { + viewModel.isIconConfirmButton.collect { isButton -> + if (isButton) { + iconView.onTouchListener { _: View, event: MotionEvent -> + viewModel.onOverlayTouch(event) + } + iconView.setOnClickListener { viewModel.confirmAuthenticated() } + } else { + iconView.setOnTouchListener(null) + iconView.setOnClickListener(null) } } - .collect { onTouch -> iconView.setOnTouchListener(onTouch) } + } else { + viewModel.isIconConfirmButton + .map { isPending -> + when { + isPending && modalities.hasFaceAndFingerprint -> + View.OnTouchListener { _: View, event: MotionEvent -> + viewModel.onOverlayTouch(event) + } + else -> null + } + } + .collect { onTouch -> iconView.setOnTouchListener(onTouch) } + } } // dismiss prompt when authenticated and confirmed @@ -358,7 +374,8 @@ object BiometricViewBinder { // Allow icon to be used as confirmation button with udfps and a11y // enabled if ( - accessibilityManager.isTouchExplorationEnabled && + !bpIconA11y() && + accessibilityManager.isTouchExplorationEnabled && modalities.hasUdfps ) { iconView.setOnClickListener { viewModel.confirmAuthenticated() } 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 4a358c0b1292..bdd4c161ad59 100644 --- a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogUiEvent.kt +++ b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogUiEvent.kt @@ -31,6 +31,8 @@ enum class BluetoothTileDialogUiEvent(val metricId: Int) : UiEventLogger.UiEvent @UiEvent(doc = "Saved clicked to connect") SAVED_DEVICE_CONNECT(1500), @UiEvent(doc = "Active device clicked to disconnect") ACTIVE_DEVICE_DISCONNECT(1507), @UiEvent(doc = "Audio sharing device clicked, do nothing") AUDIO_SHARING_DEVICE_CLICKED(1699), + @UiEvent(doc = "Available audio sharing device clicked, do nothing") + AVAILABLE_AUDIO_SHARING_DEVICE_CLICKED(1880), @UiEvent(doc = "Connected other device clicked to disconnect") CONNECTED_OTHER_DEVICE_DISCONNECT(1508), @UiEvent(doc = "The auto on toggle is clicked") BLUETOOTH_AUTO_ON_TOGGLE_CLICKED(1617), @@ -44,8 +46,14 @@ enum class BluetoothTileDialogUiEvent(val metricId: Int) : UiEventLogger.UiEvent doc = "Not broadcasting, having one connected, another saved LE audio device is clicked" ) LAUNCH_SETTINGS_NOT_SHARING_SAVED_LE_DEVICE_CLICKED(1719), + @Deprecated( + "Use case no longer needed", + ReplaceWith("LAUNCH_SETTINGS_NOT_SHARING_ACTIVE_LE_DEVICE_CLICKED") + ) @UiEvent(doc = "Not broadcasting, one of the two connected LE audio devices is clicked") - LAUNCH_SETTINGS_NOT_SHARING_CONNECTED_LE_DEVICE_CLICKED(1720); + LAUNCH_SETTINGS_NOT_SHARING_CONNECTED_LE_DEVICE_CLICKED(1720), + @UiEvent(doc = "Not broadcasting, having two connected, the active LE audio devices is clicked") + LAUNCH_SETTINGS_NOT_SHARING_ACTIVE_LE_DEVICE_CLICKED(1881); override fun getId() = metricId } diff --git a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/DeviceItem.kt b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/DeviceItem.kt index a78130f1b041..2ba4c73a0293 100644 --- a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/DeviceItem.kt +++ b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/DeviceItem.kt @@ -38,6 +38,7 @@ import com.android.settingslib.bluetooth.CachedBluetoothDevice enum class DeviceItemType { ACTIVE_MEDIA_BLUETOOTH_DEVICE, AUDIO_SHARING_MEDIA_BLUETOOTH_DEVICE, + AVAILABLE_AUDIO_SHARING_MEDIA_BLUETOOTH_DEVICE, AVAILABLE_MEDIA_BLUETOOTH_DEVICE, CONNECTED_BLUETOOTH_DEVICE, SAVED_BLUETOOTH_DEVICE, 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 9d82e7677a87..f1894d3bb111 100644 --- a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/DeviceItemActionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/DeviceItemActionInteractor.kt @@ -67,7 +67,7 @@ constructor( backgroundDispatcher, logger ), - NotSharingClickedConnected( + NotSharingClickedActive( leAudioProfile, assistantProfile, backgroundDispatcher, @@ -106,6 +106,12 @@ constructor( DeviceItemType.AUDIO_SHARING_MEDIA_BLUETOOTH_DEVICE -> { uiEventLogger.log(BluetoothTileDialogUiEvent.AUDIO_SHARING_DEVICE_CLICKED) } + DeviceItemType.AVAILABLE_AUDIO_SHARING_MEDIA_BLUETOOTH_DEVICE -> { + // TODO(b/360759048): pop up dialog + uiEventLogger.log( + BluetoothTileDialogUiEvent.AVAILABLE_AUDIO_SHARING_DEVICE_CLICKED + ) + } DeviceItemType.AVAILABLE_MEDIA_BLUETOOTH_DEVICE -> { setActive() uiEventLogger.log(BluetoothTileDialogUiEvent.CONNECTED_DEVICE_SET_ACTIVE) @@ -238,14 +244,14 @@ constructor( BluetoothTileDialogUiEvent.LAUNCH_SETTINGS_NOT_SHARING_SAVED_LE_DEVICE_CLICKED } - private class NotSharingClickedConnected( + private class NotSharingClickedActive( 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 + // If not broadcasting, having two device connected, clicked on the active LE audio + // device override suspend fun matched(inAudioSharing: Boolean, deviceItem: DeviceItem): Boolean { return withContext(backgroundDispatcher) { val matched = @@ -259,7 +265,7 @@ constructor( logger ) .size == 2 && - deviceItem.isActiveOrConnectedLeAudioSupported + deviceItem.isActiveLeAudioSupported } } ?: false @@ -275,7 +281,7 @@ constructor( } override suspend fun getClickUiEvent(deviceItem: DeviceItem) = - BluetoothTileDialogUiEvent.LAUNCH_SETTINGS_NOT_SHARING_CONNECTED_LE_DEVICE_CLICKED + BluetoothTileDialogUiEvent.LAUNCH_SETTINGS_NOT_SHARING_ACTIVE_LE_DEVICE_CLICKED } private companion object { @@ -290,10 +296,8 @@ constructor( 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.isActiveLeAudioSupported: Boolean + get() = type == DeviceItemType.ACTIVE_MEDIA_BLUETOOTH_DEVICE && isLeAudioSupported val DeviceItem.isMediaDevice: Boolean get() = diff --git a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/DeviceItemFactory.kt b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/DeviceItemFactory.kt index e846bf7b523c..7280489e0835 100644 --- a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/DeviceItemFactory.kt +++ b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/DeviceItemFactory.kt @@ -125,6 +125,37 @@ internal class AudioSharingMediaDeviceItemFactory( } } +internal class AvailableAudioSharingMediaDeviceItemFactory( + private val localBluetoothManager: LocalBluetoothManager? +) : AvailableMediaDeviceItemFactory() { + override fun isFilterMatched( + context: Context, + cachedDevice: CachedBluetoothDevice, + audioManager: AudioManager + ): Boolean { + return BluetoothUtils.isAudioSharingEnabled() && + super.isFilterMatched(context, cachedDevice, audioManager) && + BluetoothUtils.isAvailableAudioSharingMediaBluetoothDevice( + cachedDevice, + localBluetoothManager + ) + } + + override fun create(context: Context, cachedDevice: CachedBluetoothDevice): DeviceItem { + return createDeviceItem( + context, + cachedDevice, + DeviceItemType.AVAILABLE_AUDIO_SHARING_MEDIA_BLUETOOTH_DEVICE, + context.getString( + R.string.quick_settings_bluetooth_device_audio_sharing_or_switch_active + ), + if (cachedDevice.isBusy) backgroundOffBusy else backgroundOff, + "", + isActive = false + ) + } +} + internal class ActiveHearingDeviceItemFactory : ActiveMediaDeviceItemFactory() { override fun isFilterMatched( context: Context, diff --git a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/DeviceItemInteractor.kt b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/DeviceItemInteractor.kt index 95244964dc44..9114ecac7ac7 100644 --- a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/DeviceItemInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/DeviceItemInteractor.kt @@ -118,6 +118,7 @@ constructor( listOf( ActiveMediaDeviceItemFactory(), AudioSharingMediaDeviceItemFactory(localBluetoothManager), + AvailableAudioSharingMediaDeviceItemFactory(localBluetoothManager), AvailableMediaDeviceItemFactory(), ConnectedDeviceItemFactory(), SavedDeviceItemFactory() @@ -127,6 +128,7 @@ constructor( listOf( DeviceItemType.ACTIVE_MEDIA_BLUETOOTH_DEVICE, DeviceItemType.AUDIO_SHARING_MEDIA_BLUETOOTH_DEVICE, + DeviceItemType.AVAILABLE_AUDIO_SHARING_MEDIA_BLUETOOTH_DEVICE, DeviceItemType.AVAILABLE_MEDIA_BLUETOOTH_DEVICE, DeviceItemType.CONNECTED_BLUETOOTH_DEVICE, DeviceItemType.SAVED_BLUETOOTH_DEVICE, diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/ComposeBouncerViewBinder.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/ComposeBouncerViewBinder.kt index c1f7d590d08e..102ae7abb3e2 100644 --- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/ComposeBouncerViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/ComposeBouncerViewBinder.kt @@ -52,7 +52,9 @@ object ComposeBouncerViewBinder { setContent { PlatformTheme { BouncerContent( - rememberViewModel { viewModelFactory.create() }, + rememberViewModel("ComposeBouncerViewBinder") { + viewModelFactory.create() + }, dialogFactory, ) } diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModel.kt index abca518745d1..c67b35424cc9 100644 --- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModel.kt @@ -22,7 +22,6 @@ import com.android.systemui.authentication.domain.interactor.AuthenticationResul import com.android.systemui.authentication.shared.model.AuthenticationMethodModel import com.android.systemui.bouncer.domain.interactor.BouncerInteractor import com.android.systemui.lifecycle.ExclusiveActivatable -import com.android.systemui.lifecycle.SysUiViewModel import kotlinx.coroutines.awaitCancellation import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.flow.MutableStateFlow @@ -40,7 +39,10 @@ sealed class AuthMethodBouncerViewModel( * being able to attempt to unlock the device. */ val isInputEnabled: StateFlow<Boolean>, -) : SysUiViewModel, ExclusiveActivatable() { + + /** Name to use for performance tracing purposes. */ + val traceName: String, +) : ExclusiveActivatable() { private val _animateFailure = MutableStateFlow(false) /** diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerMessageViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerMessageViewModel.kt index d21eccdfb047..e54dc7dbdebb 100644 --- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerMessageViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerMessageViewModel.kt @@ -39,7 +39,6 @@ import com.android.systemui.deviceentry.shared.model.FaceTimeoutMessage import com.android.systemui.deviceentry.shared.model.FingerprintFailureMessage import com.android.systemui.deviceentry.shared.model.FingerprintLockoutMessage import com.android.systemui.lifecycle.ExclusiveActivatable -import com.android.systemui.lifecycle.SysUiViewModel import com.android.systemui.res.R.string.kg_too_many_failed_attempts_countdown import com.android.systemui.user.ui.viewmodel.UserSwitcherViewModel import com.android.systemui.util.kotlin.Utils.Companion.sample @@ -80,7 +79,7 @@ constructor( private val deviceUnlockedInteractor: DeviceUnlockedInteractor, private val deviceEntryBiometricsAllowedInteractor: DeviceEntryBiometricsAllowedInteractor, private val flags: ComposeBouncerFlags, -) : SysUiViewModel, ExclusiveActivatable() { +) : ExclusiveActivatable() { /** * A message shown when the user has attempted the wrong credential too many times and now must * wait a while before attempting to authenticate again. @@ -156,7 +155,7 @@ constructor( emptyFlow() } } - .collectLatest { messageViewModel -> message.value = messageViewModel } + .collect { messageViewModel -> message.value = messageViewModel } } private suspend fun listenForSimBouncerEvents() { @@ -171,7 +170,7 @@ constructor( emptyFlow() } } - .collectLatest { + .collect { if (it != null) { message.value = it } else { diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerSceneActionsViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerSceneActionsViewModel.kt index 2a272714db37..2d57e5b4f204 100644 --- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerSceneActionsViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerSceneActionsViewModel.kt @@ -25,7 +25,6 @@ import com.android.systemui.bouncer.domain.interactor.BouncerInteractor import com.android.systemui.scene.ui.viewmodel.SceneActionsViewModel import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject -import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.map /** @@ -46,7 +45,7 @@ constructor( Swipe(SwipeDirection.Down) to UserActionResult(prevScene), ) } - .collectLatest { actions -> setActions(actions) } + .collect { actions -> setActions(actions) } } @AssistedFactory diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerSceneContentViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerSceneContentViewModel.kt index 79e5f8d4a683..adc4bc9a14f3 100644 --- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerSceneContentViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerSceneContentViewModel.kt @@ -23,6 +23,7 @@ import android.graphics.Bitmap import androidx.compose.ui.input.key.KeyEvent import androidx.compose.ui.input.key.type import androidx.core.graphics.drawable.toBitmap +import com.android.app.tracing.coroutines.traceCoroutine import com.android.systemui.authentication.domain.interactor.AuthenticationInteractor import com.android.systemui.authentication.shared.model.AuthenticationMethodModel import com.android.systemui.authentication.shared.model.AuthenticationWipeModel @@ -34,7 +35,6 @@ import com.android.systemui.common.shared.model.Icon import com.android.systemui.common.shared.model.Text import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.lifecycle.ExclusiveActivatable -import com.android.systemui.lifecycle.SysUiViewModel import com.android.systemui.user.ui.viewmodel.UserSwitcherViewModel import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject @@ -63,7 +63,7 @@ constructor( private val pinViewModelFactory: PinBouncerViewModel.Factory, private val patternViewModelFactory: PatternBouncerViewModel.Factory, private val passwordViewModelFactory: PasswordBouncerViewModel.Factory, -) : SysUiViewModel, ExclusiveActivatable() { +) : ExclusiveActivatable() { private val _selectedUserImage = MutableStateFlow<Bitmap?>(null) val selectedUserImage: StateFlow<Bitmap?> = _selectedUserImage.asStateFlow() @@ -147,7 +147,7 @@ constructor( .map(::getChildViewModel) .collectLatest { childViewModelOrNull -> _authMethodViewModel.value = childViewModelOrNull - childViewModelOrNull?.activate() + childViewModelOrNull?.let { traceCoroutine(it.traceName) { it.activate() } } } } @@ -160,7 +160,7 @@ constructor( launch { userSwitcher.selectedUser .map { it.image.toBitmap() } - .collectLatest { _selectedUserImage.value = it } + .collect { _selectedUserImage.value = it } } launch { @@ -187,34 +187,32 @@ constructor( ) } } - .collectLatest { _userSwitcherDropdown.value = it } + .collect { _userSwitcherDropdown.value = it } } launch { combine(wipeDialogMessage, lockoutDialogMessage) { _, _ -> createDialogViewModel() } - .collectLatest { _dialogViewModel.value = it } + .collect { _dialogViewModel.value = it } } - launch { - actionButtonInteractor.actionButton.collectLatest { _actionButton.value = it } - } + launch { actionButtonInteractor.actionButton.collect { _actionButton.value = it } } launch { authMethodViewModel .map { authMethod -> isSideBySideSupported(authMethod) } - .collectLatest { _isSideBySideSupported.value = it } + .collect { _isSideBySideSupported.value = it } } launch { authMethodViewModel .map { authMethod -> isFoldSplitRequired(authMethod) } - .collectLatest { _isFoldSplitRequired.value = it } + .collect { _isFoldSplitRequired.value = it } } launch { message.isLockoutMessagePresent .map { lockoutMessagePresent -> !lockoutMessagePresent } - .collectLatest { _isInputEnabled.value = it } + .collect { _isInputEnabled.value = it } } awaitCancellation() diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModel.kt index c91fd6a1a18e..2493cf1a101b 100644 --- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModel.kt @@ -34,7 +34,6 @@ import kotlinx.coroutines.delay import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow -import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.receiveAsFlow @@ -53,6 +52,7 @@ constructor( AuthMethodBouncerViewModel( interactor = interactor, isInputEnabled = isInputEnabled, + traceName = "PasswordBouncerViewModel", ) { private val _password = MutableStateFlow("") @@ -104,11 +104,9 @@ constructor( combine(isInputEnabled, isTextFieldFocused) { hasInput, hasFocus -> hasInput && !hasFocus } - .collectLatest { _isTextFieldFocusRequested.value = it } - } - launch { - selectedUserInteractor.selectedUser.collectLatest { _selectedUserId.value = it } + .collect { _isTextFieldFocusRequested.value = it } } + launch { selectedUserInteractor.selectedUser.collect { _selectedUserId.value = it } } launch { // Re-fetch the currently-enabled IMEs whenever the selected user changes, and // whenever @@ -124,7 +122,7 @@ constructor( ) { selectedUserId, _ -> inputMethodInteractor.hasMultipleEnabledImesOrSubtypes(selectedUserId) } - .collectLatest { _isImeSwitcherButtonVisible.value = it } + .collect { _isImeSwitcherButtonVisible.value = it } } awaitCancellation() } diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModel.kt index 4c029299e16b..0a866b43429f 100644 --- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModel.kt @@ -34,7 +34,6 @@ import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow -import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.toList import kotlinx.coroutines.launch @@ -51,6 +50,7 @@ constructor( AuthMethodBouncerViewModel( interactor = interactor, isInputEnabled = isInputEnabled, + traceName = "PatternBouncerViewModel", ) { /** The number of columns in the dot grid. */ @@ -85,9 +85,7 @@ constructor( coroutineScope { launch { super.onActivated() } launch { - selectedDotSet - .map { it.toList() } - .collectLatest { selectedDotList.value = it.toList() } + selectedDotSet.map { it.toList() }.collect { selectedDotList.value = it.toList() } } awaitCancellation() } diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModel.kt index c6119544d2b0..df6ca9bf0511 100644 --- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModel.kt @@ -42,7 +42,6 @@ import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow -import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.map @@ -63,6 +62,7 @@ constructor( AuthMethodBouncerViewModel( interactor = interactor, isInputEnabled = isInputEnabled, + traceName = "PinBouncerViewModel", ) { /** * Whether the sim-related UI in the pin view is showing. @@ -122,7 +122,7 @@ constructor( } else { interactor.hintedPinLength } - .collectLatest { _hintedPinLength.value = it } + .collect { _hintedPinLength.value = it } } launch { combine( @@ -134,17 +134,17 @@ constructor( isAutoConfirmEnabled = isAutoConfirmEnabled, ) } - .collectLatest { _backspaceButtonAppearance.value = it } + .collect { _backspaceButtonAppearance.value = it } } launch { interactor.isAutoConfirmEnabled .map { if (it) ActionButtonAppearance.Hidden else ActionButtonAppearance.Shown } - .collectLatest { _confirmButtonAppearance.value = it } + .collect { _confirmButtonAppearance.value = it } } launch { interactor.isPinEnhancedPrivacyEnabled .map { !it } - .collectLatest { _isDigitButtonAnimationEnabled.value = it } + .collect { _isDigitButtonAnimationEnabled.value = it } } awaitCancellation() } diff --git a/packages/SystemUI/src/com/android/systemui/common/shared/model/Icon.kt b/packages/SystemUI/src/com/android/systemui/common/shared/model/Icon.kt index 3cdb57318e8d..aef5f1f422d1 100644 --- a/packages/SystemUI/src/com/android/systemui/common/shared/model/Icon.kt +++ b/packages/SystemUI/src/com/android/systemui/common/shared/model/Icon.kt @@ -38,5 +38,5 @@ sealed class Icon { } /** Creates [Icon.Loaded] for a given drawable with an optional [contentDescription]. */ -fun Drawable.asIcon(contentDescription: ContentDescription? = null): Icon = +fun Drawable.asIcon(contentDescription: ContentDescription? = null): Icon.Loaded = Icon.Loaded(this, contentDescription) diff --git a/packages/SystemUI/src/com/android/systemui/communal/CommunalDreamStartable.kt b/packages/SystemUI/src/com/android/systemui/communal/CommunalDreamStartable.kt index 0582cc20927c..c69cea4a6a5a 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/CommunalDreamStartable.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/CommunalDreamStartable.kt @@ -32,11 +32,14 @@ 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.scene.shared.model.Scenes +import com.android.systemui.util.kotlin.BooleanFlowOperators.allOf +import com.android.systemui.util.kotlin.BooleanFlowOperators.not 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.map import kotlinx.coroutines.flow.onEach /** @@ -54,6 +57,18 @@ constructor( private val dreamManager: DreamManager, @Background private val bgScope: CoroutineScope, ) : CoreStartable { + /** Flow that emits when the dream should be started underneath the glanceable hub. */ + val startDream = + allOf( + keyguardTransitionInteractor + .transitionValue(Scenes.Communal, KeyguardState.GLANCEABLE_HUB) + .map { it == 1f }, + not(keyguardInteractor.isDreaming), + // TODO(b/362830856): Remove this workaround. + keyguardInteractor.isKeyguardShowing, + ) + .filter { it } + @SuppressLint("MissingPermission") override fun start() { if (!communalSettingsInteractor.isCommunalFlagEnabled()) { @@ -72,17 +87,10 @@ constructor( // Restart the dream underneath the hub in order to support the ability to swipe // away the hub to enter the dream. - keyguardTransitionInteractor - .transition( - edge = Edge.create(to = Scenes.Communal), - edgeWithoutSceneContainer = Edge.create(to = KeyguardState.GLANCEABLE_HUB) - ) - .filterState(TransitionState.FINISHED) + startDream .sampleFilter(powerInteractor.isAwake) { isAwake -> - dreamManager.canStartDreaming(isAwake) + !glanceableHubAllowKeyguardWhenDreaming() && 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/dagger/CommunalModule.kt b/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalModule.kt index ba2b7bf96a30..a33e0ac0b33a 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalModule.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalModule.kt @@ -17,6 +17,7 @@ package com.android.systemui.communal.dagger import android.content.Context +import android.content.res.Resources import com.android.systemui.CoreStartable import com.android.systemui.communal.data.backup.CommunalBackupUtils import com.android.systemui.communal.data.db.CommunalDatabaseModule @@ -38,6 +39,8 @@ import com.android.systemui.communal.widgets.EditWidgetsActivityStarter import com.android.systemui.communal.widgets.EditWidgetsActivityStarterImpl import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application +import com.android.systemui.dagger.qualifiers.Main +import com.android.systemui.res.R import com.android.systemui.scene.shared.model.SceneContainerConfig import com.android.systemui.scene.shared.model.SceneDataSource import com.android.systemui.scene.shared.model.SceneDataSourceDelegator @@ -90,6 +93,7 @@ interface CommunalModule { companion object { const val LOGGABLE_PREFIXES = "loggable_prefixes" + const val LAUNCHER_PACKAGE = "launcher_package" @Provides @Communal @@ -126,5 +130,12 @@ interface CommunalModule { .getStringArray(com.android.internal.R.array.config_loggable_dream_prefixes) .toList() } + + /** The package name of the launcher */ + @Provides + @Named(LAUNCHER_PACKAGE) + fun provideLauncherPackage(@Main resources: Resources): String { + return resources.getString(R.string.launcher_overlayable_package) + } } } diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalSmartspaceRepository.kt b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalSmartspaceRepository.kt index 86241a5261d7..f77dd587dca3 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalSmartspaceRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalSmartspaceRepository.kt @@ -24,6 +24,9 @@ import com.android.systemui.communal.data.model.CommunalSmartspaceTimer import com.android.systemui.communal.smartspace.CommunalSmartspaceController import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Main +import com.android.systemui.log.LogBuffer +import com.android.systemui.log.core.Logger +import com.android.systemui.log.dagger.CommunalLog import com.android.systemui.plugins.BcSmartspaceDataPlugin import com.android.systemui.util.time.SystemClock import java.util.concurrent.Executor @@ -49,8 +52,11 @@ constructor( private val communalSmartspaceController: CommunalSmartspaceController, @Main private val uiExecutor: Executor, private val systemClock: SystemClock, + @CommunalLog logBuffer: LogBuffer, ) : CommunalSmartspaceRepository, BcSmartspaceDataPlugin.SmartspaceTargetListener { + private val logger = Logger(logBuffer, "CommunalSmartspaceRepository") + private val _timers: MutableStateFlow<List<CommunalSmartspaceTimer>> = MutableStateFlow(emptyList()) override val timers: Flow<List<CommunalSmartspaceTimer>> = _timers @@ -87,6 +93,8 @@ constructor( remoteViews = target.remoteViews!!, ) } + + logger.d({ "Smartspace timers updated: $str1" }) { str1 = _timers.value.toString() } } override fun startListening() { 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 98abbebd1951..9b96341bdd8e 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 @@ -510,7 +510,7 @@ constructor( * A flow of ongoing content, including smartspace timers and umo, ordered by creation time and * sized dynamically. */ - fun getOngoingContent(mediaHostVisible: Boolean): Flow<List<CommunalContentModel.Ongoing>> = + val ongoingContent: Flow<List<CommunalContentModel.Ongoing>> = combine(smartspaceRepository.timers, mediaRepository.mediaModel) { timers, media -> val ongoingContent = mutableListOf<CommunalContentModel.Ongoing>() @@ -526,7 +526,7 @@ constructor( ) // Add UMO - if (mediaHostVisible && media.hasAnyMediaOrRecommendation) { + if (media.hasAnyMediaOrRecommendation) { ongoingContent.add( CommunalContentModel.Umo( createdTimestampMillis = media.createdTimestampMillis, diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSceneTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSceneTransitionInteractor.kt index 5bbb46d21d8b..e04d3095d68d 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSceneTransitionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSceneTransitionInteractor.kt @@ -182,6 +182,7 @@ constructor( } private suspend fun finishCurrentTransition() { + if (currentTransitionId == null) return internalTransitionInteractor.updateTransition( currentTransitionId!!, 1f, @@ -224,7 +225,7 @@ constructor( collectProgress(transition) } else if (transition.toScene == CommunalScenes.Communal) { if (currentToState == KeyguardState.GLANCEABLE_HUB) { - transitionKtfTo(transitionInteractor.getStartedFromState()) + transitionKtfTo(transitionInteractor.startedKeyguardTransitionStep.value.from) } startTransitionToGlanceableHub() collectProgress(transition) 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 16788d15b269..65f0679c4266 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 @@ -29,6 +29,7 @@ import android.view.accessibility.AccessibilityEvent import android.view.accessibility.AccessibilityManager import androidx.activity.result.ActivityResultLauncher import com.android.internal.logging.UiEventLogger +import com.android.systemui.communal.dagger.CommunalModule.Companion.LAUNCHER_PACKAGE import com.android.systemui.communal.data.model.CommunalWidgetCategories import com.android.systemui.communal.domain.interactor.CommunalInteractor import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor @@ -81,6 +82,7 @@ constructor( @Application private val context: Context, private val accessibilityManager: AccessibilityManager, private val packageManager: PackageManager, + @Named(LAUNCHER_PACKAGE) private val launcherPackage: String, ) : BaseCommunalViewModel(communalSceneInteractor, communalInteractor, mediaHost) { private val logger = Logger(logBuffer, "CommunalEditModeViewModel") @@ -185,7 +187,6 @@ constructor( /** Launch the widget picker activity using the given {@link ActivityResultLauncher}. */ suspend fun onOpenWidgetPicker( resources: Resources, - packageManager: PackageManager, activityLauncher: ActivityResultLauncher<Intent> ): Boolean = withContext(backgroundDispatcher) { @@ -196,7 +197,7 @@ constructor( ) { it.providerInfo } - getWidgetPickerActivityIntent(resources, packageManager, excludeList)?.let { + getWidgetPickerActivityIntent(resources, excludeList)?.let { try { activityLauncher.launch(it) return@withContext true @@ -209,18 +210,10 @@ constructor( private fun getWidgetPickerActivityIntent( resources: Resources, - packageManager: PackageManager, excludeList: ArrayList<AppWidgetProviderInfo> ): Intent? { - val packageName = - getLauncherPackageName(packageManager) - ?: run { - Log.e(TAG, "Couldn't resolve launcher package name") - return@getWidgetPickerActivityIntent null - } - return Intent(Intent.ACTION_PICK).apply { - setPackage(packageName) + setPackage(launcherPackage) putExtra( EXTRA_DESIRED_WIDGET_WIDTH, resources.getDimensionPixelSize(R.dimen.communal_widget_picker_desired_width) @@ -247,16 +240,6 @@ constructor( } } - private fun getLauncherPackageName(packageManager: PackageManager): String? { - return packageManager - .resolveActivity( - Intent(Intent.ACTION_MAIN).also { it.addCategory(Intent.CATEGORY_HOME) }, - PackageManager.MATCH_DEFAULT_ONLY - ) - ?.activityInfo - ?.packageName - } - /** Sets whether edit mode is currently open */ fun setEditModeOpen(isOpen: Boolean) = communalInteractor.setEditModeOpen(isOpen) 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 5a39a6272c94..d69ba1b23aa3 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 @@ -99,7 +99,7 @@ constructor( private val logger = Logger(logBuffer, "CommunalViewModel") - private val _isMediaHostVisible = + private val isMediaHostVisible = conflatedCallbackFlow { val callback = { visible: Boolean -> trySend(visible) @@ -117,12 +117,26 @@ constructor( mediaHost.updateViewVisibility() emit(mediaHost.visible) } + .distinctUntilChanged() .onEach { logger.d({ "_isMediaHostVisible: $bool1" }) { bool1 = it } } .flowOn(mainDispatcher) /** Communal content saved from the previous emission when the flow is active (not "frozen"). */ private var frozenCommunalContent: List<CommunalContentModel>? = null + private val ongoingContent = + combine( + isMediaHostVisible, + communalInteractor.ongoingContent.onEach { mediaHost.updateViewVisibility() } + ) { mediaVisible, ongoingContent -> + if (mediaVisible) { + ongoingContent + } else { + // Media is not visible, don't show UMO + ongoingContent.filterNot { it is CommunalContentModel.Umo } + } + } + @OptIn(ExperimentalCoroutinesApi::class) private val latestCommunalContent: Flow<List<CommunalContentModel>> = tutorialInteractor.isTutorialAvailable @@ -130,8 +144,6 @@ constructor( if (isTutorialMode) { return@flatMapLatest flowOf(communalInteractor.tutorialContent) } - val ongoingContent = - _isMediaHostVisible.flatMapLatest { communalInteractor.getOngoingContent(it) } combine( ongoingContent, communalInteractor.widgetContent, @@ -254,6 +266,7 @@ constructor( expandedMatchesParentHeight = true showsOnlyActiveMedia = false falsingProtectionNeeded = false + disablePagination = true init(MediaHierarchyManager.LOCATION_COMMUNAL_HUB) } } 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 55a24d0f595a..d84dc209196e 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt @@ -270,11 +270,7 @@ constructor( private fun onOpenWidgetPicker() { lifecycleScope.launch { - communalViewModel.onOpenWidgetPicker( - resources, - packageManager, - addWidgetActivityLauncher - ) + communalViewModel.onOpenWidgetPicker(resources, addWidgetActivityLauncher) } } diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFaceAuthInteractor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFaceAuthInteractor.kt index dff391a32d6b..f5914104d87f 100644 --- a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFaceAuthInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFaceAuthInteractor.kt @@ -60,14 +60,23 @@ interface DeviceEntryFaceAuthInteractor : CoreStartable { fun unregisterListener(listener: FaceAuthenticationListener) fun onUdfpsSensorTouched() + fun onAssistantTriggeredOnLockScreen() + fun onDeviceLifted() - fun onQsExpansionStared() + + fun onQsExpansionStarted() + fun onNotificationPanelClicked() + fun onSwipeUpOnBouncer() + fun onPrimaryBouncerUserInput() + fun onAccessibilityAction() + fun onWalletLaunched() + fun onDeviceUnfolded() /** Whether face auth is considered class 3 */ diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/NoopDeviceEntryFaceAuthInteractor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/NoopDeviceEntryFaceAuthInteractor.kt index de5d0aa5aad7..b7d2a57d9a41 100644 --- a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/NoopDeviceEntryFaceAuthInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/NoopDeviceEntryFaceAuthInteractor.kt @@ -47,6 +47,7 @@ class NoopDeviceEntryFaceAuthInteractor @Inject constructor() : DeviceEntryFaceA override fun isFaceAuthEnabledAndEnrolled(): Boolean = false override fun isFaceAuthStrong(): Boolean = false + override fun start() = Unit override fun registerListener(listener: FaceAuthenticationListener) {} @@ -59,13 +60,17 @@ class NoopDeviceEntryFaceAuthInteractor @Inject constructor() : DeviceEntryFaceA override fun onDeviceLifted() {} - override fun onQsExpansionStared() {} + override fun onQsExpansionStarted() {} override fun onNotificationPanelClicked() {} override fun onSwipeUpOnBouncer() {} + override fun onPrimaryBouncerUserInput() {} + override fun onAccessibilityAction() {} + override fun onWalletLaunched() = Unit + override fun onDeviceUnfolded() {} } diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/SystemUIDeviceEntryFaceAuthInteractor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/SystemUIDeviceEntryFaceAuthInteractor.kt index 183e0e96e765..5ef63d9b856c 100644 --- a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/SystemUIDeviceEntryFaceAuthInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/SystemUIDeviceEntryFaceAuthInteractor.kt @@ -60,6 +60,7 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.filterNotNull import kotlinx.coroutines.flow.flowOn @@ -214,6 +215,16 @@ constructor( } } .launchIn(applicationScope) + + if (SceneContainerFlag.isEnabled) { + sceneInteractor + .get() + .transitionState + .filter { it.isTransitioning(from = Scenes.QuickSettings, to = Scenes.Shade) } + .distinctUntilChanged() + .onEach { onQsExpansionStarted() } + .launchIn(applicationScope) + } } private val isBouncerVisible: Flow<Boolean> by lazy { @@ -239,7 +250,7 @@ constructor( runFaceAuth(FaceAuthUiEvent.FACE_AUTH_TRIGGERED_NOTIFICATION_PANEL_CLICKED, true) } - override fun onQsExpansionStared() { + override fun onQsExpansionStarted() { runFaceAuth(FaceAuthUiEvent.FACE_AUTH_TRIGGERED_QS_EXPANDED, true) } diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java index caf5b01db846..e3f740e6ff72 100644 --- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java +++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java @@ -24,12 +24,11 @@ import static com.android.systemui.dreams.dagger.DreamModule.DREAM_TOUCH_INSET_M import static com.android.systemui.dreams.dagger.DreamModule.HOME_CONTROL_PANEL_DREAM_COMPONENT; import static com.android.systemui.util.kotlin.JavaAdapterKt.collectFlow; +import android.app.WindowConfiguration; import android.content.ComponentName; import android.content.Context; import android.content.Intent; -import android.content.pm.ActivityInfo; import android.graphics.drawable.ColorDrawable; -import android.service.dreams.DreamActivity; import android.util.Log; import android.view.View; import android.view.ViewGroup; @@ -65,6 +64,7 @@ import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.dreams.dagger.DreamOverlayComponent; import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor; import com.android.systemui.navigationbar.gestural.domain.GestureInteractor; +import com.android.systemui.navigationbar.gestural.domain.TaskMatcher; import com.android.systemui.shade.ShadeExpansionChangeEvent; import com.android.systemui.touch.TouchInsetManager; import com.android.systemui.util.concurrency.DelayableExecutor; @@ -89,6 +89,8 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ LifecycleOwner { private static final String TAG = "DreamOverlayService"; private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); + private static final TaskMatcher DREAM_TYPE_MATCHER = + new TaskMatcher.TopActivityType(WindowConfiguration.ACTIVITY_TYPE_DREAM); // The Context is used to construct the hosting constraint layout and child overlay views. private final Context mContext; @@ -141,10 +143,6 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ private final TouchInsetManager mTouchInsetManager; private final LifecycleOwner mLifecycleOwner; - - - private ComponentName mCurrentBlockedGestureDreamActivityComponent; - private final ArrayList<Job> mFlows = new ArrayList<>(); /** @@ -221,16 +219,122 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ } }; - private final DreamOverlayStateController.Callback mExitAnimationFinishedCallback = - new DreamOverlayStateController.Callback() { - @Override - public void onStateChanged() { - if (!mStateController.areExitAnimationsRunning()) { - mStateController.removeCallback(mExitAnimationFinishedCallback); - resetCurrentDreamOverlayLocked(); + /** + * {@link ResetHandler} protects resetting {@link DreamOverlayService} by making sure reset + * requests are processed before subsequent actions proceed. Requests themselves are also + * ordered between each other as well to ensure actions are correctly sequenced. + */ + private final class ResetHandler { + @FunctionalInterface + interface Callback { + void onComplete(); + } + + private record Info(Callback callback, String source) {} + + private final ArrayList<Info> mPendingCallbacks = new ArrayList<>(); + + DreamOverlayStateController.Callback mStateCallback = + new DreamOverlayStateController.Callback() { + @Override + public void onStateChanged() { + process(true); } + }; + + /** + * Called from places where there is no need to wait for the reset to complete. This still + * will defer the reset until it is okay to reset and also sequences the request with + * others. + */ + public void reset(String source) { + reset(()-> {}, source); + } + + /** + * Invoked to request a reset with a callback that will fire after reset if it is deferred. + * + * @return {@code true} if the reset happened immediately, {@code false} if it was deferred + * and will fire later, invoking the callback. + */ + public boolean reset(Callback callback, String source) { + // Always add listener pre-emptively + if (mPendingCallbacks.isEmpty()) { + mStateController.addCallback(mStateCallback); + } + + final Info info = new Info(callback, source); + mPendingCallbacks.add(info); + process(false); + + boolean processed = !mPendingCallbacks.contains(info); + + if (!processed) { + Log.d(TAG, "delayed resetting from: " + source); + } + + return processed; + } + + private void resetInternal() { + // This ensures the container view of the current dream is removed before + // the controller is potentially reset. + removeContainerViewFromParentLocked(); + + if (mStarted && mWindow != null) { + try { + mWindow.clearContentView(); + mWindowManager.removeView(mWindow.getDecorView()); + } catch (IllegalArgumentException e) { + Log.e(TAG, "Error removing decor view when resetting overlay", e); } - }; + } + + mStateController.setOverlayActive(false); + mStateController.setLowLightActive(false); + mStateController.setEntryAnimationsFinished(false); + + if (mDreamOverlayContainerViewController != null) { + mDreamOverlayContainerViewController.destroy(); + mDreamOverlayContainerViewController = null; + } + + if (mTouchMonitor != null) { + mTouchMonitor.destroy(); + mTouchMonitor = null; + } + + mWindow = null; + + // Always unregister the any set DreamActivity from being blocked from gestures. + mGestureInteractor.removeGestureBlockedMatcher(DREAM_TYPE_MATCHER, + GestureInteractor.Scope.Global); + + mStarted = false; + } + + private boolean canReset() { + return !mStateController.areExitAnimationsRunning(); + } + + private void process(boolean fromDelayedCallback) { + while (canReset() && !mPendingCallbacks.isEmpty()) { + final Info callbackInfo = mPendingCallbacks.removeFirst(); + resetInternal(); + callbackInfo.callback.onComplete(); + + if (fromDelayedCallback) { + Log.d(TAG, "reset overlay (delayed) for " + callbackInfo.source); + } + } + + if (mPendingCallbacks.isEmpty()) { + mStateController.removeCallback(mStateCallback); + } + } + } + + private final ResetHandler mResetHandler = new ResetHandler(); private final DreamOverlayStateController mStateController; @@ -344,10 +448,8 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ mExecutor.execute(() -> { setLifecycleStateLocked(Lifecycle.State.DESTROYED); - - resetCurrentDreamOverlayLocked(); - mDestroyed = true; + mResetHandler.reset("destroying"); }); mDispatcher.onServicePreSuperOnDestroy(); @@ -387,7 +489,10 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ // Reset the current dream overlay before starting a new one. This can happen // when two dreams overlap (briefly, for a smoother dream transition) and both // dreams are bound to the dream overlay service. - resetCurrentDreamOverlayLocked(); + if (!mResetHandler.reset(() -> onStartDream(layoutParams), + "starting with dream already started")) { + return; + } } mDreamOverlayContainerViewController = @@ -399,7 +504,7 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ // If we are not able to add the overlay window, reset the overlay. if (!addOverlayWindowLocked(layoutParams)) { - resetCurrentDreamOverlayLocked(); + mResetHandler.reset("couldn't add window while starting"); return; } @@ -420,7 +525,11 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ mStarted = true; updateRedirectWakeup(); - updateBlockedGestureDreamActivityComponent(); + + if (!isDreamInPreviewMode()) { + mGestureInteractor.addGestureBlockedMatcher(DREAM_TYPE_MATCHER, + GestureInteractor.Scope.Global); + } } private void updateRedirectWakeup() { @@ -431,21 +540,9 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ redirectWake(mCommunalAvailable && !glanceableHubAllowKeyguardWhenDreaming()); } - private void updateBlockedGestureDreamActivityComponent() { - // TODO(b/343815446): We should not be crafting this ActivityInfo ourselves. It should be - // in a common place, Such as DreamActivity itself. - final ActivityInfo info = new ActivityInfo(); - info.name = DreamActivity.class.getName(); - info.packageName = getDreamComponent().getPackageName(); - mCurrentBlockedGestureDreamActivityComponent = info.getComponentName(); - - mGestureInteractor.addGestureBlockedActivity(mCurrentBlockedGestureDreamActivityComponent, - GestureInteractor.Scope.Global); - } - @Override public void onEndDream() { - resetCurrentDreamOverlayLocked(); + mResetHandler.reset("ending dream"); } @Override @@ -576,49 +673,4 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ Log.w(TAG, "Removing dream overlay container view parent!"); parentView.removeView(containerView); } - - private void resetCurrentDreamOverlayLocked() { - if (mStateController.areExitAnimationsRunning()) { - mStateController.addCallback(mExitAnimationFinishedCallback); - return; - } - - // This ensures the container view of the current dream is removed before - // the controller is potentially reset. - removeContainerViewFromParentLocked(); - - if (mStarted && mWindow != null) { - try { - mWindow.clearContentView(); - mWindowManager.removeView(mWindow.getDecorView()); - } catch (IllegalArgumentException e) { - Log.e(TAG, "Error removing decor view when resetting overlay", e); - } - } - - mStateController.setOverlayActive(false); - mStateController.setLowLightActive(false); - mStateController.setEntryAnimationsFinished(false); - - if (mDreamOverlayContainerViewController != null) { - mDreamOverlayContainerViewController.destroy(); - mDreamOverlayContainerViewController = null; - } - - if (mTouchMonitor != null) { - mTouchMonitor.destroy(); - mTouchMonitor = null; - } - - mWindow = null; - - // Always unregister the any set DreamActivity from being blocked from gestures. - if (mCurrentBlockedGestureDreamActivityComponent != null) { - mGestureInteractor.removeGestureBlockedActivity( - mCurrentBlockedGestureDreamActivityComponent, GestureInteractor.Scope.Global); - mCurrentBlockedGestureDreamActivityComponent = null; - } - - mStarted = false; - } } diff --git a/packages/SystemUI/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduInteractor.kt b/packages/SystemUI/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduInteractor.kt index e88349b2b664..87eeebf333e9 100644 --- a/packages/SystemUI/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduInteractor.kt @@ -16,8 +16,16 @@ package com.android.systemui.education.domain.interactor +import android.hardware.input.InputManager +import android.hardware.input.InputManager.KeyGestureEventListener +import android.hardware.input.KeyGestureEvent import com.android.systemui.CoreStartable +import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging +import com.android.systemui.contextualeducation.GestureType +import com.android.systemui.contextualeducation.GestureType.ALL_APPS import com.android.systemui.contextualeducation.GestureType.BACK +import com.android.systemui.contextualeducation.GestureType.HOME +import com.android.systemui.contextualeducation.GestureType.OVERVIEW import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.education.dagger.ContextualEducationModule.EduClock @@ -25,10 +33,14 @@ import com.android.systemui.education.data.model.GestureEduModel import com.android.systemui.education.shared.model.EducationInfo import com.android.systemui.education.shared.model.EducationUiType import com.android.systemui.inputdevice.data.repository.UserInputDeviceRepository +import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow import java.time.Clock +import java.util.concurrent.Executor import javax.inject.Inject import kotlin.time.Duration.Companion.hours import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.channels.awaitClose +import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.launch @@ -41,10 +53,12 @@ constructor( @Background private val backgroundScope: CoroutineScope, private val contextualEducationInteractor: ContextualEducationInteractor, private val userInputDeviceRepository: UserInputDeviceRepository, + private val inputManager: InputManager, @EduClock private val clock: Clock, ) : CoreStartable { companion object { + const val TAG = "KeyboardTouchpadEduInteractor" const val MAX_SIGNAL_COUNT: Int = 2 val usageSessionDuration = 72.hours } @@ -52,6 +66,26 @@ constructor( private val _educationTriggered = MutableStateFlow<EducationInfo?>(null) val educationTriggered = _educationTriggered.asStateFlow() + private val keyboardShortcutTriggered: Flow<GestureType> = conflatedCallbackFlow { + val listener = KeyGestureEventListener { event -> + val shortcutType = + when (event.keyGestureType) { + KeyGestureEvent.KEY_GESTURE_TYPE_BACK -> BACK + KeyGestureEvent.KEY_GESTURE_TYPE_HOME -> HOME + KeyGestureEvent.KEY_GESTURE_TYPE_RECENT_APPS -> OVERVIEW + KeyGestureEvent.KEY_GESTURE_TYPE_ALL_APPS -> ALL_APPS + else -> null + } + + if (shortcutType != null) { + trySendWithFailureLogging(shortcutType, TAG) + } + } + + inputManager.registerKeyGestureEventListener(Executor(Runnable::run), listener) + awaitClose { inputManager.unregisterKeyGestureEventListener(listener) } + } + override fun start() { backgroundScope.launch { contextualEducationInteractor.backGestureModelFlow.collect { @@ -89,6 +123,12 @@ constructor( } } } + + backgroundScope.launch { + keyboardShortcutTriggered.collect { + contextualEducationInteractor.updateShortcutTriggerTime(it) + } + } } private fun isEducationNeeded(model: GestureEduModel): Boolean { diff --git a/packages/SystemUI/src/com/android/systemui/flags/FlagDependencies.kt b/packages/SystemUI/src/com/android/systemui/flags/FlagDependencies.kt index cd0b3f9b6693..6318dc000c21 100644 --- a/packages/SystemUI/src/com/android/systemui/flags/FlagDependencies.kt +++ b/packages/SystemUI/src/com/android/systemui/flags/FlagDependencies.kt @@ -41,7 +41,6 @@ import com.android.systemui.statusbar.notification.interruption.VisualInterrupti import com.android.systemui.statusbar.notification.shared.NotificationAvalancheSuppression import com.android.systemui.statusbar.notification.shared.NotificationMinimalismPrototype import com.android.systemui.statusbar.notification.shared.NotificationThrottleHun -import com.android.systemui.statusbar.notification.shared.NotificationsHeadsUpRefactor import com.android.systemui.statusbar.notification.shared.PriorityPeopleSection import javax.inject.Inject @@ -59,7 +58,6 @@ class FlagDependencies @Inject constructor(featureFlags: FeatureFlagsClassic, ha NotificationAvalancheSuppression.token dependsOn VisualInterruptionRefactor.token PriorityPeopleSection.token dependsOn SortBySectionTimeFlag.token NotificationMinimalismPrototype.token dependsOn NotificationThrottleHun.token - NotificationsHeadsUpRefactor.token dependsOn NotificationThrottleHun.token // SceneContainer dependencies SceneContainerFlag.getFlagDependencies().forEach { (alpha, beta) -> alpha dependsOn beta } diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/data/repository/KeyboardRepository.kt b/packages/SystemUI/src/com/android/systemui/keyboard/data/repository/KeyboardRepository.kt index b6543074cdef..a20dfa5a4c3e 100644 --- a/packages/SystemUI/src/com/android/systemui/keyboard/data/repository/KeyboardRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/keyboard/data/repository/KeyboardRepository.kt @@ -25,7 +25,6 @@ import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.inputdevice.data.repository.InputDeviceRepository import com.android.systemui.inputdevice.data.repository.InputDeviceRepository.DeviceAdded -import com.android.systemui.inputdevice.data.repository.InputDeviceRepository.DeviceChange import com.android.systemui.inputdevice.data.repository.InputDeviceRepository.DeviceRemoved import com.android.systemui.inputdevice.data.repository.InputDeviceRepository.FreshStart import com.android.systemui.keyboard.data.model.Keyboard @@ -78,24 +77,16 @@ constructor( inputDeviceRepository: InputDeviceRepository ) : KeyboardRepository { - private val keyboardsChange: Flow<Pair<Collection<Int>, DeviceChange>> = - inputDeviceRepository.deviceChange - .map { (ids, change) -> ids.filter { id -> isPhysicalFullKeyboard(id) } to change } - .filter { (_, change) -> - when (change) { - FreshStart -> true - is DeviceAdded -> isPhysicalFullKeyboard(change.deviceId) - is DeviceRemoved -> isPhysicalFullKeyboard(change.deviceId) - } - } - @FlowPreview override val newlyConnectedKeyboard: Flow<Keyboard> = - keyboardsChange + inputDeviceRepository.deviceChange .flatMapConcat { (devices, operation) -> when (operation) { - FreshStart -> devices.asFlow() - is DeviceAdded -> flowOf(operation.deviceId) + FreshStart -> devices.filter { id -> isPhysicalFullKeyboard(id) }.asFlow() + is DeviceAdded -> { + if (isPhysicalFullKeyboard(operation.deviceId)) flowOf(operation.deviceId) + else emptyFlow() + } is DeviceRemoved -> emptyFlow() } } @@ -103,8 +94,8 @@ constructor( .flowOn(backgroundDispatcher) override val isAnyKeyboardConnected: Flow<Boolean> = - keyboardsChange - .map { (devices, _) -> devices.isNotEmpty() } + inputDeviceRepository.deviceChange + .map { (ids, _) -> ids.any { id -> isPhysicalFullKeyboard(id) } } .distinctUntilChanged() .flowOn(backgroundDispatcher) diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/WindowManagerLockscreenVisibilityManager.kt b/packages/SystemUI/src/com/android/systemui/keyguard/WindowManagerLockscreenVisibilityManager.kt index 180afb2b259a..e89594e58aa0 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/WindowManagerLockscreenVisibilityManager.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/WindowManagerLockscreenVisibilityManager.kt @@ -23,7 +23,7 @@ import android.view.RemoteAnimationTarget import android.view.WindowManager import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Main -import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor +import com.android.systemui.keyguard.domain.interactor.KeyguardDismissTransitionInteractor import com.android.systemui.keyguard.ui.binder.KeyguardSurfaceBehindParamsApplier import com.android.systemui.statusbar.policy.KeyguardStateController import java.util.concurrent.Executor @@ -41,7 +41,7 @@ constructor( private val activityTaskManagerService: IActivityTaskManager, private val keyguardStateController: KeyguardStateController, private val keyguardSurfaceBehindAnimator: KeyguardSurfaceBehindParamsApplier, - private val keyguardTransitionInteractor: KeyguardTransitionInteractor, + private val keyguardDismissTransitionInteractor: KeyguardDismissTransitionInteractor, ) { /** @@ -148,7 +148,7 @@ constructor( // 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( + keyguardDismissTransitionInteractor.startDismissKeyguardTransition( reason = "Going away remote animation started" ) 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 1042ae32fea0..e4b0f6ea69a9 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 @@ -35,7 +35,6 @@ import javax.inject.Inject import kotlin.time.Duration.Companion.milliseconds import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.delay import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.distinctUntilChanged @@ -49,7 +48,6 @@ import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onStart import kotlinx.coroutines.launch -@ExperimentalCoroutinesApi @SysUISingleton class FromAlternateBouncerTransitionInteractor @Inject diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractor.kt index 6e04133dcb4a..4cf9ec8890d4 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractor.kt @@ -89,7 +89,7 @@ constructor( .filterRelevantKeyguardStateAnd { wakefulness -> wakefulness.isAwake() } .debounce(50L) .sample( - startedKeyguardTransitionStep, + transitionInteractor.startedKeyguardTransitionStep, wakeToGoneInteractor.canWakeDirectlyToGone, ) .collect { diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt index 4666430398ec..2434b29c0cdd 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt @@ -117,7 +117,7 @@ constructor( if (SceneContainerFlag.isEnabled) return scope.launch { keyguardInteractor.primaryBouncerShowing - .sample(startedKeyguardTransitionStep, ::Pair) + .sample(transitionInteractor.startedKeyguardTransitionStep, ::Pair) .collect { pair -> val (isBouncerShowing, lastStartedTransitionStep) = pair if ( @@ -132,7 +132,7 @@ constructor( fun startToLockscreenOrGlanceableHubTransition(openHub: Boolean) { scope.launch { if ( - transitionInteractor.startedKeyguardState.replayCache.last() == + transitionInteractor.startedKeyguardTransitionStep.value.to == KeyguardState.DREAMING ) { if (powerInteractor.detailedWakefulness.value.isAwake()) { 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 cd3df07eea55..228e01edc99b 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 @@ -134,16 +134,12 @@ constructor( .filterRelevantKeyguardState() .sampleCombine( internalTransitionInteractor.currentTransitionInfoInternal, - finishedKeyguardState, + transitionInteractor.isFinishedIn(KeyguardState.LOCKSCREEN), keyguardInteractor.isActiveDreamLockscreenHosted, ) .collect { - ( - isAbleToDream, - transitionInfo, - finishedKeyguardState, - isActiveDreamLockscreenHosted) -> - val isOnLockscreen = finishedKeyguardState == KeyguardState.LOCKSCREEN + (isAbleToDream, transitionInfo, isOnLockscreen, isActiveDreamLockscreenHosted) + -> val isTransitionInterruptible = transitionInfo.to == KeyguardState.LOCKSCREEN && !invalidFromStates.contains(transitionInfo.from) @@ -189,7 +185,7 @@ constructor( scope.launch("$TAG#listenForLockscreenToPrimaryBouncerDragging") { shadeRepository.legacyShadeExpansion .sampleCombine( - startedKeyguardTransitionStep, + transitionInteractor.startedKeyguardTransitionStep, internalTransitionInteractor.currentTransitionInfoInternal, keyguardInteractor.statusBarState, keyguardInteractor.isKeyguardDismissible, @@ -334,7 +330,7 @@ constructor( listenForSleepTransition( modeOnCanceledFromStartedStep = { startedStep -> if ( - transitionInteractor.asleepKeyguardState.value == KeyguardState.AOD && + keyguardInteractor.asleepKeyguardState.value == KeyguardState.AOD && startedStep.from == KeyguardState.AOD ) { TransitionModeOnCanceled.REVERSE diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/GlanceableHubTransitions.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/GlanceableHubTransitions.kt index f9ab1bbcc741..bde0f56aa691 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/GlanceableHubTransitions.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/GlanceableHubTransitions.kt @@ -62,16 +62,16 @@ constructor( communalInteractor .transitionProgressToScene(toScene) .sample( - transitionInteractor.startedKeyguardState, + transitionInteractor.startedKeyguardTransitionStep, ::Pair, ) - .collect { (transitionProgress, lastStartedState) -> + .collect { (transitionProgress, lastStartedStep) -> val id = transitionId if (id == null) { // No transition started. if ( transitionProgress is CommunalTransitionProgressModel.Transition && - lastStartedState == fromState + lastStartedStep.to == fromState ) { transitionId = transitionRepository.startTransition( @@ -84,7 +84,7 @@ constructor( ) } } else { - if (lastStartedState != toState) { + if (lastStartedStep.to != toState) { return@collect } // An existing `id` means a transition is started, and calls to diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissTransitionInteractor.kt new file mode 100644 index 000000000000..c19bbbce3b4b --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissTransitionInteractor.kt @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.keyguard.domain.interactor + +import android.util.Log +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository +import com.android.systemui.keyguard.shared.model.KeyguardState +import com.android.systemui.keyguard.shared.model.KeyguardState.ALTERNATE_BOUNCER +import com.android.systemui.keyguard.shared.model.KeyguardState.AOD +import com.android.systemui.keyguard.shared.model.KeyguardState.DOZING +import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN +import com.android.systemui.keyguard.shared.model.KeyguardState.PRIMARY_BOUNCER +import com.android.systemui.scene.shared.flag.SceneContainerFlag +import javax.inject.Inject + +@SysUISingleton +class KeyguardDismissTransitionInteractor +@Inject +constructor( + private val repository: KeyguardTransitionRepository, + private val fromLockscreenTransitionInteractor: FromLockscreenTransitionInteractor, + private val fromPrimaryBouncerTransitionInteractor: FromPrimaryBouncerTransitionInteractor, + private val fromAodTransitionInteractor: FromAodTransitionInteractor, + private val fromAlternateBouncerTransitionInteractor: FromAlternateBouncerTransitionInteractor, + private val fromDozingTransitionInteractor: FromDozingTransitionInteractor, + private val fromOccludedTransitionInteractor: FromOccludedTransitionInteractor, +) { + + /** + * Called to start a transition that will ultimately dismiss the keyguard from the current + * state. + * + * This is called exclusively by sources that can authoritatively say we should be unlocked, + * including KeyguardSecurityContainerController and WindowManager. + */ + fun startDismissKeyguardTransition(reason: String = "") { + if (SceneContainerFlag.isEnabled) return + Log.d(TAG, "#startDismissKeyguardTransition(reason=$reason)") + when (val startedState = repository.currentTransitionInfoInternal.value.to) { + LOCKSCREEN -> fromLockscreenTransitionInteractor.dismissKeyguard() + PRIMARY_BOUNCER -> fromPrimaryBouncerTransitionInteractor.dismissPrimaryBouncer() + ALTERNATE_BOUNCER -> fromAlternateBouncerTransitionInteractor.dismissAlternateBouncer() + AOD -> fromAodTransitionInteractor.dismissAod() + DOZING -> fromDozingTransitionInteractor.dismissFromDozing() + KeyguardState.OCCLUDED -> fromOccludedTransitionInteractor.dismissFromOccluded() + KeyguardState.GONE -> + Log.i( + TAG, + "Already transitioning to GONE; ignoring startDismissKeyguardTransition." + ) + else -> Log.e(TAG, "We don't know how to dismiss keyguard from state $startedState.") + } + } + + companion object { + private val TAG = KeyguardDismissTransitionInteractor::class.simpleName + } +} diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardEnabledInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardEnabledInteractor.kt index 4aef8084e5e7..44aafabb103a 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardEnabledInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardEnabledInteractor.kt @@ -48,7 +48,7 @@ constructor( @Application scope: CoroutineScope, val repository: KeyguardRepository, val biometricSettingsRepository: BiometricSettingsRepository, - transitionInteractor: KeyguardTransitionInteractor, + keyguardDismissTransitionInteractor: KeyguardDismissTransitionInteractor, internalTransitionInteractor: InternalKeyguardTransitionInteractor, ) { @@ -94,7 +94,9 @@ constructor( showKeyguardWhenReenabled .filter { shouldDismiss -> shouldDismiss } .collect { - transitionInteractor.startDismissKeyguardTransition("keyguard disabled") + keyguardDismissTransitionInteractor.startDismissKeyguardTransition( + "keyguard disabled" + ) } } } 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 a96d7a8f0997..f6f0cc58be71 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 @@ -36,7 +36,9 @@ import com.android.systemui.keyguard.shared.model.DozeStateModel import com.android.systemui.keyguard.shared.model.DozeStateModel.Companion.isDozeOff import com.android.systemui.keyguard.shared.model.DozeTransitionModel import com.android.systemui.keyguard.shared.model.Edge +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.DOZING 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.KeyguardState.OCCLUDED @@ -409,6 +411,12 @@ constructor( } } + /** Which keyguard state to use when the device goes to sleep. */ + val asleepKeyguardState: StateFlow<KeyguardState> = + repository.isAodAvailable + .map { aodAvailable -> if (aodAvailable) AOD else DOZING } + .stateIn(applicationScope, SharingStarted.Eagerly, DOZING) + /** * Whether the primary authentication is required for the given user due to lockdown or * encryption after reboot. diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTouchHandlingInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTouchHandlingInteractor.kt index 4a8ada7f1184..505c749d9e44 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTouchHandlingInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTouchHandlingInteractor.kt @@ -48,7 +48,6 @@ import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.launchIn -import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.launch @@ -74,9 +73,7 @@ constructor( val isLongPressHandlingEnabled: StateFlow<Boolean> = if (isFeatureEnabled()) { combine( - transitionInteractor.finishedKeyguardState.map { - it == KeyguardState.LOCKSCREEN - }, + transitionInteractor.isFinishedIn(KeyguardState.LOCKSCREEN), repository.isQuickSettingsVisible, ) { isFullyTransitionedToLockScreen, isQuickSettingsVisible -> isFullyTransitionedToLockScreen && !isQuickSettingsVisible 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 6ff369ec8711..d11a41eac3a1 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 @@ -22,15 +22,10 @@ import android.util.Log import com.android.compose.animation.scene.SceneKey import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application -import com.android.systemui.keyguard.data.repository.KeyguardRepository 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.KeyguardState.ALTERNATE_BOUNCER -import com.android.systemui.keyguard.shared.model.KeyguardState.AOD -import com.android.systemui.keyguard.shared.model.KeyguardState.DOZING -import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN -import com.android.systemui.keyguard.shared.model.KeyguardState.PRIMARY_BOUNCER +import com.android.systemui.keyguard.shared.model.KeyguardState.OFF import com.android.systemui.keyguard.shared.model.KeyguardState.UNDEFINED import com.android.systemui.keyguard.shared.model.TransitionState import com.android.systemui.keyguard.shared.model.TransitionStep @@ -47,7 +42,6 @@ import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.SharedFlow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow -import kotlinx.coroutines.flow.buffer import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.filter @@ -56,7 +50,6 @@ import kotlinx.coroutines.flow.mapLatest import kotlinx.coroutines.flow.onStart import kotlinx.coroutines.flow.shareIn import kotlinx.coroutines.flow.stateIn -import kotlinx.coroutines.flow.transform import kotlinx.coroutines.launch /** Encapsulates business-logic related to the keyguard transitions. */ @@ -66,16 +59,7 @@ class KeyguardTransitionInteractor @Inject constructor( @Application val scope: CoroutineScope, - private val keyguardRepository: KeyguardRepository, private val repository: KeyguardTransitionRepository, - private val fromLockscreenTransitionInteractor: dagger.Lazy<FromLockscreenTransitionInteractor>, - private val fromPrimaryBouncerTransitionInteractor: - dagger.Lazy<FromPrimaryBouncerTransitionInteractor>, - private val fromAodTransitionInteractor: dagger.Lazy<FromAodTransitionInteractor>, - private val fromAlternateBouncerTransitionInteractor: - dagger.Lazy<FromAlternateBouncerTransitionInteractor>, - private val fromDozingTransitionInteractor: dagger.Lazy<FromDozingTransitionInteractor>, - private val fromOccludedTransitionInteractor: dagger.Lazy<FromOccludedTransitionInteractor>, private val sceneInteractor: SceneInteractor, ) { private val transitionMap = mutableMapOf<Edge.StateToState, MutableSharedFlow<TransitionStep>>() @@ -126,8 +110,10 @@ constructor( repository.transitions .filter { it.transitionState != TransitionState.CANCELED } .collect { step -> - getTransitionValueFlow(step.from).emit(1f - step.value) - getTransitionValueFlow(step.to).emit(step.value) + val value = + if (step.transitionState == TransitionState.FINISHED) 1f else step.value + getTransitionValueFlow(step.from).emit(1f - value) + getTransitionValueFlow(step.to).emit(value) } } @@ -183,8 +169,14 @@ constructor( } } - fun transition(edge: Edge, edgeWithoutSceneContainer: Edge): Flow<TransitionStep> { - return transition(if (SceneContainerFlag.isEnabled) edge else edgeWithoutSceneContainer) + fun transition(edge: Edge, edgeWithoutSceneContainer: Edge? = null): Flow<TransitionStep> { + return transition( + if (SceneContainerFlag.isEnabled || edgeWithoutSceneContainer == null) { + edge + } else { + edgeWithoutSceneContainer + } + ) } /** Given an [edge], return a Flow to collect only relevant [TransitionStep]s. */ @@ -250,10 +242,10 @@ constructor( } fun transitionValue( - scene: SceneKey, + scene: SceneKey? = null, stateWithoutSceneContainer: KeyguardState, ): Flow<Float> { - return if (SceneContainerFlag.isEnabled) { + return if (SceneContainerFlag.isEnabled && scene != null) { sceneInteractor.transitionProgress(scene) } else { transitionValue(stateWithoutSceneContainer) @@ -277,73 +269,10 @@ constructor( } /** The last [TransitionStep] with a [TransitionState] of STARTED */ - val startedKeyguardTransitionStep: Flow<TransitionStep> = - repository.transitions.filter { step -> step.transitionState == TransitionState.STARTED } - - /** The destination state of the last [TransitionState.STARTED] transition. */ - @SuppressLint("SharedFlowCreation") - val startedKeyguardState: SharedFlow<KeyguardState> = - startedKeyguardTransitionStep - .map { step -> step.to } - .buffer(2, BufferOverflow.DROP_OLDEST) - .shareIn(scope, SharingStarted.Eagerly, replay = 1) - - /** 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") - val startedKeyguardFromState: SharedFlow<KeyguardState> = - startedKeyguardTransitionStep - .map { step -> step.from } - .buffer(2, BufferOverflow.DROP_OLDEST) - .shareIn(scope, SharingStarted.Eagerly, replay = 1) - - /** Which keyguard state to use when the device goes to sleep. */ - val asleepKeyguardState: StateFlow<KeyguardState> = - keyguardRepository.isAodAvailable - .map { aodAvailable -> if (aodAvailable) AOD else DOZING } - .stateIn(scope, SharingStarted.Eagerly, DOZING) - - /** - * The last [KeyguardState] to which we [TransitionState.FINISHED] a transition. - * - * WARNING: This will NOT emit a value if a transition is CANCELED, and will also not emit a - * value when a subsequent transition is STARTED. It will *only* emit once we have finally - * FINISHED in a state. This can have unintuitive implications. - * - * For example, if we're transitioning from GONE -> DOZING, and that transition is CANCELED in - * favor of a DOZING -> LOCKSCREEN transition, the FINISHED state is still GONE, and will remain - * GONE throughout the DOZING -> LOCKSCREEN transition until the DOZING -> LOCKSCREEN transition - * finishes (at which point we'll be FINISHED in LOCKSCREEN). - * - * Since there's no real limit to how many consecutive transitions can be canceled, it's even - * possible for the FINISHED state to be the same as the STARTED state while still - * transitioning. - * - * For example: - * 1. We're finished in GONE. - * 2. The user presses the power button, starting a GONE -> DOZING transition. We're still - * FINISHED in GONE. - * 3. The user changes their mind, pressing the power button to wake up; this starts a DOZING -> - * LOCKSCREEN transition. We're still FINISHED in GONE. - * 4. The user quickly swipes away the lockscreen prior to DOZING -> LOCKSCREEN finishing; this - * starts a LOCKSCREEN -> GONE transition. We're still FINISHED in GONE, but we've also - * STARTED a transition *to* GONE. - * 5. We'll emit KeyguardState.GONE again once the transition finishes. - * - * If you just need to know when we eventually settle into a state, this flow is likely - * sufficient. However, if you're having issues with state *during* transitions started after - * one or more canceled transitions, you probably need to use [currentKeyguardState]. - */ - @SuppressLint("SharedFlowCreation") - val finishedKeyguardState: SharedFlow<KeyguardState> = + val startedKeyguardTransitionStep: StateFlow<TransitionStep> = repository.transitions - .transform { step -> - if (step.transitionState == TransitionState.FINISHED) { - emit(step.to) - } - } - .buffer(2, BufferOverflow.DROP_OLDEST) - .shareIn(scope, SharingStarted.Eagerly, replay = 1) + .filter { step -> step.transitionState == TransitionState.STARTED } + .stateIn(scope, SharingStarted.Eagerly, TransitionStep()) /** * The [KeyguardState] we're currently in. @@ -409,8 +338,7 @@ constructor( it.from } } - .distinctUntilChanged() - .stateIn(scope, SharingStarted.Eagerly, KeyguardState.OFF) + .stateIn(scope, SharingStarted.Eagerly, OFF) val isInTransition = combine( @@ -422,33 +350,6 @@ constructor( } /** - * Called to start a transition that will ultimately dismiss the keyguard from the current - * state. - * - * This is called exclusively by sources that can authoritatively say we should be unlocked, - * including KeyguardSecurityContainerController and WindowManager. - */ - fun startDismissKeyguardTransition(reason: String = "") { - if (SceneContainerFlag.isEnabled) return - Log.d(TAG, "#startDismissKeyguardTransition(reason=$reason)") - when (val startedState = repository.currentTransitionInfoInternal.value.to) { - LOCKSCREEN -> fromLockscreenTransitionInteractor.get().dismissKeyguard() - PRIMARY_BOUNCER -> fromPrimaryBouncerTransitionInteractor.get().dismissPrimaryBouncer() - ALTERNATE_BOUNCER -> - fromAlternateBouncerTransitionInteractor.get().dismissAlternateBouncer() - AOD -> fromAodTransitionInteractor.get().dismissAod() - DOZING -> fromDozingTransitionInteractor.get().dismissFromDozing() - KeyguardState.OCCLUDED -> fromOccludedTransitionInteractor.get().dismissFromOccluded() - KeyguardState.GONE -> - Log.i( - TAG, - "Already transitioning to GONE; ignoring startDismissKeyguardTransition." - ) - else -> Log.e(TAG, "We don't know how to dismiss keyguard from state $startedState.") - } - } - - /** * Whether we're in a transition to and from the given [KeyguardState]s, but haven't yet * completed it. * @@ -506,12 +407,13 @@ constructor( fun isFinishedIn(scene: SceneKey, stateWithoutSceneContainer: KeyguardState): Flow<Boolean> { return if (SceneContainerFlag.isEnabled) { - sceneInteractor.transitionState - .map { it.isIdle(scene) || it.isTransitioning(from = scene) } - .distinctUntilChanged() - } else { - isFinishedIn(stateWithoutSceneContainer) - } + sceneInteractor.transitionState.map { + it.isIdle(scene) || it.isTransitioning(from = scene) + } + } else { + isFinishedIn(stateWithoutSceneContainer) + } + .distinctUntilChanged() } /** Whether we've FINISHED a transition to a state */ @@ -520,17 +422,26 @@ constructor( return finishedKeyguardState.map { it == state }.distinctUntilChanged() } - fun getCurrentState(): KeyguardState { - return currentKeyguardState.replayCache.last() + fun isCurrentlyIn(scene: SceneKey, stateWithoutSceneContainer: KeyguardState): Flow<Boolean> { + return if (SceneContainerFlag.isEnabled) { + // In STL there is no difference between finished/currentState + isFinishedIn(scene, stateWithoutSceneContainer) + } else { + stateWithoutSceneContainer.checkValidState() + currentKeyguardState.map { it == stateWithoutSceneContainer } + } + .distinctUntilChanged() } - fun getStartedFromState(): KeyguardState { - return startedKeyguardFromState.replayCache.last() + fun getCurrentState(): KeyguardState { + return currentKeyguardState.replayCache.last() } - fun getFinishedState(): KeyguardState { - return finishedKeyguardState.replayCache.last() - } + private val finishedKeyguardState: StateFlow<KeyguardState> = + repository.transitions + .filter { it.transitionState == TransitionState.FINISHED } + .map { it.to } + .stateIn(scope, SharingStarted.Eagerly, OFF) companion object { private val TAG = KeyguardTransitionInteractor::class.simpleName diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardWakeDirectlyToGoneInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardWakeDirectlyToGoneInteractor.kt index f0bf4029ab39..9b8d9ea11edf 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardWakeDirectlyToGoneInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardWakeDirectlyToGoneInteractor.kt @@ -37,6 +37,7 @@ import com.android.systemui.keyguard.shared.model.KeyguardState.Companion.device import com.android.systemui.keyguard.shared.model.KeyguardState.Companion.deviceIsAwakeInState import com.android.systemui.power.domain.interactor.PowerInteractor import com.android.systemui.power.shared.model.WakeSleepReason +import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.user.domain.interactor.SelectedUserInteractor import com.android.systemui.util.kotlin.sample import com.android.systemui.util.settings.SecureSettings @@ -181,14 +182,20 @@ constructor( scope.launch { powerInteractor.detailedWakefulness .distinctUntilChangedBy { it.isAwake() } - .sample(transitionInteractor.currentKeyguardState, ::Pair) - .collect { (wakefulness, currentState) -> + .sample( + transitionInteractor.isCurrentlyIn( + Scenes.Gone, + stateWithoutSceneContainer = KeyguardState.GONE + ), + ::Pair + ) + .collect { (wakefulness, finishedInGone) -> // Save isAwake for use in onDreamingStarted/onDreamingStopped. this@KeyguardWakeDirectlyToGoneInteractor.isAwake = wakefulness.isAwake() // If we're sleeping from GONE, check the timeout and lock instantly settings. // These are not relevant if we're coming from non-GONE states. - if (!isAwake && currentState == KeyguardState.GONE) { + if (!isAwake && finishedInGone) { val lockTimeoutDuration = getCanIgnoreAuthAndReturnToGoneDuration() // If the screen timed out and went to sleep, and the lock timeout is > 0ms, diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/StatusBarDisableFlagsInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/StatusBarDisableFlagsInteractor.kt index 47818cbfd2f2..e00e33df62eb 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/StatusBarDisableFlagsInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/StatusBarDisableFlagsInteractor.kt @@ -45,6 +45,7 @@ import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.map import kotlinx.coroutines.launch import kotlinx.coroutines.withContext @@ -76,7 +77,7 @@ constructor( private val disableFlagsForUserId = combine( selectedUserInteractor.selectedUser, - keyguardTransitionInteractor.startedKeyguardState, + keyguardTransitionInteractor.startedKeyguardTransitionStep.map { it.to }, deviceConfigInteractor.property( namespace = DeviceConfig.NAMESPACE_SYSTEMUI, name = SystemUiDeviceConfigFlags.NAV_BAR_HANDLE_SHOW_OVER_LOCKSCREEN, diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/SwipeToDismissInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/SwipeToDismissInteractor.kt index 906d58664de9..e404f273a768 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/SwipeToDismissInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/SwipeToDismissInteractor.kt @@ -22,12 +22,12 @@ import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.shared.model.StatusBarState import com.android.systemui.shade.data.repository.ShadeRepository import com.android.systemui.util.kotlin.Utils.Companion.sample +import javax.inject.Inject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.stateIn -import javax.inject.Inject /** * Handles logic around the swipe to dismiss gesture, where the user swipes up on the dismissable @@ -53,15 +53,15 @@ constructor( val dismissFling = shadeRepository.currentFling .sample( - transitionInteractor.startedKeyguardState, + transitionInteractor.startedKeyguardTransitionStep, keyguardInteractor.isKeyguardDismissible, keyguardInteractor.statusBarState, ) - .filter { (flingInfo, startedState, keyguardDismissable, statusBarState) -> + .filter { (flingInfo, startedStep, keyguardDismissable, statusBarState) -> flingInfo != null && - !flingInfo.expand && - statusBarState != StatusBarState.SHADE_LOCKED && - startedState == KeyguardState.LOCKSCREEN && + !flingInfo.expand && + statusBarState != StatusBarState.SHADE_LOCKED && + startedStep.to == KeyguardState.LOCKSCREEN && keyguardDismissable } .map { (flingInfo, _) -> flingInfo } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/TransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/TransitionInteractor.kt index d06ee645652c..ba12e9356ed7 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/TransitionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/TransitionInteractor.kt @@ -32,7 +32,6 @@ import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.filter -import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.map import kotlinx.coroutines.launch @@ -62,17 +61,6 @@ sealed class TransitionInteractor( abstract fun start() - /* Use background dispatcher for all [KeyguardTransitionInteractor] flows. Necessary because - * the [sample] utility internally runs a collect on the Unconfined dispatcher, resulting - * in continuations on the main thread. We don't want that for classes that inherit from this. - */ - val startedKeyguardTransitionStep = - transitionInteractor.startedKeyguardTransitionStep.flowOn(bgDispatcher) - // The following are MutableSharedFlows, and do not require flowOn - val startedKeyguardState = transitionInteractor.startedKeyguardState - val finishedKeyguardState = transitionInteractor.finishedKeyguardState - val currentKeyguardState = transitionInteractor.currentKeyguardState - suspend fun startTransitionTo( toState: KeyguardState, animator: ValueAnimator? = getDefaultAnimatorForTransitionsToState(toState), @@ -92,17 +80,6 @@ sealed class TransitionInteractor( " $fromState. This should never happen - check currentTransitionInfoInternal" + " or use filterRelevantKeyguardState before starting transitions." ) - - if (fromState == transitionInteractor.finishedKeyguardState.replayCache.last()) { - Log.e( - name, - "This transition would not have been ignored prior to ag/26681239, since we " + - "are FINISHED in $fromState (but have since started another transition). " + - "If ignoring this transition has caused a regression, fix it by ensuring " + - "that transitions are exclusively started from the most recently started " + - "state." - ) - } return null } @@ -207,11 +184,11 @@ sealed class TransitionInteractor( powerInteractor.isAsleep .filter { isAsleep -> isAsleep } .filterRelevantKeyguardState() - .sample(startedKeyguardTransitionStep) + .sample(transitionInteractor.startedKeyguardTransitionStep) .map(modeOnCanceledFromStartedStep) .collect { modeOnCanceled -> startTransitionTo( - toState = transitionInteractor.asleepKeyguardState.value, + toState = keyguardInteractor.asleepKeyguardState.value, modeOnCanceled = modeOnCanceled, ownerReason = "Sleep transition triggered" ) 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 25b2b7cad7ec..ac874005b612 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 @@ -63,10 +63,13 @@ constructor( ) { private val defaultSurfaceBehindVisibility = combine( - transitionInteractor.finishedKeyguardState, + transitionInteractor.isFinishedIn( + scene = Scenes.Gone, + stateWithoutSceneContainer = KeyguardState.GONE + ), wakeToGoneInteractor.canWakeDirectlyToGone, - ) { finishedState, canWakeDirectlyToGone -> - isSurfaceVisible(finishedState) || canWakeDirectlyToGone + ) { isOnGone, canWakeDirectlyToGone -> + isOnGone || canWakeDirectlyToGone } /** @@ -196,18 +199,20 @@ constructor( edge = Edge.create(to = Scenes.Gone), edgeWithoutSceneContainer = Edge.create(to = KeyguardState.GONE) ), - transitionInteractor.finishedKeyguardState, + transitionInteractor.isFinishedIn( + scene = Scenes.Gone, + stateWithoutSceneContainer = KeyguardState.GONE + ), surfaceBehindInteractor.isAnimatingSurface, notificationLaunchAnimationInteractor.isLaunchAnimationRunning, - ) { isInTransitionToGone, finishedState, isAnimatingSurface, notifLaunchRunning -> + ) { isInTransitionToGone, isOnGone, isAnimatingSurface, notifLaunchRunning -> // Using the animation if we're animating it directly, or if the // ActivityLaunchAnimator is in the process of animating it. val animationsRunning = isAnimatingSurface || notifLaunchRunning // We may still be animating the surface after the keyguard is fully GONE, since // some animations (like the translation spring) are not tied directly to the // transition step amount. - isInTransitionToGone || - (finishedState == KeyguardState.GONE && animationsRunning) + isInTransitionToGone || (isOnGone && animationsRunning) } .distinctUntilChanged() } @@ -248,7 +253,7 @@ constructor( // transition. Same for waking directly to gone, due to the lockscreen being // disabled or because the device was woken back up before the lock timeout // duration elapsed. - KeyguardState.lockscreenVisibleInState(KeyguardState.GONE) + false } else if (canWakeDirectlyToGone) { // Never show the lockscreen if we can wake directly to GONE. This means // that the lock timeout has not yet elapsed, or the keyguard is disabled. @@ -274,8 +279,7 @@ constructor( // *not* play the going away animation or related animations. false } else { - // Otherwise, use the visibility of the current state. - KeyguardState.lockscreenVisibleInState(currentState) + currentState != KeyguardState.GONE } } .distinctUntilChanged() @@ -302,10 +306,4 @@ constructor( !BiometricUnlockMode.isWakeAndUnlock(biometricUnlockState.mode) } .distinctUntilChanged() - - companion object { - fun isSurfaceVisible(state: KeyguardState): Boolean { - return !KeyguardState.lockscreenVisibleInState(state) - } - } } 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 b8500952d90a..ffd7812166db 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 @@ -116,7 +116,7 @@ constructor( } else { val targetState = if (idle.currentScene == Scenes.Lockscreen) { - transitionInteractor.getStartedFromState() + transitionInteractor.startedKeyguardTransitionStep.value.from } else { UNDEFINED } @@ -155,7 +155,7 @@ constructor( val currentToState = internalTransitionInteractor.currentTransitionInfoInternal.value.to if (currentToState == UNDEFINED) { - transitionKtfTo(transitionInteractor.getStartedFromState()) + transitionKtfTo(transitionInteractor.startedKeyguardTransitionStep.value.from) } } startTransitionFromLockscreen() diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardState.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardState.kt index 24db3c2c70a2..080ddfd18370 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardState.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardState.kt @@ -156,12 +156,6 @@ enum class KeyguardState { companion object { - /** Whether the lockscreen is visible when we're FINISHED in the given state. */ - fun lockscreenVisibleInState(state: KeyguardState): Boolean { - // TODO(b/349784682): Transform deprecated states for Flexiglass - return state != GONE - } - /** * Whether the device is awake ([PowerInteractor.isAwake]) when we're FINISHED in the given * keyguard state. 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 0032c2f036d3..e2ad46353c51 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 @@ -46,6 +46,7 @@ import com.android.systemui.keyguard.ui.viewmodel.LockscreenToGoneTransitionView import com.android.systemui.keyguard.ui.viewmodel.LockscreenToOccludedTransitionViewModel import com.android.systemui.keyguard.ui.viewmodel.LockscreenToPrimaryBouncerTransitionViewModel import com.android.systemui.keyguard.ui.viewmodel.OccludedToAodTransitionViewModel +import com.android.systemui.keyguard.ui.viewmodel.OccludedToDozingTransitionViewModel import com.android.systemui.keyguard.ui.viewmodel.OccludedToGlanceableHubTransitionViewModel import com.android.systemui.keyguard.ui.viewmodel.OccludedToLockscreenTransitionViewModel import com.android.systemui.keyguard.ui.viewmodel.OffToLockscreenTransitionViewModel @@ -205,6 +206,12 @@ abstract class DeviceEntryIconTransitionModule { @Binds @IntoSet + abstract fun occludedToDozing( + impl: OccludedToDozingTransitionViewModel + ): DeviceEntryIconTransition + + @Binds + @IntoSet abstract fun occludedToLockscreen( impl: OccludedToLockscreenTransitionViewModel ): DeviceEntryIconTransition diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModel.kt index 7b0b23ffb2ff..b5d9e2ae888c 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModel.kt @@ -19,6 +19,7 @@ package com.android.systemui.keyguard.ui.viewmodel import android.graphics.Color import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor +import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor import com.android.systemui.keyguard.DismissCallbackRegistry import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor import com.android.systemui.keyguard.shared.model.KeyguardState.ALTERNATE_BOUNCER @@ -41,6 +42,7 @@ constructor( keyguardTransitionInteractor: KeyguardTransitionInteractor, private val dismissCallbackRegistry: DismissCallbackRegistry, alternateBouncerInteractor: Lazy<AlternateBouncerInteractor>, + private val primaryBouncerInteractor: PrimaryBouncerInteractor, ) { // When we're fully transitioned to the AlternateBouncer, the alpha of the scrim should be: private val alternateBouncerScrimAlpha = .66f @@ -73,5 +75,6 @@ constructor( fun onBackRequested() { statusBarKeyguardViewManager.hideAlternateBouncer(false) dismissCallbackRegistry.notifyDismissCancelled() + primaryBouncerInteractor.setDismissAction(null, null) } } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryBackgroundViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryBackgroundViewModel.kt index 6f8389fc8b7c..9f6821051757 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryBackgroundViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryBackgroundViewModel.kt @@ -59,6 +59,7 @@ constructor( alternateBouncerToDozingTransitionViewModel: AlternateBouncerToDozingTransitionViewModel, dreamingToAodTransitionViewModel: DreamingToAodTransitionViewModel, primaryBouncerToLockscreenTransitionViewModel: PrimaryBouncerToLockscreenTransitionViewModel, + occludedToDozingTransitionViewModel: OccludedToDozingTransitionViewModel, ) { val color: Flow<Int> = deviceEntryIconViewModel.useBackgroundProtection.flatMapLatest { useBackground -> @@ -103,6 +104,7 @@ constructor( dreamingToAodTransitionViewModel.deviceEntryBackgroundViewAlpha, primaryBouncerToLockscreenTransitionViewModel .deviceEntryBackgroundViewAlpha, + occludedToDozingTransitionViewModel.deviceEntryBackgroundViewAlpha, ) .merge() .onStart { 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 06b76b3c0f37..87c32a54438e 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 @@ -53,10 +53,10 @@ constructor( ) { private val isShowingAodOrDozing: Flow<Boolean> = combine( - transitionInteractor.startedKeyguardState, + transitionInteractor.startedKeyguardTransitionStep, transitionInteractor.transitionValue(KeyguardState.DOZING), - ) { startedKeyguardState, dozingTransitionValue -> - startedKeyguardState == KeyguardState.AOD || dozingTransitionValue == 1f + ) { startedKeyguardStep, dozingTransitionValue -> + startedKeyguardStep.to == 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 5ce1b5e3dcc5..d3bb4f5d7508 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 @@ -83,8 +83,8 @@ constructor( private val intEvaluator = IntEvaluator() private val floatEvaluator = FloatEvaluator() private val showingAlternateBouncer: Flow<Boolean> = - transitionInteractor.startedKeyguardState.map { keyguardState -> - keyguardState == KeyguardState.ALTERNATE_BOUNCER + transitionInteractor.startedKeyguardTransitionStep.map { keyguardStep -> + keyguardStep.to == KeyguardState.ALTERNATE_BOUNCER } private val qsProgress: Flow<Float> = shadeInteractor.qsExpansion.onStart { emit(0f) } private val shadeExpansion: Flow<Float> = shadeInteractor.shadeExpansion.onStart { emit(0f) } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModel.kt index c885c9a5a29b..fe4ebfedee40 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModel.kt @@ -85,9 +85,7 @@ constructor( private val previewMode = MutableStateFlow(PreviewMode()) private val showingLockscreen: Flow<Boolean> = - transitionInteractor.finishedKeyguardState.map { keyguardState -> - keyguardState == KeyguardState.LOCKSCREEN - } + transitionInteractor.isFinishedIn(KeyguardState.LOCKSCREEN) /** The only time the expansion is important is while lockscreen is actively displayed */ private val shadeExpansionAlpha = 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 a96869df001a..ebdcaa0c91a6 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 @@ -132,8 +132,8 @@ constructor( val burnInModel = _burnInModel.asStateFlow() val burnInLayerVisibility: Flow<Int> = - keyguardTransitionInteractor.startedKeyguardState - .filter { it == AOD || it == LOCKSCREEN } + keyguardTransitionInteractor.startedKeyguardTransitionStep + .filter { it.to == AOD || it.to == LOCKSCREEN } .map { VISIBLE } val goneToAodTransition = @@ -333,16 +333,17 @@ constructor( .transitionValue(LOCKSCREEN) .map { it > 0f } .onStart { emit(false) }, - keyguardTransitionInteractor.finishedKeyguardState.map { - KeyguardState.lockscreenVisibleInState(it) - }, + keyguardTransitionInteractor.isFinishedIn( + scene = Scenes.Gone, + stateWithoutSceneContainer = GONE + ), deviceEntryInteractor.isBypassEnabled, areNotifsFullyHiddenAnimated(), isPulseExpandingAnimated(), ) { flows -> val goneToAodTransitionRunning = flows[0] as Boolean val isOnLockscreen = flows[1] as Boolean - val onKeyguard = flows[2] as Boolean + val isOnGone = flows[2] as Boolean val isBypassEnabled = flows[3] as Boolean val notifsFullyHidden = flows[4] as AnimatedValue<Boolean> val pulseExpanding = flows[5] as AnimatedValue<Boolean> @@ -352,8 +353,7 @@ constructor( // animation is playing, in which case we want them to be visible if we're // animating in the AOD UI and will be switching to KEYGUARD shortly. goneToAodTransitionRunning || - (!onKeyguard && - !screenOffAnimationController.shouldShowAodIconsWhenShade()) -> + (isOnGone && !screenOffAnimationController.shouldShowAodIconsWhenShade()) -> AnimatedValue.NotAnimating(false) else -> zip(notifsFullyHidden, pulseExpanding) { diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModel.kt index 2b6c3c080b78..adb63b7b3e69 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModel.kt @@ -25,7 +25,6 @@ import com.android.systemui.keyguard.domain.interactor.KeyguardBlueprintInteract import com.android.systemui.keyguard.domain.interactor.KeyguardClockInteractor import com.android.systemui.keyguard.shared.model.ClockSize import com.android.systemui.lifecycle.ExclusiveActivatable -import com.android.systemui.lifecycle.SysUiViewModel import com.android.systemui.res.R import com.android.systemui.scene.domain.interactor.SceneContainerOcclusionInteractor import com.android.systemui.scene.shared.model.Scenes @@ -41,7 +40,6 @@ import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow -import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.flowOf @@ -60,7 +58,7 @@ constructor( private val unfoldTransitionInteractor: UnfoldTransitionInteractor, private val occlusionInteractor: SceneContainerOcclusionInteractor, private val deviceEntryInteractor: DeviceEntryInteractor, -) : SysUiViewModel, ExclusiveActivatable() { +) : ExclusiveActivatable() { @VisibleForTesting val clockSize = clockInteractor.clockSize val isUdfpsVisible: Boolean @@ -92,13 +90,13 @@ constructor( end = end, ) } - .collectLatest { _unfoldTranslations.value = it } + .collect { _unfoldTranslations.value = it } } launch { occlusionInteractor.isOccludingActivityShown .map { !it } - .collectLatest { _isContentVisible.value = it } + .collect { _isContentVisible.value = it } } awaitCancellation() diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneActionsViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneActionsViewModel.kt index 7383f57bc4e5..2819e617629d 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneActionsViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneActionsViewModel.kt @@ -35,7 +35,6 @@ import com.android.systemui.util.kotlin.filterValuesNotNull import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.flowOf @@ -100,7 +99,7 @@ constructor( } } } - .collectLatest { setActions(it) } + .collect { setActions(it) } } private fun swipeDownFromTop(pointerCount: Int): Swipe { diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModel.kt index e64c61490204..c0b9efaaec01 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModel.kt @@ -23,7 +23,9 @@ import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN import com.android.systemui.keyguard.shared.model.KeyguardState.PRIMARY_BOUNCER import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition +import com.android.systemui.scene.shared.flag.SceneContainerFlag import com.android.systemui.scene.shared.model.Scenes +import com.android.systemui.scene.ui.composable.transitions.FROM_LOCK_SCREEN_TO_BOUNCER_FADE_FRACTION import javax.inject.Inject import kotlin.time.Duration.Companion.milliseconds import kotlinx.coroutines.ExperimentalCoroutinesApi @@ -51,10 +53,18 @@ constructor( edge = Edge.create(from = LOCKSCREEN, to = PRIMARY_BOUNCER), ) + private val alphaForAnimationStep: (Float) -> Float = + when { + SceneContainerFlag.isEnabled -> { step -> + 1f - Math.min((step / FROM_LOCK_SCREEN_TO_BOUNCER_FADE_FRACTION), 1f) + } + else -> { step -> 1f - step } + } + val shortcutsAlpha: Flow<Float> = transitionAnimation.sharedFlow( duration = FromLockscreenTransitionInteractor.TO_PRIMARY_BOUNCER_DURATION, - onStep = { 1f - it } + onStep = alphaForAnimationStep ) val lockscreenAlpha: Flow<Float> = shortcutsAlpha diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToDozingTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToDozingTransitionViewModel.kt index af019300c764..4fb2b9b35e88 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToDozingTransitionViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToDozingTransitionViewModel.kt @@ -17,15 +17,19 @@ package com.android.systemui.keyguard.ui.viewmodel import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor import com.android.systemui.keyguard.domain.interactor.FromOccludedTransitionInteractor import com.android.systemui.keyguard.shared.model.Edge import com.android.systemui.keyguard.shared.model.KeyguardState.DOZING import com.android.systemui.keyguard.shared.model.KeyguardState.OCCLUDED import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow +import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition import javax.inject.Inject import kotlin.time.Duration.Companion.milliseconds import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.emptyFlow +import kotlinx.coroutines.flow.flatMapLatest /** * Breaks down OCCLUDED->DOZING transition into discrete steps for corresponding views to consume. @@ -35,8 +39,9 @@ import kotlinx.coroutines.flow.Flow class OccludedToDozingTransitionViewModel @Inject constructor( + deviceEntryUdfpsInteractor: DeviceEntryUdfpsInteractor, animationFlow: KeyguardTransitionAnimationFlow, -) { +) : DeviceEntryIconTransition { private val transitionAnimation = animationFlow.setup( duration = FromOccludedTransitionInteractor.TO_DOZING_DURATION, @@ -50,4 +55,17 @@ constructor( duration = 250.milliseconds, onStep = { it }, ) + + val deviceEntryBackgroundViewAlpha: Flow<Float> = + transitionAnimation.immediatelyTransitionTo(0f) + + override val deviceEntryParentViewAlpha: Flow<Float> = + deviceEntryUdfpsInteractor.isUdfpsEnrolledAndEnabled.flatMapLatest { udfpsEnrolledAndEnabled + -> + if (udfpsEnrolledAndEnabled) { + transitionAnimation.immediatelyTransitionTo(1f) + } else { + emptyFlow() + } + } } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/ShadeDependentFlows.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/ShadeDependentFlows.kt index e45d537155fd..708b4085da7f 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/ShadeDependentFlows.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/ShadeDependentFlows.kt @@ -34,7 +34,9 @@ constructor( ) { /** When the last keyguard state transition started, was the shade fully expanded? */ private val lastStartedTransitionHadShadeFullyExpanded: Flow<Boolean> = - transitionInteractor.startedKeyguardState.sample(shadeInteractor.isAnyFullyExpanded) + transitionInteractor.startedKeyguardTransitionStep.sample( + shadeInteractor.isAnyFullyExpanded + ) /** * Decide which flow to use depending on the shade expansion state at the start of the last diff --git a/packages/SystemUI/src/com/android/systemui/lifecycle/Activatable.kt b/packages/SystemUI/src/com/android/systemui/lifecycle/Activatable.kt index bd3d40b114e3..c1768a4d903c 100644 --- a/packages/SystemUI/src/com/android/systemui/lifecycle/Activatable.kt +++ b/packages/SystemUI/src/com/android/systemui/lifecycle/Activatable.kt @@ -19,6 +19,7 @@ package com.android.systemui.lifecycle import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.remember +import com.android.app.tracing.coroutines.traceCoroutine /** Defines interface for classes that can be activated to do coroutine work. */ interface Activatable { @@ -66,13 +67,19 @@ interface Activatable { * * If the [key] changes, the old [Activatable] is deactivated and a new one will be instantiated, * activated, and returned. + * + * The [traceName] is used for coroutine performance tracing purposes. Please try to use a label + * that's unique enough and easy enough to find in code search; this should help correlate + * performance findings with actual code. One recommendation: prefer whole string literals instead + * of some complex concatenation or templating scheme. */ @Composable fun <T : Activatable> rememberActivated( + traceName: String, key: Any = Unit, factory: () -> T, ): T { val instance = remember(key) { factory() } - LaunchedEffect(instance) { instance.activate() } + LaunchedEffect(instance) { traceCoroutine(traceName) { instance.activate() } } return instance } diff --git a/packages/SystemUI/src/com/android/systemui/lifecycle/Hydrator.kt b/packages/SystemUI/src/com/android/systemui/lifecycle/Hydrator.kt index 59ec2af2d697..df1394bbfa5f 100644 --- a/packages/SystemUI/src/com/android/systemui/lifecycle/Hydrator.kt +++ b/packages/SystemUI/src/com/android/systemui/lifecycle/Hydrator.kt @@ -19,6 +19,8 @@ package com.android.systemui.lifecycle import androidx.compose.runtime.State import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.snapshots.StateFactoryMarker +import com.android.app.tracing.coroutines.launch +import com.android.app.tracing.coroutines.traceCoroutine import kotlinx.coroutines.awaitCancellation import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.flow.Flow @@ -37,35 +39,55 @@ import kotlinx.coroutines.launch * } * ``` */ -class Hydrator : ExclusiveActivatable() { +class Hydrator( + /** + * A name for performance tracing purposes. + * + * Please use a short string literal that's easy to find in code search. Try to avoid + * concatenation or templating. + */ + private val traceName: String, +) : ExclusiveActivatable() { - private val children = mutableListOf<Activatable>() + private val children = mutableListOf<NamedActivatable>() /** - * Returns a snapshot [State] that's kept up-to-date as long as the [SysUiViewModel] is active. + * Returns a snapshot [State] that's kept up-to-date as long as its owner is active. * + * @param traceName Used for coroutine performance tracing purposes. Please try to use a label + * that's unique enough and easy enough to find in code search; this should help correlate + * performance findings with actual code. One recommendation: prefer whole string literals + * instead of some complex concatenation or templating scheme. * @param source The upstream [StateFlow] to collect from; values emitted to it will be * automatically set on the returned [State]. */ @StateFactoryMarker fun <T> hydratedStateOf( + traceName: String, source: StateFlow<T>, ): State<T> { return hydratedStateOf( + traceName = traceName, initialValue = source.value, source = source, ) } /** - * Returns a snapshot [State] that's kept up-to-date as long as the [SysUiViewModel] is active. + * Returns a snapshot [State] that's kept up-to-date as long as its owner is active. * + * @param traceName Used for coroutine performance tracing purposes. Please try to use a label + * that's unique enough and easy enough to find in code search; this should help correlate + * performance findings with actual code. One recommendation: prefer whole string literals + * instead of some complex concatenation or templating scheme. Use `null` to disable + * performance tracing for this state. * @param initialValue The first value to place on the [State] * @param source The upstream [Flow] to collect from; values emitted to it will be automatically * set on the returned [State]. */ @StateFactoryMarker fun <T> hydratedStateOf( + traceName: String?, initialValue: T, source: Flow<T>, ): State<T> { @@ -73,18 +95,35 @@ class Hydrator : ExclusiveActivatable() { val mutableState = mutableStateOf(initialValue) children.add( - object : ExclusiveActivatable() { - override suspend fun onActivated(): Nothing { - source.collect { mutableState.value = it } - awaitCancellation() - } - } + NamedActivatable( + traceName = traceName, + activatable = + object : ExclusiveActivatable() { + override suspend fun onActivated(): Nothing { + source.collect { mutableState.value = it } + awaitCancellation() + } + }, + ) ) return mutableState } override suspend fun onActivated() = coroutineScope { - children.forEach { child -> launch { child.activate() } } - awaitCancellation() + traceCoroutine(traceName) { + children.forEach { child -> + if (child.traceName != null) { + launch(spanName = child.traceName) { child.activatable.activate() } + } else { + launch { child.activatable.activate() } + } + } + awaitCancellation() + } } + + private data class NamedActivatable( + val traceName: String?, + val activatable: Activatable, + ) } diff --git a/packages/SystemUI/src/com/android/systemui/lifecycle/SysUiViewModel.kt b/packages/SystemUI/src/com/android/systemui/lifecycle/SysUiViewModel.kt index 29ffcbd15125..508b04eee91a 100644 --- a/packages/SystemUI/src/com/android/systemui/lifecycle/SysUiViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/lifecycle/SysUiViewModel.kt @@ -20,36 +20,46 @@ import android.view.View import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.remember +import com.android.app.tracing.coroutines.traceCoroutine import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch -/** Defines interface for all System UI view-models. */ -interface SysUiViewModel - /** - * Returns a remembered [SysUiViewModel] of the type [T]. If the returned instance is also an + * Returns a remembered view-model of the type [T]. If the returned instance is also an * [Activatable], it's automatically kept active until this composable leaves the composition; if - * the [key] changes, the old [SysUiViewModel] is deactivated and a new one will be instantiated, + * the [key] changes, the old view-model is deactivated and a new one will be instantiated, * activated, and returned. + * + * The [traceName] is used for coroutine performance tracing purposes. Please try to use a label + * that's unique enough and easy enough to find in code search; this should help correlate + * performance findings with actual code. One recommendation: prefer whole string literals instead + * of some complex concatenation or templating scheme. */ @Composable -fun <T : SysUiViewModel> rememberViewModel( +fun <T> rememberViewModel( + traceName: String, key: Any = Unit, factory: () -> T, ): T { val instance = remember(key) { factory() } if (instance is Activatable) { - LaunchedEffect(instance) { instance.activate() } + LaunchedEffect(instance) { traceCoroutine(traceName) { instance.activate() } } } return instance } /** - * Invokes [block] in a new coroutine with a new [SysUiViewModel] that is automatically activated - * whenever `this` [View]'s Window's [WindowLifecycleState] is at least at - * [minWindowLifecycleState], and is automatically canceled once that is no longer the case. + * Invokes [block] in a new coroutine with a new view-model that is automatically activated whenever + * `this` [View]'s Window's [WindowLifecycleState] is at least at [minWindowLifecycleState], and is + * automatically canceled once that is no longer the case. + * + * The [traceName] is used for coroutine performance tracing purposes. Please try to use a label + * that's unique enough and easy enough to find in code search; this should help correlate + * performance findings with actual code. One recommendation: prefer whole string literals instead + * of some complex concatenation or templating scheme. */ -suspend fun <T : SysUiViewModel> View.viewModel( +suspend fun <T> View.viewModel( + traceName: String, minWindowLifecycleState: WindowLifecycleState, factory: () -> T, block: suspend CoroutineScope.(T) -> Unit, @@ -57,7 +67,7 @@ suspend fun <T : SysUiViewModel> View.viewModel( repeatOnWindowLifecycle(minWindowLifecycleState) { val instance = factory() if (instance is Activatable) { - launch { instance.activate() } + launch { traceCoroutine(traceName) { instance.activate() } } } block(instance) } diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java index ba3c1d216099..ed766469094e 100644 --- a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java +++ b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java @@ -382,6 +382,16 @@ public class LogModule { return factory.create("MediaLog", 20); } + /** + * Provides a buffer for media device changes + */ + @Provides + @SysUISingleton + @MediaDeviceLog + public static LogBuffer providesMediaDeviceLogBuffer(LogBufferFactory factory) { + return factory.create("MediaDeviceLog", 50); + } + /** Allows logging buffers to be tweaked via adb on debug builds but not on prod builds. */ @Provides @SysUISingleton diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/MediaDeviceLog.kt b/packages/SystemUI/src/com/android/systemui/log/dagger/MediaDeviceLog.kt new file mode 100644 index 000000000000..06bd26971232 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/log/dagger/MediaDeviceLog.kt @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.log.dagger + +import com.android.systemui.log.LogBuffer +import javax.inject.Qualifier + +/** A [LogBuffer] for [com.android.systemui.media.controls.domain.pipeline.MediaDeviceLogger] */ +@Qualifier +@MustBeDocumented +@Retention(AnnotationRetention.RUNTIME) +annotation class MediaDeviceLog diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaActions.kt b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaActions.kt index 70189b79ea58..378a147c2c82 100644 --- a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaActions.kt +++ b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaActions.kt @@ -30,6 +30,7 @@ import android.util.Log import androidx.media.utils.MediaConstants import com.android.systemui.media.controls.domain.pipeline.LegacyMediaDataManagerImpl.Companion.MAX_COMPACT_ACTIONS import com.android.systemui.media.controls.domain.pipeline.LegacyMediaDataManagerImpl.Companion.MAX_NOTIFICATION_ACTIONS +import com.android.systemui.media.controls.shared.MediaControlDrawables import com.android.systemui.media.controls.shared.model.MediaAction import com.android.systemui.media.controls.shared.model.MediaButton import com.android.systemui.plugins.ActivityStarter @@ -58,14 +59,13 @@ fun createActionsFromState( val playOrPause = if (isConnectingState(state.state)) { // Spinner needs to be animating to render anything. Start it here. - val drawable = - context.getDrawable(com.android.internal.R.drawable.progress_small_material) + val drawable = MediaControlDrawables.getProgress(context) (drawable as Animatable).start() MediaAction( drawable, null, // no action to perform when clicked context.getString(R.string.controls_media_button_connecting), - context.getDrawable(R.drawable.ic_media_connecting_container), + MediaControlDrawables.getConnecting(context), // Specify a rebind id to prevent the spinner from restarting on later binds. com.android.internal.R.drawable.progress_small_material ) @@ -153,23 +153,23 @@ private fun getStandardAction( return when (action) { PlaybackState.ACTION_PLAY -> { MediaAction( - context.getDrawable(R.drawable.ic_media_play), + MediaControlDrawables.getPlayIcon(context), { controller.transportControls.play() }, context.getString(R.string.controls_media_button_play), - context.getDrawable(R.drawable.ic_media_play_container) + MediaControlDrawables.getPlayBackground(context) ) } PlaybackState.ACTION_PAUSE -> { MediaAction( - context.getDrawable(R.drawable.ic_media_pause), + MediaControlDrawables.getPauseIcon(context), { controller.transportControls.pause() }, context.getString(R.string.controls_media_button_pause), - context.getDrawable(R.drawable.ic_media_pause_container) + MediaControlDrawables.getPauseBackground(context) ) } PlaybackState.ACTION_SKIP_TO_PREVIOUS -> { MediaAction( - context.getDrawable(R.drawable.ic_media_prev), + MediaControlDrawables.getPrevIcon(context), { controller.transportControls.skipToPrevious() }, context.getString(R.string.controls_media_button_prev), null @@ -177,7 +177,7 @@ private fun getStandardAction( } PlaybackState.ACTION_SKIP_TO_NEXT -> { MediaAction( - context.getDrawable(R.drawable.ic_media_next), + MediaControlDrawables.getNextIcon(context), { controller.transportControls.skipToNext() }, context.getString(R.string.controls_media_button_next), null 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 916f8b2e1730..415449f4454f 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 @@ -16,9 +16,8 @@ package com.android.systemui.media.controls.domain.pipeline +import android.annotation.MainThread import android.annotation.SuppressLint -import android.app.ActivityOptions -import android.app.BroadcastOptions import android.app.Notification import android.app.Notification.EXTRA_SUBSTITUTE_APP_NAME import android.app.PendingIntent @@ -39,7 +38,6 @@ import android.content.pm.ApplicationInfo import android.content.pm.PackageManager import android.graphics.Bitmap import android.graphics.ImageDecoder -import android.graphics.drawable.Animatable import android.graphics.drawable.Icon import android.media.MediaDescription import android.media.MediaMetadata @@ -47,7 +45,6 @@ import android.media.session.MediaController import android.media.session.MediaSession import android.media.session.PlaybackState import android.net.Uri -import android.os.Handler import android.os.Parcelable import android.os.Process import android.os.UserHandle @@ -63,6 +60,7 @@ import com.android.internal.annotations.Keep import com.android.internal.logging.InstanceId import com.android.keyguard.KeyguardUpdateMonitor import com.android.systemui.CoreStartable +import com.android.systemui.Flags import com.android.systemui.broadcast.BroadcastDispatcher import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application @@ -92,7 +90,6 @@ import com.android.systemui.plugins.ActivityStarter import com.android.systemui.plugins.BcSmartspaceDataPlugin import com.android.systemui.res.R import com.android.systemui.scene.shared.flag.SceneContainerFlag -import com.android.systemui.statusbar.NotificationMediaManager.isConnectingState import com.android.systemui.statusbar.NotificationMediaManager.isPlayingState import com.android.systemui.statusbar.notification.row.HybridGroupManager import com.android.systemui.util.Assert @@ -137,7 +134,7 @@ class MediaDataProcessor( @Background private val backgroundExecutor: Executor, @Main private val uiExecutor: Executor, @Main private val foregroundExecutor: DelayableExecutor, - @Main private val handler: Handler, + @Main private val mainDispatcher: CoroutineDispatcher, private val mediaControllerFactory: MediaControllerFactory, private val broadcastDispatcher: BroadcastDispatcher, private val dumpManager: DumpManager, @@ -152,6 +149,7 @@ class MediaDataProcessor( private val smartspaceManager: SmartspaceManager?, private val keyguardUpdateMonitor: KeyguardUpdateMonitor, private val mediaDataRepository: MediaDataRepository, + private val mediaDataLoader: dagger.Lazy<MediaDataLoader>, ) : CoreStartable, BcSmartspaceDataPlugin.SmartspaceTargetListener { companion object { @@ -217,7 +215,7 @@ class MediaDataProcessor( threadFactory: ThreadFactory, @Main uiExecutor: Executor, @Main foregroundExecutor: DelayableExecutor, - @Main handler: Handler, + @Main mainDispatcher: CoroutineDispatcher, mediaControllerFactory: MediaControllerFactory, dumpManager: DumpManager, broadcastDispatcher: BroadcastDispatcher, @@ -230,6 +228,7 @@ class MediaDataProcessor( smartspaceManager: SmartspaceManager?, keyguardUpdateMonitor: KeyguardUpdateMonitor, mediaDataRepository: MediaDataRepository, + mediaDataLoader: dagger.Lazy<MediaDataLoader>, ) : this( context, applicationScope, @@ -239,7 +238,7 @@ class MediaDataProcessor( threadFactory.buildExecutorOnNewThread(TAG), uiExecutor, foregroundExecutor, - handler, + mainDispatcher, mediaControllerFactory, broadcastDispatcher, dumpManager, @@ -254,6 +253,7 @@ class MediaDataProcessor( smartspaceManager, keyguardUpdateMonitor, mediaDataRepository, + mediaDataLoader, ) private val appChangeReceiver = @@ -436,16 +436,30 @@ class MediaDataProcessor( logSingleVsMultipleMediaAdded(appUid, packageName, instanceId) logger.logResumeMediaAdded(appUid, packageName, instanceId) } - backgroundExecutor.execute { - loadMediaDataInBgForResumption( - userId, - desc, - action, - token, - appName, - appIntent, - packageName - ) + if (Flags.mediaLoadMetadataViaMediaDataLoader()) { + applicationScope.launch { + loadMediaDataForResumption( + userId, + desc, + action, + token, + appName, + appIntent, + packageName + ) + } + } else { + backgroundExecutor.execute { + loadMediaDataInBgForResumption( + userId, + desc, + action, + token, + appName, + appIntent, + packageName + ) + } } } @@ -471,7 +485,13 @@ class MediaDataProcessor( oldKey: String?, isNewlyActiveEntry: Boolean = false, ) { - backgroundExecutor.execute { loadMediaDataInBg(key, sbn, oldKey, isNewlyActiveEntry) } + if (Flags.mediaLoadMetadataViaMediaDataLoader()) { + applicationScope.launch { + loadMediaDataWithLoader(key, sbn, oldKey, isNewlyActiveEntry) + } + } else { + backgroundExecutor.execute { loadMediaDataInBg(key, sbn, oldKey, isNewlyActiveEntry) } + } } /** Add a listener for internal events. */ @@ -646,6 +666,75 @@ class MediaDataProcessor( } } + private suspend fun loadMediaDataForResumption( + userId: Int, + desc: MediaDescription, + resumeAction: Runnable, + token: MediaSession.Token, + appName: String, + appIntent: PendingIntent, + packageName: String + ) = + withContext(backgroundDispatcher) { + val lastActive = systemClock.elapsedRealtime() + val currentEntry = mediaDataRepository.mediaEntries.value[packageName] + val createdTimestampMillis = currentEntry?.createdTimestampMillis ?: 0L + val result = + mediaDataLoader + .get() + .loadMediaDataForResumption( + userId, + desc, + resumeAction, + currentEntry, + token, + appName, + appIntent, + packageName + ) + if (result == null || desc.title.isNullOrBlank()) { + Log.d(TAG, "No MediaData result for resumption") + mediaDataRepository.removeMediaEntry(packageName) + return@withContext + } + + val instanceId = currentEntry?.instanceId ?: logger.getNewInstanceId() + withContext(mainDispatcher) { + onMediaDataLoaded( + packageName, + null, + MediaData( + userId = userId, + initialized = true, + app = result.appName, + appIcon = null, + artist = result.artist, + song = result.song, + artwork = result.artworkIcon, + actions = result.actionIcons, + actionsToShowInCompact = result.actionsToShowInCompact, + semanticActions = result.semanticActions, + packageName = packageName, + token = result.token, + clickIntent = result.clickIntent, + device = result.device, + active = false, + resumeAction = resumeAction, + resumption = true, + notificationKey = packageName, + hasCheckedForResume = true, + lastActive = lastActive, + createdTimestampMillis = createdTimestampMillis, + instanceId = instanceId, + appUid = result.appUid, + isExplicit = result.isExplicit, + resumeProgress = result.resumeProgress, + ) + ) + } + } + + @Deprecated("Cleanup when media_load_metadata_via_media_data_loader is cleaned up") private fun loadMediaDataInBgForResumption( userId: Int, desc: MediaDescription, @@ -730,6 +819,82 @@ class MediaDataProcessor( } } + private suspend fun loadMediaDataWithLoader( + key: String, + sbn: StatusBarNotification, + oldKey: String?, + isNewlyActiveEntry: Boolean = false, + ) = + withContext(backgroundDispatcher) { + val lastActive = systemClock.elapsedRealtime() + val result = mediaDataLoader.get().loadMediaData(key, sbn) + if (result == null) { + Log.d(TAG, "No result from loadMediaData") + return@withContext + } + + val currentEntry = mediaDataRepository.mediaEntries.value[key] + val instanceId = currentEntry?.instanceId ?: logger.getNewInstanceId() + val createdTimestampMillis = currentEntry?.createdTimestampMillis ?: 0L + val resumeAction: Runnable? = currentEntry?.resumeAction + val hasCheckedForResume = currentEntry?.hasCheckedForResume == true + val active = currentEntry?.active ?: true + + // We need to log the correct media added. + if (isNewlyActiveEntry) { + logSingleVsMultipleMediaAdded(result.appUid, sbn.packageName, instanceId) + logger.logActiveMediaAdded( + result.appUid, + sbn.packageName, + instanceId, + result.playbackLocation + ) + } else if (result.playbackLocation != currentEntry?.playbackLocation) { + logger.logPlaybackLocationChange( + result.appUid, + sbn.packageName, + instanceId, + result.playbackLocation + ) + } + + withContext(mainDispatcher) { + onMediaDataLoaded( + key, + oldKey, + MediaData( + userId = sbn.normalizedUserId, + initialized = true, + app = result.appName, + appIcon = result.appIcon, + artist = result.artist, + song = result.song, + artwork = result.artworkIcon, + actions = result.actionIcons, + actionsToShowInCompact = result.actionsToShowInCompact, + semanticActions = result.semanticActions, + packageName = sbn.packageName, + token = result.token, + clickIntent = result.clickIntent, + device = result.device, + active = active, + resumeAction = resumeAction, + playbackLocation = result.playbackLocation, + notificationKey = key, + hasCheckedForResume = hasCheckedForResume, + isPlaying = result.isPlaying, + isClearable = !sbn.isOngoing, + lastActive = lastActive, + createdTimestampMillis = createdTimestampMillis, + instanceId = instanceId, + appUid = result.appUid, + isExplicit = result.isExplicit, + ) + ) + } + } + + @Deprecated("Cleanup when media_load_metadata_via_media_data_loader is cleaned up") fun loadMediaDataInBg( key: String, sbn: StatusBarNotification, @@ -843,7 +1008,7 @@ class MediaDataProcessor( var actionsToShowCollapsed: List<Int> = emptyList() val semanticActions = createActionsFromState(sbn.packageName, mediaController, sbn.user) if (semanticActions == null) { - val actions = createActionsFromNotification(sbn) + val actions = createActionsFromNotification(context, activityStarter, sbn) actionIcons = actions.first actionsToShowCollapsed = actions.second } @@ -926,6 +1091,7 @@ class MediaDataProcessor( } } + @Deprecated("Cleanup when media_load_metadata_via_media_data_loader is cleaned up") private fun getAppInfoFromPackage(packageName: String): ApplicationInfo? { try { return context.packageManager.getApplicationInfo(packageName, 0) @@ -935,6 +1101,7 @@ class MediaDataProcessor( return null } + @Deprecated("Cleanup when media_load_metadata_via_media_data_loader is cleaned up") private fun getAppName(sbn: StatusBarNotification, appInfo: ApplicationInfo?): String { val name = sbn.notification.extras.getString(EXTRA_SUBSTITUTE_APP_NAME) if (name != null) { @@ -948,78 +1115,6 @@ class MediaDataProcessor( } } - /** Generate action buttons based on notification actions */ - private fun createActionsFromNotification( - sbn: StatusBarNotification - ): Pair<List<MediaAction>, List<Int>> { - val notif = sbn.notification - val actionIcons: MutableList<MediaAction> = ArrayList() - val actions = notif.actions - var actionsToShowCollapsed = - notif.extras.getIntArray(Notification.EXTRA_COMPACT_ACTIONS)?.toMutableList() - ?: mutableListOf() - if (actionsToShowCollapsed.size > MAX_COMPACT_ACTIONS) { - Log.e( - TAG, - "Too many compact actions for ${sbn.key}," + - "limiting to first $MAX_COMPACT_ACTIONS" - ) - actionsToShowCollapsed = actionsToShowCollapsed.subList(0, MAX_COMPACT_ACTIONS) - } - - if (actions != null) { - for ((index, action) in actions.withIndex()) { - if (index == MAX_NOTIFICATION_ACTIONS) { - Log.w( - TAG, - "Too many notification actions for ${sbn.key}," + - " limiting to first $MAX_NOTIFICATION_ACTIONS" - ) - break - } - if (action.getIcon() == null) { - if (DEBUG) Log.i(TAG, "No icon for action $index ${action.title}") - actionsToShowCollapsed.remove(index) - continue - } - val runnable = - if (action.actionIntent != null) { - Runnable { - if (action.actionIntent.isActivity) { - activityStarter.startPendingIntentDismissingKeyguard( - action.actionIntent - ) - } else if (action.isAuthenticationRequired()) { - activityStarter.dismissKeyguardThenExecute( - { - var result = sendPendingIntent(action.actionIntent) - result - }, - {}, - true - ) - } else { - sendPendingIntent(action.actionIntent) - } - } - } else { - null - } - val mediaActionIcon = - if (action.getIcon()?.getType() == Icon.TYPE_RESOURCE) { - Icon.createWithResource(sbn.packageName, action.getIcon()!!.getResId()) - } else { - action.getIcon() - } - .setTint(themeText) - .loadDrawable(context) - val mediaAction = MediaAction(mediaActionIcon, runnable, action.title, null) - actionIcons.add(mediaAction) - } - } - return Pair(actionIcons, actionsToShowCollapsed) - } - /** * Generates action button info for this media session based on the PlaybackState * @@ -1036,174 +1131,14 @@ class MediaDataProcessor( controller: MediaController, user: UserHandle ): MediaButton? { - val state = controller.playbackState - if (state == null || !mediaFlags.areMediaSessionActionsEnabled(packageName, user)) { + if (!mediaFlags.areMediaSessionActionsEnabled(packageName, user)) { return null } - - // First, check for standard actions - val playOrPause = - if (isConnectingState(state.state)) { - // Spinner needs to be animating to render anything. Start it here. - val drawable = MediaControlDrawables.getProgress(context) - (drawable as Animatable).start() - MediaAction( - drawable, - null, // no action to perform when clicked - context.getString(R.string.controls_media_button_connecting), - MediaControlDrawables.getConnecting(context), - // Specify a rebind id to prevent the spinner from restarting on later binds. - com.android.internal.R.drawable.progress_small_material - ) - } else if (isPlayingState(state.state)) { - getStandardAction(controller, state.actions, PlaybackState.ACTION_PAUSE) - } else { - getStandardAction(controller, state.actions, PlaybackState.ACTION_PLAY) - } - val prevButton = - getStandardAction(controller, state.actions, PlaybackState.ACTION_SKIP_TO_PREVIOUS) - val nextButton = - getStandardAction(controller, state.actions, PlaybackState.ACTION_SKIP_TO_NEXT) - - // Then, create a way to build any custom actions that will be needed - val customActions = - state.customActions - .asSequence() - .filterNotNull() - .map { getCustomAction(packageName, controller, it) } - .iterator() - fun nextCustomAction() = if (customActions.hasNext()) customActions.next() else null - - // Finally, assign the remaining button slots: play/pause A B C D - // A = previous, else custom action (if not reserved) - // B = next, else custom action (if not reserved) - // C and D are always custom actions - val reservePrev = - controller.extras?.getBoolean( - MediaConstants.SESSION_EXTRAS_KEY_SLOT_RESERVATION_SKIP_TO_PREV - ) == true - val reserveNext = - controller.extras?.getBoolean( - MediaConstants.SESSION_EXTRAS_KEY_SLOT_RESERVATION_SKIP_TO_NEXT - ) == true - - val prevOrCustom = - if (prevButton != null) { - prevButton - } else if (!reservePrev) { - nextCustomAction() - } else { - null - } - - val nextOrCustom = - if (nextButton != null) { - nextButton - } else if (!reserveNext) { - nextCustomAction() - } else { - null - } - - return MediaButton( - playOrPause, - nextOrCustom, - prevOrCustom, - nextCustomAction(), - nextCustomAction(), - reserveNext, - reservePrev - ) - } - - /** - * Create a [MediaAction] for a given action and media session - * - * @param controller MediaController for the session - * @param stateActions The actions included with the session's [PlaybackState] - * @param action A [PlaybackState.Actions] value representing what action to generate. One of: - * ``` - * [PlaybackState.ACTION_PLAY] - * [PlaybackState.ACTION_PAUSE] - * [PlaybackState.ACTION_SKIP_TO_PREVIOUS] - * [PlaybackState.ACTION_SKIP_TO_NEXT] - * @return - * ``` - * - * A [MediaAction] with correct values set, or null if the state doesn't support it - */ - private fun getStandardAction( - controller: MediaController, - stateActions: Long, - @PlaybackState.Actions action: Long - ): MediaAction? { - if (!includesAction(stateActions, action)) { - return null - } - - return when (action) { - PlaybackState.ACTION_PLAY -> { - MediaAction( - MediaControlDrawables.getPlayIcon(context), - { controller.transportControls.play() }, - context.getString(R.string.controls_media_button_play), - MediaControlDrawables.getPlayBackground(context) - ) - } - PlaybackState.ACTION_PAUSE -> { - MediaAction( - MediaControlDrawables.getPauseIcon(context), - { controller.transportControls.pause() }, - context.getString(R.string.controls_media_button_pause), - MediaControlDrawables.getPauseBackground(context) - ) - } - PlaybackState.ACTION_SKIP_TO_PREVIOUS -> { - MediaAction( - MediaControlDrawables.getPrevIcon(context), - { controller.transportControls.skipToPrevious() }, - context.getString(R.string.controls_media_button_prev), - null - ) - } - PlaybackState.ACTION_SKIP_TO_NEXT -> { - MediaAction( - MediaControlDrawables.getNextIcon(context), - { controller.transportControls.skipToNext() }, - context.getString(R.string.controls_media_button_next), - null - ) - } - else -> null - } - } - - /** Check whether the actions from a [PlaybackState] include a specific action */ - private fun includesAction(stateActions: Long, @PlaybackState.Actions action: Long): Boolean { - if ( - (action == PlaybackState.ACTION_PLAY || action == PlaybackState.ACTION_PAUSE) && - (stateActions and PlaybackState.ACTION_PLAY_PAUSE > 0L) - ) { - return true - } - return (stateActions and action != 0L) - } - - /** Get a [MediaAction] representing a [PlaybackState.CustomAction] */ - private fun getCustomAction( - packageName: String, - controller: MediaController, - customAction: PlaybackState.CustomAction - ): MediaAction { - return MediaAction( - Icon.createWithResource(packageName, customAction.icon).loadDrawable(context), - { controller.transportControls.sendCustomAction(customAction, customAction.extras) }, - customAction.name, - null - ) + return createActionsFromState(context, packageName, controller) } /** Load a bitmap from the various Art metadata URIs */ + @Deprecated("Cleanup when media_load_metadata_via_media_data_loader is cleaned up") private fun loadBitmapFromUri(metadata: MediaMetadata): Bitmap? { for (uri in ART_URIS) { val uriString = metadata.getString(uri) @@ -1218,21 +1153,6 @@ class MediaDataProcessor( return null } - private fun sendPendingIntent(intent: PendingIntent): Boolean { - return try { - val options = BroadcastOptions.makeBasic() - options.setInteractive(true) - options.setPendingIntentBackgroundActivityStartMode( - ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED - ) - intent.send(options.toBundle()) - true - } catch (e: PendingIntent.CanceledException) { - Log.d(TAG, "Intent canceled", e) - false - } - } - /** Returns a bitmap if the user can access the given URI, else null */ private fun loadBitmapFromUriForUser( uri: Uri, @@ -1313,6 +1233,7 @@ class MediaDataProcessor( ) } + @MainThread fun onMediaDataLoaded(key: String, oldKey: String?, data: MediaData) = traceSection("MediaDataProcessor#onMediaDataLoaded") { Assert.isMainThread() diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceLogger.kt b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceLogger.kt new file mode 100644 index 000000000000..f886166b986b --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceLogger.kt @@ -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.media.controls.domain.pipeline + +import android.media.session.MediaController +import com.android.settingslib.media.MediaDevice +import com.android.systemui.log.LogBuffer +import com.android.systemui.log.core.LogLevel +import com.android.systemui.log.dagger.MediaDeviceLog +import com.android.systemui.media.controls.shared.model.MediaDeviceData +import javax.inject.Inject + +/** A [LogBuffer] for media device changes */ +class MediaDeviceLogger @Inject constructor(@MediaDeviceLog private val buffer: LogBuffer) { + + fun logBroadcastEvent(event: String, reason: Int, broadcastId: Int) { + buffer.log( + TAG, + LogLevel.DEBUG, + { + str1 = event + int1 = reason + int2 = broadcastId + }, + { "$str1, reason = $int1, broadcastId = $int2" } + ) + } + + fun logBroadcastEvent(event: String, reason: Int) { + buffer.log( + TAG, + LogLevel.DEBUG, + { + str1 = event + int1 = reason + }, + { "$str1, reason = $int1" } + ) + } + + fun logBroadcastMetadataChanged(broadcastId: Int, metadata: String) { + buffer.log( + TAG, + LogLevel.DEBUG, + { + int1 = broadcastId + str1 = metadata + }, + { "onBroadcastMetadataChanged, broadcastId = $int1, metadata = $str1" } + ) + } + + fun logNewDeviceName(name: String?) { + buffer.log(TAG, LogLevel.DEBUG, { str1 = name }, { "New device name $str1" }) + } + + fun logLocalDevice(sassDevice: MediaDeviceData?, connectedDevice: MediaDeviceData?) { + buffer.log( + TAG, + LogLevel.DEBUG, + { + str1 = sassDevice?.name?.toString() + str2 = connectedDevice?.name?.toString() + }, + { "Local device: $str1 or $str2" } + ) + } + + fun logRemoteDevice(routingSessionName: CharSequence?, connectedDevice: MediaDeviceData?) { + buffer.log( + TAG, + LogLevel.DEBUG, + { + str1 = routingSessionName?.toString() + str2 = connectedDevice?.name?.toString() + }, + { "Remote device: $str1 or $str2 or unknown" } + ) + } + + fun logDeviceName( + device: MediaDevice?, + controller: MediaController?, + routingSessionName: CharSequence?, + selectedRouteName: CharSequence? + ) { + buffer.log( + TAG, + LogLevel.DEBUG, + { + str1 = "device $device, controller: $controller" + str2 = routingSessionName?.toString() + str3 = selectedRouteName?.toString() + }, + { "$str1, routingSession $str2 or selected route $str3" } + ) + } + + companion object { + private const val TAG = "MediaDeviceLog" + } +} 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 a193f7f8f498..49b53c2d78ae 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 @@ -71,6 +71,7 @@ constructor( private val localBluetoothManager: Lazy<LocalBluetoothManager?>, @Main private val fgExecutor: Executor, @Background private val bgExecutor: Executor, + private val logger: MediaDeviceLogger, ) : MediaDataManager.Listener { private val listeners: MutableSet<Listener> = mutableSetOf() @@ -281,59 +282,38 @@ constructor( } override fun onBroadcastStarted(reason: Int, broadcastId: Int) { - if (DEBUG) { - Log.d(TAG, "onBroadcastStarted(), reason = $reason , broadcastId = $broadcastId") - } + logger.logBroadcastEvent("onBroadcastStarted", reason, broadcastId) updateCurrent() } override fun onBroadcastStartFailed(reason: Int) { - if (DEBUG) { - Log.d(TAG, "onBroadcastStartFailed(), reason = $reason") - } + logger.logBroadcastEvent("onBroadcastStartFailed", reason) } override fun onBroadcastMetadataChanged( broadcastId: Int, metadata: BluetoothLeBroadcastMetadata ) { - if (DEBUG) { - Log.d( - TAG, - "onBroadcastMetadataChanged(), broadcastId = $broadcastId , " + - "metadata = $metadata" - ) - } + logger.logBroadcastMetadataChanged(broadcastId, metadata.toString()) updateCurrent() } override fun onBroadcastStopped(reason: Int, broadcastId: Int) { - if (DEBUG) { - Log.d(TAG, "onBroadcastStopped(), reason = $reason , broadcastId = $broadcastId") - } + logger.logBroadcastEvent("onBroadcastStopped", reason, broadcastId) updateCurrent() } override fun onBroadcastStopFailed(reason: Int) { - if (DEBUG) { - Log.d(TAG, "onBroadcastStopFailed(), reason = $reason") - } + logger.logBroadcastEvent("onBroadcastStopFailed", reason) } override fun onBroadcastUpdated(reason: Int, broadcastId: Int) { - if (DEBUG) { - Log.d(TAG, "onBroadcastUpdated(), reason = $reason , broadcastId = $broadcastId") - } + logger.logBroadcastEvent("onBroadcastUpdated", reason, broadcastId) updateCurrent() } override fun onBroadcastUpdateFailed(reason: Int, broadcastId: Int) { - if (DEBUG) { - Log.d( - TAG, - "onBroadcastUpdateFailed(), reason = $reason , " + "broadcastId = $broadcastId" - ) - } + logger.logBroadcastEvent("onBroadcastUpdateFailed", reason, broadcastId) } override fun onPlaybackStarted(reason: Int, broadcastId: Int) {} @@ -381,12 +361,16 @@ constructor( name = context.getString(R.string.media_seamless_other_device), showBroadcastButton = false ) + logger.logRemoteDevice(routingSession?.name, connectedDevice) } else { // Prefer SASS if available when playback is local. - activeDevice = getSassDevice() ?: connectedDevice + val sassDevice = getSassDevice() + activeDevice = sassDevice ?: connectedDevice + logger.logLocalDevice(sassDevice, connectedDevice) } current = activeDevice ?: EMPTY_AND_DISABLED_MEDIA_DEVICE_DATA + logger.logNewDeviceName(current?.name?.toString()) } else { val aboutToConnect = aboutToConnectDeviceOverride if ( @@ -407,9 +391,7 @@ constructor( val enabled = device != null && (controller == null || routingSession != null) val name = getDeviceName(device, routingSession) - if (DEBUG) { - Log.d(TAG, "new device name $name") - } + logger.logNewDeviceName(name) current = MediaDeviceData( enabled, @@ -463,14 +445,12 @@ constructor( ): String? { val selectedRoutes = routingSession?.let { mr2manager.get().getSelectedRoutes(it) } - if (DEBUG) { - Log.d( - TAG, - "device is $device, controller $controller," + - " routingSession ${routingSession?.name}" + - " or ${selectedRoutes?.firstOrNull()?.name}" - ) - } + logger.logDeviceName( + device, + controller, + routingSession?.name, + selectedRoutes?.firstOrNull()?.name + ) if (controller == null) { // In resume state, we don't have a controller - just use the device name 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 19cdee7befdd..bf9ef8c5d24e 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 @@ -45,10 +45,10 @@ 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.deviceentry.domain.interactor.DeviceEntryInteractor import com.android.systemui.dump.DumpManager 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.KeyguardState.GONE import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN import com.android.systemui.keyguard.shared.model.TransitionState @@ -75,7 +75,6 @@ import com.android.systemui.plugins.ActivityStarter import com.android.systemui.plugins.FalsingManager import com.android.systemui.qs.PageIndicator import com.android.systemui.res.R -import com.android.systemui.scene.domain.interactor.SceneInteractor import com.android.systemui.scene.shared.flag.SceneContainerFlag import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.shared.system.SysUiStatsLog @@ -103,13 +102,16 @@ import javax.inject.Inject import javax.inject.Provider import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.Job +import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onStart +import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.launch import kotlinx.coroutines.withContext @@ -121,6 +123,7 @@ private val DEBUG = Log.isLoggable(TAG, Log.DEBUG) * Class that is responsible for keeping the view carousel up to date. This also handles changes in * state and applies them to the media carousel like the expansion. */ +@ExperimentalCoroutinesApi @SysUISingleton class MediaCarouselController @Inject @@ -149,7 +152,7 @@ constructor( private val secureSettings: SecureSettings, private val mediaCarouselViewModel: MediaCarouselViewModel, private val mediaViewControllerFactory: Provider<MediaViewController>, - private val sceneInteractor: SceneInteractor, + private val deviceEntryInteractor: DeviceEntryInteractor, ) : Dumpable { /** The current width of the carousel */ var currentCarouselWidth: Int = 0 @@ -164,6 +167,9 @@ constructor( /** Is the player currently visible (at the end of the transformation */ private var playersVisible: Boolean = false + /** Are we currently disabling pagination only allowing one media session to show */ + private var currentlyDisablePagination: Boolean = false + /** * The desired location where we'll be at the end of the transformation. Usually this matches * the end location, except when we're still waiting on a state update call. @@ -327,6 +333,11 @@ constructor( private val controllerById = mutableMapOf<String, MediaViewController>() private val commonViewModels = mutableListOf<MediaCommonViewModel>() + private val isOnGone = + keyguardTransitionInteractor + .isFinishedIn(Scenes.Gone, GONE) + .stateIn(applicationScope, SharingStarted.Eagerly, true) + init { dumpManager.registerDumpable(TAG, this) mediaFrame = inflateMediaCarousel() @@ -904,9 +915,13 @@ constructor( /** Return true if the carousel should be hidden because lockscreen is currently visible */ fun isLockedAndHidden(): Boolean { - val keyguardState = keyguardTransitionInteractor.getFinishedState() - return !allowMediaPlayerOnLockScreen && - KeyguardState.lockscreenVisibleInState(keyguardState) + val isOnLockscreen = + if (SceneContainerFlag.isEnabled) { + !deviceEntryInteractor.isDeviceEntered.value + } else { + !isOnGone.value + } + return !allowMediaPlayerOnLockScreen && isOnLockscreen } private fun reorderAllPlayers( @@ -1347,14 +1362,20 @@ constructor( val endShowsActive = hostStates[currentEndLocation]?.showsOnlyActiveMedia ?: true val startShowsActive = hostStates[currentStartLocation]?.showsOnlyActiveMedia ?: endShowsActive + val startDisablePagination = hostStates[currentStartLocation]?.disablePagination ?: false + val endDisablePagination = hostStates[currentEndLocation]?.disablePagination ?: false + if ( currentlyShowingOnlyActive != endShowsActive || + currentlyDisablePagination != endDisablePagination || ((currentTransitionProgress != 1.0f && currentTransitionProgress != 0.0f) && - startShowsActive != endShowsActive) + (startShowsActive != endShowsActive || + startDisablePagination != endDisablePagination)) ) { // Whenever we're transitioning from between differing states or the endstate differs // we reset the translation currentlyShowingOnlyActive = endShowsActive + currentlyDisablePagination = endDisablePagination mediaCarouselScrollHandler.resetTranslation(animate = true) } } diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/view/MediaHost.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/view/MediaHost.kt index 91050c8bfab3..09a618110f21 100644 --- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/view/MediaHost.kt +++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/view/MediaHost.kt @@ -44,6 +44,7 @@ class MediaHost( lateinit var hostView: UniqueObjectHostView var location: Int = -1 private set + private var visibleChangedListeners: ArraySet<(Boolean) -> Unit> = ArraySet() private val tmpLocationOnScreen: IntArray = intArrayOf(0, 0) @@ -287,6 +288,15 @@ class MediaHost( changedListener?.invoke() } + override var disablePagination: Boolean = false + set(value) { + if (field == value) { + return + } + field = value + changedListener?.invoke() + } + private var lastDisappearHash = disappearParameters.hashCode() /** A listener for all changes. This won't be copied over when invoking [copy] */ @@ -303,6 +313,7 @@ class MediaHost( mediaHostState.visible = visible mediaHostState.disappearParameters = disappearParameters.deepCopy() mediaHostState.falsingProtectionNeeded = falsingProtectionNeeded + mediaHostState.disablePagination = disablePagination return mediaHostState } @@ -331,6 +342,9 @@ class MediaHost( if (!disappearParameters.equals(other.disappearParameters)) { return false } + if (disablePagination != other.disablePagination) { + return false + } return true } @@ -342,6 +356,7 @@ class MediaHost( result = 31 * result + showsOnlyActiveMedia.hashCode() result = 31 * result + if (visible) 1 else 2 result = 31 * result + disappearParameters.hashCode() + result = 31 * result + disablePagination.hashCode() return result } } @@ -400,6 +415,12 @@ interface MediaHostState { */ var disappearParameters: DisappearParameters + /** + * Whether pagination should be disabled for this host, meaning that when there are multiple + * media sessions, only the first one will appear. + */ + var disablePagination: Boolean + /** Get a copy of this view state, deepcopying all appropriate members */ fun copy(): MediaHostState } diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java index 6f82d5dfff15..173a964cc5d3 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java @@ -80,6 +80,7 @@ import com.android.systemui.navigationbar.gestural.EdgeBackGestureHandler; import com.android.systemui.recents.OverviewProxyService; import com.android.systemui.settings.DisplayTracker; import com.android.systemui.settings.UserTracker; +import com.android.systemui.shared.Flags; import com.android.systemui.shared.rotation.RotationPolicyUtil; import com.android.systemui.shared.statusbar.phone.BarTransitions.TransitionMode; import com.android.systemui.shared.system.QuickStepContract; @@ -501,9 +502,11 @@ public final class NavBarHelper implements Settings.Secure.ASSIST_TOUCH_GESTURE_ENABLED, gestureDefault ? 1 : 0, mUserTracker.getUserId()) != 0; + boolean supportsSwipeGesture = QuickStepContract.isGesturalMode(mNavBarMode) + || (QuickStepContract.isLegacyMode(mNavBarMode) && Flags.threeButtonCornerSwipe()); mAssistantAvailable = assistantAvailableForUser && mAssistantTouchGestureEnabled - && QuickStepContract.isGesturalMode(mNavBarMode); + && supportsSwipeGesture; dispatchAssistantEventUpdate(mAssistantAvailable, mLongPressHomeEnabled); } 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 0f82e024afe6..6bd880d56bbb 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java @@ -78,6 +78,7 @@ import com.android.systemui.dagger.qualifiers.Background; import com.android.systemui.model.SysUiState; import com.android.systemui.navigationbar.NavigationModeController; import com.android.systemui.navigationbar.gestural.domain.GestureInteractor; +import com.android.systemui.navigationbar.gestural.domain.TaskMatcher; import com.android.systemui.plugins.FalsingManager; import com.android.systemui.plugins.NavigationEdgeBackPlugin; import com.android.systemui.plugins.PluginListener; @@ -474,9 +475,14 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack } else { String[] gestureBlockingActivities = resources.getStringArray(resId); for (String gestureBlockingActivity : gestureBlockingActivities) { - mGestureInteractor.addGestureBlockedActivity( - ComponentName.unflattenFromString(gestureBlockingActivity), - GestureInteractor.Scope.Local); + final ComponentName component = + ComponentName.unflattenFromString(gestureBlockingActivity); + + if (component != null) { + mGestureInteractor.addGestureBlockedMatcher( + new TaskMatcher.TopActivityComponent(component), + GestureInteractor.Scope.Local); + } } } } catch (NameNotFoundException e) { diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/data/respository/GestureRepository.kt b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/data/respository/GestureRepository.kt index 8f35343626e8..c1f238a03be2 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/data/respository/GestureRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/data/respository/GestureRepository.kt @@ -16,10 +16,9 @@ package com.android.systemui.navigationbar.gestural.data.respository -import android.content.ComponentName -import android.util.ArraySet import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Main +import com.android.systemui.navigationbar.gestural.domain.TaskMatcher import javax.inject.Inject import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.flow.MutableStateFlow @@ -28,36 +27,43 @@ import kotlinx.coroutines.withContext /** A repository for storing gesture related information */ interface GestureRepository { - /** A {@link StateFlow} tracking activities currently blocked from gestures. */ - val gestureBlockedActivities: StateFlow<Set<ComponentName>> + /** A {@link StateFlow} tracking matchers that can block gestures. */ + val gestureBlockedMatchers: StateFlow<Set<TaskMatcher>> - /** Adds an activity to be blocked from gestures. */ - suspend fun addGestureBlockedActivity(activity: ComponentName) + /** Adds a matcher to determine whether a gesture should be blocked. */ + suspend fun addGestureBlockedMatcher(matcher: TaskMatcher) - /** Removes an activity from being blocked from gestures. */ - suspend fun removeGestureBlockedActivity(activity: ComponentName) + /** Removes a matcher from blocking from gestures. */ + suspend fun removeGestureBlockedMatcher(matcher: TaskMatcher) } @SysUISingleton class GestureRepositoryImpl @Inject constructor(@Main private val mainDispatcher: CoroutineDispatcher) : GestureRepository { - private val _gestureBlockedActivities = MutableStateFlow<Set<ComponentName>>(ArraySet()) + private val _gestureBlockedMatchers = MutableStateFlow<Set<TaskMatcher>>(emptySet()) - override val gestureBlockedActivities: StateFlow<Set<ComponentName>> - get() = _gestureBlockedActivities + override val gestureBlockedMatchers: StateFlow<Set<TaskMatcher>> + get() = _gestureBlockedMatchers - override suspend fun addGestureBlockedActivity(activity: ComponentName) = + override suspend fun addGestureBlockedMatcher(matcher: TaskMatcher) = withContext(mainDispatcher) { - _gestureBlockedActivities.emit( - _gestureBlockedActivities.value.toMutableSet().apply { add(activity) } - ) + val existingMatchers = _gestureBlockedMatchers.value + if (existingMatchers.contains(matcher)) { + return@withContext + } + + _gestureBlockedMatchers.value = existingMatchers.toMutableSet().apply { add(matcher) } } - override suspend fun removeGestureBlockedActivity(activity: ComponentName) = + override suspend fun removeGestureBlockedMatcher(matcher: TaskMatcher) = withContext(mainDispatcher) { - _gestureBlockedActivities.emit( - _gestureBlockedActivities.value.toMutableSet().apply { remove(activity) } - ) + val existingMatchers = _gestureBlockedMatchers.value + if (!existingMatchers.contains(matcher)) { + return@withContext + } + + _gestureBlockedMatchers.value = + existingMatchers.toMutableSet().apply { remove(matcher) } } } diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/domain/GestureInteractor.kt b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/domain/GestureInteractor.kt index 61828783cdd9..96386e520d5a 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/domain/GestureInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/domain/GestureInteractor.kt @@ -16,7 +16,6 @@ package com.android.systemui.navigationbar.gestural.domain -import android.content.ComponentName import com.android.app.tracing.coroutines.flow.flowOn import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.dagger.qualifiers.Background @@ -25,7 +24,6 @@ import com.android.systemui.navigationbar.gestural.data.respository.GestureRepos import com.android.systemui.shared.system.ActivityManagerWrapper import com.android.systemui.shared.system.TaskStackChangeListener import com.android.systemui.shared.system.TaskStackChangeListeners -import com.android.systemui.util.kotlin.combine import com.android.systemui.util.kotlin.emitOnStart import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow import javax.inject.Inject @@ -60,7 +58,7 @@ constructor( Global } - private val _localGestureBlockedActivities = MutableStateFlow<Set<ComponentName>>(setOf()) + private val _localGestureBlockedMatchers = MutableStateFlow<Set<TaskMatcher>>(setOf()) private val _topActivity = conflatedCallbackFlow { @@ -79,53 +77,47 @@ constructor( .mapLatest { getTopActivity() } .distinctUntilChanged() - private suspend fun getTopActivity(): ComponentName? = + private suspend fun getTopActivity(): TaskInfo? = withContext(backgroundCoroutineContext) { - val runningTask = activityManagerWrapper.runningTask - runningTask?.topActivity + activityManagerWrapper.runningTask?.let { TaskInfo(it.topActivity, it.activityType) } } val topActivityBlocked = combine( _topActivity, - gestureRepository.gestureBlockedActivities, - _localGestureBlockedActivities.asStateFlow() - ) { activity, global, local -> - activity != null && (global + local).contains(activity) + gestureRepository.gestureBlockedMatchers, + _localGestureBlockedMatchers.asStateFlow() + ) { runningTask, global, local -> + runningTask != null && (global + local).any { it.matches(runningTask) } } - /** - * Adds an {@link Activity} to be blocked based on component when the topmost, focused {@link - * Activity}. - */ - fun addGestureBlockedActivity(activity: ComponentName, gestureScope: Scope) { + /** Adds an [TaskMatcher] to decide whether gestures should be blocked. */ + fun addGestureBlockedMatcher(matcher: TaskMatcher, gestureScope: Scope) { scope.launch { when (gestureScope) { Scope.Local -> { - _localGestureBlockedActivities.emit( - _localGestureBlockedActivities.value.toMutableSet().apply { add(activity) } + _localGestureBlockedMatchers.emit( + _localGestureBlockedMatchers.value.toMutableSet().apply { add(matcher) } ) } Scope.Global -> { - gestureRepository.addGestureBlockedActivity(activity) + gestureRepository.addGestureBlockedMatcher(matcher) } } } } - /** Removes an {@link Activity} from being blocked from gestures. */ - fun removeGestureBlockedActivity(activity: ComponentName, gestureScope: Scope) { + /** Removes a gesture from deciding whether gestures should be blocked */ + fun removeGestureBlockedMatcher(matcher: TaskMatcher, gestureScope: Scope) { scope.launch { when (gestureScope) { Scope.Local -> { - _localGestureBlockedActivities.emit( - _localGestureBlockedActivities.value.toMutableSet().apply { - remove(activity) - } + _localGestureBlockedMatchers.emit( + _localGestureBlockedMatchers.value.toMutableSet().apply { remove(matcher) } ) } Scope.Global -> { - gestureRepository.removeGestureBlockedActivity(activity) + gestureRepository.removeGestureBlockedMatcher(matcher) } } } diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/domain/TaskMatcher.kt b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/domain/TaskMatcher.kt new file mode 100644 index 000000000000..d62b2c001fca --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/domain/TaskMatcher.kt @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.navigationbar.gestural.domain + +import android.content.ComponentName + +/** + * A simple data class for capturing details around a task. Implements equality to ensure changes + * can be identified between emitted values. + */ +data class TaskInfo(val topActivity: ComponentName?, val topActivityType: Int) { + override fun equals(other: Any?): Boolean { + return other is TaskInfo && + other.topActivityType == topActivityType && + other.topActivity == topActivity + } +} + +/** + * [TaskMatcher] provides a way to identify a task based on particular attributes, such as the top + * activity type or component name. + */ +sealed interface TaskMatcher { + fun matches(info: TaskInfo): Boolean + + class TopActivityType(private val type: Int) : TaskMatcher { + override fun matches(info: TaskInfo): Boolean { + return info.topActivity != null && info.topActivityType == type + } + } + + class TopActivityComponent(private val component: ComponentName) : TaskMatcher { + override fun matches(info: TaskInfo): Boolean { + return component == info.topActivity + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt b/packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt index c3274b7ca28b..c39ff557e54f 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt @@ -32,6 +32,7 @@ import androidx.compose.foundation.layout.Column 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.navigationBars import androidx.compose.foundation.layout.windowInsetsPadding import androidx.compose.runtime.Composable @@ -403,30 +404,40 @@ constructor( onDispose { qqsVisible.value = false } } Column(modifier = Modifier.sysuiResTag("quick_qs_panel")) { - QuickQuickSettings( - viewModel = viewModel.containerViewModel.quickQuickSettingsViewModel, - modifier = - Modifier.onGloballyPositioned { coordinates -> - val (leftFromRoot, topFromRoot) = coordinates.positionInRoot().round() - val (width, height) = coordinates.size - qqsPositionOnRoot.set( - leftFromRoot, - topFromRoot, - leftFromRoot + width, - topFromRoot + height - ) - } - .layout { measurable, constraints -> - val placeable = measurable.measure(constraints) - qqsHeight.value = placeable.height + Box(modifier = Modifier.fillMaxWidth()) { + val qsEnabled by viewModel.qsEnabled.collectAsStateWithLifecycle() + if (qsEnabled) { + QuickQuickSettings( + viewModel = viewModel.containerViewModel.quickQuickSettingsViewModel, + modifier = + Modifier.onGloballyPositioned { coordinates -> + val (leftFromRoot, topFromRoot) = + coordinates.positionInRoot().round() + val (width, height) = coordinates.size + qqsPositionOnRoot.set( + leftFromRoot, + topFromRoot, + leftFromRoot + width, + topFromRoot + height + ) + } + .layout { measurable, constraints -> + val placeable = measurable.measure(constraints) + qqsHeight.value = placeable.height - layout(placeable.width, placeable.height) { placeable.place(0, 0) } - } - .padding(top = { qqsPadding }) - .collapseExpandSemanticAction( - stringResource(id = R.string.accessibility_quick_settings_expand) - ) - ) + layout(placeable.width, placeable.height) { + placeable.place(0, 0) + } + } + .padding(top = { qqsPadding }) + .collapseExpandSemanticAction( + stringResource( + id = R.string.accessibility_quick_settings_expand + ) + ) + ) + } + } Spacer(modifier = Modifier.weight(1f)) } } @@ -441,18 +452,23 @@ constructor( stringResource(id = R.string.accessibility_quick_settings_collapse) ) ) { - Box(modifier = Modifier.fillMaxSize().weight(1f)) { - Column { - Spacer(modifier = Modifier.height { qqsPadding + qsExtraPadding.roundToPx() }) - ShadeBody(viewModel = viewModel.containerViewModel) + val qsEnabled by viewModel.qsEnabled.collectAsStateWithLifecycle() + if (qsEnabled) { + Box(modifier = Modifier.fillMaxSize().weight(1f)) { + Column { + Spacer( + modifier = Modifier.height { qqsPadding + qsExtraPadding.roundToPx() } + ) + ShadeBody(viewModel = viewModel.containerViewModel) + } + } + QuickSettingsTheme { + FooterActions( + viewModel = viewModel.footerActionsViewModel, + qsVisibilityLifecycleOwner = this@QSFragmentCompose, + modifier = Modifier.sysuiResTag("qs_footer_actions") + ) } - } - QuickSettingsTheme { - FooterActions( - viewModel = viewModel.footerActionsViewModel, - qsVisibilityLifecycleOwner = this@QSFragmentCompose, - modifier = Modifier.sysuiResTag("qs_footer_actions") - ) } } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModel.kt index df77878b88d9..16133f482f7b 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModel.kt @@ -132,13 +132,17 @@ constructor( _stackScrollerOverscrolling.value = value } - private val qsDisabled = + /** + * Whether QS is enabled by policy. This is normally true, except when it's disabled by some + * policy. See [DisableFlagsRepository]. + */ + val qsEnabled = disableFlagsRepository.disableFlags - .map { !it.isQuickSettingsEnabled() } + .map { it.isQuickSettingsEnabled() } .stateIn( lifecycleScope, SharingStarted.WhileSubscribed(), - !disableFlagsRepository.disableFlags.value.isQuickSettingsEnabled() + disableFlagsRepository.disableFlags.value.isQuickSettingsEnabled() ) private val _showCollapsedOnKeyguard = MutableStateFlow(false) diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/InfiniteGridLayout.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/InfiniteGridLayout.kt index d948dfd7d3b9..c75b601ab4a8 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/InfiniteGridLayout.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/InfiniteGridLayout.kt @@ -16,7 +16,6 @@ package com.android.systemui.qs.panels.ui.compose -import androidx.compose.foundation.layout.height import androidx.compose.foundation.lazy.grid.GridCells import androidx.compose.foundation.lazy.grid.GridItemSpan import androidx.compose.runtime.Composable @@ -24,7 +23,6 @@ import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.remember import androidx.compose.ui.Modifier -import androidx.compose.ui.res.dimensionResource import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.android.systemui.dagger.SysUISingleton import com.android.systemui.qs.panels.shared.model.SizedTileImpl @@ -33,7 +31,6 @@ import com.android.systemui.qs.panels.ui.viewmodel.FixedColumnsSizeViewModel import com.android.systemui.qs.panels.ui.viewmodel.IconTilesViewModel import com.android.systemui.qs.panels.ui.viewmodel.TileViewModel import com.android.systemui.qs.pipeline.shared.TileSpec -import com.android.systemui.res.R import javax.inject.Inject @SysUISingleton @@ -64,7 +61,7 @@ constructor( Tile( tile = sizedTiles[index].tile, iconOnly = iconTilesViewModel.isIconTile(sizedTiles[index].tile.spec), - modifier = Modifier.height(dimensionResource(id = R.dimen.qs_tile_height)) + modifier = Modifier ) } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/QuickQuickSettings.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/QuickQuickSettings.kt index a9027ff92996..eeb55ca19bc3 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/QuickQuickSettings.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/QuickQuickSettings.kt @@ -16,18 +16,15 @@ package com.android.systemui.qs.panels.ui.compose -import androidx.compose.foundation.layout.height import androidx.compose.foundation.lazy.grid.GridCells import androidx.compose.foundation.lazy.grid.GridItemSpan import androidx.compose.runtime.Composable import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.getValue import androidx.compose.ui.Modifier -import androidx.compose.ui.res.dimensionResource import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.android.systemui.compose.modifiers.sysuiResTag import com.android.systemui.qs.panels.ui.viewmodel.QuickQuickSettingsViewModel -import com.android.systemui.res.R @Composable fun QuickQuickSettings( @@ -54,11 +51,7 @@ fun QuickQuickSettings( key = { index -> sizedTiles[index].tile.spec.spec }, span = { index -> GridItemSpan(sizedTiles[index].width) } ) { index -> - Tile( - tile = tiles[index], - iconOnly = sizedTiles[index].isIcon, - modifier = Modifier.height(dimensionResource(id = R.dimen.qs_tile_height)) - ) + Tile(tile = tiles[index], iconOnly = sizedTiles[index].isIcon, modifier = Modifier) } } } 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 79c2eb90af20..24af09d62313 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 @@ -44,7 +44,6 @@ import androidx.compose.foundation.layout.BoxScope import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.aspectRatio import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth @@ -136,23 +135,23 @@ fun Tile( // TODO(b/361789146): Draw the shapes instead of clipping val tileShape = TileDefaults.animateTileShape(uiState.state) - val iconShape = TileDefaults.animateIconShape(uiState.state) TileContainer( colors = colors, showLabels = showLabels, label = uiState.label, iconOnly = iconOnly, - shape = if (iconOnly) iconShape else tileShape, + shape = tileShape, clickEnabled = true, onClick = tile::onClick, onLongClick = tile::onLongClick, - modifier = modifier, + modifier = modifier.height(tileHeight()), ) { val icon = getTileIcon(icon = uiState.icon) if (iconOnly) { TileIcon(icon = icon, color = colors.icon, modifier = Modifier.align(Alignment.Center)) } else { + val iconShape = TileDefaults.animateIconShape(uiState.state) LargeTileContent( label = uiState.label, secondaryLabel = uiState.secondaryLabel, @@ -199,7 +198,7 @@ private fun TileContainer( Expandable( color = backgroundColor, shape = shape, - modifier = Modifier.height(dimensionResource(id = R.dimen.qs_tile_height)).clip(shape) + modifier = Modifier.height(tileHeight()).clip(shape) ) { Box( modifier = @@ -246,7 +245,7 @@ private fun LargeTileContent( // Icon Box( modifier = - Modifier.fillMaxHeight().aspectRatio(1f).thenIf(toggleClickSupported) { + Modifier.size(TileDefaults.ToggleTargetSize).thenIf(toggleClickSupported) { Modifier.clip(iconShape) .background(colors.iconBackground, { 1f }) .combinedClickable(onClick = onClick, onLongClick = onLongClick) @@ -673,7 +672,7 @@ private fun TileIcon( animateToEnd: Boolean = false, modifier: Modifier = Modifier, ) { - val iconModifier = modifier.size(dimensionResource(id = R.dimen.qs_icon_size)) + val iconModifier = modifier.size(TileDefaults.IconSize) val context = LocalContext.current val loadedDrawable = remember(icon, context) { @@ -710,17 +709,12 @@ private fun TileIcon( } } -@Composable private fun Modifier.tilePadding(): Modifier { - return padding(dimensionResource(id = R.dimen.qs_label_container_margin)) + return padding(TileDefaults.TilePadding) } -@Composable private fun tileHorizontalArrangement(): Arrangement.Horizontal { - return spacedBy( - space = dimensionResource(id = R.dimen.qs_label_container_margin), - alignment = Alignment.Start - ) + return spacedBy(space = TileDefaults.TileArrangementPadding, alignment = Alignment.Start) } @Composable @@ -728,7 +722,7 @@ fun tileHeight(iconWithLabel: Boolean = false): Dp { return if (iconWithLabel) { TileDefaults.IconTileWithLabelHeight } else { - dimensionResource(id = R.dimen.qs_tile_height) + TileDefaults.TileHeight } } @@ -749,6 +743,14 @@ private object TileDefaults { val InactiveCornerRadius = 50.dp val ActiveIconCornerRadius = 16.dp val ActiveTileCornerRadius = 24.dp + + val ToggleTargetSize = 56.dp + val IconSize = 24.dp + + val TilePadding = 8.dp + val TileArrangementPadding = 6.dp + + val TileHeight = 72.dp val IconTileWithLabelHeight = 140.dp /** An active tile without dual target uses the active color as background */ @@ -812,7 +814,7 @@ private object TileDefaults { fun animateIconShape(state: Int): Shape { return animateShape( state = state, - activeCornerRadius = ActiveTileCornerRadius, + activeCornerRadius = ActiveIconCornerRadius, label = "QSTileCornerRadius", ) } @@ -821,7 +823,7 @@ private object TileDefaults { fun animateTileShape(state: Int): Shape { return animateShape( state = state, - activeCornerRadius = ActiveIconCornerRadius, + activeCornerRadius = ActiveTileCornerRadius, label = "QSTileIconCornerRadius", ) } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileDataInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileDataInteractor.kt index 3f18fc2066eb..664951d199a7 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileDataInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileDataInteractor.kt @@ -20,10 +20,12 @@ import android.app.Flags import android.content.Context import android.os.UserHandle import com.android.app.tracing.coroutines.flow.map +import com.android.systemui.common.shared.model.asIcon import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger import com.android.systemui.qs.tiles.base.interactor.QSTileDataInteractor import com.android.systemui.qs.tiles.impl.modes.domain.model.ModesTileModel +import com.android.systemui.res.R import com.android.systemui.statusbar.policy.domain.interactor.ZenModeInteractor import javax.inject.Inject import kotlinx.coroutines.CoroutineDispatcher @@ -52,18 +54,32 @@ constructor( */ fun tileData() = zenModeInteractor.activeModes - .map { modes -> - ModesTileModel( - isActivated = modes.isNotEmpty(), - icon = - if (Flags.modesApi() && Flags.modesUi() && Flags.modesUiIcons()) - zenModeInteractor.getActiveModeIcon(modes) - else null, - activeModes = modes.map { it.name } - ) + .map { activeModes -> + val modesIconResId = R.drawable.qs_dnd_icon_off + + if (usesModeIcons()) { + val mainModeDrawable = activeModes.mainMode?.icon?.drawable + val iconResId = if (mainModeDrawable == null) modesIconResId else null + + ModesTileModel( + isActivated = activeModes.isAnyActive(), + icon = (mainModeDrawable ?: context.getDrawable(modesIconResId)!!).asIcon(), + iconResId = iconResId, + activeModes = activeModes.modeNames + ) + } else { + ModesTileModel( + isActivated = activeModes.isAnyActive(), + icon = context.getDrawable(modesIconResId)!!.asIcon(), + iconResId = modesIconResId, + activeModes = activeModes.modeNames + ) + } } .flowOn(bgDispatcher) .distinctUntilChanged() override fun availability(user: UserHandle): Flow<Boolean> = flowOf(Flags.modesUi()) + + private fun usesModeIcons() = Flags.modesApi() && Flags.modesUi() && Flags.modesUiIcons() } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/domain/model/ModesTileModel.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/domain/model/ModesTileModel.kt index 904ff3aaad26..db4812342050 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/domain/model/ModesTileModel.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/domain/model/ModesTileModel.kt @@ -21,5 +21,12 @@ import com.android.systemui.common.shared.model.Icon data class ModesTileModel( val isActivated: Boolean, val activeModes: List<String>, - val icon: Icon? = null + val icon: Icon.Loaded, + + /** + * Resource id corresponding to [icon]. Will only be present if it's know to correspond to a + * resource with a known id in SystemUI (such as resources from `android.R`, + * `com.android.internal.R`, or `com.android.systemui.res` itself). + */ + val iconResId: Int? = null ) diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/ui/ModesTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/ui/ModesTileMapper.kt index 83c3335ebffb..7f571b135fc8 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/ui/ModesTileMapper.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/ui/ModesTileMapper.kt @@ -16,11 +16,9 @@ package com.android.systemui.qs.tiles.impl.modes.ui -import android.app.Flags import android.content.res.Resources import android.icu.text.MessageFormat import android.widget.Button -import com.android.systemui.common.shared.model.asIcon import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.qs.tiles.base.interactor.QSTileDataToStateMapper import com.android.systemui.qs.tiles.impl.modes.domain.model.ModesTileModel @@ -38,15 +36,10 @@ constructor( ) : QSTileDataToStateMapper<ModesTileModel> { override fun map(config: QSTileConfig, data: ModesTileModel): QSTileState = QSTileState.build(resources, theme, config.uiConfig) { - if (Flags.modesApi() && Flags.modesUi() && Flags.modesUiIcons() && data.icon != null) { - icon = { data.icon } - } else { - val iconRes = - if (data.isActivated) R.drawable.qs_dnd_icon_on else R.drawable.qs_dnd_icon_off - val icon = resources.getDrawable(iconRes, theme).asIcon() - this.iconRes = iconRes - this.icon = { icon } + if (!android.app.Flags.modesUiIcons()) { + iconRes = data.iconResId } + icon = { data.icon } activationState = if (data.isActivated) { QSTileState.ActivationState.ACTIVE diff --git a/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneActionsViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneActionsViewModel.kt index af55f5a9a968..2bb5dc66bc16 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneActionsViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneActionsViewModel.kt @@ -31,7 +31,6 @@ import com.android.systemui.scene.ui.viewmodel.SceneActionsViewModel import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.map @@ -78,7 +77,7 @@ constructor( } } } - .collectLatest { actions -> setActions(actions) } + .collect { actions -> setActions(actions) } } @AssistedFactory diff --git a/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneContentViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneContentViewModel.kt index 12f3c9cd0b1b..93bf73fbfae5 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneContentViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneContentViewModel.kt @@ -17,7 +17,6 @@ package com.android.systemui.qs.ui.viewmodel import androidx.lifecycle.LifecycleOwner -import com.android.systemui.lifecycle.SysUiViewModel 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 @@ -44,7 +43,7 @@ constructor( private val footerActionsViewModelFactory: FooterActionsViewModel.Factory, private val footerActionsController: FooterActionsController, val mediaCarouselInteractor: MediaCarouselInteractor, -) : SysUiViewModel { +) { val isMediaVisible: StateFlow<Boolean> = mediaCarouselInteractor.hasAnyMediaOrRecommendation diff --git a/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeSceneActionsViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeSceneActionsViewModel.kt index d2967b87b967..9956a46d4701 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeSceneActionsViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeSceneActionsViewModel.kt @@ -29,7 +29,6 @@ import com.android.systemui.shade.shared.model.ShadeAlignment import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.map /** @@ -62,7 +61,7 @@ constructor( } } } - .collectLatest { actions -> setActions(actions) } + .collect { actions -> setActions(actions) } } @AssistedFactory diff --git a/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeSceneContentViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeSceneContentViewModel.kt index cb99be48912e..924a93923b3a 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeSceneContentViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeSceneContentViewModel.kt @@ -18,7 +18,6 @@ package com.android.systemui.qs.ui.viewmodel -import com.android.systemui.lifecycle.SysUiViewModel import com.android.systemui.shade.ui.viewmodel.OverlayShadeViewModel import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject @@ -35,7 +34,7 @@ class QuickSettingsShadeSceneContentViewModel constructor( val overlayShadeViewModelFactory: OverlayShadeViewModel.Factory, val quickSettingsContainerViewModel: QuickSettingsContainerViewModel, -) : SysUiViewModel { +) { @AssistedFactory interface Factory { diff --git a/packages/SystemUI/src/com/android/systemui/scene/EmptySceneModule.kt b/packages/SystemUI/src/com/android/systemui/scene/EmptySceneModule.kt index efb9375a21f0..4c730a03f0a9 100644 --- a/packages/SystemUI/src/com/android/systemui/scene/EmptySceneModule.kt +++ b/packages/SystemUI/src/com/android/systemui/scene/EmptySceneModule.kt @@ -17,6 +17,7 @@ package com.android.systemui.scene import com.android.systemui.scene.shared.model.Scene +import com.android.systemui.scene.ui.composable.Overlay import dagger.Module import dagger.Provides import dagger.multibindings.ElementsIntoSet @@ -29,4 +30,10 @@ object EmptySceneModule { fun emptySceneSet(): Set<Scene> { return emptySet() } + + @Provides + @ElementsIntoSet + fun emptyOverlaySet(): Set<Overlay> { + return emptySet() + } } diff --git a/packages/SystemUI/src/com/android/systemui/scene/ShadelessSceneContainerFrameworkModule.kt b/packages/SystemUI/src/com/android/systemui/scene/ShadelessSceneContainerFrameworkModule.kt index 9a7eef8f76b9..16ed59f4e6f2 100644 --- a/packages/SystemUI/src/com/android/systemui/scene/ShadelessSceneContainerFrameworkModule.kt +++ b/packages/SystemUI/src/com/android/systemui/scene/ShadelessSceneContainerFrameworkModule.kt @@ -53,6 +53,7 @@ object ShadelessSceneContainerFrameworkModule { Scenes.Bouncer, ), initialSceneKey = Scenes.Lockscreen, + overlayKeys = emptyList(), navigationDistances = mapOf( Scenes.Gone to 0, diff --git a/packages/SystemUI/src/com/android/systemui/scene/data/repository/SceneContainerRepository.kt b/packages/SystemUI/src/com/android/systemui/scene/data/repository/SceneContainerRepository.kt index beb6816d70a9..d60f05e685bb 100644 --- a/packages/SystemUI/src/com/android/systemui/scene/data/repository/SceneContainerRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/scene/data/repository/SceneContainerRepository.kt @@ -18,7 +18,9 @@ package com.android.systemui.scene.data.repository +import com.android.compose.animation.scene.ContentKey import com.android.compose.animation.scene.ObservableTransitionState +import com.android.compose.animation.scene.OverlayKey import com.android.compose.animation.scene.SceneKey import com.android.compose.animation.scene.TransitionKey import com.android.systemui.dagger.SysUISingleton @@ -43,11 +45,27 @@ class SceneContainerRepository @Inject constructor( @Application applicationScope: CoroutineScope, - private val config: SceneContainerConfig, + config: SceneContainerConfig, private val dataSource: SceneDataSource, ) { + /** + * The keys of all scenes and overlays in the container. + * + * They will be sorted in z-order such that the last one is the one that should be rendered on + * top of all previous ones. + */ + val allContentKeys: List<ContentKey> = config.sceneKeys + config.overlayKeys + val currentScene: StateFlow<SceneKey> = dataSource.currentScene + /** + * The current set of overlays to be shown (may be empty). + * + * Note that during a transition between overlays, a different set of overlays may be rendered - + * but only the ones in this set are considered the current overlays. + */ + val currentOverlays: StateFlow<Set<OverlayKey>> = dataSource.currentOverlays + private val _isVisible = MutableStateFlow(true) val isVisible: StateFlow<Boolean> = _isVisible.asStateFlow() @@ -72,16 +90,6 @@ constructor( initialValue = defaultTransitionState, ) - /** - * Returns the keys to all scenes in the container. - * - * The scenes will be sorted in z-order such that the last one is the one that should be - * rendered on top of all previous ones. - */ - fun allSceneKeys(): List<SceneKey> { - return config.sceneKeys - } - fun changeScene( toScene: SceneKey, transitionKey: TransitionKey? = null, @@ -100,6 +108,48 @@ constructor( ) } + /** + * Request to show [overlay] so that it animates in from [currentScene] and ends up being + * visible on screen. + * + * After this returns, this overlay will be included in [currentOverlays]. This does nothing if + * [overlay] is already shown. + */ + fun showOverlay(overlay: OverlayKey, transitionKey: TransitionKey? = null) { + dataSource.showOverlay( + overlay = overlay, + transitionKey = transitionKey, + ) + } + + /** + * Request to hide [overlay] so that it animates out to [currentScene] and ends up *not* being + * visible on screen. + * + * After this returns, this overlay will not be included in [currentOverlays]. This does nothing + * if [overlay] is already hidden. + */ + fun hideOverlay(overlay: OverlayKey, transitionKey: TransitionKey? = null) { + dataSource.hideOverlay( + overlay = overlay, + transitionKey = transitionKey, + ) + } + + /** + * Replace [from] by [to] so that [from] ends up not being visible on screen and [to] ends up + * being visible. + * + * This throws if [from] is not currently shown or if [to] is already shown. + */ + fun replaceOverlay(from: OverlayKey, to: OverlayKey, transitionKey: TransitionKey? = null) { + dataSource.replaceOverlay( + from = from, + to = to, + transitionKey = transitionKey, + ) + } + /** Sets whether the container is visible. */ fun setVisible(isVisible: Boolean) { _isVisible.value = isVisible diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneContainerOcclusionInteractor.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneContainerOcclusionInteractor.kt index ea61bd32c1f2..04620d6982d2 100644 --- a/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneContainerOcclusionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneContainerOcclusionInteractor.kt @@ -118,7 +118,7 @@ constructor( get() = when (this) { is ObservableTransitionState.Idle -> currentScene.canBeOccluded - is ObservableTransitionState.Transition.ChangeCurrentScene -> + is ObservableTransitionState.Transition.ChangeScene -> fromScene.canBeOccluded && toScene.canBeOccluded is ObservableTransitionState.Transition.ReplaceOverlay, is ObservableTransitionState.Transition.ShowOrHideOverlay -> 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 4c404e29018d..a2142b6ce30c 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 @@ -16,7 +16,9 @@ package com.android.systemui.scene.domain.interactor +import com.android.compose.animation.scene.ContentKey import com.android.compose.animation.scene.ObservableTransitionState +import com.android.compose.animation.scene.OverlayKey import com.android.compose.animation.scene.SceneKey import com.android.compose.animation.scene.TransitionKey import com.android.systemui.dagger.SysUISingleton @@ -51,6 +53,7 @@ import kotlinx.coroutines.flow.stateIn * other feature modules should depend on and call into this class when their parts of the * application state change. */ +@OptIn(ExperimentalCoroutinesApi::class) @SysUISingleton class SceneInteractor @Inject @@ -76,6 +79,14 @@ constructor( private val onSceneAboutToChangeListener = mutableSetOf<OnSceneAboutToChangeListener>() /** + * The keys of all scenes and overlays in the container. + * + * They will be sorted in z-order such that the last one is the one that should be rendered on + * top of all previous ones. + */ + val allContentKeys: List<ContentKey> = repository.allContentKeys + + /** * The current scene. * * Note that during a transition between scenes, more than one scene might be rendered but only @@ -84,6 +95,14 @@ constructor( val currentScene: StateFlow<SceneKey> = repository.currentScene /** + * The current set of overlays to be shown (may be empty). + * + * Note that during a transition between overlays, a different set of overlays may be rendered - + * but only the ones in this set are considered the current overlays. + */ + val currentOverlays: StateFlow<Set<OverlayKey>> = repository.currentOverlays + + /** * The current state of the transition. * * Consumers should use this state to know: @@ -112,7 +131,7 @@ constructor( .map { state -> when (state) { is ObservableTransitionState.Idle -> null - is ObservableTransitionState.Transition.ChangeCurrentScene -> state.toScene + is ObservableTransitionState.Transition.ChangeScene -> state.toScene is ObservableTransitionState.Transition.ShowOrHideOverlay, is ObservableTransitionState.Transition.ReplaceOverlay -> TODO("b/359173565: Handle overlay transitions") @@ -192,16 +211,6 @@ constructor( } } - /** - * 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 - * rendered on top of all previous ones. - */ - fun allSceneKeys(): List<SceneKey> { - return repository.allSceneKeys() - } - fun registerSceneStateProcessor(processor: OnSceneAboutToChangeListener) { onSceneAboutToChangeListener.add(processor) } @@ -284,6 +293,105 @@ constructor( } /** + * Request to show [overlay] so that it animates in from [currentScene] and ends up being + * visible on screen. + * + * After this returns, this overlay will be included in [currentOverlays]. This does nothing if + * [overlay] is already shown. + * + * @param overlay The overlay to be shown + * @param loggingReason The reason why the transition is requested, for logging purposes + * @param transitionKey The transition key for this animated transition + */ + @JvmOverloads + fun showOverlay( + overlay: OverlayKey, + loggingReason: String, + transitionKey: TransitionKey? = null, + ) { + if (!validateOverlayChange(to = overlay, loggingReason = loggingReason)) { + return + } + + logger.logOverlayChangeRequested( + to = overlay, + reason = loggingReason, + ) + + repository.showOverlay( + overlay = overlay, + transitionKey = transitionKey, + ) + } + + /** + * Request to hide [overlay] so that it animates out to [currentScene] and ends up *not* being + * visible on screen. + * + * After this returns, this overlay will not be included in [currentOverlays]. This does nothing + * if [overlay] is already hidden. + * + * @param overlay The overlay to be hidden + * @param loggingReason The reason why the transition is requested, for logging purposes + * @param transitionKey The transition key for this animated transition + */ + @JvmOverloads + fun hideOverlay( + overlay: OverlayKey, + loggingReason: String, + transitionKey: TransitionKey? = null, + ) { + if (!validateOverlayChange(from = overlay, loggingReason = loggingReason)) { + return + } + + logger.logOverlayChangeRequested( + from = overlay, + reason = loggingReason, + ) + + repository.hideOverlay( + overlay = overlay, + transitionKey = transitionKey, + ) + } + + /** + * Replace [from] by [to] so that [from] ends up not being visible on screen and [to] ends up + * being visible. + * + * This throws if [from] is not currently shown or if [to] is already shown. + * + * @param from The overlay to be hidden, if any + * @param to The overlay to be shown, if any + * @param loggingReason The reason why the transition is requested, for logging purposes + * @param transitionKey The transition key for this animated transition + */ + @JvmOverloads + fun replaceOverlay( + from: OverlayKey, + to: OverlayKey, + loggingReason: String, + transitionKey: TransitionKey? = null, + ) { + if (!validateOverlayChange(from = from, to = to, loggingReason = loggingReason)) { + return + } + + logger.logOverlayChangeRequested( + from = from, + to = to, + reason = loggingReason, + ) + + repository.replaceOverlay( + from = from, + to = to, + transitionKey = transitionKey, + ) + } + + /** * Sets the visibility of the container. * * Please do not call this from outside of the scene framework. If you are trying to force the @@ -388,7 +496,7 @@ constructor( to: SceneKey, loggingReason: String, ): Boolean { - if (!repository.allSceneKeys().contains(to)) { + if (to !in repository.allContentKeys) { return false } @@ -409,6 +517,34 @@ constructor( return from != to } + /** + * Validates that the given overlay change is allowed. + * + * Will throw a runtime exception for illegal states. + * + * @param from The overlay to be hidden, if any + * @param to The overlay to be shown, if any + * @param loggingReason The reason why the transition is requested, for logging purposes + * @return `true` if the scene change is valid; `false` if it shouldn't happen + */ + private fun validateOverlayChange( + from: OverlayKey? = null, + to: OverlayKey? = null, + loggingReason: String, + ): Boolean { + check(from != null || to != null) { + "No overlay key provided for requested change." + + " Current transition state is ${transitionState.value}." + + " Logging reason for overlay change was: $loggingReason" + } + + val isFromValid = (from == null) || (from in currentOverlays.value) + val isToValid = + (to == null) || (to !in currentOverlays.value && to in repository.allContentKeys) + + return isFromValid && isToValid && 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) } 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 cc46216b6e43..7eb48d6a06ff 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 @@ -479,7 +479,7 @@ constructor( switchToScene( targetSceneKey = Scenes.Lockscreen, loggingReason = "device is starting to sleep", - sceneState = keyguardTransitionInteractor.asleepKeyguardState.value, + sceneState = keyguardInteractor.asleepKeyguardState.value, ) } else { val canSwipeToEnter = deviceEntryInteractor.canSwipeToEnter.value diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/ScrimStartable.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/ScrimStartable.kt index ec743ba5c91e..d1629c799732 100644 --- a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/ScrimStartable.kt +++ b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/ScrimStartable.kt @@ -112,7 +112,7 @@ constructor( // It // happens only when unlocking or when dismissing a dismissible lockscreen. val isTransitioningAwayFromKeyguard = - transitionState is ObservableTransitionState.Transition.ChangeCurrentScene && + transitionState is ObservableTransitionState.Transition.ChangeScene && transitionState.fromScene.isKeyguard() && transitionState.toScene == Scenes.Gone @@ -120,7 +120,7 @@ constructor( val isCurrentSceneShade = currentScene.isShade() // This is true when moving into one of the shade scenes when a non-shade scene. val isTransitioningToShade = - transitionState is ObservableTransitionState.Transition.ChangeCurrentScene && + transitionState is ObservableTransitionState.Transition.ChangeScene && !transitionState.fromScene.isShade() && transitionState.toScene.isShade() diff --git a/packages/SystemUI/src/com/android/systemui/scene/shared/flag/SceneContainerFlag.kt b/packages/SystemUI/src/com/android/systemui/scene/shared/flag/SceneContainerFlag.kt index 6c63c9762e5e..751448fe607e 100644 --- a/packages/SystemUI/src/com/android/systemui/scene/shared/flag/SceneContainerFlag.kt +++ b/packages/SystemUI/src/com/android/systemui/scene/shared/flag/SceneContainerFlag.kt @@ -27,7 +27,7 @@ import com.android.systemui.keyguard.KeyguardBottomAreaRefactor import com.android.systemui.keyguard.KeyguardWmStateRefactor import com.android.systemui.keyguard.MigrateClocksToBlueprint import com.android.systemui.keyguard.shared.ComposeLockscreen -import com.android.systemui.statusbar.notification.shared.NotificationsHeadsUpRefactor +import com.android.systemui.statusbar.notification.shared.NotificationThrottleHun import com.android.systemui.statusbar.phone.PredictiveBackSysUiFlag /** Helper for reading or using the scene container flag state. */ @@ -43,7 +43,7 @@ object SceneContainerFlag { KeyguardBottomAreaRefactor.isEnabled && KeyguardWmStateRefactor.isEnabled && MigrateClocksToBlueprint.isEnabled && - NotificationsHeadsUpRefactor.isEnabled && + NotificationThrottleHun.isEnabled && PredictiveBackSysUiFlag.isEnabled && DeviceEntryUdfpsRefactor.isEnabled @@ -59,7 +59,7 @@ object SceneContainerFlag { KeyguardBottomAreaRefactor.token, KeyguardWmStateRefactor.token, MigrateClocksToBlueprint.token, - NotificationsHeadsUpRefactor.token, + NotificationThrottleHun.token, PredictiveBackSysUiFlag.token, DeviceEntryUdfpsRefactor.token, // NOTE: Changes should also be made in isEnabled and @EnableSceneContainer diff --git a/packages/SystemUI/src/com/android/systemui/scene/shared/logger/SceneLogger.kt b/packages/SystemUI/src/com/android/systemui/scene/shared/logger/SceneLogger.kt index 045a8879f572..aa418e61598c 100644 --- a/packages/SystemUI/src/com/android/systemui/scene/shared/logger/SceneLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/scene/shared/logger/SceneLogger.kt @@ -17,6 +17,7 @@ package com.android.systemui.scene.shared.logger import com.android.compose.animation.scene.ObservableTransitionState +import com.android.compose.animation.scene.OverlayKey import com.android.compose.animation.scene.SceneKey import com.android.systemui.log.LogBuffer import com.android.systemui.log.core.LogLevel @@ -94,6 +95,34 @@ class SceneLogger @Inject constructor(@SceneFrameworkLog private val logBuffer: } } + fun logOverlayChangeRequested( + from: OverlayKey? = null, + to: OverlayKey? = null, + reason: String, + ) { + logBuffer.log( + tag = TAG, + level = LogLevel.INFO, + messageInitializer = { + str1 = from?.toString() + str2 = to?.toString() + str3 = reason + }, + messagePrinter = { + buildString { + append("Overlay change requested: ") + if (str1 != null) { + append(str1) + append(if (str2 == null) " (hidden)" else " → $str2") + } else { + append("$str2 (shown)") + } + append(", reason: $str3") + } + }, + ) + } + fun logVisibilityChange( from: Boolean, to: Boolean, diff --git a/packages/SystemUI/src/com/android/systemui/scene/shared/model/SceneContainerConfig.kt b/packages/SystemUI/src/com/android/systemui/scene/shared/model/SceneContainerConfig.kt index 0a30c31ca739..2311e47abfae 100644 --- a/packages/SystemUI/src/com/android/systemui/scene/shared/model/SceneContainerConfig.kt +++ b/packages/SystemUI/src/com/android/systemui/scene/shared/model/SceneContainerConfig.kt @@ -16,6 +16,7 @@ package com.android.systemui.scene.shared.model +import com.android.compose.animation.scene.OverlayKey import com.android.compose.animation.scene.SceneKey /** Models the configuration of the scene container. */ @@ -38,6 +39,13 @@ data class SceneContainerConfig( val initialSceneKey: SceneKey, /** + * The keys to all overlays in the container, sorted by z-order such that the last one renders + * on top of all previous ones. Overlay keys within the same container must not repeat but it's + * okay to have the same overlay keys in different containers. + */ + val overlayKeys: List<OverlayKey> = emptyList(), + + /** * Navigation distance of each scene. * * The navigation distance is a measure of how many non-back user action "steps" away from the diff --git a/packages/SystemUI/src/com/android/systemui/scene/shared/model/SceneDataSource.kt b/packages/SystemUI/src/com/android/systemui/scene/shared/model/SceneDataSource.kt index 034da25f1a45..4538d1ca48f8 100644 --- a/packages/SystemUI/src/com/android/systemui/scene/shared/model/SceneDataSource.kt +++ b/packages/SystemUI/src/com/android/systemui/scene/shared/model/SceneDataSource.kt @@ -16,6 +16,7 @@ package com.android.systemui.scene.shared.model +import com.android.compose.animation.scene.OverlayKey import com.android.compose.animation.scene.SceneKey import com.android.compose.animation.scene.TransitionKey import kotlinx.coroutines.flow.StateFlow @@ -33,6 +34,14 @@ interface SceneDataSource { val currentScene: StateFlow<SceneKey> /** + * The current set of overlays to be shown (may be empty). + * + * Note that during a transition between overlays, a different set of overlays may be rendered - + * but only the ones in this set are considered the current overlays. + */ + val currentOverlays: StateFlow<Set<OverlayKey>> + + /** * Asks for an asynchronous scene switch to [toScene], which will use the corresponding * installed transition or the one specified by [transitionKey], if provided. */ @@ -47,4 +56,40 @@ interface SceneDataSource { fun snapToScene( toScene: SceneKey, ) + + /** + * Request to show [overlay] so that it animates in from [currentScene] and ends up being + * visible on screen. + * + * After this returns, this overlay will be included in [currentOverlays]. This does nothing if + * [overlay] is already shown. + */ + fun showOverlay( + overlay: OverlayKey, + transitionKey: TransitionKey? = null, + ) + + /** + * Request to hide [overlay] so that it animates out to [currentScene] and ends up *not* being + * visible on screen. + * + * After this returns, this overlay will not be included in [currentOverlays]. This does nothing + * if [overlay] is already hidden. + */ + fun hideOverlay( + overlay: OverlayKey, + transitionKey: TransitionKey? = null, + ) + + /** + * Replace [from] by [to] so that [from] ends up not being visible on screen and [to] ends up + * being visible. + * + * This throws if [from] is not currently shown or if [to] is already shown. + */ + fun replaceOverlay( + from: OverlayKey, + to: OverlayKey, + transitionKey: TransitionKey? = null, + ) } diff --git a/packages/SystemUI/src/com/android/systemui/scene/shared/model/SceneDataSourceDelegator.kt b/packages/SystemUI/src/com/android/systemui/scene/shared/model/SceneDataSourceDelegator.kt index 43c3635f32fc..eb4c0f24c58a 100644 --- a/packages/SystemUI/src/com/android/systemui/scene/shared/model/SceneDataSourceDelegator.kt +++ b/packages/SystemUI/src/com/android/systemui/scene/shared/model/SceneDataSourceDelegator.kt @@ -18,6 +18,7 @@ package com.android.systemui.scene.shared.model +import com.android.compose.animation.scene.OverlayKey import com.android.compose.animation.scene.SceneKey import com.android.compose.animation.scene.TransitionKey import kotlinx.coroutines.CoroutineScope @@ -49,6 +50,15 @@ class SceneDataSourceDelegator( initialValue = config.initialSceneKey, ) + override val currentOverlays: StateFlow<Set<OverlayKey>> = + delegateMutable + .flatMapLatest { delegate -> delegate.currentOverlays } + .stateIn( + scope = applicationScope, + started = SharingStarted.WhileSubscribed(), + initialValue = emptySet(), + ) + override fun changeScene(toScene: SceneKey, transitionKey: TransitionKey?) { delegateMutable.value.changeScene( toScene = toScene, @@ -62,6 +72,28 @@ class SceneDataSourceDelegator( ) } + override fun showOverlay(overlay: OverlayKey, transitionKey: TransitionKey?) { + delegateMutable.value.showOverlay( + overlay = overlay, + transitionKey = transitionKey, + ) + } + + override fun hideOverlay(overlay: OverlayKey, transitionKey: TransitionKey?) { + delegateMutable.value.hideOverlay( + overlay = overlay, + transitionKey = transitionKey, + ) + } + + override fun replaceOverlay(from: OverlayKey, to: OverlayKey, transitionKey: TransitionKey?) { + delegateMutable.value.replaceOverlay( + from = from, + to = to, + transitionKey = transitionKey, + ) + } + /** * Binds the current, dependency injection provided [SceneDataSource] to the given object. * @@ -82,8 +114,21 @@ class SceneDataSourceDelegator( override val currentScene: StateFlow<SceneKey> = MutableStateFlow(initialSceneKey).asStateFlow() + override val currentOverlays: StateFlow<Set<OverlayKey>> = + MutableStateFlow(emptySet<OverlayKey>()).asStateFlow() + override fun changeScene(toScene: SceneKey, transitionKey: TransitionKey?) = Unit override fun snapToScene(toScene: SceneKey) = Unit + + override fun showOverlay(overlay: OverlayKey, transitionKey: TransitionKey?) = Unit + + override fun hideOverlay(overlay: OverlayKey, transitionKey: TransitionKey?) = Unit + + override fun replaceOverlay( + from: OverlayKey, + to: OverlayKey, + transitionKey: TransitionKey? + ) = Unit } } diff --git a/packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneWindowRootView.kt b/packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneWindowRootView.kt index 8aa601f3ecf0..c1bb6fb57685 100644 --- a/packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneWindowRootView.kt +++ b/packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneWindowRootView.kt @@ -9,6 +9,7 @@ import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerDependencies import com.android.systemui.scene.shared.model.Scene import com.android.systemui.scene.shared.model.SceneContainerConfig import com.android.systemui.scene.shared.model.SceneDataSourceDelegator +import com.android.systemui.scene.ui.composable.Overlay import com.android.systemui.scene.ui.viewmodel.SceneContainerViewModel import com.android.systemui.shade.TouchLogger import com.android.systemui.statusbar.notification.stack.ui.view.SharedNotificationContainer @@ -35,6 +36,7 @@ class SceneWindowRootView( containerConfig: SceneContainerConfig, sharedNotificationContainer: SharedNotificationContainer, scenes: Set<Scene>, + overlays: Set<Overlay>, layoutInsetController: LayoutInsetsController, sceneDataSourceDelegator: SceneDataSourceDelegator, alternateBouncerDependencies: AlternateBouncerDependencies, @@ -50,6 +52,7 @@ class SceneWindowRootView( containerConfig = containerConfig, sharedNotificationContainer = sharedNotificationContainer, scenes = scenes, + overlays = overlays, onVisibilityChangedInternal = { isVisible -> super.setVisibility(if (isVisible) View.VISIBLE else View.INVISIBLE) }, diff --git a/packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneWindowRootViewBinder.kt b/packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneWindowRootViewBinder.kt index 0f05af65187d..ec6513a99cad 100644 --- a/packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneWindowRootViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneWindowRootViewBinder.kt @@ -29,6 +29,7 @@ import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import androidx.core.view.isVisible import androidx.lifecycle.Lifecycle +import com.android.compose.animation.scene.OverlayKey import com.android.compose.animation.scene.SceneKey import com.android.compose.theme.PlatformTheme import com.android.internal.policy.ScreenDecorationsUtils @@ -47,6 +48,7 @@ import com.android.systemui.scene.shared.model.Scene import com.android.systemui.scene.shared.model.SceneContainerConfig import com.android.systemui.scene.shared.model.SceneDataSourceDelegator import com.android.systemui.scene.ui.composable.ComposableScene +import com.android.systemui.scene.ui.composable.Overlay import com.android.systemui.scene.ui.composable.SceneContainer import com.android.systemui.scene.ui.viewmodel.SceneContainerViewModel import com.android.systemui.statusbar.notification.stack.ui.view.SharedNotificationContainer @@ -70,6 +72,7 @@ object SceneWindowRootViewBinder { containerConfig: SceneContainerConfig, sharedNotificationContainer: SharedNotificationContainer, scenes: Set<Scene>, + overlays: Set<Overlay>, onVisibilityChangedInternal: (isVisible: Boolean) -> Unit, dataSourceDelegator: SceneDataSourceDelegator, alternateBouncerDependencies: AlternateBouncerDependencies, @@ -86,8 +89,22 @@ object SceneWindowRootViewBinder { } } + val unsortedOverlayByKey: Map<OverlayKey, Overlay> = + overlays.associateBy { overlay -> overlay.key } + val sortedOverlayByKey: Map<OverlayKey, Overlay> = buildMap { + containerConfig.overlayKeys.forEach { overlayKey -> + val overlay = + checkNotNull(unsortedOverlayByKey[overlayKey]) { + "Overlay not found for key \"$overlayKey\"!" + } + + put(overlayKey, overlay) + } + } + view.repeatWhenAttached { view.viewModel( + traceName = "SceneWindowRootViewBinder", minWindowLifecycleState = WindowLifecycleState.ATTACHED, factory = { viewModelFactory.create(motionEventHandlerReceiver) }, ) { viewModel -> @@ -112,6 +129,7 @@ object SceneWindowRootViewBinder { viewModel = viewModel, windowInsets = windowInsets, sceneByKey = sortedSceneByKey, + overlayByKey = sortedOverlayByKey, dataSourceDelegator = dataSourceDelegator, containerConfig = containerConfig, ) @@ -156,6 +174,7 @@ object SceneWindowRootViewBinder { viewModel: SceneContainerViewModel, windowInsets: StateFlow<WindowInsets?>, sceneByKey: Map<SceneKey, Scene>, + overlayByKey: Map<OverlayKey, Overlay>, dataSourceDelegator: SceneDataSourceDelegator, containerConfig: SceneContainerConfig, ): View { @@ -170,6 +189,7 @@ object SceneWindowRootViewBinder { viewModel = viewModel, sceneByKey = sceneByKey.mapValues { (_, scene) -> scene as ComposableScene }, + overlayByKey = overlayByKey, initialSceneKey = containerConfig.initialSceneKey, dataSourceDelegator = dataSourceDelegator, ) diff --git a/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/GoneSceneActionsViewModel.kt b/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/GoneSceneActionsViewModel.kt index b707a5ae4739..88d4c4f63fdf 100644 --- a/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/GoneSceneActionsViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/GoneSceneActionsViewModel.kt @@ -29,7 +29,6 @@ import com.android.systemui.shade.domain.interactor.ShadeInteractor import com.android.systemui.shade.shared.model.ShadeMode import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject -import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.map class GoneSceneActionsViewModel @@ -82,7 +81,7 @@ constructor( } } } - .collectLatest { setActions(it) } + .collect { setActions(it) } } @AssistedFactory diff --git a/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneActionsViewModel.kt b/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneActionsViewModel.kt index 9144f16d9251..368e4fa06a26 100644 --- a/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneActionsViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneActionsViewModel.kt @@ -19,7 +19,6 @@ package com.android.systemui.scene.ui.viewmodel import com.android.compose.animation.scene.UserAction import com.android.compose.animation.scene.UserActionResult import com.android.systemui.lifecycle.ExclusiveActivatable -import com.android.systemui.lifecycle.SysUiViewModel import kotlinx.coroutines.awaitCancellation import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow @@ -33,7 +32,7 @@ import kotlinx.coroutines.flow.asStateFlow * need to worry about resetting the value of [actions] when the view-model is deactivated/canceled, * this base class takes care of it. */ -abstract class SceneActionsViewModel : SysUiViewModel, ExclusiveActivatable() { +abstract class SceneActionsViewModel : ExclusiveActivatable() { private val _actions = MutableStateFlow<Map<UserAction, UserActionResult>>(emptyMap()) /** 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 8b4b77f83218..a73c39da256a 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 @@ -26,7 +26,6 @@ import com.android.systemui.classifier.Classifier import com.android.systemui.classifier.domain.interactor.FalsingInteractor import com.android.systemui.lifecycle.ExclusiveActivatable import com.android.systemui.lifecycle.Hydrator -import com.android.systemui.lifecycle.SysUiViewModel import com.android.systemui.power.domain.interactor.PowerInteractor import com.android.systemui.scene.domain.interactor.SceneInteractor import com.android.systemui.scene.shared.logger.SceneLogger @@ -47,22 +46,15 @@ constructor( private val powerInteractor: PowerInteractor, private val logger: SceneLogger, @Assisted private val motionEventHandlerReceiver: (MotionEventHandler?) -> Unit, -) : SysUiViewModel, ExclusiveActivatable() { - /** - * 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 - * rendered on top of all previous ones. - */ - val allSceneKeys: List<SceneKey> = sceneInteractor.allSceneKeys() +) : ExclusiveActivatable() { /** The scene that should be rendered. */ val currentScene: StateFlow<SceneKey> = sceneInteractor.currentScene - private val hydrator = Hydrator() + private val hydrator = Hydrator("SceneContainerViewModel.hydrator") /** Whether the container is visible. */ - val isVisible: Boolean by hydrator.hydratedStateOf(sceneInteractor.isVisible) + val isVisible: Boolean by hydrator.hydratedStateOf("isVisible", sceneInteractor.isVisible) override suspend fun onActivated(): Nothing { try { @@ -187,8 +179,20 @@ constructor( actionResultMap: Map<UserAction, UserActionResult>, ): Map<UserAction, UserActionResult> { return actionResultMap.mapValues { (_, actionResult) -> - sceneInteractor.resolveSceneFamilyOrNull(actionResult.toScene)?.value?.let { - actionResult.copy(toScene = it) + when (actionResult) { + is UserActionResult.ChangeScene -> { + sceneInteractor.resolveSceneFamilyOrNull(actionResult.toScene)?.value?.let { + toScene -> + UserActionResult( + toScene = toScene, + transitionKey = actionResult.transitionKey, + requiresFullDistanceSwipe = actionResult.requiresFullDistanceSwipe, + ) + } + } + is UserActionResult.ShowOverlay, + is UserActionResult.HideOverlay, + is UserActionResult.ReplaceByOverlay -> TODO("b/353679003: Support overlays") } ?: actionResult } } diff --git a/packages/SystemUI/src/com/android/systemui/settings/brightness/ui/viewModel/BrightnessMirrorViewModel.kt b/packages/SystemUI/src/com/android/systemui/settings/brightness/ui/viewModel/BrightnessMirrorViewModel.kt index 706797d9bbd2..52bc25dc5e23 100644 --- a/packages/SystemUI/src/com/android/systemui/settings/brightness/ui/viewModel/BrightnessMirrorViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/ui/viewModel/BrightnessMirrorViewModel.kt @@ -20,7 +20,6 @@ import android.content.res.Resources import android.util.Log import android.view.View import com.android.systemui.dagger.qualifiers.Main -import com.android.systemui.lifecycle.SysUiViewModel import com.android.systemui.res.R import com.android.systemui.settings.brightness.BrightnessSliderController import com.android.systemui.settings.brightness.MirrorController @@ -37,7 +36,7 @@ constructor( private val brightnessMirrorShowingInteractor: BrightnessMirrorShowingInteractor, @Main private val resources: Resources, val sliderControllerFactory: BrightnessSliderController.Factory, -) : SysUiViewModel, MirrorController { +) : MirrorController { private val tempPosition = IntArray(2) diff --git a/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt b/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt index 4639e2235346..3bb494b7deca 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt @@ -203,6 +203,13 @@ constructor( */ private var shadeConsumingTouches = false + /** + * True if the shade is showing at all. + * + * Inverse of [ShadeInteractor.isShadeFullyCollapsed] + */ + private var shadeShowing = false + /** True if the keyguard transition state is finished on [KeyguardState.LOCKSCREEN]. */ private var onLockscreen = false @@ -414,6 +421,7 @@ constructor( ), { (isFullyExpanded, isUserInteracting, isShadeFullyCollapsed) -> shadeConsumingTouches = isUserInteracting + shadeShowing = !isShadeFullyCollapsed val expandedAndNotInteractive = isFullyExpanded && !isUserInteracting // If we ever are fully expanded and not interacting, capture this state as we @@ -529,7 +537,7 @@ constructor( val isMove = ev.actionMasked == MotionEvent.ACTION_MOVE val isCancel = ev.actionMasked == MotionEvent.ACTION_CANCEL - val hubOccluded = anyBouncerShowing || shadeShowingAndConsumingTouches + val hubOccluded = anyBouncerShowing || shadeConsumingTouches || shadeShowing if ((isDown || isMove) && !hubOccluded) { if (isDown) { diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java index c023b8358a01..31813b240c37 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java +++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java @@ -191,12 +191,10 @@ import com.android.systemui.statusbar.notification.PropertyAnimator; import com.android.systemui.statusbar.notification.ViewGroupFadeHelper; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.domain.interactor.ActiveNotificationsInteractor; -import com.android.systemui.statusbar.notification.domain.interactor.HeadsUpNotificationInteractor; import com.android.systemui.statusbar.notification.footer.shared.FooterViewRefactor; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.row.ExpandableView; import com.android.systemui.statusbar.notification.row.NotificationGutsManager; -import com.android.systemui.statusbar.notification.shared.NotificationsHeadsUpRefactor; import com.android.systemui.statusbar.notification.stack.AmbientState; import com.android.systemui.statusbar.notification.stack.AnimationProperties; import com.android.systemui.statusbar.notification.stack.NotificationListContainer; @@ -439,7 +437,6 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump private boolean mExpandingFromHeadsUp; private boolean mCollapsedOnDown; private boolean mClosingWithAlphaFadeOut; - private boolean mHeadsUpVisible; private boolean mHeadsUpAnimatingAway; private final FalsingManager mFalsingManager; private final FalsingCollector mFalsingCollector; @@ -610,7 +607,6 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump private final PrimaryBouncerToGoneTransitionViewModel mPrimaryBouncerToGoneTransitionViewModel; private final SharedNotificationContainerInteractor mSharedNotificationContainerInteractor; private final ActiveNotificationsInteractor mActiveNotificationsInteractor; - private final HeadsUpNotificationInteractor mHeadsUpNotificationInteractor; private final KeyguardTransitionInteractor mKeyguardTransitionInteractor; private final KeyguardInteractor mKeyguardInteractor; private final PowerInteractor mPowerInteractor; @@ -776,7 +772,6 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump ActivityStarter activityStarter, SharedNotificationContainerInteractor sharedNotificationContainerInteractor, ActiveNotificationsInteractor activeNotificationsInteractor, - HeadsUpNotificationInteractor headsUpNotificationInteractor, ShadeAnimationInteractor shadeAnimationInteractor, KeyguardViewConfigurator keyguardViewConfigurator, DeviceEntryFaceAuthInteractor deviceEntryFaceAuthInteractor, @@ -811,7 +806,6 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump mKeyguardTransitionInteractor = keyguardTransitionInteractor; mSharedNotificationContainerInteractor = sharedNotificationContainerInteractor; mActiveNotificationsInteractor = activeNotificationsInteractor; - mHeadsUpNotificationInteractor = headsUpNotificationInteractor; mKeyguardInteractor = keyguardInteractor; mPowerInteractor = powerInteractor; mKeyguardViewConfigurator = keyguardViewConfigurator; @@ -1222,11 +1216,6 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump } }, mMainDispatcher); - - if (NotificationsHeadsUpRefactor.isEnabled()) { - collectFlow(mView, mHeadsUpNotificationInteractor.isHeadsUpOrAnimatingAway(), - setHeadsUpVisible(), mMainDispatcher); - } } @VisibleForTesting @@ -3077,21 +3066,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump mPanelAlphaEndAction = r; } - private Consumer<Boolean> setHeadsUpVisible() { - return (Boolean isHeadsUpVisible) -> { - mHeadsUpVisible = isHeadsUpVisible; - - if (isHeadsUpVisible) { - updateNotificationTranslucency(); - } - updateExpansionAndVisibility(); - updateGestureExclusionRect(); - mKeyguardStatusBarViewController.updateForHeadsUp(); - }; - } - private void setHeadsUpAnimatingAway(boolean headsUpAnimatingAway) { - NotificationsHeadsUpRefactor.assertInLegacyMode(); mHeadsUpAnimatingAway = headsUpAnimatingAway; mNotificationStackScrollLayoutController.setHeadsUpAnimatingAway(headsUpAnimatingAway); updateVisibility(); @@ -3107,16 +3082,13 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump } private boolean shouldPanelBeVisible() { - boolean headsUpVisible = NotificationsHeadsUpRefactor.isEnabled() ? mHeadsUpVisible - : (mHeadsUpAnimatingAway || mHeadsUpPinnedMode); + boolean headsUpVisible = mHeadsUpAnimatingAway || mHeadsUpPinnedMode; return headsUpVisible || isExpanded() || mBouncerShowing; } private void setHeadsUpManager(HeadsUpManager headsUpManager) { mHeadsUpManager = headsUpManager; - if (!NotificationsHeadsUpRefactor.isEnabled()) { - mHeadsUpManager.addListener(mOnHeadsUpChangedListener); - } + mHeadsUpManager.addListener(mOnHeadsUpChangedListener); mHeadsUpTouchHelper = new HeadsUpTouchHelper( headsUpManager, mStatusBarService, @@ -3204,8 +3176,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump } private boolean isPanelVisibleBecauseOfHeadsUp() { - boolean headsUpVisible = NotificationsHeadsUpRefactor.isEnabled() ? mHeadsUpVisible - : (mHeadsUpManager.hasPinnedHeadsUp() || mHeadsUpAnimatingAway); + boolean headsUpVisible = mHeadsUpManager.hasPinnedHeadsUp() || mHeadsUpAnimatingAway; return headsUpVisible && mBarState == StatusBarState.SHADE; } @@ -3521,7 +3492,6 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump ipw.print("mExpandingFromHeadsUp="); ipw.println(mExpandingFromHeadsUp); ipw.print("mCollapsedOnDown="); ipw.println(mCollapsedOnDown); ipw.print("mClosingWithAlphaFadeOut="); ipw.println(mClosingWithAlphaFadeOut); - ipw.print("mHeadsUpVisible="); ipw.println(mHeadsUpVisible); ipw.print("mHeadsUpAnimatingAway="); ipw.println(mHeadsUpAnimatingAway); ipw.print("mShowIconsWhenExpanded="); ipw.println(mShowIconsWhenExpanded); ipw.print("mIndicationBottomPadding="); ipw.println(mIndicationBottomPadding); @@ -4446,8 +4416,6 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump private final class ShadeHeadsUpChangedListener implements OnHeadsUpChangedListener { @Override public void onHeadsUpPinnedModeChanged(final boolean inPinnedMode) { - NotificationsHeadsUpRefactor.assertInLegacyMode(); - if (inPinnedMode) { mHeadsUpExistenceChangedRunnable.run(); updateNotificationTranslucency(); @@ -4464,8 +4432,6 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump @Override public void onHeadsUpPinned(NotificationEntry entry) { - NotificationsHeadsUpRefactor.assertInLegacyMode(); - if (!isKeyguardShowing()) { mNotificationStackScrollLayoutController.generateHeadsUpAnimation(entry, true); } @@ -4473,8 +4439,6 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump @Override public void onHeadsUpUnPinned(NotificationEntry entry) { - NotificationsHeadsUpRefactor.assertInLegacyMode(); - // When we're unpinning the notification via active edge they remain heads-upped, // we need to make sure that an animation happens in this case, otherwise the // notification diff --git a/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsControllerImpl.java b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsControllerImpl.java index 16aef6586ee9..34c0cb7c7a31 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsControllerImpl.java @@ -1005,7 +1005,7 @@ public class QuickSettingsControllerImpl implements QuickSettingsController, Dum // When expanding QS, let's authenticate the user if possible, // this will speed up notification actions. if (height == 0 && !mKeyguardStateController.canDismissLockScreen()) { - mDeviceEntryFaceAuthInteractor.onQsExpansionStared(); + mDeviceEntryFaceAuthInteractor.onQsExpansionStarted(); } } diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeViewProviderModule.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeViewProviderModule.kt index 606fef0bff62..018144b8a704 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/ShadeViewProviderModule.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeViewProviderModule.kt @@ -14,6 +14,8 @@ * limitations under the License. */ +@file:OptIn(ExperimentalCoroutinesApi::class) + package com.android.systemui.shade import android.annotation.SuppressLint @@ -38,6 +40,7 @@ import com.android.systemui.scene.shared.flag.SceneContainerFlag import com.android.systemui.scene.shared.model.Scene import com.android.systemui.scene.shared.model.SceneContainerConfig import com.android.systemui.scene.shared.model.SceneDataSourceDelegator +import com.android.systemui.scene.ui.composable.Overlay import com.android.systemui.scene.ui.view.SceneWindowRootView import com.android.systemui.scene.ui.view.WindowRootView import com.android.systemui.scene.ui.viewmodel.SceneContainerViewModel @@ -59,6 +62,7 @@ import dagger.Module import dagger.Provides import javax.inject.Named import javax.inject.Provider +import kotlinx.coroutines.ExperimentalCoroutinesApi /** Module for providing views related to the shade. */ @Module @@ -82,6 +86,7 @@ abstract class ShadeViewProviderModule { viewModelFactory: SceneContainerViewModel.Factory, containerConfigProvider: Provider<SceneContainerConfig>, scenesProvider: Provider<Set<@JvmSuppressWildcards Scene>>, + overlaysProvider: Provider<Set<@JvmSuppressWildcards Overlay>>, layoutInsetController: NotificationInsetsController, sceneDataSourceDelegator: Provider<SceneDataSourceDelegator>, alternateBouncerDependencies: Provider<AlternateBouncerDependencies>, @@ -96,6 +101,7 @@ abstract class ShadeViewProviderModule { sharedNotificationContainer = sceneWindowRootView.requireViewById(R.id.shared_notification_container), scenes = scenesProvider.get(), + overlays = overlaysProvider.get(), layoutInsetController = layoutInsetController, sceneDataSourceDelegator = sceneDataSourceDelegator.get(), alternateBouncerDependencies = alternateBouncerDependencies.get(), diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/PanelExpansionInteractorImpl.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/PanelExpansionInteractorImpl.kt index 7d6712166a21..e276f8807df7 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/PanelExpansionInteractorImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/PanelExpansionInteractorImpl.kt @@ -64,7 +64,7 @@ constructor( 0f } ) - is ObservableTransitionState.Transition.ChangeCurrentScene -> + is ObservableTransitionState.Transition.ChangeScene -> when { state.fromScene == Scenes.Gone -> if (state.toScene.isExpandable()) { diff --git a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/NotificationShadeWindowModel.kt b/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/NotificationShadeWindowModel.kt index f270e821840a..9c4bf1faf5cd 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/NotificationShadeWindowModel.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/NotificationShadeWindowModel.kt @@ -26,6 +26,7 @@ import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.util.kotlin.BooleanFlowOperators.any import javax.inject.Inject import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.map /** Models UI state for the shade window. */ @SysUISingleton @@ -42,9 +43,11 @@ constructor( val isKeyguardOccluded: Flow<Boolean> = listOf( // Finished in state... - keyguardTransitionInteractor.isFinishedIn(OCCLUDED), - keyguardTransitionInteractor.isFinishedIn(DREAMING), - keyguardTransitionInteractor.isFinishedIn(Scenes.Communal, GLANCEABLE_HUB), + keyguardTransitionInteractor.transitionValue(OCCLUDED).map { it == 1f }, + keyguardTransitionInteractor.transitionValue(DREAMING).map { it == 1f }, + keyguardTransitionInteractor.transitionValue(Scenes.Communal, GLANCEABLE_HUB).map { + it == 1f + }, // ... or transitions between those states keyguardTransitionInteractor.isInTransition(Edge.create(OCCLUDED, DREAMING)), 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 25ae44ef8cfe..abf1f4cb8b85 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 @@ -18,7 +18,6 @@ package com.android.systemui.shade.ui.viewmodel import com.android.compose.animation.scene.SceneKey import com.android.systemui.lifecycle.ExclusiveActivatable -import com.android.systemui.lifecycle.SysUiViewModel import com.android.systemui.scene.domain.interactor.SceneInteractor import com.android.systemui.scene.shared.model.SceneFamilies import com.android.systemui.scene.shared.model.Scenes @@ -29,7 +28,6 @@ import kotlinx.coroutines.awaitCancellation import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow -import kotlinx.coroutines.flow.collectLatest /** * Models UI state and handles user input for the overlay shade UI, which shows a shade as an @@ -37,8 +35,10 @@ import kotlinx.coroutines.flow.collectLatest */ class OverlayShadeViewModel @AssistedInject -constructor(private val sceneInteractor: SceneInteractor, shadeInteractor: ShadeInteractor) : - SysUiViewModel, ExclusiveActivatable() { +constructor( + private val sceneInteractor: SceneInteractor, + shadeInteractor: ShadeInteractor, +) : ExclusiveActivatable() { private val _backgroundScene = MutableStateFlow(Scenes.Lockscreen) /** The scene to show in the background when the overlay shade is open. */ val backgroundScene: StateFlow<SceneKey> = _backgroundScene.asStateFlow() @@ -47,7 +47,7 @@ constructor(private val sceneInteractor: SceneInteractor, shadeInteractor: Shade val panelAlignment = shadeInteractor.shadeAlignment override suspend fun onActivated(): Nothing { - sceneInteractor.resolveSceneFamily(SceneFamilies.Home).collectLatest { sceneKey -> + sceneInteractor.resolveSceneFamily(SceneFamilies.Home).collect { sceneKey -> _backgroundScene.value = sceneKey } awaitCancellation() 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 edfe79ad91b8..a154e91feca1 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 @@ -25,7 +25,6 @@ import android.os.UserHandle import android.provider.Settings import com.android.systemui.broadcast.BroadcastDispatcher import com.android.systemui.lifecycle.ExclusiveActivatable -import com.android.systemui.lifecycle.SysUiViewModel import com.android.systemui.plugins.ActivityStarter import com.android.systemui.privacy.OngoingPrivacyChip import com.android.systemui.privacy.PrivacyItem @@ -47,7 +46,6 @@ import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow -import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onEach @@ -66,7 +64,7 @@ constructor( private val privacyChipInteractor: PrivacyChipInteractor, private val clockInteractor: ShadeHeaderClockInteractor, private val broadcastDispatcher: BroadcastDispatcher, -) : SysUiViewModel, ExclusiveActivatable() { +) : ExclusiveActivatable() { /** True if there is exactly one mobile connection. */ val isSingleCarrier: StateFlow<Boolean> = mobileIconsInteractor.isSingleCarrier @@ -133,12 +131,10 @@ constructor( launch { mobileIconsInteractor.filteredSubscriptions .map { list -> list.map { it.subscriptionId } } - .collectLatest { _mobileSubIds.value = it } + .collect { _mobileSubIds.value = it } } - launch { - shadeInteractor.isQsEnabled.map { !it }.collectLatest { _isDisabled.value = it } - } + launch { shadeInteractor.isQsEnabled.map { !it }.collect { _isDisabled.value = it } } awaitCancellation() } diff --git a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneActionsViewModel.kt b/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneActionsViewModel.kt index bdc0fdba1dea..ab719132b93e 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneActionsViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneActionsViewModel.kt @@ -29,7 +29,6 @@ import com.android.systemui.shade.domain.interactor.ShadeInteractor import com.android.systemui.shade.shared.model.ShadeMode import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject -import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.combine /** @@ -67,7 +66,7 @@ constructor( } } } - .collectLatest { actions -> setActions(actions) } + .collect { actions -> setActions(actions) } } @AssistedFactory diff --git a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneContentViewModel.kt b/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneContentViewModel.kt index f0f2a65d9abb..7c707592f5ab 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneContentViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneContentViewModel.kt @@ -21,7 +21,6 @@ package com.android.systemui.shade.ui.viewmodel import androidx.lifecycle.LifecycleOwner import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor import com.android.systemui.lifecycle.ExclusiveActivatable -import com.android.systemui.lifecycle.SysUiViewModel 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 @@ -36,12 +35,10 @@ import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject import java.util.concurrent.atomic.AtomicBoolean import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.awaitCancellation import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow -import kotlinx.coroutines.flow.collectLatest /** * Models UI state used to render the content of the shade scene. @@ -62,7 +59,7 @@ constructor( private val unfoldTransitionInteractor: UnfoldTransitionInteractor, private val deviceEntryInteractor: DeviceEntryInteractor, private val sceneInteractor: SceneInteractor, -) : SysUiViewModel, ExclusiveActivatable() { +) : ExclusiveActivatable() { val shadeMode: StateFlow<ShadeMode> = shadeInteractor.shadeMode @@ -76,11 +73,9 @@ constructor( private val footerActionsControllerInitialized = AtomicBoolean(false) override suspend fun onActivated(): Nothing { - deviceEntryInteractor.isDeviceEntered.collectLatest { isDeviceEntered -> + deviceEntryInteractor.isDeviceEntered.collect { isDeviceEntered -> _isEmptySpaceClickable.value = !isDeviceEntered } - - awaitCancellation() } /** diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java index 6eadd2627399..2b44c2f9ea7f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java @@ -58,6 +58,7 @@ import androidx.core.graphics.ColorUtils; import com.android.app.animation.Interpolators; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.statusbar.StatusBarIcon; +import com.android.internal.statusbar.StatusBarIcon.Shape; import com.android.internal.util.ContrastColorUtil; import com.android.systemui.Flags; import com.android.systemui.res.R; @@ -211,16 +212,19 @@ public class StatusBarIconView extends AnimatedImageView implements StatusIconDi /** Should always be preceded by {@link #reloadDimens()} */ @VisibleForTesting public void maybeUpdateIconScaleDimens() { - // We do not resize and scale system icons (on the right), only notification icons (on the - // left). - if (isNotification()) { - updateIconScaleForNotifications(); + // We scale notification icons (on the left) plus icons on the right that explicitly + // want FIXED_SPACE. + boolean useNonSystemIconScaling = isNotification() + || (usesModeIcons() && mIcon != null && mIcon.shape == Shape.FIXED_SPACE); + + if (useNonSystemIconScaling) { + updateIconScaleForNonSystemIcons(); } else { updateIconScaleForSystemIcons(); } } - private void updateIconScaleForNotifications() { + private void updateIconScaleForNonSystemIcons() { float iconScale; // we need to scale the image size to be same as the original size // (fit mOriginalStatusBarIconSize), then we can scale it with mScaleToFitNewIconSize @@ -411,7 +415,9 @@ public class StatusBarIconView extends AnimatedImageView implements StatusIconDi if (!levelEquals) { setImageLevel(icon.iconLevel); } - + if (usesModeIcons()) { + setScaleType(icon.shape == Shape.FIXED_SPACE ? ScaleType.FIT_CENTER : ScaleType.CENTER); + } if (!visibilityEquals) { setVisibility(icon.visible && !mBlocked ? VISIBLE : GONE); } @@ -501,7 +507,12 @@ public class StatusBarIconView extends AnimatedImageView implements StatusIconDi @Nullable private Drawable loadDrawable(Context context, StatusBarIcon statusBarIcon) { if (usesModeIcons() && statusBarIcon.preloadedIcon != null) { - return statusBarIcon.preloadedIcon.mutate(); + Drawable.ConstantState cached = statusBarIcon.preloadedIcon.getConstantState(); + if (cached != null) { + return cached.newDrawable(mContext.getResources()).mutate(); + } else { + return statusBarIcon.preloadedIcon.mutate(); + } } else { int userId = statusBarIcon.user.getIdentifier(); if (userId == UserHandle.USER_ALL) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/StatusBarChipsModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/StatusBarChipsModule.kt index 173ff37cc3b8..be733d4e1e8e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/StatusBarChipsModule.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/StatusBarChipsModule.kt @@ -16,14 +16,24 @@ package com.android.systemui.statusbar.chips +import com.android.systemui.CoreStartable import com.android.systemui.dagger.SysUISingleton import com.android.systemui.log.LogBuffer import com.android.systemui.log.LogBufferFactory +import com.android.systemui.statusbar.chips.ron.demo.ui.viewmodel.DemoRonChipViewModel +import dagger.Binds import dagger.Module import dagger.Provides +import dagger.multibindings.ClassKey +import dagger.multibindings.IntoMap @Module abstract class StatusBarChipsModule { + @Binds + @IntoMap + @ClassKey(DemoRonChipViewModel::class) + abstract fun binds(impl: DemoRonChipViewModel): CoreStartable + companion object { @Provides @SysUISingleton diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModel.kt index 18ea0b445481..e82525810c64 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModel.kt @@ -69,7 +69,7 @@ constructor( state.notificationIconView ) } else { - OngoingActivityChipModel.ChipIcon.Basic(phoneIcon) + OngoingActivityChipModel.ChipIcon.SingleColorIcon(phoneIcon) } // This block mimics OngoingCallController#updateChip. diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/viewmodel/CastToOtherDeviceChipViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/viewmodel/CastToOtherDeviceChipViewModel.kt index cf4e7072a7d1..d4ad6ee0d04b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/viewmodel/CastToOtherDeviceChipViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/viewmodel/CastToOtherDeviceChipViewModel.kt @@ -190,7 +190,7 @@ constructor( ): OngoingActivityChipModel.Shown { return OngoingActivityChipModel.Shown.Timer( icon = - OngoingActivityChipModel.ChipIcon.Basic( + OngoingActivityChipModel.ChipIcon.SingleColorIcon( Icon.Resource( CAST_TO_OTHER_DEVICE_ICON, // This string is "Casting screen" @@ -215,7 +215,7 @@ constructor( private fun createIconOnlyCastChip(deviceName: String?): OngoingActivityChipModel.Shown { return OngoingActivityChipModel.Shown.IconOnly( icon = - OngoingActivityChipModel.ChipIcon.Basic( + OngoingActivityChipModel.ChipIcon.SingleColorIcon( Icon.Resource( CAST_TO_OTHER_DEVICE_ICON, // This string is just "Casting" diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ron/demo/ui/viewmodel/DemoRonChipViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ron/demo/ui/viewmodel/DemoRonChipViewModel.kt new file mode 100644 index 000000000000..84ccaec62867 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ron/demo/ui/viewmodel/DemoRonChipViewModel.kt @@ -0,0 +1,158 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.chips.ron.demo.ui.viewmodel + +import android.content.pm.PackageManager +import android.content.pm.PackageManager.NameNotFoundException +import android.graphics.drawable.Drawable +import com.android.systemui.CoreStartable +import com.android.systemui.common.shared.model.Icon +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.statusbar.chips.ron.shared.StatusBarRonChips +import com.android.systemui.statusbar.chips.ui.model.ColorsModel +import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel +import com.android.systemui.statusbar.chips.ui.viewmodel.OngoingActivityChipViewModel +import com.android.systemui.statusbar.commandline.CommandRegistry +import com.android.systemui.statusbar.commandline.ParseableCommand +import com.android.systemui.statusbar.commandline.Type +import com.android.systemui.util.time.SystemClock +import java.io.PrintWriter +import javax.inject.Inject +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow + +/** + * A view model that will emit demo RON chips (rich ongoing notification chips) from [chip] based on + * adb commands sent by the user. + * + * Example adb commands: + * + * To show a chip with the SysUI icon and custom text: + * ``` + * adb shell cmd statusbar demo-ron -p com.android.systemui -t 10min + * ``` + * + * To hide the chip: + * ``` + * adb shell cmd statusbar demo-ron --hide + * ``` + * + * See [DemoRonCommand] for more information on the adb command spec. + */ +@SysUISingleton +class DemoRonChipViewModel +@Inject +constructor( + private val commandRegistry: CommandRegistry, + private val packageManager: PackageManager, + private val systemClock: SystemClock, +) : OngoingActivityChipViewModel, CoreStartable { + override fun start() { + commandRegistry.registerCommand("demo-ron") { DemoRonCommand() } + } + + private val _chip = + MutableStateFlow<OngoingActivityChipModel>(OngoingActivityChipModel.Hidden()) + override val chip: StateFlow<OngoingActivityChipModel> = _chip.asStateFlow() + + private inner class DemoRonCommand : ParseableCommand("demo-ron") { + private val packageName: String? by + param( + longName = "packageName", + shortName = "p", + description = "The package name for the demo RON app", + valueParser = Type.String, + ) + + private val text: String? by + param( + longName = "text", + shortName = "t", + description = "Text to display in the chip", + valueParser = Type.String, + ) + + private val hide by + flag( + longName = "hide", + description = "Hides any existing demo RON chip", + ) + + override fun execute(pw: PrintWriter) { + if (!StatusBarRonChips.isEnabled) { + pw.println( + "Error: com.android.systemui.status_bar_ron_chips must be enabled " + + "before using this demo feature" + ) + return + } + + if (hide) { + _chip.value = OngoingActivityChipModel.Hidden() + return + } + + val currentPackageName = packageName + if (currentPackageName == null) { + pw.println("--packageName (or -p) must be included") + return + } + + val appIcon = getAppIcon(currentPackageName) + if (appIcon == null) { + pw.println("Package $currentPackageName could not be found") + return + } + + val currentText = text + if (currentText != null) { + _chip.value = + OngoingActivityChipModel.Shown.Text( + icon = appIcon, + // TODO(b/361346412): Include a demo with a custom color theme. + colors = ColorsModel.Themed, + text = currentText, + ) + } else { + _chip.value = + OngoingActivityChipModel.Shown.Timer( + icon = appIcon, + // TODO(b/361346412): Include a demo with a custom color theme. + colors = ColorsModel.Themed, + startTimeMs = systemClock.elapsedRealtime(), + onClickListener = null, + ) + } + } + + private fun getAppIcon(packageName: String): OngoingActivityChipModel.ChipIcon? { + lateinit var iconDrawable: Drawable + try { + // Note: For the real implementation, we should check if applicationInfo exists + // before fetching the icon, so that we either don't show the chip or show a good + // backup icon in case the app info can't be found for some reason. + iconDrawable = packageManager.getApplicationIcon(packageName) + } catch (e: NameNotFoundException) { + return null + } + return OngoingActivityChipModel.ChipIcon.FullColorAppIcon( + Icon.Loaded(drawable = iconDrawable, contentDescription = null), + ) + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ron/shared/StatusBarRonChips.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ron/shared/StatusBarRonChips.kt new file mode 100644 index 000000000000..4ef190991f19 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ron/shared/StatusBarRonChips.kt @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.chips.ron.shared + +import com.android.systemui.Flags +import com.android.systemui.flags.FlagToken +import com.android.systemui.flags.RefactorFlagUtils + +/** Helper for reading or using the status bar RON chips flag state. */ +@Suppress("NOTHING_TO_INLINE") +object StatusBarRonChips { + /** The aconfig flag name */ + const val FLAG_NAME = Flags.FLAG_STATUS_BAR_RON_CHIPS + + /** A token used for dependency declaration */ + val token: FlagToken + get() = FlagToken(FLAG_NAME, isEnabled) + + /** Is the refactor enabled */ + @JvmStatic + inline val isEnabled + get() = Flags.statusBarRonChips() + + /** + * Called to ensure code is only run when the flag is enabled. This protects users from the + * unintended behaviors caused by accidentally running new logic, while also crashing on an eng + * build to ensure that the refactor author catches issues in testing. + */ + @JvmStatic + inline fun isUnexpectedlyInLegacyMode() = + RefactorFlagUtils.isUnexpectedlyInLegacyMode(isEnabled, FLAG_NAME) + + /** + * Called to ensure code is only run when the flag is disabled. This will throw an exception if + * the flag is not enabled to ensure that the refactor author catches issues in testing. + * Caution!! Using this check incorrectly will cause crashes in nextfood builds! + */ + @JvmStatic + inline fun assertInNewMode() = RefactorFlagUtils.assertInNewMode(isEnabled, FLAG_NAME) + + /** + * Called to ensure code is only run when the flag is disabled. This will throw an exception if + * the flag is enabled to ensure that the refactor author catches issues in testing. + */ + @JvmStatic + inline fun assertInLegacyMode() = RefactorFlagUtils.assertInLegacyMode(isEnabled, FLAG_NAME) +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/screenrecord/ui/viewmodel/ScreenRecordChipViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/screenrecord/ui/viewmodel/ScreenRecordChipViewModel.kt index 9e6cacb8b9ff..eb735211a970 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/screenrecord/ui/viewmodel/ScreenRecordChipViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/screenrecord/ui/viewmodel/ScreenRecordChipViewModel.kt @@ -80,7 +80,7 @@ constructor( is ScreenRecordChipModel.Recording -> { OngoingActivityChipModel.Shown.Timer( icon = - OngoingActivityChipModel.ChipIcon.Basic( + OngoingActivityChipModel.ChipIcon.SingleColorIcon( Icon.Resource( ICON, ContentDescription.Resource( diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/sharetoapp/ui/viewmodel/ShareToAppChipViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/sharetoapp/ui/viewmodel/ShareToAppChipViewModel.kt index 7897f93b6496..d99a916b78a7 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/sharetoapp/ui/viewmodel/ShareToAppChipViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/sharetoapp/ui/viewmodel/ShareToAppChipViewModel.kt @@ -110,7 +110,7 @@ constructor( ): OngoingActivityChipModel.Shown { return OngoingActivityChipModel.Shown.Timer( icon = - OngoingActivityChipModel.ChipIcon.Basic( + OngoingActivityChipModel.ChipIcon.SingleColorIcon( Icon.Resource( SHARE_TO_APP_ICON, ContentDescription.Resource(R.string.share_to_app_chip_accessibility_label), diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/model/OngoingActivityChipModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/model/OngoingActivityChipModel.kt index 26a2f9139608..62622a893ec5 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/model/OngoingActivityChipModel.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/model/OngoingActivityChipModel.kt @@ -89,6 +89,16 @@ sealed class OngoingActivityChipModel { ) : Shown(icon = null, colors, onClickListener = null) { override val logName = "Shown.Countdown" } + + /** This chip shows the specified [text] in the chip. */ + data class Text( + override val icon: ChipIcon, + override val colors: ColorsModel, + // TODO(b/361346412): Enforce a max length requirement? + val text: String, + ) : Shown(icon, colors, onClickListener = null) { + override val logName = "Shown.Text" + } } /** Represents an icon to show on the chip. */ @@ -106,7 +116,13 @@ sealed class OngoingActivityChipModel { } } - /** The icon is a basic resource or drawable icon that System UI created internally. */ - data class Basic(val impl: Icon) : ChipIcon + /** + * This icon is a single color and it came from basic resource or drawable icon that System + * UI created internally. + */ + data class SingleColorIcon(val impl: Icon) : ChipIcon + + /** This icon is an app icon in full color (so it should not get tinted in any way). */ + data class FullColorAppIcon(val impl: Icon) : ChipIcon } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModel.kt index b0d897def53f..04c4516c9bed 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModel.kt @@ -23,6 +23,8 @@ import com.android.systemui.log.core.LogLevel import com.android.systemui.statusbar.chips.StatusBarChipsLog import com.android.systemui.statusbar.chips.call.ui.viewmodel.CallChipViewModel import com.android.systemui.statusbar.chips.casttootherdevice.ui.viewmodel.CastToOtherDeviceChipViewModel +import com.android.systemui.statusbar.chips.ron.demo.ui.viewmodel.DemoRonChipViewModel +import com.android.systemui.statusbar.chips.ron.shared.StatusBarRonChips import com.android.systemui.statusbar.chips.screenrecord.ui.viewmodel.ScreenRecordChipViewModel import com.android.systemui.statusbar.chips.sharetoapp.ui.viewmodel.ShareToAppChipViewModel import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel @@ -51,6 +53,7 @@ constructor( shareToAppChipViewModel: ShareToAppChipViewModel, castToOtherDeviceChipViewModel: CastToOtherDeviceChipViewModel, callChipViewModel: CallChipViewModel, + demoRonChipViewModel: DemoRonChipViewModel, @StatusBarChipsLog private val logger: LogBuffer, ) { private enum class ChipType { @@ -58,6 +61,8 @@ constructor( ShareToApp, CastToOtherDevice, Call, + /** A demo of a RON chip (rich ongoing notification chip), used just for testing. */ + DemoRon, } /** Model that helps us internally track the various chip states from each of the types. */ @@ -78,6 +83,7 @@ constructor( val shareToApp: OngoingActivityChipModel.Hidden, val castToOtherDevice: OngoingActivityChipModel.Hidden, val call: OngoingActivityChipModel.Hidden, + val demoRon: OngoingActivityChipModel.Hidden, ) : InternalChipModel } @@ -87,7 +93,8 @@ constructor( shareToAppChipViewModel.chip, castToOtherDeviceChipViewModel.chip, callChipViewModel.chip, - ) { screenRecord, shareToApp, castToOtherDevice, call -> + demoRonChipViewModel.chip, + ) { screenRecord, shareToApp, castToOtherDevice, call, demoRon -> logger.log( TAG, LogLevel.INFO, @@ -98,7 +105,15 @@ constructor( }, { "Chips: ScreenRecord=$str1 > ShareToApp=$str2 > CastToOther=$str3..." }, ) - logger.log(TAG, LogLevel.INFO, { str1 = call.logName }, { "... > Call=$str1" }) + logger.log( + TAG, + LogLevel.INFO, + { + str1 = call.logName + str2 = demoRon.logName + }, + { "... > Call=$str1 > DemoRon=$str2" } + ) // This `when` statement shows the priority order of the chips. when { // Screen recording also activates the media projection APIs, so whenever the @@ -113,17 +128,23 @@ constructor( InternalChipModel.Shown(ChipType.CastToOtherDevice, castToOtherDevice) call is OngoingActivityChipModel.Shown -> InternalChipModel.Shown(ChipType.Call, call) + demoRon is OngoingActivityChipModel.Shown -> { + StatusBarRonChips.assertInNewMode() + InternalChipModel.Shown(ChipType.DemoRon, demoRon) + } else -> { // We should only get here if all chip types are hidden check(screenRecord is OngoingActivityChipModel.Hidden) check(shareToApp is OngoingActivityChipModel.Hidden) check(castToOtherDevice is OngoingActivityChipModel.Hidden) check(call is OngoingActivityChipModel.Hidden) + check(demoRon is OngoingActivityChipModel.Hidden) InternalChipModel.Hidden( screenRecord = screenRecord, shareToApp = shareToApp, castToOtherDevice = castToOtherDevice, call = call, + demoRon = demoRon, ) } } @@ -154,6 +175,7 @@ constructor( ChipType.ShareToApp -> new.shareToApp ChipType.CastToOtherDevice -> new.castToOtherDevice ChipType.Call -> new.call + ChipType.DemoRon -> new.demoRon } } else if (new is InternalChipModel.Shown) { // If we have a chip to show, always show it. @@ -179,6 +201,7 @@ constructor( shareToApp = OngoingActivityChipModel.Hidden(), castToOtherDevice = OngoingActivityChipModel.Hidden(), call = OngoingActivityChipModel.Hidden(), + demoRon = OngoingActivityChipModel.Hidden(), ) } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarModule.kt index ecb6d7fd7be4..406a66449f82 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarModule.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarModule.kt @@ -22,6 +22,7 @@ import com.android.systemui.log.LogBuffer import com.android.systemui.log.LogBufferFactory import com.android.systemui.statusbar.data.StatusBarDataLayerModule import com.android.systemui.statusbar.phone.LightBarController +import com.android.systemui.statusbar.phone.StatusBarSignalPolicy import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallController import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallLog import com.android.systemui.statusbar.ui.SystemBarUtilsProxyImpl @@ -51,6 +52,11 @@ abstract class StatusBarModule { @ClassKey(LightBarController::class) abstract fun bindLightBarController(impl: LightBarController): CoreStartable + @Binds + @IntoMap + @ClassKey(StatusBarSignalPolicy::class) + abstract fun bindStatusBarSignalPolicy(impl: StatusBarSignalPolicy): CoreStartable + companion object { @Provides @SysUISingleton diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/HeadsUpNotificationInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/HeadsUpNotificationInteractor.kt index 74ec7edd5398..aa203d7c0aa9 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/HeadsUpNotificationInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/HeadsUpNotificationInteractor.kt @@ -21,11 +21,11 @@ package com.android.systemui.statusbar.notification.domain.interactor import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor import com.android.systemui.keyguard.shared.model.KeyguardState +import com.android.systemui.scene.shared.flag.SceneContainerFlag import com.android.systemui.shade.domain.interactor.ShadeInteractor import com.android.systemui.statusbar.notification.data.repository.HeadsUpRepository import com.android.systemui.statusbar.notification.data.repository.HeadsUpRowRepository import com.android.systemui.statusbar.notification.shared.HeadsUpRowKey -import com.android.systemui.statusbar.notification.shared.NotificationsHeadsUpRefactor import javax.inject.Inject import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.Flow @@ -58,7 +58,7 @@ constructor( /** Set of currently pinned top-level heads up rows to be displayed. */ val pinnedHeadsUpRows: Flow<Set<HeadsUpRowKey>> by lazy { - if (NotificationsHeadsUpRefactor.isUnexpectedlyInLegacyMode()) { + if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) { flowOf(emptySet()) } else { headsUpRepository.activeHeadsUpRows.flatMapLatest { repositories -> @@ -80,7 +80,7 @@ constructor( /** Are there any pinned heads up rows to display? */ val hasPinnedRows: Flow<Boolean> by lazy { - if (NotificationsHeadsUpRefactor.isUnexpectedlyInLegacyMode()) { + if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) { flowOf(false) } else { headsUpRepository.activeHeadsUpRows.flatMapLatest { rows -> @@ -95,7 +95,7 @@ constructor( } val isHeadsUpOrAnimatingAway: Flow<Boolean> by lazy { - if (NotificationsHeadsUpRefactor.isUnexpectedlyInLegacyMode()) { + if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) { flowOf(false) } else { combine(hasPinnedRows, headsUpRepository.isHeadsUpAnimatingAway) { @@ -123,7 +123,7 @@ constructor( } val showHeadsUpStatusBar: Flow<Boolean> by lazy { - if (NotificationsHeadsUpRefactor.isUnexpectedlyInLegacyMode()) { + if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) { flowOf(false) } else { combine(hasPinnedRows, canShowHeadsUp) { hasPinnedRows, canShowHeadsUp -> diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java index a16129b076f8..4be638f5b41f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java @@ -94,7 +94,6 @@ public class AmbientState implements Dumpable { private boolean mIsSmallScreen; private boolean mPulsing; private float mHideAmount; - private boolean mAppearing; private float mPulseHeight = MAX_PULSE_HEIGHT; /** @@ -718,14 +717,6 @@ public class AmbientState implements Dumpable { return mHideAmount != 0; } - public void setAppearing(boolean appearing) { - mAppearing = appearing; - } - - public boolean isAppearing() { - return mAppearing; - } - public void setPulseHeight(float height) { if (height != mPulseHeight) { mPulseHeight = height; @@ -856,7 +847,6 @@ public class AmbientState implements Dumpable { pw.println("mFractionToShade=" + mFractionToShade); pw.println("mHideAmount=" + mHideAmount); pw.println("mAppearFraction=" + mAppearFraction); - pw.println("mAppearing=" + mAppearing); pw.println("mExpansionFraction=" + mExpansionFraction); pw.println("mQsExpansionFraction=" + mQsExpansionFraction); pw.println("mExpandingVelocity=" + mExpandingVelocity); 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 bf00a39c42ff..1f767aa1cbbc 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 @@ -114,7 +114,6 @@ 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.NotificationThrottleHun; -import com.android.systemui.statusbar.notification.shared.NotificationsHeadsUpRefactor; import com.android.systemui.statusbar.notification.shared.NotificationsImprovedHunAnimation; import com.android.systemui.statusbar.notification.shared.NotificationsLiveDataStoreRefactor; import com.android.systemui.statusbar.notification.stack.shared.model.ShadeScrimBounds; @@ -1616,7 +1615,6 @@ public class NotificationStackScrollLayout float translationY; float appearFraction = 1.0f; boolean appearing = calculateAppearFraction(height) < 1; - mAmbientState.setAppearing(appearing); if (!appearing) { translationY = 0; if (mShouldShowShelfOnly) { @@ -2317,6 +2315,7 @@ public class NotificationStackScrollLayout private void setOverScrollAmountInternal(float amount, boolean onTop, boolean animate, boolean isRubberbanded) { + SceneContainerFlag.assertInLegacyMode(); amount = Math.max(0, amount); if (animate) { mStateAnimator.animateOverScrollToAmount(amount, onTop, isRubberbanded); @@ -4304,7 +4303,7 @@ public class NotificationStackScrollLayout // Resetting headsUpAnimatingAway on Shade expansion avoids delays caused by // waiting for all child animations to finish. // TODO(b/328390331) Do we need to reset this on QS expanded as well? - if (NotificationsHeadsUpRefactor.isEnabled()) { + if (SceneContainerFlag.isEnabled()) { setHeadsUpAnimatingAway(false); } } else { @@ -4415,7 +4414,7 @@ public class NotificationStackScrollLayout void onChildAnimationFinished() { setAnimationRunning(false); - if (NotificationsHeadsUpRefactor.isEnabled()) { + if (SceneContainerFlag.isEnabled()) { setHeadsUpAnimatingAway(false); } requestChildrenUpdate(); @@ -4961,7 +4960,7 @@ public class NotificationStackScrollLayout } public void generateHeadsUpAnimation(NotificationEntry entry, boolean isHeadsUp) { - NotificationsHeadsUpRefactor.assertInLegacyMode(); + SceneContainerFlag.assertInLegacyMode(); ExpandableNotificationRow row = entry.getHeadsUpAnimationView(); generateHeadsUpAnimation(row, isHeadsUp); } @@ -5004,7 +5003,7 @@ public class NotificationStackScrollLayout mNeedsAnimation = true; if (!mIsExpanded && !mWillExpand && !isHeadsUp) { row.setHeadsUpAnimatingAway(true); - if (NotificationsHeadsUpRefactor.isEnabled()) { + if (SceneContainerFlag.isEnabled()) { setHeadsUpAnimatingAway(true); } } @@ -5198,7 +5197,7 @@ public class NotificationStackScrollLayout updateClipping(); } - /** TODO(b/328390331) make this private, when {@link NotificationsHeadsUpRefactor} is removed */ + /** TODO(b/328390331) make this private, when {@link SceneContainerFlag} is removed */ public void setHeadsUpAnimatingAway(boolean headsUpAnimatingAway) { if (mHeadsUpAnimatingAway != headsUpAnimatingAway) { mHeadsUpAnimatingAway = headsUpAnimatingAway; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java index 4e73529b911d..08d3e9f2b634 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java @@ -127,7 +127,6 @@ import com.android.systemui.statusbar.notification.row.NotificationGuts; import com.android.systemui.statusbar.notification.row.NotificationGutsManager; import com.android.systemui.statusbar.notification.row.NotificationSnooze; import com.android.systemui.statusbar.notification.shared.GroupHunAnimationFix; -import com.android.systemui.statusbar.notification.shared.NotificationsHeadsUpRefactor; import com.android.systemui.statusbar.notification.stack.ui.viewbinder.NotificationListViewBinder; import com.android.systemui.statusbar.phone.HeadsUpAppearanceController; import com.android.systemui.statusbar.phone.HeadsUpNotificationViewControllerEmptyImpl; @@ -686,13 +685,13 @@ public class NotificationStackScrollLayoutController implements Dumpable { new OnHeadsUpChangedListener() { @Override public void onHeadsUpPinnedModeChanged(boolean inPinnedMode) { - NotificationsHeadsUpRefactor.assertInLegacyMode(); + SceneContainerFlag.assertInLegacyMode(); mView.setInHeadsUpPinnedMode(inPinnedMode); } @Override public void onHeadsUpStateChanged(NotificationEntry entry, boolean isHeadsUp) { - NotificationsHeadsUpRefactor.assertInLegacyMode(); + SceneContainerFlag.assertInLegacyMode(); NotificationEntry topEntry = mHeadsUpManager.getTopEntry(); mView.setTopHeadsUpRow(topEntry != null ? topEntry.getRow() : null); generateHeadsUpAnimation(entry, isHeadsUp); @@ -880,7 +879,7 @@ public class NotificationStackScrollLayoutController implements Dumpable { }); } - if (!NotificationsHeadsUpRefactor.isEnabled()) { + if (!SceneContainerFlag.isEnabled()) { mHeadsUpManager.addListener(mOnHeadsUpChangedListener); } mHeadsUpManager.setAnimationStateHandler(mView::setHeadsUpGoingAwayAnimationsAllowed); @@ -1508,7 +1507,7 @@ public class NotificationStackScrollLayoutController implements Dumpable { } public void setHeadsUpAnimatingAway(boolean headsUpAnimatingAway) { - NotificationsHeadsUpRefactor.assertInLegacyMode(); + SceneContainerFlag.assertInLegacyMode(); mView.setHeadsUpAnimatingAway(headsUpAnimatingAway); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinder.kt index 5572f8e5cbe8..d770b2003f3b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinder.kt @@ -27,6 +27,7 @@ import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.lifecycle.repeatWhenAttached import com.android.systemui.plugins.FalsingManager import com.android.systemui.res.R +import com.android.systemui.scene.shared.flag.SceneContainerFlag import com.android.systemui.statusbar.NotificationShelf import com.android.systemui.statusbar.notification.NotificationActivityStarter import com.android.systemui.statusbar.notification.collection.render.SectionHeaderController @@ -36,7 +37,6 @@ import com.android.systemui.statusbar.notification.footer.ui.view.FooterView import com.android.systemui.statusbar.notification.footer.ui.viewbinder.FooterViewBinder import com.android.systemui.statusbar.notification.footer.ui.viewmodel.FooterViewModel import com.android.systemui.statusbar.notification.icon.ui.viewbinder.NotificationIconContainerShelfViewBinder -import com.android.systemui.statusbar.notification.shared.NotificationsHeadsUpRefactor import com.android.systemui.statusbar.notification.shared.NotificationsLiveDataStoreRefactor import com.android.systemui.statusbar.notification.shelf.ui.viewbinder.NotificationShelfViewBinder import com.android.systemui.statusbar.notification.stack.DisplaySwitchNotificationsHiderTracker @@ -93,7 +93,7 @@ constructor( view.repeatWhenAttached { lifecycleScope.launch { - if (NotificationsHeadsUpRefactor.isEnabled) { + if (SceneContainerFlag.isEnabled) { launch { hunBinder.bindHeadsUpNotifications(view) } } launch { bindShelf(shelf) } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationScrollViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationScrollViewBinder.kt index 3cc6e81986c5..6d5553fec6b4 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationScrollViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationScrollViewBinder.kt @@ -67,6 +67,7 @@ constructor( suspend fun bind(): Nothing = view.asView().viewModel( + traceName = "NotificationScrollViewBinder", minWindowLifecycleState = WindowLifecycleState.ATTACHED, factory = viewModelFactory::create, ) { viewModel -> diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModel.kt index 5fba615e020b..e55492e67d02 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModel.kt @@ -18,6 +18,7 @@ package com.android.systemui.statusbar.notification.stack.ui.viewmodel import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.dump.DumpManager +import com.android.systemui.scene.shared.flag.SceneContainerFlag import com.android.systemui.shade.domain.interactor.ShadeInteractor import com.android.systemui.statusbar.domain.interactor.RemoteInputInteractor import com.android.systemui.statusbar.notification.domain.interactor.ActiveNotificationsInteractor @@ -26,7 +27,6 @@ import com.android.systemui.statusbar.notification.domain.interactor.SeenNotific import com.android.systemui.statusbar.notification.footer.shared.FooterViewRefactor import com.android.systemui.statusbar.notification.footer.ui.viewmodel.FooterViewModel import com.android.systemui.statusbar.notification.shared.HeadsUpRowKey -import com.android.systemui.statusbar.notification.shared.NotificationsHeadsUpRefactor import com.android.systemui.statusbar.notification.shelf.ui.viewmodel.NotificationShelfViewModel import com.android.systemui.statusbar.notification.stack.domain.interactor.NotificationStackInteractor import com.android.systemui.statusbar.policy.domain.interactor.UserSetupInteractor @@ -256,7 +256,7 @@ constructor( } val topHeadsUpRow: Flow<HeadsUpRowKey?> by lazy { - if (NotificationsHeadsUpRefactor.isUnexpectedlyInLegacyMode()) { + if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) { flowOf(null) } else { headsUpNotificationInteractor.topHeadsUpRow.dumpWhileCollecting("topHeadsUpRow") @@ -264,7 +264,7 @@ constructor( } val pinnedHeadsUpRows: Flow<Set<HeadsUpRowKey>> by lazy { - if (NotificationsHeadsUpRefactor.isUnexpectedlyInLegacyMode()) { + if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) { flowOf(emptySet()) } else { headsUpNotificationInteractor.pinnedHeadsUpRows.dumpWhileCollecting("pinnedHeadsUpRows") @@ -272,7 +272,7 @@ constructor( } val headsUpAnimationsEnabled: Flow<Boolean> by lazy { - if (NotificationsHeadsUpRefactor.isUnexpectedlyInLegacyMode()) { + if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) { flowOf(false) } else { flowOf(true).dumpWhileCollecting("headsUpAnimationsEnabled") @@ -280,7 +280,7 @@ constructor( } val hasPinnedHeadsUpRow: Flow<Boolean> by lazy { - if (NotificationsHeadsUpRefactor.isUnexpectedlyInLegacyMode()) { + if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) { flowOf(false) } else { headsUpNotificationInteractor.hasPinnedRows.dumpWhileCollecting("hasPinnedHeadsUpRow") 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 3999578b83e3..3e42413932f4 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 @@ -19,12 +19,11 @@ package com.android.systemui.statusbar.notification.stack.ui.viewmodel import com.android.compose.animation.scene.ObservableTransitionState.Idle import com.android.compose.animation.scene.ObservableTransitionState.Transition -import com.android.compose.animation.scene.ObservableTransitionState.Transition.ChangeCurrentScene +import com.android.compose.animation.scene.ObservableTransitionState.Transition.ChangeScene import com.android.compose.animation.scene.SceneKey import com.android.systemui.dump.DumpManager import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor import com.android.systemui.lifecycle.ExclusiveActivatable -import com.android.systemui.lifecycle.SysUiViewModel import com.android.systemui.scene.domain.interactor.SceneInteractor import com.android.systemui.scene.shared.flag.SceneContainerFlag import com.android.systemui.scene.shared.model.SceneFamilies @@ -63,7 +62,6 @@ constructor( keyguardInteractor: Lazy<KeyguardInteractor>, ) : ActivatableFlowDumper by ActivatableFlowDumperImpl(dumpManager, "NotificationScrollViewModel"), - SysUiViewModel, ExclusiveActivatable() { override suspend fun onActivated(): Nothing { @@ -79,7 +77,7 @@ constructor( } } - private fun fullyExpandedDuringSceneChange(change: ChangeCurrentScene): Boolean { + private fun fullyExpandedDuringSceneChange(change: ChangeScene): Boolean { // The lockscreen stack is visible during all transitions away from the lockscreen, so keep // the stack expanded until those transitions finish. return (expandedInScene(change.fromScene) && expandedInScene(change.toScene)) || @@ -87,7 +85,7 @@ constructor( } private fun expandFractionDuringSceneChange( - change: ChangeCurrentScene, + change: ChangeScene, shadeExpansion: Float, qsExpansion: Float, ): Float { @@ -120,7 +118,7 @@ constructor( ) { shadeExpansion, _, qsExpansion, transitionState, _ -> when (transitionState) { is Idle -> if (expandedInScene(transitionState.currentScene)) 1f else 0f - is ChangeCurrentScene -> + is ChangeScene -> expandFractionDuringSceneChange( transitionState, shadeExpansion, @@ -250,7 +248,7 @@ constructor( } } -private fun ChangeCurrentScene.isBetween( +private fun ChangeScene.isBetween( a: (SceneKey) -> Boolean, - b: (SceneKey) -> Boolean + b: (SceneKey) -> Boolean, ): Boolean = (a(fromScene) && b(toScene)) || (b(fromScene) && a(toScene)) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModel.kt index d891f62b4563..69c1bf3b61b7 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModel.kt @@ -21,7 +21,6 @@ import com.android.systemui.dump.DumpManager import com.android.systemui.flags.FeatureFlagsClassic import com.android.systemui.flags.Flags import com.android.systemui.lifecycle.ExclusiveActivatable -import com.android.systemui.lifecycle.SysUiViewModel import com.android.systemui.scene.domain.interactor.SceneInteractor import com.android.systemui.scene.shared.flag.SceneContainerFlag import com.android.systemui.shade.domain.interactor.ShadeInteractor @@ -53,7 +52,6 @@ constructor( featureFlags: FeatureFlagsClassic, dumpManager: DumpManager, ) : - SysUiViewModel, ExclusiveActivatable(), ActivatableFlowDumper by ActivatableFlowDumperImpl( dumpManager = dumpManager, 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 f63ee7b9520d..aed00d8cd5be 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 @@ -20,6 +20,7 @@ package com.android.systemui.statusbar.notification.stack.ui.viewmodel import androidx.annotation.VisibleForTesting +import com.android.compose.animation.scene.SceneKey import com.android.systemui.common.shared.model.NotificationContainerBounds import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor import com.android.systemui.dagger.SysUISingleton @@ -141,10 +142,6 @@ constructor( private val communalSceneInteractor: CommunalSceneInteractor, unfoldTransitionInteractor: UnfoldTransitionInteractor, ) : FlowDumperImpl(dumpManager) { - // TODO(b/349784682): Transform deprecated states for Flexiglass - private val statesForConstrainedNotifications: Set<KeyguardState> = - setOf(AOD, LOCKSCREEN, DOZING, ALTERNATE_BOUNCER, PRIMARY_BOUNCER) - private val statesForHiddenKeyguard: Set<KeyguardState> = setOf(GONE, OCCLUDED) /** * Is either shade/qs expanded? This intentionally does not use the [ShadeInteractor] version, @@ -217,14 +214,16 @@ constructor( /** If the user is visually on one of the unoccluded lockscreen states. */ val isOnLockscreen: Flow<Boolean> = - combine( - keyguardTransitionInteractor.finishedKeyguardState.map { - statesForConstrainedNotifications.contains(it) - }, + anyOf( + keyguardTransitionInteractor.isFinishedIn(AOD), + keyguardTransitionInteractor.isFinishedIn(DOZING), + keyguardTransitionInteractor.isFinishedIn(ALTERNATE_BOUNCER), + keyguardTransitionInteractor.isFinishedIn( + scene = Scenes.Bouncer, + stateWithoutSceneContainer = PRIMARY_BOUNCER + ), keyguardTransitionInteractor.transitionValue(LOCKSCREEN).map { it > 0f }, - ) { constrainedNotificationState, transitioningToOrFromLockscreen -> - constrainedNotificationState || transitioningToOrFromLockscreen - } + ) .stateIn( scope = applicationScope, started = SharingStarted.Eagerly, @@ -250,9 +249,10 @@ constructor( /** If the user is visually on the glanceable hub or transitioning to/from it */ private val isOnGlanceableHub: Flow<Boolean> = combine( - keyguardTransitionInteractor.finishedKeyguardState.map { state -> - state == GLANCEABLE_HUB - }, + keyguardTransitionInteractor.isFinishedIn( + scene = Scenes.Communal, + stateWithoutSceneContainer = GLANCEABLE_HUB + ), anyOf( keyguardTransitionInteractor.isInTransition( edge = Edge.create(to = Scenes.Communal), @@ -424,32 +424,19 @@ constructor( .onStart { emit(1f) } .dumpWhileCollecting("alphaForShadeAndQsExpansion") - private fun toFlowArray( - states: Set<KeyguardState>, - flow: (KeyguardState) -> Flow<Boolean> - ): Array<Flow<Boolean>> { - return states.map { flow(it) }.toTypedArray() - } - private val isTransitioningToHiddenKeyguard: Flow<Boolean> = flow { while (currentCoroutineContext().isActive) { emit(false) // Ensure states are inactive to start - allOf( - *toFlowArray(statesForHiddenKeyguard) { state -> - keyguardTransitionInteractor.transitionValue(state).map { it == 0f } - } - ) - .first { it } + allOf(isNotOnState(OCCLUDED), isNotOnState(GONE, Scenes.Gone)).first { it } // Wait for a qualifying transition to begin anyOf( - *toFlowArray(statesForHiddenKeyguard) { state -> - keyguardTransitionInteractor - .transition(Edge.create(to = state)) - .map { it.value > 0f && it.transitionState == RUNNING } - .onStart { emit(false) } - } + transitionToIsRunning(Edge.create(to = OCCLUDED)), + transitionToIsRunning( + edge = Edge.create(to = Scenes.Gone), + edgeWithoutSceneContainer = Edge.create(to = GONE) + ) ) .first { it } emit(true) @@ -458,13 +445,7 @@ constructor( // it is considered safe to reset alpha to 1f for HUNs. combine( keyguardInteractor.statusBarState, - allOf( - *toFlowArray(statesForHiddenKeyguard) { state -> - keyguardTransitionInteractor.transitionValue(state).map { - it == 0f - } - } - ) + allOf(isNotOnState(OCCLUDED), isNotOnState(GONE, Scenes.Gone)) ) { statusBarState, stateIsReversed -> statusBarState == SHADE || stateIsReversed } @@ -473,6 +454,17 @@ constructor( } .dumpWhileCollecting("isTransitioningToHiddenKeyguard") + private fun isNotOnState(stateWithoutSceneContainer: KeyguardState, scene: SceneKey? = null) = + keyguardTransitionInteractor + .transitionValue(scene = scene, stateWithoutSceneContainer = stateWithoutSceneContainer) + .map { it == 0f } + + private fun transitionToIsRunning(edge: Edge, edgeWithoutSceneContainer: Edge? = null) = + keyguardTransitionInteractor + .transition(edge = edge, edgeWithoutSceneContainer = edgeWithoutSceneContainer) + .map { it.value > 0f && it.transitionState == RUNNING } + .onStart { emit(false) } + val panelAlpha = keyguardInteractor.panelAlpha private fun bouncerToGoneNotificationAlpha(viewState: ViewStateAccessor): Flow<Float> = 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 e3242d13077f..7227b9380b61 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java @@ -31,6 +31,7 @@ import static com.android.systemui.Flags.keyboardShortcutHelperRewrite; import static com.android.systemui.Flags.lightRevealMigration; import static com.android.systemui.Flags.newAodTransition; import static com.android.systemui.Flags.relockWithPowerButtonImmediately; +import static com.android.systemui.Flags.statusBarSignalPolicyRefactor; import static com.android.systemui.charging.WirelessChargingAnimation.UNKNOWN_BATTERY_LEVEL; import static com.android.systemui.flags.Flags.SHORTCUT_LIST_SEARCH_LAYOUT; import static com.android.systemui.statusbar.NotificationLockscreenUserManager.PERMISSION_SELF; @@ -870,7 +871,10 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { mBubblesOptional.ifPresent(this::initBubbles); mKeyguardBypassController.listenForQsExpandedChange(); - mStatusBarSignalPolicy.init(); + if (!statusBarSignalPolicyRefactor()) { + mStatusBarSignalPolicy.init(); + } + mKeyguardIndicationController.init(); mColorExtractor.addOnColorsChangedListener(mOnColorsChangedListener); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java index 0ea28a75094c..1efad3b9fa66 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java @@ -47,7 +47,6 @@ import com.android.systemui.statusbar.notification.data.repository.HeadsUpReposi import com.android.systemui.statusbar.notification.data.repository.HeadsUpRowRepository; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.shared.NotificationThrottleHun; -import com.android.systemui.statusbar.notification.shared.NotificationsHeadsUpRefactor; import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper; import com.android.systemui.statusbar.policy.AnimationStateHandler; import com.android.systemui.statusbar.policy.AvalancheController; @@ -284,7 +283,7 @@ public class HeadsUpManagerPhone extends BaseHeadsUpManager implements private void onShadeOrQsExpanded(Boolean isExpanded) { if (isExpanded != mIsExpanded) { mIsExpanded = isExpanded; - if (!NotificationsHeadsUpRefactor.isEnabled() && isExpanded) { + if (!SceneContainerFlag.isEnabled() && isExpanded) { mHeadsUpAnimatingAway.setValue(false); } } @@ -517,7 +516,7 @@ public class HeadsUpManagerPhone extends BaseHeadsUpManager implements @Nullable private HeadsUpEntryPhone getTopHeadsUpEntryPhone() { - if (NotificationsHeadsUpRefactor.isEnabled()) { + if (SceneContainerFlag.isEnabled()) { return (HeadsUpEntryPhone) mTopHeadsUpRow.getValue(); } else { return (HeadsUpEntryPhone) getTopHeadsUpEntry(); @@ -711,7 +710,7 @@ public class HeadsUpManagerPhone extends BaseHeadsUpManager implements } private NotificationEntry requireEntry() { - /* check if */ NotificationsHeadsUpRefactor.isUnexpectedlyInLegacyMode(); + /* check if */ SceneContainerFlag.isUnexpectedlyInLegacyMode(); return Objects.requireNonNull(mEntry); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java index 05bd1a7676ae..ba39c3bb4124 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java @@ -44,7 +44,7 @@ import android.view.View; import androidx.lifecycle.Observer; -import com.android.settingslib.notification.modes.ZenMode; +import com.android.internal.statusbar.StatusBarIcon; import com.android.systemui.Flags; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.dagger.qualifiers.DisplayId; @@ -80,6 +80,7 @@ import com.android.systemui.statusbar.policy.SensorPrivacyController; import com.android.systemui.statusbar.policy.UserInfoController; import com.android.systemui.statusbar.policy.ZenModeController; import com.android.systemui.statusbar.policy.domain.interactor.ZenModeInteractor; +import com.android.systemui.statusbar.policy.domain.model.ZenModeInfo; import com.android.systemui.util.RingerModeTracker; import com.android.systemui.util.kotlin.JavaAdapter; import com.android.systemui.util.time.DateFormatUtil; @@ -363,7 +364,7 @@ public class PhoneStatusBarPolicy // Note that we're not fully replacing ZenModeController with ZenModeInteractor, so // we listen for the extra event here but still add the ZMC callback. mJavaAdapter.alwaysCollectFlow(mZenModeInteractor.getMainActiveMode(), - this::onActiveModeChanged); + this::onMainActiveModeChanged); } mZenController.addCallback(mZenControllerCallback); if (!Flags.statusBarScreenSharingChips()) { @@ -395,20 +396,23 @@ public class PhoneStatusBarPolicy () -> mResources.getString(R.string.accessibility_managed_profile)); } - private void onActiveModeChanged(@Nullable ZenMode mode) { + private void onMainActiveModeChanged(@Nullable ZenModeInfo mainActiveMode) { if (!usesModeIcons()) { - Log.wtf(TAG, "onActiveModeChanged shouldn't be called if MODES_UI_ICONS is disabled"); + Log.wtf(TAG, "onMainActiveModeChanged shouldn't run if MODES_UI_ICONS is disabled"); return; } - boolean visible = mode != null; - if (visible) { - // TODO: b/360399800 - Get the resource id, package, and cached drawable from the mode; - // this is a shortcut for testing. - String resPackage = mode.getIconKey().resPackage(); - int iconResId = mode.getIconKey().resId(); - mIconController.setResourceIcon(mSlotZen, resPackage, iconResId, - /* preloadedIcon= */ null, mode.getName()); + boolean visible = mainActiveMode != null; + if (visible) { + // Shape=FIXED_SPACE because mode icons can be from 3P packages and may not be square; + // we don't want to allow apps to set incredibly wide icons and take up too much space + // in the status bar. + mIconController.setResourceIcon(mSlotZen, + mainActiveMode.getIcon().key().resPackage(), + mainActiveMode.getIcon().key().resId(), + mainActiveMode.getIcon().drawable(), + mainActiveMode.getName(), + StatusBarIcon.Shape.FIXED_SPACE); } if (visible != mZenVisible) { mIconController.setIconVisibility(mSlotZen, visible); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeadsUpChangeListener.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeadsUpChangeListener.java index da5877b6417f..8f2d4f931b91 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeadsUpChangeListener.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeadsUpChangeListener.java @@ -19,12 +19,12 @@ package com.android.systemui.statusbar.phone; import com.android.systemui.CoreStartable; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.scene.shared.flag.SceneContainerFlag; import com.android.systemui.shade.ShadeViewController; import com.android.systemui.shade.domain.interactor.PanelExpansionInteractor; import com.android.systemui.statusbar.NotificationRemoteInputManager; import com.android.systemui.statusbar.NotificationShadeWindowController; import com.android.systemui.statusbar.StatusBarState; -import com.android.systemui.statusbar.notification.shared.NotificationsHeadsUpRefactor; import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController; import com.android.systemui.statusbar.policy.HeadsUpManager; import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener; @@ -112,7 +112,7 @@ public class StatusBarHeadsUpChangeListener implements OnHeadsUpChangedListener, } private void setHeadsAnimatingAway(boolean headsUpAnimatingAway) { - if (!NotificationsHeadsUpRefactor.isEnabled()) { + if (!SceneContainerFlag.isEnabled()) { mHeadsUpManager.setHeadsUpAnimatingAway(headsUpAnimatingAway); } } 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 f11fd7b29c18..43f9af6016f1 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java @@ -68,11 +68,11 @@ import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor; import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor; import com.android.systemui.dock.DockManager; import com.android.systemui.dreams.DreamOverlayStateController; +import com.android.systemui.keyguard.DismissCallbackRegistry; import com.android.systemui.keyguard.KeyguardWmStateRefactor; +import com.android.systemui.keyguard.domain.interactor.KeyguardDismissTransitionInteractor; import com.android.systemui.keyguard.domain.interactor.KeyguardDismissActionInteractor; -import com.android.systemui.keyguard.domain.interactor.KeyguardSurfaceBehindInteractor; 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; @@ -170,6 +170,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb private final Lazy<ShadeController> mShadeController; private final Lazy<SceneInteractor> mSceneInteractorLazy; private final Lazy<DeviceEntryInteractor> mDeviceEntryInteractorLazy; + private final DismissCallbackRegistry mDismissCallbackRegistry; private Job mListenForAlternateBouncerTransitionSteps = null; private Job mListenForKeyguardAuthenticatedBiometricsHandled = null; @@ -360,8 +361,6 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb } } }; - private Lazy<WindowManagerLockscreenVisibilityInteractor> mWmLockscreenVisibilityInteractor; - private Lazy<KeyguardSurfaceBehindInteractor> mSurfaceBehindInteractor; private Lazy<KeyguardDismissActionInteractor> mKeyguardDismissActionInteractor; private final JavaAdapter mJavaAdapter; private StatusBarKeyguardViewManagerInteractor mStatusBarKeyguardViewManagerInteractor; @@ -391,16 +390,16 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb UdfpsOverlayInteractor udfpsOverlayInteractor, ActivityStarter activityStarter, KeyguardTransitionInteractor keyguardTransitionInteractor, + KeyguardDismissTransitionInteractor keyguardDismissTransitionInteractor, @Main CoroutineDispatcher mainDispatcher, - Lazy<WindowManagerLockscreenVisibilityInteractor> wmLockscreenVisibilityInteractor, Lazy<KeyguardDismissActionInteractor> keyguardDismissActionInteractorLazy, SelectedUserInteractor selectedUserInteractor, - Lazy<KeyguardSurfaceBehindInteractor> surfaceBehindInteractor, JavaAdapter javaAdapter, Lazy<SceneInteractor> sceneInteractorLazy, StatusBarKeyguardViewManagerInteractor statusBarKeyguardViewManagerInteractor, @Main DelayableExecutor executor, - Lazy<DeviceEntryInteractor> deviceEntryInteractorLazy + Lazy<DeviceEntryInteractor> deviceEntryInteractorLazy, + DismissCallbackRegistry dismissCallbackRegistry ) { mContext = context; mExecutor = executor; @@ -428,18 +427,19 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb mUdfpsOverlayInteractor = udfpsOverlayInteractor; mActivityStarter = activityStarter; mKeyguardTransitionInteractor = keyguardTransitionInteractor; + mKeyguardDismissTransitionInteractor = keyguardDismissTransitionInteractor; mMainDispatcher = mainDispatcher; - mWmLockscreenVisibilityInteractor = wmLockscreenVisibilityInteractor; mKeyguardDismissActionInteractor = keyguardDismissActionInteractorLazy; mSelectedUserInteractor = selectedUserInteractor; - mSurfaceBehindInteractor = surfaceBehindInteractor; mJavaAdapter = javaAdapter; mSceneInteractorLazy = sceneInteractorLazy; mStatusBarKeyguardViewManagerInteractor = statusBarKeyguardViewManagerInteractor; mDeviceEntryInteractorLazy = deviceEntryInteractorLazy; + mDismissCallbackRegistry = dismissCallbackRegistry; } KeyguardTransitionInteractor mKeyguardTransitionInteractor; + KeyguardDismissTransitionInteractor mKeyguardDismissTransitionInteractor; CoroutineDispatcher mMainDispatcher; @Override @@ -994,6 +994,8 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb } if (!SceneContainerFlag.isEnabled() && hideBouncerWhenShowing) { hideAlternateBouncer(true); + mDismissCallbackRegistry.notifyDismissCancelled(); + mPrimaryBouncerInteractor.setDismissAction(null, null); } mKeyguardUpdateManager.sendKeyguardReset(); updateStates(); @@ -1609,7 +1611,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb } if (KeyguardWmStateRefactor.isEnabled()) { - mKeyguardTransitionInteractor.startDismissKeyguardTransition( + mKeyguardDismissTransitionInteractor.startDismissKeyguardTransition( "SBKVM#keyguardAuthenticated"); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java index ba5939835a08..d5fafe222c40 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java @@ -16,6 +16,8 @@ package com.android.systemui.statusbar.phone; +import static com.android.systemui.Flags.statusBarSignalPolicyRefactor; + import android.annotation.NonNull; import android.content.Context; import android.os.Handler; @@ -23,16 +25,19 @@ import android.util.ArraySet; import android.util.Log; import com.android.settingslib.mobile.TelephonyIcons; +import com.android.systemui.CoreStartable; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.res.R; import com.android.systemui.statusbar.connectivity.IconState; import com.android.systemui.statusbar.connectivity.NetworkController; import com.android.systemui.statusbar.connectivity.SignalCallback; import com.android.systemui.statusbar.phone.ui.StatusBarIconController; +import com.android.systemui.statusbar.pipeline.airplane.domain.interactor.AirplaneModeInteractor; import com.android.systemui.statusbar.policy.SecurityController; import com.android.systemui.tuner.TunerService; import com.android.systemui.tuner.TunerService.Tunable; import com.android.systemui.util.CarrierConfigTracker; +import com.android.systemui.util.kotlin.JavaAdapter; import java.util.ArrayList; import java.util.List; @@ -40,10 +45,13 @@ import java.util.Objects; import javax.inject.Inject; -/** Controls the signal policies for icons shown in the statusbar. **/ +/** Controls the signal policies for icons shown in the statusbar. */ @SysUISingleton -public class StatusBarSignalPolicy implements SignalCallback, - SecurityController.SecurityControllerCallback, Tunable { +public class StatusBarSignalPolicy + implements SignalCallback, + SecurityController.SecurityControllerCallback, + Tunable, + CoreStartable { private static final String TAG = "StatusBarSignalPolicy"; private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); @@ -61,16 +69,15 @@ public class StatusBarSignalPolicy implements SignalCallback, private final Handler mHandler = Handler.getMain(); private final CarrierConfigTracker mCarrierConfigTracker; private final TunerService mTunerService; + private final JavaAdapter mJavaAdapter; + private final AirplaneModeInteractor mAirplaneModeInteractor; private boolean mHideAirplane; private boolean mHideMobile; private boolean mHideEthernet; - private boolean mActivityEnabled; - - // Track as little state as possible, and only for padding purposes - private boolean mIsAirplaneMode = false; + private final boolean mActivityEnabled; - private ArrayList<CallIndicatorIconState> mCallIndicatorStates = new ArrayList<>(); + private final ArrayList<CallIndicatorIconState> mCallIndicatorStates = new ArrayList<>(); private boolean mInitialized; @Inject @@ -80,15 +87,19 @@ public class StatusBarSignalPolicy implements SignalCallback, CarrierConfigTracker carrierConfigTracker, NetworkController networkController, SecurityController securityController, - TunerService tunerService + TunerService tunerService, + JavaAdapter javaAdapter, + AirplaneModeInteractor airplaneModeInteractor ) { mContext = context; mIconController = iconController; mCarrierConfigTracker = carrierConfigTracker; + mJavaAdapter = javaAdapter; mNetworkController = networkController; mSecurityController = securityController; mTunerService = tunerService; + mAirplaneModeInteractor = airplaneModeInteractor; mSlotAirplane = mContext.getString(com.android.internal.R.string.status_bar_airplane); mSlotMobile = mContext.getString(com.android.internal.R.string.status_bar_mobile); @@ -100,15 +111,35 @@ public class StatusBarSignalPolicy implements SignalCallback, mActivityEnabled = mContext.getResources().getBoolean(R.bool.config_showActivity); } + @Override + public void start() { + if (!statusBarSignalPolicyRefactor()) { + return; + } + + mTunerService.addTunable(this, StatusBarIconController.ICON_HIDE_LIST); + mNetworkController.addCallback(this); + mSecurityController.addCallback(this); + + mJavaAdapter.alwaysCollectFlow( + mAirplaneModeInteractor.isAirplaneMode(), this::updateAirplaneModeIcon); + } + /** Call to initialize and register this class with the system. */ public void init() { - if (mInitialized) { + if (mInitialized || statusBarSignalPolicyRefactor()) { return; } mInitialized = true; mTunerService.addTunable(this, StatusBarIconController.ICON_HIDE_LIST); mNetworkController.addCallback(this); mSecurityController.addCallback(this); + + if (statusBarSignalPolicyRefactor()) { + mJavaAdapter.alwaysCollectFlow( + mAirplaneModeInteractor.isAirplaneMode(), + this::updateAirplaneModeIcon); + } } public void destroy() { @@ -222,15 +253,19 @@ public class StatusBarSignalPolicy implements SignalCallback, @Override public void setIsAirplaneMode(IconState icon) { + if (statusBarSignalPolicyRefactor()) { + return; + } + if (DEBUG) { Log.d(TAG, "setIsAirplaneMode: " + "icon = " + (icon == null ? "" : icon.toString())); } - mIsAirplaneMode = icon.visible && !mHideAirplane; + boolean isAirplaneMode = icon.visible && !mHideAirplane; int resId = icon.icon; String description = icon.contentDescription; - if (mIsAirplaneMode && resId > 0) { + if (isAirplaneMode && resId > 0) { mIconController.setIcon(mSlotAirplane, resId, description); mIconController.setIconVisibility(mSlotAirplane, true); } else { @@ -238,6 +273,21 @@ public class StatusBarSignalPolicy implements SignalCallback, } } + public void updateAirplaneModeIcon(boolean isAirplaneModeOn) { + if (StatusBarSignalPolicyRefactor.isUnexpectedlyInLegacyMode()) { + return; + } + + boolean isAirplaneMode = isAirplaneModeOn && !mHideAirplane; + mIconController.setIconVisibility(mSlotAirplane, isAirplaneMode); + if (isAirplaneMode) { + mIconController.setIcon( + mSlotAirplane, + TelephonyIcons.FLIGHT_MODE_ICON, + mContext.getString(R.string.accessibility_airplane_mode)); + } + } + /** * Stores the statusbar state for no Calling & SMS. */ diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/NotificationsHeadsUpRefactor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicyRefactor.kt index 62641fe2f229..0577f495dd4f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/NotificationsHeadsUpRefactor.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicyRefactor.kt @@ -14,17 +14,17 @@ * limitations under the License. */ -package com.android.systemui.statusbar.notification.shared +package com.android.systemui.statusbar.phone import com.android.systemui.Flags import com.android.systemui.flags.FlagToken import com.android.systemui.flags.RefactorFlagUtils -/** Helper for reading or using the notifications heads up refactor flag state. */ +/** Helper for reading or using the status_bar_signal_policy_refactor flag state. */ @Suppress("NOTHING_TO_INLINE") -object NotificationsHeadsUpRefactor { +object StatusBarSignalPolicyRefactor { /** The aconfig flag name */ - const val FLAG_NAME = Flags.FLAG_NOTIFICATIONS_HEADS_UP_REFACTOR + const val FLAG_NAME = Flags.FLAG_STATUS_BAR_SIGNAL_POLICY_REFACTOR /** A token used for dependency declaration */ val token: FlagToken @@ -33,7 +33,7 @@ object NotificationsHeadsUpRefactor { /** Is the refactor enabled */ @JvmStatic inline val isEnabled - get() = Flags.notificationsHeadsUpRefactor() + get() = Flags.statusBarSignalPolicyRefactor() /** * Called to ensure code is only run when the flag is enabled. This protects users from the diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ui/DarkIconManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ui/DarkIconManager.java index 8871dae3c620..6c303303c8f8 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ui/DarkIconManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ui/DarkIconManager.java @@ -16,7 +16,6 @@ package com.android.systemui.statusbar.phone.ui; -import android.view.ViewGroup; import android.widget.LinearLayout; import com.android.internal.statusbar.StatusBarIcon; @@ -64,9 +63,8 @@ public class DarkIconManager extends IconManager { } @Override - protected LinearLayout.LayoutParams onCreateLayoutParams() { - LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams( - ViewGroup.LayoutParams.WRAP_CONTENT, mIconSize); + protected LinearLayout.LayoutParams onCreateLayoutParams(StatusBarIcon.Shape shape) { + LinearLayout.LayoutParams lp = super.onCreateLayoutParams(shape); lp.setMargins(mIconHorizontalMargin, 0, mIconHorizontalMargin, 0); return lp; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ui/IconManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ui/IconManager.java index 5ad737684ca1..91ead614ffa4 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ui/IconManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ui/IconManager.java @@ -20,6 +20,7 @@ import static com.android.systemui.statusbar.phone.StatusBarIconHolder.TYPE_BIND import static com.android.systemui.statusbar.phone.StatusBarIconHolder.TYPE_ICON; import static com.android.systemui.statusbar.phone.StatusBarIconHolder.TYPE_MOBILE_NEW; import static com.android.systemui.statusbar.phone.StatusBarIconHolder.TYPE_WIFI_NEW; +import static com.android.systemui.statusbar.phone.ui.StatusBarIconControllerImpl.usesModeIcons; import android.annotation.Nullable; import android.content.Context; @@ -27,9 +28,8 @@ import android.os.Bundle; import android.view.ViewGroup; import android.widget.LinearLayout; -import androidx.annotation.VisibleForTesting; - import com.android.internal.statusbar.StatusBarIcon; +import com.android.internal.statusbar.StatusBarIcon.Shape; import com.android.systemui.demomode.DemoModeCommandReceiver; import com.android.systemui.statusbar.BaseStatusBarFrameLayout; import com.android.systemui.statusbar.StatusBarIconView; @@ -155,12 +155,11 @@ public class IconManager implements DemoModeCommandReceiver { }; } - @VisibleForTesting protected StatusBarIconView addIcon(int index, String slot, boolean blocked, StatusBarIcon icon) { StatusBarIconView view = onCreateStatusBarIconView(slot, blocked); view.set(icon); - mGroup.addView(view, index, onCreateLayoutParams()); + mGroup.addView(view, index, onCreateLayoutParams(icon.shape)); return view; } @@ -174,7 +173,7 @@ public class IconManager implements DemoModeCommandReceiver { int index) { mBindableIcons.put(holder.getSlot(), holder); ModernStatusBarView view = holder.getInitializer().createAndBind(mContext); - mGroup.addView(view, index, onCreateLayoutParams()); + mGroup.addView(view, index, onCreateLayoutParams(Shape.WRAP_CONTENT)); if (mIsInDemoMode) { mDemoStatusIcons.addBindableIcon(holder); } @@ -183,7 +182,7 @@ public class IconManager implements DemoModeCommandReceiver { protected StatusIconDisplayable addNewWifiIcon(int index, String slot) { ModernStatusBarWifiView view = onCreateModernStatusBarWifiView(slot); - mGroup.addView(view, index, onCreateLayoutParams()); + mGroup.addView(view, index, onCreateLayoutParams(Shape.WRAP_CONTENT)); if (mIsInDemoMode) { mDemoStatusIcons.addModernWifiView(mWifiViewModel); @@ -199,7 +198,7 @@ public class IconManager implements DemoModeCommandReceiver { int subId ) { BaseStatusBarFrameLayout view = onCreateModernStatusBarMobileView(slot, subId); - mGroup.addView(view, index, onCreateLayoutParams()); + mGroup.addView(view, index, onCreateLayoutParams(Shape.WRAP_CONTENT)); if (mIsInDemoMode) { Context mobileContext = mMobileContextProvider @@ -233,8 +232,12 @@ public class IconManager implements DemoModeCommandReceiver { ); } - protected LinearLayout.LayoutParams onCreateLayoutParams() { - return new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, mIconSize); + protected LinearLayout.LayoutParams onCreateLayoutParams(Shape shape) { + int width = usesModeIcons() && shape == StatusBarIcon.Shape.FIXED_SPACE + ? mIconSize + : ViewGroup.LayoutParams.WRAP_CONTENT; + + return new LinearLayout.LayoutParams(width, mIconSize); } protected void destroy() { @@ -256,6 +259,13 @@ public class IconManager implements DemoModeCommandReceiver { /** Called once an icon has been set. */ public void onSetIcon(int viewIndex, StatusBarIcon icon) { StatusBarIconView view = (StatusBarIconView) mGroup.getChildAt(viewIndex); + if (usesModeIcons()) { + ViewGroup.LayoutParams current = view.getLayoutParams(); + ViewGroup.LayoutParams desired = onCreateLayoutParams(icon.shape); + if (desired.width != current.width || desired.height != current.height) { + view.setLayoutParams(desired); + } + } view.set(icon); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ui/StatusBarIconController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ui/StatusBarIconController.java index ee528e915079..0459b9749e0a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ui/StatusBarIconController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ui/StatusBarIconController.java @@ -70,7 +70,8 @@ public interface StatusBarIconController { * @param preloadedIcon optional drawable corresponding to {@code iconResId}, if known */ void setResourceIcon(String slot, @Nullable String resPackage, @DrawableRes int iconResId, - @Nullable Drawable preloadedIcon, CharSequence contentDescription); + @Nullable Drawable preloadedIcon, CharSequence contentDescription, + StatusBarIcon.Shape shape); /** * Sets up a wifi icon using the new data pipeline. No effect if the wifi icon has already been 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 ad3a9e350c4b..9b6d32bd179d 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 @@ -234,13 +234,14 @@ public class StatusBarIconControllerImpl implements Tunable, Icon.createWithResource(mContext, resourceId), /* preloadedIcon= */ null, contentDescription, - StatusBarIcon.Type.SystemIcon); + StatusBarIcon.Type.SystemIcon, + StatusBarIcon.Shape.WRAP_CONTENT); } @Override public void setResourceIcon(String slot, @Nullable String resPackage, @DrawableRes int iconResId, @Nullable Drawable preloadedIcon, - CharSequence contentDescription) { + CharSequence contentDescription, StatusBarIcon.Shape shape) { if (!usesModeIcons()) { Log.wtf("TAG", "StatusBarIconController.setResourceIcon() should not be called without " @@ -260,12 +261,13 @@ public class StatusBarIconControllerImpl implements Tunable, icon, preloadedIcon, contentDescription, - StatusBarIcon.Type.ResourceIcon); + StatusBarIcon.Type.ResourceIcon, + shape); } private void setResourceIconInternal(String slot, Icon resourceIcon, @Nullable Drawable preloadedIcon, CharSequence contentDescription, - StatusBarIcon.Type type) { + StatusBarIcon.Type type, StatusBarIcon.Shape shape) { checkArgument(resourceIcon.getType() == Icon.TYPE_RESOURCE, "Expected Icon of TYPE_RESOURCE, but got " + resourceIcon.getType()); String resPackage = resourceIcon.getResPackage(); @@ -277,7 +279,7 @@ public class StatusBarIconControllerImpl implements Tunable, if (holder == null) { StatusBarIcon icon = new StatusBarIcon(UserHandle.SYSTEM, resPackage, resourceIcon, /* iconLevel= */ 0, /* number=*/ 0, - contentDescription, type); + contentDescription, type, shape); icon.preloadedIcon = preloadedIcon; holder = StatusBarIconHolder.fromIcon(icon); setIcon(slot, holder); @@ -286,6 +288,7 @@ public class StatusBarIconControllerImpl implements Tunable, holder.getIcon().icon = resourceIcon; holder.getIcon().contentDescription = contentDescription; holder.getIcon().type = type; + holder.getIcon().shape = shape; holder.getIcon().preloadedIcon = preloadedIcon; handleSet(slot, holder); } @@ -578,7 +581,7 @@ public class StatusBarIconControllerImpl implements Tunable, } } - private static boolean usesModeIcons() { + static boolean usesModeIcons() { return android.app.Flags.modesApi() && android.app.Flags.modesUi() && android.app.Flags.modesUiIcons(); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/data/repository/ConnectivityRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/data/repository/ConnectivityRepository.kt index 1a55f7d90e97..f5cfc8c5b307 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/data/repository/ConnectivityRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/data/repository/ConnectivityRepository.kt @@ -31,7 +31,7 @@ import android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID import androidx.annotation.ArrayRes import androidx.annotation.VisibleForTesting import com.android.systemui.Dumpable -import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow +import com.android.systemui.Flags import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.dump.DumpManager @@ -47,6 +47,7 @@ import com.android.systemui.statusbar.pipeline.shared.data.model.DefaultConnecti import com.android.systemui.statusbar.pipeline.shared.data.model.DefaultConnectionModel.Wifi import com.android.systemui.statusbar.pipeline.shared.data.repository.ConnectivityRepositoryImpl.Companion.getMainOrUnderlyingWifiInfo import com.android.systemui.tuner.TunerService +import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow import java.io.PrintWriter import javax.inject.Inject import kotlinx.coroutines.CoroutineScope @@ -252,7 +253,10 @@ constructor( } // Only CELLULAR networks may have underlying wifi information that's relevant to SysUI, // so skip the underlying network check if it's not CELLULAR. - if (!this.hasTransport(TRANSPORT_CELLULAR)) { + if ( + !this.hasTransport(TRANSPORT_CELLULAR) && + !Flags.statusBarAlwaysCheckUnderlyingNetworks() + ) { return mainWifiInfo } 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 d46aaf45b1a3..c24d69465043 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 @@ -35,6 +35,7 @@ import com.android.systemui.lifecycle.repeatWhenAttached import com.android.systemui.res.R import com.android.systemui.scene.shared.flag.SceneContainerFlag import com.android.systemui.statusbar.StatusBarIconView +import com.android.systemui.statusbar.chips.ron.shared.StatusBarRonChips import com.android.systemui.statusbar.chips.ui.binder.ChipChronometerBinder import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel import com.android.systemui.statusbar.chips.ui.view.ChipBackgroundContainer @@ -124,10 +125,6 @@ class CollapsedStatusBarViewBinderImpl @Inject constructor() : CollapsedStatusBa // Colors val textColor = chipModel.colors.text(chipContext) - chipDefaultIconView.imageTintList = - ColorStateList.valueOf(textColor) - chipBackgroundView.getCustomIconView()?.imageTintList = - ColorStateList.valueOf(textColor) chipTimeView.setTextColor(textColor) chipTextView.setTextColor(textColor) (chipBackgroundView.background as GradientDrawable).color = @@ -173,13 +170,22 @@ class CollapsedStatusBarViewBinderImpl @Inject constructor() : CollapsedStatusBa // it. backgroundView.removeView(backgroundView.getCustomIconView()) + val iconTint = chipModel.colors.text(defaultIconView.context) + when (val icon = chipModel.icon) { null -> { defaultIconView.visibility = View.GONE } - is OngoingActivityChipModel.ChipIcon.Basic -> { + is OngoingActivityChipModel.ChipIcon.SingleColorIcon -> { + IconViewBinder.bind(icon.impl, defaultIconView) + defaultIconView.visibility = View.VISIBLE + defaultIconView.tintView(iconTint) + } + is OngoingActivityChipModel.ChipIcon.FullColorAppIcon -> { + StatusBarRonChips.assertInNewMode() IconViewBinder.bind(icon.impl, defaultIconView) defaultIconView.visibility = View.VISIBLE + defaultIconView.untintView() } is OngoingActivityChipModel.ChipIcon.StatusBarView -> { // Hide the default icon since we'll show this custom icon instead. @@ -194,6 +200,7 @@ class CollapsedStatusBarViewBinderImpl @Inject constructor() : CollapsedStatusBa // maybe include the app name. contentDescription = context.resources.getString(R.string.ongoing_phone_call_content_description) + tintView(iconTint) } // 2. If we just reinflated the view, we may need to detach the icon view from the @@ -219,6 +226,14 @@ class CollapsedStatusBarViewBinderImpl @Inject constructor() : CollapsedStatusBa return this.findViewById(CUSTOM_ICON_VIEW_ID) } + private fun ImageView.tintView(color: Int) { + this.imageTintList = ColorStateList.valueOf(color) + } + + private fun ImageView.untintView() { + this.imageTintList = null + } + private fun generateCustomIconLayoutParams(iconView: ImageView): FrameLayout.LayoutParams { val customIconSize = iconView.context.resources.getDimensionPixelSize( @@ -237,10 +252,13 @@ class CollapsedStatusBarViewBinderImpl @Inject constructor() : CollapsedStatusBa chipTextView.text = chipModel.secondsUntilStarted.toString() chipTextView.visibility = View.VISIBLE - // The Chronometer should be stopped to prevent leaks -- see b/192243808 and - // [Chronometer.start]. - chipTimeView.stop() - chipTimeView.visibility = View.GONE + chipTimeView.hide() + } + is OngoingActivityChipModel.Shown.Text -> { + chipTextView.text = chipModel.text + chipTextView.visibility = View.VISIBLE + + chipTimeView.hide() } is OngoingActivityChipModel.Shown.Timer -> { ChipChronometerBinder.bind(chipModel.startTimeMs, chipTimeView) @@ -250,14 +268,18 @@ class CollapsedStatusBarViewBinderImpl @Inject constructor() : CollapsedStatusBa } is OngoingActivityChipModel.Shown.IconOnly -> { chipTextView.visibility = View.GONE - // The Chronometer should be stopped to prevent leaks -- see b/192243808 and - // [Chronometer.start]. - chipTimeView.stop() - chipTimeView.visibility = View.GONE + chipTimeView.hide() } } } + private fun ChipChronometer.hide() { + // The Chronometer should be stopped to prevent leaks -- see b/192243808 and + // [Chronometer.start]. + this.stop() + this.visibility = View.GONE + } + private fun updateChipPadding( chipModel: OngoingActivityChipModel.Shown, backgroundView: View, @@ -356,6 +378,7 @@ class CollapsedStatusBarViewBinderImpl @Inject constructor() : CollapsedStatusBa chipView.accessibilityLiveRegion = View.ACCESSIBILITY_LIVE_REGION_ASSERTIVE } is OngoingActivityChipModel.Shown.Timer, + is OngoingActivityChipModel.Shown.Text, is OngoingActivityChipModel.Shown.IconOnly -> { chipView.accessibilityLiveRegion = View.ACCESSIBILITY_LIVE_REGION_NONE } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java index 71bcdfcba049..6cebcbd2731a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java @@ -22,8 +22,10 @@ import android.os.UserManager; import com.android.internal.R; import com.android.settingslib.devicestate.DeviceStateRotationLockSettingsManager; +import com.android.settingslib.notification.modes.ZenIconLoader; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Main; +import com.android.systemui.dagger.qualifiers.UiBackground; import com.android.systemui.log.LogBuffer; import com.android.systemui.log.LogBufferFactory; import com.android.systemui.settings.UserTracker; @@ -79,6 +81,7 @@ import dagger.Module; import dagger.Provides; import java.util.concurrent.Executor; +import java.util.concurrent.ExecutorService; import javax.inject.Named; @@ -236,4 +239,12 @@ public interface StatusBarPolicyModule { static LogBuffer provideCastControllerLog(LogBufferFactory factory) { return factory.create("CastControllerLog", 50); } + + /** Provides a {@link ZenIconLoader} that fetches icons in a background thread. */ + @Provides + @SysUISingleton + static ZenIconLoader provideZenIconLoader( + @UiBackground ExecutorService backgroundExecutorService) { + return new ZenIconLoader(backgroundExecutorService); + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/domain/interactor/ZenModeInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/domain/interactor/ZenModeInteractor.kt index a67b47a9a0c9..93c631f65df7 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/domain/interactor/ZenModeInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/domain/interactor/ZenModeInteractor.kt @@ -23,16 +23,20 @@ import android.provider.Settings.Secure.ZEN_DURATION_PROMPT import android.util.Log import androidx.concurrent.futures.await import com.android.settingslib.notification.data.repository.ZenModeRepository +import com.android.settingslib.notification.modes.ZenIcon import com.android.settingslib.notification.modes.ZenIconLoader import com.android.settingslib.notification.modes.ZenMode -import com.android.systemui.common.shared.model.Icon -import com.android.systemui.common.shared.model.asIcon +import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.shared.notifications.data.repository.NotificationSettingsRepository +import com.android.systemui.statusbar.policy.domain.model.ActiveZenModes +import com.android.systemui.statusbar.policy.domain.model.ZenModeInfo import java.time.Duration import javax.inject.Inject +import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.map /** @@ -45,9 +49,9 @@ constructor( private val context: Context, private val zenModeRepository: ZenModeRepository, private val notificationSettingsRepository: NotificationSettingsRepository, + @Background private val bgDispatcher: CoroutineDispatcher, + private val iconLoader: ZenIconLoader, ) { - private val iconLoader: ZenIconLoader = ZenIconLoader.getInstance() - val isZenModeEnabled: Flow<Boolean> = zenModeRepository.globalZenMode .map { @@ -76,34 +80,27 @@ constructor( val modes: Flow<List<ZenMode>> = zenModeRepository.modes - val activeModes: Flow<List<ZenMode>> = - modes.map { modes -> modes.filter { mode -> mode.isActive } }.distinctUntilChanged() - - /** Flow returning the most prioritized of the active modes, if any. */ - val mainActiveMode: Flow<ZenMode?> = - activeModes.map { modes -> getMainActiveMode(modes) }.distinctUntilChanged() - - /** - * Given the list of modes (which may include zero or more currently active modes), returns the - * most prioritized of the active modes, if any. - */ - private fun getMainActiveMode(modes: List<ZenMode>): ZenMode? { - return modes.sortedWith(ZenMode.PRIORITIZING_COMPARATOR).firstOrNull { it.isActive } - } + /** Flow returning the currently active mode(s), if any. */ + val activeModes: Flow<ActiveZenModes> = + modes + .map { modes -> + val activeModesList = + modes + .filter { mode -> mode.isActive } + .sortedWith(ZenMode.PRIORITIZING_COMPARATOR) + val mainActiveMode = + activeModesList.firstOrNull()?.let { ZenModeInfo(it.name, getModeIcon(it)) } + + ActiveZenModes(activeModesList.map { m -> m.name }, mainActiveMode) + } + .flowOn(bgDispatcher) + .distinctUntilChanged() - suspend fun getModeIcon(mode: ZenMode): Icon { - return iconLoader.getIcon(context, mode).await().drawable().asIcon() - } + val mainActiveMode: Flow<ZenModeInfo?> = + activeModes.map { a -> a.mainMode }.distinctUntilChanged() - /** - * Given the list of modes (which may include zero or more currently active modes), returns an - * icon representing the active mode, if any (or, if multiple modes are active, to the most - * prioritized one). This icon is suitable for use in the status bar or lockscreen (uses the - * standard DND icon for implicit modes, instead of the launcher icon of the associated - * package). - */ - suspend fun getActiveModeIcon(modes: List<ZenMode>): Icon? { - return getMainActiveMode(modes)?.let { m -> getModeIcon(m) } + suspend fun getModeIcon(mode: ZenMode): ZenIcon { + return iconLoader.getIcon(context, mode).await() } fun activateMode(zenMode: ZenMode) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/domain/model/ActiveZenModes.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/domain/model/ActiveZenModes.kt new file mode 100644 index 000000000000..569e517d6ed5 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/domain/model/ActiveZenModes.kt @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.policy.domain.model + +import com.android.settingslib.notification.modes.ZenMode + +/** + * Represents the list of [ZenMode] instances that are currently active. + * + * @property modeNames Names of all the active modes, sorted by their priority. + * @property mainMode The most prioritized active mode, if any modes active. Guaranteed to be + * non-null if [modeNames] is not empty. + */ +data class ActiveZenModes(val modeNames: List<String>, val mainMode: ZenModeInfo?) { + fun isAnyActive(): Boolean = modeNames.isNotEmpty() +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/domain/model/ZenModeInfo.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/domain/model/ZenModeInfo.kt new file mode 100644 index 000000000000..5004f4c21371 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/domain/model/ZenModeInfo.kt @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.policy.domain.model + +import com.android.settingslib.notification.modes.ZenIcon +import com.android.settingslib.notification.modes.ZenMode + +/** Name and icon of a [ZenMode] */ +data class ZenModeInfo(val name: String, val icon: ZenIcon) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ui/dialog/viewmodel/ModesDialogViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ui/dialog/viewmodel/ModesDialogViewModel.kt index be90bec03e52..841071347c08 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ui/dialog/viewmodel/ModesDialogViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ui/dialog/viewmodel/ModesDialogViewModel.kt @@ -23,6 +23,7 @@ import android.provider.Settings.ACTION_AUTOMATIC_ZEN_RULE_SETTINGS import android.provider.Settings.EXTRA_AUTOMATIC_ZEN_RULE_ID import com.android.settingslib.notification.modes.EnableZenModeDialog import com.android.settingslib.notification.modes.ZenMode +import com.android.systemui.common.shared.model.asIcon import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.qs.tiles.dialog.QSZenModeDialogMetricsLogger @@ -88,7 +89,7 @@ constructor( modesList.map { mode -> ModeTileViewModel( id = mode.id, - icon = zenModeInteractor.getModeIcon(mode), + icon = zenModeInteractor.getModeIcon(mode).drawable().asIcon(), text = mode.name, subtext = getTileSubtext(mode), enabled = mode.isActive, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ui/viewmodel/KeyguardStatusBarViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/ui/viewmodel/KeyguardStatusBarViewModel.kt index 89227cfb2b23..fe1d64736991 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/ui/viewmodel/KeyguardStatusBarViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/ui/viewmodel/KeyguardStatusBarViewModel.kt @@ -21,10 +21,10 @@ import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application 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.Scenes import com.android.systemui.statusbar.domain.interactor.KeyguardStatusBarInteractor import com.android.systemui.statusbar.notification.domain.interactor.HeadsUpNotificationInteractor -import com.android.systemui.statusbar.notification.shared.NotificationsHeadsUpRefactor import com.android.systemui.statusbar.policy.BatteryController import com.android.systemui.statusbar.policy.BatteryController.BatteryStateChangeCallback import javax.inject.Inject @@ -58,7 +58,7 @@ constructor( ) { private val showingHeadsUpStatusBar: Flow<Boolean> = - if (NotificationsHeadsUpRefactor.isEnabled) { + if (SceneContainerFlag.isEnabled) { headsUpNotificationInteractor.showHeadsUpStatusBar } else { flowOf(false) diff --git a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java index c7fc44513473..9c8ef0421888 100644 --- a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java +++ b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java @@ -89,6 +89,9 @@ import com.android.systemui.util.settings.SecureSettings; import com.google.ux.material.libmonet.dynamiccolor.DynamicColor; import com.google.ux.material.libmonet.dynamiccolor.MaterialDynamicColors; +import kotlinx.coroutines.flow.Flow; +import kotlinx.coroutines.flow.StateFlow; + import org.json.JSONException; import org.json.JSONObject; @@ -162,6 +165,7 @@ public class ThemeOverlayController implements CoreStartable, Dumpable { private final WakefulnessLifecycle mWakefulnessLifecycle; private final JavaAdapter mJavaAdapter; private final KeyguardTransitionInteractor mKeyguardTransitionInteractor; + private final StateFlow<Boolean> mIsKeyguardOnAsleepState; private final UiModeManager mUiModeManager; private ColorScheme mDarkColorScheme; private ColorScheme mLightColorScheme; @@ -202,8 +206,7 @@ public class ThemeOverlayController implements CoreStartable, Dumpable { } boolean currentUser = userId == mUserTracker.getUserId(); boolean isAsleep = themeOverlayControllerWakefulnessDeprecation() - ? KeyguardState.Companion.deviceIsAsleepInState( - mKeyguardTransitionInteractor.getFinishedState()) + ? ThemeOverlayController.this.mIsKeyguardOnAsleepState.getValue() : mWakefulnessLifecycle.getWakefulness() != WAKEFULNESS_ASLEEP; if (currentUser && !mAcceptColorEvents && isAsleep) { @@ -434,6 +437,10 @@ public class ThemeOverlayController implements CoreStartable, Dumpable { mUiModeManager = uiModeManager; mActivityManager = activityManager; dumpManager.registerDumpable(TAG, this); + + Flow<Boolean> isFinishedInAsleepStateFlow = mKeyguardTransitionInteractor + .isFinishedInStateWhere(KeyguardState.Companion::deviceIsAsleepInState); + mIsKeyguardOnAsleepState = mJavaAdapter.stateInApp(isFinishedInAsleepStateFlow, false); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/util/concurrency/GlobalConcurrencyModule.java b/packages/SystemUI/src/com/android/systemui/util/concurrency/GlobalConcurrencyModule.java index ecf1165566dc..70774f13fe6b 100644 --- a/packages/SystemUI/src/com/android/systemui/util/concurrency/GlobalConcurrencyModule.java +++ b/packages/SystemUI/src/com/android/systemui/util/concurrency/GlobalConcurrencyModule.java @@ -28,6 +28,7 @@ import dagger.Module; import dagger.Provides; import java.util.concurrent.Executor; +import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import javax.inject.Singleton; @@ -81,6 +82,18 @@ public abstract class GlobalConcurrencyModule { @Singleton @UiBackground public static Executor provideUiBackgroundExecutor() { + return provideUiBackgroundExecutorService(); + } + + /** + * Provide an ExecutorService specifically for running UI operations on a separate thread. + * + * <p>Keep submitted runnables short and to the point, just as with any other UI code. + */ + @Provides + @Singleton + @UiBackground + public static ExecutorService provideUiBackgroundExecutorService() { return Executors.newSingleThreadExecutor(); } diff --git a/packages/SystemUI/src/com/android/systemui/util/kotlin/FlowDumper.kt b/packages/SystemUI/src/com/android/systemui/util/kotlin/FlowDumper.kt index 727e51fdef11..315a89b96c25 100644 --- a/packages/SystemUI/src/com/android/systemui/util/kotlin/FlowDumper.kt +++ b/packages/SystemUI/src/com/android/systemui/util/kotlin/FlowDumper.kt @@ -20,7 +20,6 @@ import android.util.IndentingPrintWriter import com.android.systemui.Dumpable import com.android.systemui.dump.DumpManager import com.android.systemui.lifecycle.ExclusiveActivatable -import com.android.systemui.lifecycle.SysUiViewModel import com.android.systemui.util.asIndenting import com.android.systemui.util.printCollection import java.io.PrintWriter diff --git a/packages/SystemUI/src/com/android/systemui/util/kotlin/JavaAdapter.kt b/packages/SystemUI/src/com/android/systemui/util/kotlin/JavaAdapter.kt index 055671cf32ca..64e056da97d4 100644 --- a/packages/SystemUI/src/com/android/systemui/util/kotlin/JavaAdapter.kt +++ b/packages/SystemUI/src/com/android/systemui/util/kotlin/JavaAdapter.kt @@ -31,7 +31,10 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.DisposableHandle import kotlinx.coroutines.Job import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.launch /** A class allowing Java classes to collect on Kotlin flows. */ @@ -58,6 +61,15 @@ constructor( ): Job { return scope.launch { flow.collect { consumer.accept(it) } } } + + @JvmOverloads + fun <T> stateInApp( + flow: Flow<T>, + initialValue: T, + started: SharingStarted = SharingStarted.Eagerly + ): StateFlow<T> { + return flow.stateIn(scope, started, initialValue) + } } /** diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeControllerCollector.kt b/packages/SystemUI/src/com/android/systemui/volume/VolumeControllerAdapter.kt index 68591910031d..e8367315c3c9 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/VolumeControllerCollector.kt +++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeControllerAdapter.kt @@ -17,7 +17,8 @@ package com.android.systemui.volume import android.media.IVolumeController -import com.android.settingslib.media.data.repository.VolumeControllerEvent +import com.android.settingslib.volume.data.model.VolumeControllerEvent +import com.android.settingslib.volume.data.repository.AudioRepository import com.android.systemui.dagger.qualifiers.Application import javax.inject.Inject import kotlinx.coroutines.CoroutineScope @@ -29,17 +30,17 @@ import kotlinx.coroutines.launch * [com.android.settingslib.volume.data.repository.AudioRepository.volumeControllerEvents] and the * old code that uses [IVolumeController] interface directly. */ -class VolumeControllerCollector +class VolumeControllerAdapter @Inject -constructor(@Application private val coroutineScope: CoroutineScope) { +constructor( + @Application private val coroutineScope: CoroutineScope, + private val audioRepository: AudioRepository, +) { /** Collects [Flow] of [VolumeControllerEvent] into [IVolumeController]. */ - fun collectToController( - eventsFlow: Flow<VolumeControllerEvent>, - controller: IVolumeController - ) = + fun collectToController(controller: IVolumeController) { coroutineScope.launch { - eventsFlow.collect { event -> + audioRepository.volumeControllerEvents.collect { event -> when (event) { is VolumeControllerEvent.VolumeChanged -> controller.volumeChanged(event.streamType, event.flags) @@ -56,4 +57,9 @@ constructor(@Application private val coroutineScope: CoroutineScope) { } } } + } + + fun notifyVolumeControllerVisible(isVisible: Boolean) { + coroutineScope.launch { audioRepository.notifyVolumeControllerVisible(isVisible) } + } } diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java index 1522cc490b43..28effe909521 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java @@ -68,6 +68,7 @@ import androidx.lifecycle.Observer; import com.android.internal.annotations.GuardedBy; import com.android.settingslib.volume.MediaSessions; import com.android.systemui.Dumpable; +import com.android.systemui.Flags; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dump.DumpManager; @@ -124,7 +125,6 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa static final ArrayMap<Integer, Integer> STREAMS = new ArrayMap<>(); static { STREAMS.put(AudioSystem.STREAM_ALARM, R.string.stream_alarm); - STREAMS.put(AudioSystem.STREAM_BLUETOOTH_SCO, R.string.stream_bluetooth_sco); STREAMS.put(AudioSystem.STREAM_DTMF, R.string.stream_dtmf); STREAMS.put(AudioSystem.STREAM_MUSIC, R.string.stream_music); STREAMS.put(AudioSystem.STREAM_ACCESSIBILITY, R.string.stream_accessibility); @@ -153,6 +153,7 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa private final KeyguardManager mKeyguardManager; private final ActivityManager mActivityManager; private final UserTracker mUserTracker; + private final VolumeControllerAdapter mVolumeControllerAdapter; protected C mCallbacks = new C(); private final State mState = new State(); protected final MediaSessionsCallbacks mMediaSessionsCallbacksW; @@ -197,6 +198,7 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa NotificationManager notificationManager, VibratorHelper vibrator, IAudioService iAudioService, + VolumeControllerAdapter volumeControllerAdapter, AccessibilityManager accessibilityManager, PackageManager packageManager, WakefulnessLifecycle wakefulnessLifecycle, @@ -233,6 +235,7 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa mVibrator = vibrator; mHasVibrator = mVibrator.hasVibrator(); mAudioService = iAudioService; + mVolumeControllerAdapter = volumeControllerAdapter; mKeyguardManager = keyguardManager; mActivityManager = activityManager; mUserTracker = userTracker; @@ -259,10 +262,14 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa } protected void setVolumeController() { - try { - mAudio.setVolumeController(mVolumeController); - } catch (SecurityException e) { - Log.w(TAG, "Unable to set the volume controller", e); + if (Flags.useVolumeController()) { + mVolumeControllerAdapter.collectToController(mVolumeController); + } else { + try { + mAudio.setVolumeController(mVolumeController); + } catch (SecurityException e) { + Log.w(TAG, "Unable to set the volume controller", e); + } } } @@ -384,7 +391,11 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa } public void notifyVisible(boolean visible) { - mWorker.obtainMessage(W.NOTIFY_VISIBLE, visible ? 1 : 0, 0).sendToTarget(); + if (Flags.useVolumeController()) { + mVolumeControllerAdapter.notifyVolumeControllerVisible(visible); + } else { + mWorker.obtainMessage(W.NOTIFY_VISIBLE, visible ? 1 : 0, 0).sendToTarget(); + } } public void userActivity() { @@ -642,7 +653,6 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa private static boolean isLogWorthy(int stream) { switch (stream) { case AudioSystem.STREAM_ALARM: - case AudioSystem.STREAM_BLUETOOTH_SCO: case AudioSystem.STREAM_MUSIC: case AudioSystem.STREAM_RING: case AudioSystem.STREAM_SYSTEM: diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java index eb9151868b79..7786453814e0 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java +++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java @@ -704,8 +704,6 @@ public class VolumeDialogImpl implements VolumeDialog, Dumpable, addRow(AudioManager.STREAM_VOICE_CALL, com.android.internal.R.drawable.ic_phone, com.android.internal.R.drawable.ic_phone, false, false); - addRow(AudioManager.STREAM_BLUETOOTH_SCO, - R.drawable.ic_volume_bt_sco, R.drawable.ic_volume_bt_sco, false, false); addRow(AudioManager.STREAM_SYSTEM, R.drawable.ic_volume_system, R.drawable.ic_volume_system_mute, false, false); } diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java index 68d12f69215a..536403ca970e 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java +++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java @@ -20,9 +20,9 @@ import static com.android.settingslib.flags.Flags.volumeDialogAudioSharingFix; import android.content.Context; import android.content.res.Configuration; -import android.os.Handler; import android.util.Log; +import com.android.settingslib.volume.data.repository.AudioRepository; import com.android.systemui.CoreStartable; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.qs.tiles.DndTile; @@ -39,23 +39,26 @@ public class VolumeUI implements CoreStartable, ConfigurationController.Configur private static final String TAG = "VolumeUI"; private static boolean LOGD = Log.isLoggable(TAG, Log.DEBUG); - private final Handler mHandler = new Handler(); - private boolean mEnabled; private final Context mContext; private VolumeDialogComponent mVolumeComponent; private AudioSharingInteractor mAudioSharingInteractor; + private AudioRepository mAudioRepository; @Inject - public VolumeUI(Context context, VolumeDialogComponent volumeDialogComponent, + public VolumeUI(Context context, + VolumeDialogComponent volumeDialogComponent, + AudioRepository audioRepository, AudioSharingInteractor audioSharingInteractor) { mContext = context; mVolumeComponent = volumeDialogComponent; + mAudioRepository = audioRepository; mAudioSharingInteractor = audioSharingInteractor; } @Override public void start() { + mAudioRepository.init(); boolean enableVolumeUi = mContext.getResources().getBoolean(R.bool.enable_volume_ui); boolean enableSafetyWarning = mContext.getResources().getBoolean(R.bool.enable_safety_warning); @@ -77,7 +80,8 @@ public class VolumeUI implements CoreStartable, ConfigurationController.Configur @Override public void dump(PrintWriter pw, String[] args) { - pw.print("mEnabled="); pw.println(mEnabled); + pw.print("mEnabled="); + pw.println(mEnabled); if (!mEnabled) return; mVolumeComponent.dump(pw, args); } diff --git a/packages/SystemUI/src/com/android/systemui/volume/dagger/AudioModule.kt b/packages/SystemUI/src/com/android/systemui/volume/dagger/AudioModule.kt index d39daafd2311..20d598a9334b 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/dagger/AudioModule.kt +++ b/packages/SystemUI/src/com/android/systemui/volume/dagger/AudioModule.kt @@ -70,6 +70,7 @@ interface AudioModule { coroutineContext, coroutineScope, volumeLogger, + com.android.systemui.Flags.useVolumeController(), ) @Provides diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/domain/interactor/AudioSlidersInteractor.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/domain/interactor/AudioSlidersInteractor.kt index 0451ce6d9fce..4be680ef66f1 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/domain/interactor/AudioSlidersInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/domain/interactor/AudioSlidersInteractor.kt @@ -16,9 +16,7 @@ package com.android.systemui.volume.panel.component.volume.domain.interactor -import android.media.AudioDeviceInfo import android.media.AudioManager -import com.android.settingslib.volume.data.repository.AudioRepository import com.android.settingslib.volume.domain.interactor.AudioModeInteractor import com.android.settingslib.volume.shared.model.AudioStream import com.android.systemui.volume.panel.component.mediaoutput.domain.interactor.MediaOutputInteractor @@ -42,7 +40,6 @@ class AudioSlidersInteractor constructor( @VolumePanelScope scope: CoroutineScope, mediaOutputInteractor: MediaOutputInteractor, - audioRepository: AudioRepository, audioModeInteractor: AudioModeInteractor, ) { @@ -50,13 +47,12 @@ constructor( combineTransform( mediaOutputInteractor.activeMediaDeviceSessions, mediaOutputInteractor.defaultActiveMediaSession.filterData(), - audioRepository.communicationDevice, audioModeInteractor.isOngoingCall, - ) { activeSessions, defaultSession, communicationDevice, isOngoingCall -> + ) { activeSessions, defaultSession, isOngoingCall -> coroutineScope { val viewModels = buildList { if (isOngoingCall) { - addCall(communicationDevice?.type) + addStream(AudioManager.STREAM_VOICE_CALL) } if (defaultSession?.isTheSameSession(activeSessions.remote) == true) { @@ -68,7 +64,7 @@ constructor( } if (!isOngoingCall) { - addCall(communicationDevice?.type) + addStream(AudioManager.STREAM_VOICE_CALL) } addStream(AudioManager.STREAM_RING) @@ -80,14 +76,6 @@ constructor( } .stateIn(scope, SharingStarted.Eagerly, emptyList()) - private fun MutableList<SliderType>.addCall(communicationDeviceType: Int?) { - if (communicationDeviceType == AudioDeviceInfo.TYPE_BLUETOOTH_SCO) { - addStream(AudioManager.STREAM_BLUETOOTH_SCO) - } else { - addStream(AudioManager.STREAM_VOICE_CALL) - } - } - private fun MutableList<SliderType>.addSession(remoteMediaDeviceSession: MediaDeviceSession?) { if (remoteMediaDeviceSession?.canAdjustVolume == true) { add(SliderType.MediaDeviceCast(remoteMediaDeviceSession)) 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 521f608878cb..ffb1f11c4970 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 @@ -66,7 +66,6 @@ constructor( mapOf( AudioStream(AudioManager.STREAM_MUSIC) to R.drawable.ic_music_note, AudioStream(AudioManager.STREAM_VOICE_CALL) to R.drawable.ic_call, - AudioStream(AudioManager.STREAM_BLUETOOTH_SCO) to R.drawable.ic_call, AudioStream(AudioManager.STREAM_RING) to R.drawable.ic_ring_volume, AudioStream(AudioManager.STREAM_NOTIFICATION) to R.drawable.ic_volume_ringer, AudioStream(AudioManager.STREAM_ALARM) to R.drawable.ic_volume_alarm, @@ -75,7 +74,6 @@ constructor( mapOf( AudioStream(AudioManager.STREAM_MUSIC) to R.string.stream_music, AudioStream(AudioManager.STREAM_VOICE_CALL) to R.string.stream_voice_call, - AudioStream(AudioManager.STREAM_BLUETOOTH_SCO) to R.string.stream_voice_call, AudioStream(AudioManager.STREAM_RING) to R.string.stream_ring, AudioStream(AudioManager.STREAM_NOTIFICATION) to R.string.stream_notification, AudioStream(AudioManager.STREAM_ALARM) to R.string.stream_alarm, @@ -91,8 +89,6 @@ constructor( VolumePanelUiEvent.VOLUME_PANEL_MUSIC_SLIDER_TOUCHED, AudioStream(AudioManager.STREAM_VOICE_CALL) to VolumePanelUiEvent.VOLUME_PANEL_VOICE_CALL_SLIDER_TOUCHED, - AudioStream(AudioManager.STREAM_BLUETOOTH_SCO) to - VolumePanelUiEvent.VOLUME_PANEL_VOICE_CALL_SLIDER_TOUCHED, AudioStream(AudioManager.STREAM_RING) to VolumePanelUiEvent.VOLUME_PANEL_RING_SLIDER_TOUCHED, AudioStream(AudioManager.STREAM_NOTIFICATION) to 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 113a8c05ee66..5e37d4cd1faf 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 @@ -28,6 +28,7 @@ import static org.mockito.Mockito.when; import android.content.Context; import android.content.ContextWrapper; import android.hardware.display.DisplayManager; +import android.os.Handler; import android.os.UserHandle; import android.provider.Settings; import android.testing.TestableLooper; @@ -80,6 +81,7 @@ public class AccessibilityFloatingMenuControllerTest extends SysuiTestCase { private AccessibilityManager mAccessibilityManager; private KeyguardUpdateMonitor mKeyguardUpdateMonitor; private AccessibilityFloatingMenuController mController; + private TestableLooper mTestableLooper; @Mock private AccessibilityButtonTargetsObserver mTargetsObserver; @Mock @@ -108,6 +110,7 @@ public class AccessibilityFloatingMenuControllerTest extends SysuiTestCase { mViewCaptureAwareWindowManager = new ViewCaptureAwareWindowManager(mWindowManager, mLazyViewCapture, /* isViewCaptureEnabled= */ false); mAccessibilityManager = mContext.getSystemService(AccessibilityManager.class); + mTestableLooper = TestableLooper.get(this); when(mTargetsObserver.getCurrentAccessibilityButtonTargets()) .thenReturn(Settings.Secure.getStringForUser(mContextWrapper.getContentResolver(), @@ -231,7 +234,8 @@ public class AccessibilityFloatingMenuControllerTest extends SysuiTestCase { mKeyguardCallback.onKeyguardVisibilityChanged(false); mKeyguardCallback.onUserSwitching(fakeUserId); - mKeyguardCallback.onUserSwitchComplete(fakeUserId); + mController.mUserInitializationCompleteCallback.onUserInitializationComplete(1); + mTestableLooper.processAllMessages(); assertThat(mController.mFloatingMenu).isNotNull(); } @@ -346,7 +350,8 @@ public class AccessibilityFloatingMenuControllerTest extends SysuiTestCase { new AccessibilityFloatingMenuController(mContextWrapper, windowManager, viewCaptureAwareWindowManager, displayManager, mAccessibilityManager, mTargetsObserver, mModeObserver, mKeyguardUpdateMonitor, mSecureSettings, - displayTracker, mNavigationModeController); + displayTracker, mNavigationModeController, new Handler( + mTestableLooper.getLooper())); controller.init(); return controller; 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 index 74bc9282eebb..681ea754e630 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/DeviceItemActionInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/DeviceItemActionInteractorTest.kt @@ -59,6 +59,7 @@ class DeviceItemActionInteractorTest : SysuiTestCase() { private lateinit var mockitoSession: StaticMockitoSession private lateinit var activeMediaDeviceItem: DeviceItem private lateinit var notConnectedDeviceItem: DeviceItem + private lateinit var connectedAudioSharingMediaDeviceItem: DeviceItem private lateinit var connectedMediaDeviceItem: DeviceItem private lateinit var connectedOtherDeviceItem: DeviceItem @Mock private lateinit var dialog: SystemUIDialog @@ -100,6 +101,15 @@ class DeviceItemActionInteractorTest : SysuiTestCase() { iconWithDescription = null, background = null ) + connectedAudioSharingMediaDeviceItem = + DeviceItem( + type = DeviceItemType.AVAILABLE_AUDIO_SHARING_MEDIA_BLUETOOTH_DEVICE, + cachedBluetoothDevice = cachedBluetoothDevice, + deviceName = DEVICE_NAME, + connectionSummary = DEVICE_CONNECTION_SUMMARY, + iconWithDescription = null, + background = null + ) connectedOtherDeviceItem = DeviceItem( type = DeviceItemType.CONNECTED_BLUETOOTH_DEVICE, @@ -186,6 +196,21 @@ class DeviceItemActionInteractorTest : SysuiTestCase() { } @Test + fun testOnClick_connectedAudioSharingMediaDevice_logClick() { + with(kosmos) { + testScope.runTest { + whenever(cachedBluetoothDevice.address).thenReturn(DEVICE_ADDRESS) + actionInteractorImpl.onClick(connectedAudioSharingMediaDeviceItem, dialog) + verify(bluetoothTileDialogLogger) + .logDeviceClick( + cachedBluetoothDevice.address, + DeviceItemType.AVAILABLE_AUDIO_SHARING_MEDIA_BLUETOOTH_DEVICE + ) + } + } + } + + @Test fun testOnClick_audioSharingDisabled_shouldNotLaunchSettings() { with(kosmos) { testScope.runTest { @@ -415,7 +440,7 @@ class DeviceItemActionInteractorTest : SysuiTestCase() { } @Test - fun testOnClick_hasTwoConnectedLeDevice_clickedConnectedLe_shouldLaunchSettings() { + fun testOnClick_hasTwoConnectedLeDevice_clickedActiveLe_shouldLaunchSettings() { with(kosmos) { testScope.runTest { whenever(cachedBluetoothDevice.device).thenReturn(bluetoothDevice) @@ -438,7 +463,7 @@ class DeviceItemActionInteractorTest : SysuiTestCase() { if (device == bluetoothDevice) GROUP_ID_1 else GROUP_ID_2 } - actionInteractorImpl.onClick(connectedMediaDeviceItem, dialog) + actionInteractorImpl.onClick(activeMediaDeviceItem, dialog) verify(activityStarter) .postStartActivityDismissingKeyguard( ArgumentMatchers.any(), 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 a27ccc67d584..ef441c1dc12c 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 @@ -17,18 +17,24 @@ package com.android.systemui.bluetooth.qsdialog import android.bluetooth.BluetoothDevice -import android.content.pm.ApplicationInfo -import android.content.pm.PackageManager +import android.graphics.drawable.Drawable import android.media.AudioManager import android.platform.test.annotations.DisableFlags import android.platform.test.annotations.EnableFlags import android.testing.TestableLooper +import android.util.Pair 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 +import com.android.settingslib.bluetooth.BluetoothUtils import com.android.settingslib.bluetooth.CachedBluetoothDevice +import com.android.settingslib.bluetooth.LocalBluetoothManager import com.android.settingslib.flags.Flags import com.android.systemui.SysuiTestCase +import com.android.systemui.res.R import com.google.common.truth.Truth.assertThat +import org.junit.After import org.junit.Before import org.junit.Rule import org.junit.Test @@ -37,6 +43,7 @@ import org.mockito.Mock import org.mockito.Mockito.`when` import org.mockito.junit.MockitoJUnit import org.mockito.junit.MockitoRule +import org.mockito.kotlin.any @SmallTest @RunWith(AndroidJUnit4::class) @@ -44,9 +51,11 @@ import org.mockito.junit.MockitoRule class DeviceItemFactoryTest : SysuiTestCase() { @get:Rule val mockitoRule: MockitoRule = MockitoJUnit.rule() + private lateinit var mockitoSession: StaticMockitoSession @Mock private lateinit var cachedDevice: CachedBluetoothDevice @Mock private lateinit var bluetoothDevice: BluetoothDevice - @Mock private lateinit var packageManager: PackageManager + @Mock private lateinit var localBluetoothManager: LocalBluetoothManager + @Mock private lateinit var drawable: Drawable private val availableMediaDeviceItemFactory = AvailableMediaDeviceItemFactory() private val connectedDeviceItemFactory = ConnectedDeviceItemFactory() @@ -56,16 +65,21 @@ class DeviceItemFactoryTest : SysuiTestCase() { @Before fun setup() { - `when`(cachedDevice.name).thenReturn(DEVICE_NAME) - `when`(cachedDevice.address).thenReturn(DEVICE_ADDRESS) - `when`(cachedDevice.device).thenReturn(bluetoothDevice) - `when`(cachedDevice.connectionSummary).thenReturn(CONNECTION_SUMMARY) + mockitoSession = + mockitoSession().initMocks(this).mockStatic(BluetoothUtils::class.java).startMocking() + } - context.setMockPackageManager(packageManager) + @After + fun tearDown() { + mockitoSession.finishMocking() } @Test fun testAvailableMediaDeviceItemFactory_createFromCachedDevice() { + `when`(cachedDevice.name).thenReturn(DEVICE_NAME) + `when`(cachedDevice.connectionSummary).thenReturn(CONNECTION_SUMMARY) + `when`(BluetoothUtils.getBtClassDrawableWithDescription(any(), any())) + .thenReturn(Pair.create(drawable, "")) val deviceItem = availableMediaDeviceItemFactory.create(context, cachedDevice) assertDeviceItem(deviceItem, DeviceItemType.AVAILABLE_MEDIA_BLUETOOTH_DEVICE) @@ -73,6 +87,10 @@ class DeviceItemFactoryTest : SysuiTestCase() { @Test fun testConnectedDeviceItemFactory_createFromCachedDevice() { + `when`(cachedDevice.name).thenReturn(DEVICE_NAME) + `when`(cachedDevice.connectionSummary).thenReturn(CONNECTION_SUMMARY) + `when`(BluetoothUtils.getBtClassDrawableWithDescription(any(), any())) + .thenReturn(Pair.create(drawable, "")) val deviceItem = connectedDeviceItemFactory.create(context, cachedDevice) assertDeviceItem(deviceItem, DeviceItemType.CONNECTED_BLUETOOTH_DEVICE) @@ -80,6 +98,10 @@ class DeviceItemFactoryTest : SysuiTestCase() { @Test fun testSavedDeviceItemFactory_createFromCachedDevice() { + `when`(cachedDevice.name).thenReturn(DEVICE_NAME) + `when`(cachedDevice.connectionSummary).thenReturn(CONNECTION_SUMMARY) + `when`(BluetoothUtils.getBtClassDrawableWithDescription(any(), any())) + .thenReturn(Pair.create(drawable, "")) val deviceItem = savedDeviceItemFactory.create(context, cachedDevice) assertDeviceItem(deviceItem, DeviceItemType.SAVED_BLUETOOTH_DEVICE) @@ -87,6 +109,90 @@ class DeviceItemFactoryTest : SysuiTestCase() { } @Test + fun testAvailableAudioSharingMediaDeviceItemFactory_createFromCachedDevice() { + `when`(cachedDevice.name).thenReturn(DEVICE_NAME) + `when`(BluetoothUtils.getBtClassDrawableWithDescription(any(), any())) + .thenReturn(Pair.create(drawable, "")) + val deviceItem = + AvailableAudioSharingMediaDeviceItemFactory(localBluetoothManager) + .create(context, cachedDevice) + + assertThat(deviceItem).isNotNull() + assertThat(deviceItem.type) + .isEqualTo(DeviceItemType.AVAILABLE_AUDIO_SHARING_MEDIA_BLUETOOTH_DEVICE) + assertThat(deviceItem.cachedBluetoothDevice).isEqualTo(cachedDevice) + assertThat(deviceItem.deviceName).isEqualTo(DEVICE_NAME) + assertThat(deviceItem.isActive).isFalse() + assertThat(deviceItem.connectionSummary) + .isEqualTo( + context.getString( + R.string.quick_settings_bluetooth_device_audio_sharing_or_switch_active + ) + ) + } + + @Test + fun testAvailableAudioSharingMediaDeviceItemFactory_isFilterMatched_flagOff_returnsFalse() { + // Flags.FLAG_ENABLE_LE_AUDIO_SHARING off or the device doesn't support broadcast + // source or assistant. + `when`(BluetoothUtils.isAudioSharingEnabled()).thenReturn(false) + + assertThat( + AvailableAudioSharingMediaDeviceItemFactory(localBluetoothManager) + .isFilterMatched(context, cachedDevice, audioManager) + ) + .isFalse() + } + + @Test + fun testAvailableAudioSharingMediaDeviceItemFactory_isFilterMatched_isActiveDevice_returnsFalse() { + // Flags.FLAG_ENABLE_LE_AUDIO_SHARING on and the device support broadcast source and + // assistant. + `when`(BluetoothUtils.isAudioSharingEnabled()).thenReturn(true) + `when`(BluetoothUtils.isActiveMediaDevice(any())).thenReturn(true) + + assertThat( + AvailableAudioSharingMediaDeviceItemFactory(localBluetoothManager) + .isFilterMatched(context, cachedDevice, audioManager) + ) + .isFalse() + } + + @Test + fun testAvailableAudioSharingMediaDeviceItemFactory_isFilterMatched_isNotAvailable_returnsFalse() { + // Flags.FLAG_ENABLE_LE_AUDIO_SHARING on and the device support broadcast source and + // assistant. + `when`(BluetoothUtils.isAudioSharingEnabled()).thenReturn(true) + `when`(BluetoothUtils.isActiveMediaDevice(any())).thenReturn(false) + `when`(BluetoothUtils.isAvailableMediaBluetoothDevice(any(), any())).thenReturn(true) + `when`(BluetoothUtils.isAvailableAudioSharingMediaBluetoothDevice(any(), any())) + .thenReturn(false) + + assertThat( + AvailableAudioSharingMediaDeviceItemFactory(localBluetoothManager) + .isFilterMatched(context, cachedDevice, audioManager) + ) + .isFalse() + } + + @Test + fun testAvailableAudioSharingMediaDeviceItemFactory_isFilterMatched_returnsTrue() { + // Flags.FLAG_ENABLE_LE_AUDIO_SHARING on and the device support broadcast source and + // assistant. + `when`(BluetoothUtils.isAudioSharingEnabled()).thenReturn(true) + `when`(BluetoothUtils.isActiveMediaDevice(any())).thenReturn(false) + `when`(BluetoothUtils.isAvailableMediaBluetoothDevice(any(), any())).thenReturn(true) + `when`(BluetoothUtils.isAvailableAudioSharingMediaBluetoothDevice(any(), any())) + .thenReturn(true) + + assertThat( + AvailableAudioSharingMediaDeviceItemFactory(localBluetoothManager) + .isFilterMatched(context, cachedDevice, audioManager) + ) + .isTrue() + } + + @Test @DisableFlags(Flags.FLAG_ENABLE_HIDE_EXCLUSIVELY_MANAGED_BLUETOOTH_DEVICE) fun testSavedFactory_isFilterMatched_bondedAndNotConnected_returnsTrue() { `when`(cachedDevice.bondState).thenReturn(BluetoothDevice.BOND_BONDED) @@ -110,7 +216,6 @@ class DeviceItemFactoryTest : SysuiTestCase() { @DisableFlags(Flags.FLAG_ENABLE_HIDE_EXCLUSIVELY_MANAGED_BLUETOOTH_DEVICE) fun testSavedFactory_isFilterMatched_notBonded_returnsFalse() { `when`(cachedDevice.bondState).thenReturn(BluetoothDevice.BOND_NONE) - `when`(cachedDevice.isConnected).thenReturn(false) assertThat(savedDeviceItemFactory.isFilterMatched(context, cachedDevice, audioManager)) .isFalse() @@ -119,12 +224,8 @@ class DeviceItemFactoryTest : SysuiTestCase() { @Test @EnableFlags(Flags.FLAG_ENABLE_HIDE_EXCLUSIVELY_MANAGED_BLUETOOTH_DEVICE) fun testSavedFactory_isFilterMatched_exclusivelyManaged_returnsFalse() { - `when`(bluetoothDevice.getMetadata(BluetoothDevice.METADATA_EXCLUSIVE_MANAGER)) - .thenReturn(TEST_EXCLUSIVE_MANAGER.toByteArray()) - `when`(packageManager.getApplicationInfo(TEST_EXCLUSIVE_MANAGER, 0)) - .thenReturn(ApplicationInfo()) - `when`(cachedDevice.bondState).thenReturn(BluetoothDevice.BOND_BONDED) - `when`(cachedDevice.isConnected).thenReturn(false) + `when`(cachedDevice.device).thenReturn(bluetoothDevice) + `when`(BluetoothUtils.isExclusivelyManagedBluetoothDevice(any(), any())).thenReturn(true) assertThat(savedDeviceItemFactory.isFilterMatched(context, cachedDevice, audioManager)) .isFalse() @@ -132,35 +233,9 @@ class DeviceItemFactoryTest : SysuiTestCase() { @Test @EnableFlags(Flags.FLAG_ENABLE_HIDE_EXCLUSIVELY_MANAGED_BLUETOOTH_DEVICE) - fun testSavedFactory_isFilterMatched_noExclusiveManager_returnsTrue() { - `when`(cachedDevice.bondState).thenReturn(BluetoothDevice.BOND_BONDED) - `when`(cachedDevice.isConnected).thenReturn(false) - - assertThat(savedDeviceItemFactory.isFilterMatched(context, cachedDevice, audioManager)) - .isTrue() - } - - @Test - @EnableFlags(Flags.FLAG_ENABLE_HIDE_EXCLUSIVELY_MANAGED_BLUETOOTH_DEVICE) - fun testSavedFactory_isFilterMatched_exclusiveManagerNotEnabled_returnsTrue() { - `when`(bluetoothDevice.getMetadata(BluetoothDevice.METADATA_EXCLUSIVE_MANAGER)) - .thenReturn(TEST_EXCLUSIVE_MANAGER.toByteArray()) - `when`(packageManager.getApplicationInfo(TEST_EXCLUSIVE_MANAGER, 0)) - .thenReturn(ApplicationInfo().also { it.enabled = false }) - `when`(cachedDevice.bondState).thenReturn(BluetoothDevice.BOND_BONDED) - `when`(cachedDevice.isConnected).thenReturn(false) - - assertThat(savedDeviceItemFactory.isFilterMatched(context, cachedDevice, audioManager)) - .isTrue() - } - - @Test - @EnableFlags(Flags.FLAG_ENABLE_HIDE_EXCLUSIVELY_MANAGED_BLUETOOTH_DEVICE) - fun testSavedFactory_isFilterMatched_exclusiveManagerNotInstalled_returnsTrue() { - `when`(bluetoothDevice.getMetadata(BluetoothDevice.METADATA_EXCLUSIVE_MANAGER)) - .thenReturn(TEST_EXCLUSIVE_MANAGER.toByteArray()) - `when`(packageManager.getApplicationInfo(TEST_EXCLUSIVE_MANAGER, 0)) - .thenThrow(PackageManager.NameNotFoundException("Test!")) + fun testSavedFactory_isFilterMatched_notExclusiveManaged_returnsTrue() { + `when`(cachedDevice.device).thenReturn(bluetoothDevice) + `when`(BluetoothUtils.isExclusivelyManagedBluetoothDevice(any(), any())).thenReturn(false) `when`(cachedDevice.bondState).thenReturn(BluetoothDevice.BOND_BONDED) `when`(cachedDevice.isConnected).thenReturn(false) @@ -170,17 +245,9 @@ class DeviceItemFactoryTest : SysuiTestCase() { @Test @EnableFlags(Flags.FLAG_ENABLE_HIDE_EXCLUSIVELY_MANAGED_BLUETOOTH_DEVICE) - fun testSavedFactory_isFilterMatched_notExclusivelyManaged_notBonded_returnsFalse() { - `when`(cachedDevice.bondState).thenReturn(BluetoothDevice.BOND_NONE) - `when`(cachedDevice.isConnected).thenReturn(false) - - assertThat(savedDeviceItemFactory.isFilterMatched(context, cachedDevice, audioManager)) - .isFalse() - } - - @Test - @EnableFlags(Flags.FLAG_ENABLE_HIDE_EXCLUSIVELY_MANAGED_BLUETOOTH_DEVICE) fun testSavedFactory_isFilterMatched_notExclusivelyManaged_connected_returnsFalse() { + `when`(cachedDevice.device).thenReturn(bluetoothDevice) + `when`(BluetoothUtils.isExclusivelyManagedBluetoothDevice(any(), any())).thenReturn(false) `when`(cachedDevice.bondState).thenReturn(BluetoothDevice.BOND_BONDED) `when`(cachedDevice.isConnected).thenReturn(true) @@ -191,9 +258,7 @@ class DeviceItemFactoryTest : SysuiTestCase() { @Test @DisableFlags(Flags.FLAG_ENABLE_HIDE_EXCLUSIVELY_MANAGED_BLUETOOTH_DEVICE) fun testConnectedFactory_isFilterMatched_bondedAndConnected_returnsTrue() { - `when`(bluetoothDevice.bondState).thenReturn(BluetoothDevice.BOND_BONDED) - `when`(bluetoothDevice.isConnected).thenReturn(true) - audioManager.setMode(AudioManager.MODE_NORMAL) + `when`(BluetoothUtils.isConnectedBluetoothDevice(any(), any())).thenReturn(true) assertThat(connectedDeviceItemFactory.isFilterMatched(context, cachedDevice, audioManager)) .isTrue() @@ -202,21 +267,6 @@ class DeviceItemFactoryTest : SysuiTestCase() { @Test @DisableFlags(Flags.FLAG_ENABLE_HIDE_EXCLUSIVELY_MANAGED_BLUETOOTH_DEVICE) fun testConnectedFactory_isFilterMatched_notConnected_returnsFalse() { - `when`(bluetoothDevice.bondState).thenReturn(BluetoothDevice.BOND_BONDED) - `when`(bluetoothDevice.isConnected).thenReturn(false) - audioManager.setMode(AudioManager.MODE_NORMAL) - - assertThat(connectedDeviceItemFactory.isFilterMatched(context, cachedDevice, audioManager)) - .isFalse() - } - - @Test - @DisableFlags(Flags.FLAG_ENABLE_HIDE_EXCLUSIVELY_MANAGED_BLUETOOTH_DEVICE) - fun testConnectedFactory_isFilterMatched_notBonded_returnsFalse() { - `when`(bluetoothDevice.bondState).thenReturn(BluetoothDevice.BOND_NONE) - `when`(bluetoothDevice.isConnected).thenReturn(true) - audioManager.setMode(AudioManager.MODE_NORMAL) - assertThat(connectedDeviceItemFactory.isFilterMatched(context, cachedDevice, audioManager)) .isFalse() } @@ -224,13 +274,8 @@ class DeviceItemFactoryTest : SysuiTestCase() { @Test @EnableFlags(Flags.FLAG_ENABLE_HIDE_EXCLUSIVELY_MANAGED_BLUETOOTH_DEVICE) fun testConnectedFactory_isFilterMatched_exclusivelyManaged_returnsFalse() { - `when`(bluetoothDevice.getMetadata(BluetoothDevice.METADATA_EXCLUSIVE_MANAGER)) - .thenReturn(TEST_EXCLUSIVE_MANAGER.toByteArray()) - `when`(packageManager.getApplicationInfo(TEST_EXCLUSIVE_MANAGER, 0)) - .thenReturn(ApplicationInfo()) - `when`(bluetoothDevice.bondState).thenReturn(BluetoothDevice.BOND_BONDED) - `when`(bluetoothDevice.isConnected).thenReturn(true) - audioManager.setMode(AudioManager.MODE_NORMAL) + `when`(cachedDevice.device).thenReturn(bluetoothDevice) + `when`(BluetoothUtils.isExclusivelyManagedBluetoothDevice(any(), any())).thenReturn(true) assertThat(connectedDeviceItemFactory.isFilterMatched(context, cachedDevice, audioManager)) .isFalse() @@ -239,39 +284,9 @@ class DeviceItemFactoryTest : SysuiTestCase() { @Test @EnableFlags(Flags.FLAG_ENABLE_HIDE_EXCLUSIVELY_MANAGED_BLUETOOTH_DEVICE) fun testConnectedFactory_isFilterMatched_noExclusiveManager_returnsTrue() { - `when`(bluetoothDevice.bondState).thenReturn(BluetoothDevice.BOND_BONDED) - `when`(bluetoothDevice.isConnected).thenReturn(true) - audioManager.setMode(AudioManager.MODE_NORMAL) - - assertThat(connectedDeviceItemFactory.isFilterMatched(context, cachedDevice, audioManager)) - .isTrue() - } - - @Test - @EnableFlags(Flags.FLAG_ENABLE_HIDE_EXCLUSIVELY_MANAGED_BLUETOOTH_DEVICE) - fun testConnectedFactory_isFilterMatched_exclusiveManagerNotEnabled_returnsTrue() { - `when`(bluetoothDevice.getMetadata(BluetoothDevice.METADATA_EXCLUSIVE_MANAGER)) - .thenReturn(TEST_EXCLUSIVE_MANAGER.toByteArray()) - `when`(packageManager.getApplicationInfo(TEST_EXCLUSIVE_MANAGER, 0)) - .thenReturn(ApplicationInfo().also { it.enabled = false }) - `when`(bluetoothDevice.bondState).thenReturn(BluetoothDevice.BOND_BONDED) - `when`(bluetoothDevice.isConnected).thenReturn(true) - audioManager.setMode(AudioManager.MODE_NORMAL) - - assertThat(connectedDeviceItemFactory.isFilterMatched(context, cachedDevice, audioManager)) - .isTrue() - } - - @Test - @EnableFlags(Flags.FLAG_ENABLE_HIDE_EXCLUSIVELY_MANAGED_BLUETOOTH_DEVICE) - fun testConnectedFactory_isFilterMatched_exclusiveManagerNotInstalled_returnsTrue() { - `when`(bluetoothDevice.getMetadata(BluetoothDevice.METADATA_EXCLUSIVE_MANAGER)) - .thenReturn(TEST_EXCLUSIVE_MANAGER.toByteArray()) - `when`(packageManager.getApplicationInfo(TEST_EXCLUSIVE_MANAGER, 0)) - .thenThrow(PackageManager.NameNotFoundException("Test!")) - `when`(bluetoothDevice.bondState).thenReturn(BluetoothDevice.BOND_BONDED) - `when`(bluetoothDevice.isConnected).thenReturn(true) - audioManager.setMode(AudioManager.MODE_NORMAL) + `when`(cachedDevice.device).thenReturn(bluetoothDevice) + `when`(BluetoothUtils.isExclusivelyManagedBluetoothDevice(any(), any())).thenReturn(false) + `when`(BluetoothUtils.isConnectedBluetoothDevice(any(), any())).thenReturn(true) assertThat(connectedDeviceItemFactory.isFilterMatched(context, cachedDevice, audioManager)) .isTrue() @@ -279,21 +294,10 @@ class DeviceItemFactoryTest : SysuiTestCase() { @Test @EnableFlags(Flags.FLAG_ENABLE_HIDE_EXCLUSIVELY_MANAGED_BLUETOOTH_DEVICE) - fun testConnectedFactory_isFilterMatched_notExclusivelyManaged_notBonded_returnsFalse() { - `when`(bluetoothDevice.bondState).thenReturn(BluetoothDevice.BOND_NONE) - `when`(bluetoothDevice.isConnected).thenReturn(true) - audioManager.setMode(AudioManager.MODE_NORMAL) - - assertThat(connectedDeviceItemFactory.isFilterMatched(context, cachedDevice, audioManager)) - .isFalse() - } - - @Test - @EnableFlags(Flags.FLAG_ENABLE_HIDE_EXCLUSIVELY_MANAGED_BLUETOOTH_DEVICE) fun testConnectedFactory_isFilterMatched_notExclusivelyManaged_notConnected_returnsFalse() { - `when`(bluetoothDevice.bondState).thenReturn(BluetoothDevice.BOND_BONDED) - `when`(bluetoothDevice.isConnected).thenReturn(false) - audioManager.setMode(AudioManager.MODE_NORMAL) + `when`(cachedDevice.device).thenReturn(bluetoothDevice) + `when`(BluetoothUtils.isExclusivelyManagedBluetoothDevice(any(), any())).thenReturn(false) + `when`(BluetoothUtils.isConnectedBluetoothDevice(any(), any())).thenReturn(false) assertThat(connectedDeviceItemFactory.isFilterMatched(context, cachedDevice, audioManager)) .isFalse() @@ -310,7 +314,5 @@ class DeviceItemFactoryTest : SysuiTestCase() { companion object { const val DEVICE_NAME = "DeviceName" const val CONNECTION_SUMMARY = "ConnectionSummary" - private const val TEST_EXCLUSIVE_MANAGER = "com.test.manager" - private const val DEVICE_ADDRESS = "04:52:C7:0B:D8:3C" } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/composable/BouncerContentTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/composable/BouncerContentTest.kt index 8e215f994e4d..fd550b05fdc9 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/composable/BouncerContentTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/composable/BouncerContentTest.kt @@ -83,7 +83,9 @@ class BouncerContentTest : SysuiTestCase() { PlatformTheme { BouncerContent( viewModel = - rememberViewModel { kosmos.bouncerSceneContentViewModelFactory.create() }, + rememberViewModel("test") { + kosmos.bouncerSceneContentViewModelFactory.create() + }, layout = BouncerSceneLayout.BESIDE_USER_SWITCHER, modifier = Modifier.fillMaxSize().testTag("BouncerContent"), dialogFactory = bouncerDialogFactory diff --git a/packages/SystemUI/tests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFaceAuthInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFaceAuthInteractorTest.kt index 6aecc0e2b8fc..4883d1bb9400 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFaceAuthInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFaceAuthInteractorTest.kt @@ -63,6 +63,7 @@ import com.android.systemui.util.mockito.eq 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.TestScope import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest @@ -365,7 +366,7 @@ class DeviceEntryFaceAuthInteractorTest : SysuiTestCase() { testScope.runTest { underTest.start() - underTest.onQsExpansionStared() + underTest.onQsExpansionStarted() runCurrent() assertThat(faceAuthRepository.runningAuthRequest.value) @@ -373,6 +374,79 @@ class DeviceEntryFaceAuthInteractorTest : SysuiTestCase() { } @Test + @EnableSceneContainer + fun faceAuthIsRequestedWhenQuickSettingsIsExpandedToTheShade() = + testScope.runTest { + underTest.start() + faceAuthRepository.canRunFaceAuth.value = true + kosmos.sceneInteractor.snapToScene(toScene = Scenes.QuickSettings, "for-test") + runCurrent() + + kosmos.sceneInteractor.changeScene(toScene = Scenes.Shade, loggingReason = "for-test") + kosmos.sceneInteractor.setTransitionState( + MutableStateFlow( + ObservableTransitionState.Transition( + fromScene = Scenes.QuickSettings, + toScene = Scenes.Shade, + currentScene = flowOf(Scenes.QuickSettings), + progress = MutableStateFlow(0.2f), + isInitiatedByUserInput = true, + isUserInputOngoing = flowOf(false), + ) + ) + ) + + runCurrent() + assertThat(faceAuthRepository.runningAuthRequest.value) + .isEqualTo(Pair(FaceAuthUiEvent.FACE_AUTH_TRIGGERED_QS_EXPANDED, true)) + } + + @Test + @EnableSceneContainer + fun faceAuthIsRequestedOnlyOnceWhenQuickSettingsIsExpandedToTheShade() = + testScope.runTest { + underTest.start() + faceAuthRepository.canRunFaceAuth.value = true + kosmos.sceneInteractor.snapToScene(toScene = Scenes.QuickSettings, "for-test") + runCurrent() + + kosmos.sceneInteractor.changeScene(toScene = Scenes.Shade, loggingReason = "for-test") + kosmos.sceneInteractor.setTransitionState( + MutableStateFlow( + ObservableTransitionState.Transition( + fromScene = Scenes.QuickSettings, + toScene = Scenes.Shade, + currentScene = flowOf(Scenes.QuickSettings), + progress = MutableStateFlow(0.2f), + isInitiatedByUserInput = true, + isUserInputOngoing = flowOf(false), + ) + ) + ) + + runCurrent() + assertThat(faceAuthRepository.runningAuthRequest.value) + .isEqualTo(Pair(FaceAuthUiEvent.FACE_AUTH_TRIGGERED_QS_EXPANDED, true)) + faceAuthRepository.runningAuthRequest.value = null + + // expansion progress shouldn't trigger face auth again + kosmos.sceneInteractor.setTransitionState( + MutableStateFlow( + ObservableTransitionState.Transition( + fromScene = Scenes.QuickSettings, + toScene = Scenes.Shade, + currentScene = flowOf(Scenes.QuickSettings), + progress = MutableStateFlow(0.5f), + isInitiatedByUserInput = true, + isUserInputOngoing = flowOf(false), + ) + ) + ) + + assertThat(faceAuthRepository.runningAuthRequest.value).isNull() + } + + @Test fun faceAuthIsRequestedWhenNotificationPanelClicked() = testScope.runTest { underTest.start() 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 4e1b12f70da5..43c7ed6a769d 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 @@ -21,7 +21,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.keyguard.WindowManagerLockscreenVisibilityManager -import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor +import com.android.systemui.keyguard.domain.interactor.KeyguardDismissTransitionInteractor import com.android.systemui.statusbar.policy.KeyguardStateController import com.android.systemui.util.concurrency.FakeExecutor import com.android.systemui.util.time.FakeSystemClock @@ -44,7 +44,8 @@ class WindowManagerLockscreenVisibilityManagerTest : SysuiTestCase() { @Mock private lateinit var activityTaskManagerService: IActivityTaskManager @Mock private lateinit var keyguardStateController: KeyguardStateController @Mock private lateinit var keyguardSurfaceBehindAnimator: KeyguardSurfaceBehindParamsApplier - @Mock private lateinit var keyguardTransitionInteractor: KeyguardTransitionInteractor + @Mock + private lateinit var keyguardDismissTransitionInteractor: KeyguardDismissTransitionInteractor @Before fun setUp() { @@ -57,7 +58,7 @@ class WindowManagerLockscreenVisibilityManagerTest : SysuiTestCase() { activityTaskManagerService = activityTaskManagerService, keyguardStateController = keyguardStateController, keyguardSurfaceBehindAnimator = keyguardSurfaceBehindAnimator, - keyguardTransitionInteractor = keyguardTransitionInteractor, + keyguardDismissTransitionInteractor = keyguardDismissTransitionInteractor, ) } diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModelTest.kt index 664a0bdedec4..844a166be47b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModelTest.kt @@ -21,6 +21,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.internal.policy.IKeyguardDismissCallback import com.android.systemui.SysuiTestCase +import com.android.systemui.bouncer.domain.interactor.primaryBouncerInteractor import com.android.systemui.concurrency.fakeExecutor import com.android.systemui.coroutines.collectLastValue import com.android.systemui.coroutines.collectValues @@ -30,6 +31,7 @@ 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.plugins.ActivityStarter import com.android.systemui.statusbar.phone.statusBarKeyguardViewManager import com.android.systemui.testKosmos import com.android.systemui.util.mockito.any @@ -69,6 +71,12 @@ class AlternateBouncerViewModelTest : SysuiTestCase() { @Test fun onBackRequested() = testScope.runTest { + kosmos.primaryBouncerInteractor.setDismissAction( + mock(ActivityStarter.OnDismissAction::class.java), + {}, + ) + assertThat(kosmos.primaryBouncerInteractor.bouncerDismissAction).isNotNull() + val dismissCallback = mock(IKeyguardDismissCallback::class.java) kosmos.dismissCallbackRegistry.addCallback(dismissCallback) @@ -76,6 +84,7 @@ class AlternateBouncerViewModelTest : SysuiTestCase() { kosmos.fakeExecutor.runAllReady() verify(statusBarKeyguardViewManager).hideAlternateBouncer(any()) verify(dismissCallback).onDismissCancelled() + assertThat(kosmos.primaryBouncerInteractor.bouncerDismissAction).isNull() } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModelTest.kt index 73b9f5783004..07f7557d965a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModelTest.kt @@ -47,8 +47,11 @@ import com.android.systemui.keyguard.data.repository.KeyguardQuickAffordanceRepo import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory import com.android.systemui.keyguard.domain.interactor.KeyguardQuickAffordanceInteractor -import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor -import com.android.systemui.keyguard.shared.model.KeyguardState +import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor +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.TransitionStep import com.android.systemui.keyguard.shared.quickaffordance.ActivationState import com.android.systemui.keyguard.shared.quickaffordance.KeyguardQuickAffordancePosition import com.android.systemui.keyguard.shared.quickaffordance.KeyguardQuickAffordancesMetricsLogger @@ -56,7 +59,10 @@ import com.android.systemui.kosmos.testDispatcher import com.android.systemui.kosmos.testScope import com.android.systemui.plugins.ActivityStarter import com.android.systemui.res.R +import com.android.systemui.scene.data.repository.Idle +import com.android.systemui.scene.data.repository.setTransition import com.android.systemui.scene.domain.interactor.sceneInteractor +import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.settings.UserFileManager import com.android.systemui.settings.UserTracker import com.android.systemui.shade.domain.interactor.ShadeInteractor @@ -144,7 +150,6 @@ class KeyguardQuickAffordancesCombinedViewModelTest : SysuiTestCase() { @Mock private lateinit var glanceableHubToLockscreenTransitionViewModel: GlanceableHubToLockscreenTransitionViewModel - @Mock private lateinit var transitionInteractor: KeyguardTransitionInteractor private val kosmos = testKosmos() @@ -163,8 +168,6 @@ class KeyguardQuickAffordancesCombinedViewModelTest : SysuiTestCase() { // the viewModel does a `map { 1 - it }` on this value, which is why it's different private val intendedShadeAlphaMutableStateFlow: MutableStateFlow<Float> = MutableStateFlow(0f) - private val intendedFinishedKeyguardStateFlow = MutableStateFlow(KeyguardState.LOCKSCREEN) - @Before fun setUp() { MockitoAnnotations.initMocks(this) @@ -256,7 +259,6 @@ class KeyguardQuickAffordancesCombinedViewModelTest : SysuiTestCase() { intendedAlphaMutableStateFlow.value = 1f intendedShadeAlphaMutableStateFlow.value = 0f - intendedFinishedKeyguardStateFlow.value = KeyguardState.LOCKSCREEN whenever(aodToLockscreenTransitionViewModel.shortcutsAlpha) .thenReturn(intendedAlphaMutableStateFlow) whenever(dozingToLockscreenTransitionViewModel.shortcutsAlpha).thenReturn(emptyFlow()) @@ -283,8 +285,6 @@ class KeyguardQuickAffordancesCombinedViewModelTest : SysuiTestCase() { whenever(glanceableHubToLockscreenTransitionViewModel.shortcutsAlpha) .thenReturn(emptyFlow()) whenever(shadeInteractor.anyExpansion).thenReturn(intendedShadeAlphaMutableStateFlow) - whenever(transitionInteractor.finishedKeyguardState) - .thenReturn(intendedFinishedKeyguardStateFlow) underTest = KeyguardQuickAffordancesCombinedViewModel( @@ -334,7 +334,7 @@ class KeyguardQuickAffordancesCombinedViewModelTest : SysuiTestCase() { lockscreenToPrimaryBouncerTransitionViewModel, lockscreenToGlanceableHubTransitionViewModel = lockscreenToGlanceableHubTransitionViewModel, - transitionInteractor = transitionInteractor, + transitionInteractor = kosmos.keyguardTransitionInteractor, ) } @@ -776,7 +776,10 @@ class KeyguardQuickAffordancesCombinedViewModelTest : SysuiTestCase() { @Test fun shadeExpansionAlpha_changes_whenOnLockscreen() = testScope.runTest { - intendedFinishedKeyguardStateFlow.value = KeyguardState.LOCKSCREEN + kosmos.setTransition( + sceneTransition = Idle(Scenes.Lockscreen), + stateTransition = TransitionStep(from = AOD, to = LOCKSCREEN) + ) intendedShadeAlphaMutableStateFlow.value = 0.25f val underTest = collectLastValue(underTest.transitionAlpha) assertEquals(0.75f, underTest()) @@ -788,7 +791,10 @@ class KeyguardQuickAffordancesCombinedViewModelTest : SysuiTestCase() { @Test fun shadeExpansionAlpha_alwaysZero_whenNotOnLockscreen() = testScope.runTest { - intendedFinishedKeyguardStateFlow.value = KeyguardState.GONE + kosmos.setTransition( + sceneTransition = Idle(Scenes.Gone), + stateTransition = TransitionStep(from = AOD, to = GONE) + ) intendedShadeAlphaMutableStateFlow.value = 0.5f val underTest = collectLastValue(underTest.transitionAlpha) assertEquals(0f, underTest()) diff --git a/packages/SystemUI/tests/src/com/android/systemui/lifecycle/ActivatableTest.kt b/packages/SystemUI/tests/src/com/android/systemui/lifecycle/ActivatableTest.kt index 67517a25ec87..2ba670ceb76a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/lifecycle/ActivatableTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/lifecycle/ActivatableTest.kt @@ -40,7 +40,7 @@ class ActivatableTest : SysuiTestCase() { composeRule.setContent { val keepAlive by keepAliveMutable if (keepAlive) { - rememberActivated { + rememberActivated("test") { FakeActivatable( onActivation = { isActive = true }, onDeactivation = { isActive = false }, @@ -58,7 +58,7 @@ class ActivatableTest : SysuiTestCase() { composeRule.setContent { val keepAlive by keepAliveMutable if (keepAlive) { - rememberActivated { + rememberActivated("name") { FakeActivatable( onActivation = { isActive = true }, onDeactivation = { isActive = false }, diff --git a/packages/SystemUI/tests/src/com/android/systemui/lifecycle/SysUiViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/lifecycle/SysUiViewModelTest.kt index c7acd78c5623..73f724e7daef 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/lifecycle/SysUiViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/lifecycle/SysUiViewModelTest.kt @@ -51,7 +51,7 @@ class SysUiViewModelTest : SysuiTestCase() { composeRule.setContent { val keepAlive by keepAliveMutable if (keepAlive) { - rememberViewModel { + rememberViewModel("test") { FakeSysUiViewModel( onActivation = { isActive = true }, onDeactivation = { isActive = false }, @@ -69,21 +69,25 @@ class SysUiViewModelTest : SysuiTestCase() { var isActive2 = false composeRule.setContent { val key by keyMutable - rememberViewModel(key) { - when (key) { - 1 -> - FakeSysUiViewModel( - onActivation = { isActive1 = true }, - onDeactivation = { isActive1 = false }, - ) - 2 -> - FakeSysUiViewModel( - onActivation = { isActive2 = true }, - onDeactivation = { isActive2 = false }, - ) - else -> error("unsupported key $key") + // Need to explicitly state the type to avoid a weird issue where the factory seems to + // return Unit instead of FakeSysUiViewModel. It might be an issue with the compose + // compiler. + val unused: FakeSysUiViewModel = + rememberViewModel("test", key) { + when (key) { + 1 -> + FakeSysUiViewModel( + onActivation = { isActive1 = true }, + onDeactivation = { isActive1 = false }, + ) + 2 -> + FakeSysUiViewModel( + onActivation = { isActive2 = true }, + onDeactivation = { isActive2 = false }, + ) + else -> error("unsupported key $key") + } } - } } assertThat(isActive1).isTrue() assertThat(isActive2).isFalse() @@ -106,7 +110,7 @@ class SysUiViewModelTest : SysuiTestCase() { composeRule.setContent { val keepAlive by keepAliveMutable if (keepAlive) { - rememberViewModel { + rememberViewModel("test") { FakeSysUiViewModel( onActivation = { isActive = true }, onDeactivation = { isActive = false }, @@ -130,6 +134,7 @@ class SysUiViewModelTest : SysuiTestCase() { val viewModel = FakeViewModel() backgroundScope.launch { view.viewModel( + traceName = "test", minWindowLifecycleState = WindowLifecycleState.ATTACHED, factory = { viewModel }, ) { @@ -151,7 +156,7 @@ class SysUiViewModelTest : SysuiTestCase() { } } -private class FakeViewModel : SysUiViewModel, ExclusiveActivatable() { +private class FakeViewModel : ExclusiveActivatable() { var isActivated = false override suspend fun onActivated(): Nothing { 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 99c5b7cdfdc5..9eccd9fd3cdf 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 @@ -41,12 +41,11 @@ import android.os.Bundle 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.provider.Settings import android.service.notification.StatusBarNotification -import android.testing.TestableLooper import android.testing.TestableLooper.RunWithLooper import androidx.media.utils.MediaConstants -import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.dx.mockito.inline.extended.ExtendedMockito import com.android.internal.logging.InstanceId @@ -58,9 +57,15 @@ import com.android.systemui.broadcast.BroadcastDispatcher import com.android.systemui.coroutines.collectLastValue import com.android.systemui.dump.DumpManager import com.android.systemui.flags.EnableSceneContainer +import com.android.systemui.flags.Flags.MEDIA_REMOTE_RESUME +import com.android.systemui.flags.Flags.MEDIA_RESUME_PROGRESS +import com.android.systemui.flags.Flags.MEDIA_RETAIN_RECOMMENDATIONS +import com.android.systemui.flags.Flags.MEDIA_RETAIN_SESSIONS +import com.android.systemui.flags.Flags.MEDIA_SESSION_ACTIONS +import com.android.systemui.flags.fakeFeatureFlagsClassic +import com.android.systemui.kosmos.testDispatcher import com.android.systemui.kosmos.testScope -import com.android.systemui.media.controls.data.repository.MediaDataRepository -import com.android.systemui.media.controls.data.repository.MediaFilterRepository +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.interactor.MediaCarouselInteractor import com.android.systemui.media.controls.domain.resume.MediaResumeListener @@ -70,10 +75,10 @@ import com.android.systemui.media.controls.shared.model.EXTRA_VALUE_TRIGGER_PERI import com.android.systemui.media.controls.shared.model.MediaData import com.android.systemui.media.controls.shared.model.SmartspaceMediaData import com.android.systemui.media.controls.shared.model.SmartspaceMediaDataProvider -import com.android.systemui.media.controls.util.MediaControllerFactory -import com.android.systemui.media.controls.util.MediaFlags import com.android.systemui.media.controls.util.MediaUiEventLogger -import com.android.systemui.plugins.ActivityStarter +import com.android.systemui.media.controls.util.fakeMediaControllerFactory +import com.android.systemui.media.controls.util.mediaFlags +import com.android.systemui.plugins.activityStarter import com.android.systemui.res.R import com.android.systemui.statusbar.SbnBuilder import com.android.systemui.statusbar.notificationLockscreenUserManager @@ -81,13 +86,10 @@ import com.android.systemui.testKosmos import com.android.systemui.util.concurrency.FakeExecutor import com.android.systemui.util.settings.FakeSettings import com.android.systemui.util.time.FakeSystemClock -import com.android.systemui.utils.os.FakeHandler import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.test.TestDispatcher import kotlinx.coroutines.test.TestScope -import kotlinx.coroutines.test.UnconfinedTestDispatcher -import kotlinx.coroutines.test.runTest +import kotlinx.coroutines.test.runCurrent import org.junit.After import org.junit.Before import org.junit.Rule @@ -111,6 +113,8 @@ import org.mockito.kotlin.capture import org.mockito.kotlin.eq import org.mockito.kotlin.whenever import org.mockito.quality.Strictness +import platform.test.runner.parameterized.ParameterizedAndroidJunit4 +import platform.test.runner.parameterized.Parameters private const val KEY = "KEY" private const val KEY_2 = "KEY_2" @@ -134,13 +138,10 @@ private fun <T> anyObject(): T { @OptIn(ExperimentalCoroutinesApi::class) @SmallTest @RunWithLooper(setAsMainLooper = true) -@RunWith(AndroidJUnit4::class) +@RunWith(ParameterizedAndroidJunit4::class) @EnableSceneContainer -class MediaDataProcessorTest : SysuiTestCase() { - val kosmos = testKosmos() - +class MediaDataProcessorTest(flags: FlagsParameterization) : SysuiTestCase() { @JvmField @Rule val mockito = MockitoJUnit.rule() - @Mock lateinit var mediaControllerFactory: MediaControllerFactory @Mock lateinit var controller: MediaController @Mock lateinit var transportControls: MediaController.TransportControls @Mock lateinit var playbackInfo: MediaController.PlaybackInfo @@ -158,7 +159,6 @@ class MediaDataProcessorTest : SysuiTestCase() { @Mock lateinit var mediaDataCombineLatest: MediaDataCombineLatest @Mock lateinit var listener: MediaDataManager.Listener @Mock lateinit var pendingIntent: PendingIntent - @Mock lateinit var activityStarter: ActivityStarter @Mock lateinit var smartspaceManager: SmartspaceManager @Mock lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor private lateinit var smartspaceMediaDataProvider: SmartspaceMediaDataProvider @@ -166,7 +166,6 @@ class MediaDataProcessorTest : SysuiTestCase() { @Mock private lateinit var mediaRecommendationItem: SmartspaceAction private lateinit var validRecommendationList: List<SmartspaceAction> @Mock private lateinit var mediaSmartspaceBaseAction: SmartspaceAction - @Mock private lateinit var mediaFlags: MediaFlags @Mock private lateinit var logger: MediaUiEventLogger private lateinit var mediaCarouselInteractor: MediaCarouselInteractor private lateinit var mediaDataProcessor: MediaDataProcessor @@ -179,11 +178,30 @@ class MediaDataProcessorTest : SysuiTestCase() { @Captor lateinit var smartSpaceConfigBuilderCaptor: ArgumentCaptor<SmartspaceConfig> @Mock private lateinit var ugm: IUriGrantsManager @Mock private lateinit var imageSource: ImageDecoder.Source - private lateinit var mediaDataRepository: MediaDataRepository - private lateinit var testScope: TestScope - private lateinit var testDispatcher: TestDispatcher - private lateinit var testableLooper: TestableLooper - private lateinit var fakeHandler: FakeHandler + + companion object { + @JvmStatic + @Parameters(name = "{0}") + fun getParams(): List<FlagsParameterization> { + return FlagsParameterization.progressionOf( + Flags.FLAG_MEDIA_LOAD_METADATA_VIA_MEDIA_DATA_LOADER + ) + } + } + + init { + mSetFlagsRule.setFlagsParameterization(flags) + } + + private val kosmos = testKosmos() + private val testDispatcher = kosmos.testDispatcher + private val testScope = kosmos.testScope + private val fakeFeatureFlags = kosmos.fakeFeatureFlagsClassic + private val activityStarter = kosmos.activityStarter + private val mediaControllerFactory = kosmos.fakeMediaControllerFactory + private val notificationLockscreenUserManager = kosmos.notificationLockscreenUserManager + private val mediaFilterRepository = kosmos.mediaFilterRepository + private val mediaDataFilter = kosmos.mediaDataFilter private val settings = FakeSettings() private val instanceIdSequence = InstanceIdSequenceFake(1 shl 20) @@ -194,9 +212,6 @@ class MediaDataProcessorTest : SysuiTestCase() { Settings.Secure.MEDIA_CONTROLS_RECOMMENDATION, 1 ) - private val notificationLockscreenUserManager = kosmos.notificationLockscreenUserManager - private val mediaFilterRepository: MediaFilterRepository = kosmos.mediaFilterRepository - private val mediaDataFilter: MediaDataFilterImpl = kosmos.mediaDataFilter private lateinit var staticMockSession: MockitoSession @@ -212,17 +227,12 @@ class MediaDataProcessorTest : SysuiTestCase() { foregroundExecutor = FakeExecutor(clock) backgroundExecutor = FakeExecutor(clock) uiExecutor = FakeExecutor(clock) - testableLooper = TestableLooper.get(this) - fakeHandler = FakeHandler(testableLooper.looper) smartspaceMediaDataProvider = SmartspaceMediaDataProvider() Settings.Secure.putInt( context.contentResolver, Settings.Secure.MEDIA_CONTROLS_RECOMMENDATION, 1 ) - testDispatcher = UnconfinedTestDispatcher() - testScope = TestScope(testDispatcher) - mediaDataRepository = MediaDataRepository(mediaFlags, dumpManager) mediaDataProcessor = MediaDataProcessor( context = context, @@ -231,7 +241,7 @@ class MediaDataProcessorTest : SysuiTestCase() { backgroundExecutor = backgroundExecutor, uiExecutor = uiExecutor, foregroundExecutor = foregroundExecutor, - handler = fakeHandler, + mainDispatcher = testDispatcher, mediaControllerFactory = mediaControllerFactory, broadcastDispatcher = broadcastDispatcher, dumpManager = dumpManager, @@ -241,13 +251,15 @@ class MediaDataProcessorTest : SysuiTestCase() { useQsMediaPlayer = true, systemClock = clock, secureSettings = settings, - mediaFlags = mediaFlags, + mediaFlags = kosmos.mediaFlags, logger = logger, smartspaceManager = smartspaceManager, keyguardUpdateMonitor = keyguardUpdateMonitor, - mediaDataRepository = mediaDataRepository, + mediaDataRepository = kosmos.mediaDataRepository, + mediaDataLoader = { kosmos.mediaDataLoader }, ) mediaDataProcessor.start() + testScope.runCurrent() mediaCarouselInteractor = MediaCarouselInteractor( applicationScope = testScope.backgroundScope, @@ -259,7 +271,7 @@ class MediaDataProcessorTest : SysuiTestCase() { mediaDataCombineLatest = mediaDataCombineLatest, mediaDataFilter = mediaDataFilter, mediaFilterRepository = mediaFilterRepository, - mediaFlags = mediaFlags + mediaFlags = kosmos.mediaFlags ) mediaCarouselInteractor.start() verify(mediaTimeoutListener).stateCallback = capture(stateCallbackCaptor) @@ -295,7 +307,7 @@ class MediaDataProcessorTest : SysuiTestCase() { putString(MediaMetadata.METADATA_KEY_TITLE, SESSION_TITLE) } verify(smartspaceManager).createSmartspaceSession(capture(smartSpaceConfigBuilderCaptor)) - whenever(mediaControllerFactory.create(eq(session.sessionToken))).thenReturn(controller) + mediaControllerFactory.setControllerForToken(session.sessionToken, controller) whenever(controller.transportControls).thenReturn(transportControls) whenever(controller.playbackInfo).thenReturn(playbackInfo) whenever(controller.metadata).thenReturn(metadataBuilder.build()) @@ -325,10 +337,11 @@ class MediaDataProcessorTest : SysuiTestCase() { whenever(mediaSmartspaceTarget.iconGrid).thenReturn(validRecommendationList) whenever(mediaSmartspaceTarget.creationTimeMillis).thenReturn(SMARTSPACE_CREATION_TIME) whenever(mediaSmartspaceTarget.expiryTimeMillis).thenReturn(SMARTSPACE_EXPIRY_TIME) - whenever(mediaFlags.areMediaSessionActionsEnabled(any(), any())).thenReturn(false) - whenever(mediaFlags.isRetainingPlayersEnabled()).thenReturn(false) - whenever(mediaFlags.isPersistentSsCardEnabled()).thenReturn(false) - whenever(mediaFlags.isRemoteResumeAllowed()).thenReturn(false) + fakeFeatureFlags.set(MEDIA_SESSION_ACTIONS, false) + fakeFeatureFlags.set(MEDIA_RETAIN_SESSIONS, false) + fakeFeatureFlags.set(MEDIA_RESUME_PROGRESS, false) + fakeFeatureFlags.set(MEDIA_REMOTE_RESUME, false) + fakeFeatureFlags.set(MEDIA_RETAIN_RECOMMENDATIONS, false) whenever(logger.getNewInstanceId()).thenReturn(instanceIdSequence.newInstanceId()) whenever(keyguardUpdateMonitor.isUserInLockdown(any())).thenReturn(false) } @@ -374,6 +387,7 @@ class MediaDataProcessorTest : SysuiTestCase() { PACKAGE_NAME ) + testScope.runCurrent() backgroundExecutor.runAllReady() foregroundExecutor.runAllReady() verify(listener) @@ -399,7 +413,7 @@ class MediaDataProcessorTest : SysuiTestCase() { @Test fun testLoadsMetadataOnBackground() { mediaDataProcessor.onNotificationAdded(KEY, mediaNotification) - assertThat(backgroundExecutor.numPending()).isEqualTo(1) + testScope.assertRunAllReady(foreground = 0, background = 1) } @Test @@ -416,8 +430,7 @@ class MediaDataProcessorTest : SysuiTestCase() { mediaDataProcessor.onNotificationAdded(KEY, mediaNotification) - assertThat(backgroundExecutor.runAllReady()).isEqualTo(1) - assertThat(foregroundExecutor.runAllReady()).isEqualTo(1) + testScope.assertRunAllReady(foreground = 1, background = 1) verify(listener) .onMediaDataLoaded( eq(KEY), @@ -434,8 +447,7 @@ class MediaDataProcessorTest : SysuiTestCase() { fun testOnMetaDataLoaded_withoutExplicitIndicator() { mediaDataProcessor.onNotificationAdded(KEY, mediaNotification) - assertThat(backgroundExecutor.runAllReady()).isEqualTo(1) - assertThat(foregroundExecutor.runAllReady()).isEqualTo(1) + testScope.assertRunAllReady(foreground = 1, background = 1) verify(listener) .onMediaDataLoaded( eq(KEY), @@ -462,10 +474,8 @@ class MediaDataProcessorTest : SysuiTestCase() { @Test fun testOnMetaDataLoaded_conservesActiveFlag() { - whenever(mediaControllerFactory.create(anyObject())).thenReturn(controller) mediaDataProcessor.onNotificationAdded(KEY, mediaNotification) - assertThat(backgroundExecutor.runAllReady()).isEqualTo(1) - assertThat(foregroundExecutor.runAllReady()).isEqualTo(1) + testScope.assertRunAllReady(foreground = 1, background = 1) verify(listener) .onMediaDataLoaded( eq(KEY), @@ -509,8 +519,7 @@ class MediaDataProcessorTest : SysuiTestCase() { } mediaDataProcessor.onNotificationAdded(KEY, notif) - assertThat(backgroundExecutor.runAllReady()).isEqualTo(1) - assertThat(foregroundExecutor.runAllReady()).isEqualTo(1) + testScope.assertRunAllReady(foreground = 1, background = 1) verify(listener) .onMediaDataLoaded( eq(KEY), @@ -596,8 +605,7 @@ class MediaDataProcessorTest : SysuiTestCase() { mediaDataProcessor.onNotificationAdded(KEY, mediaNotification) // Then a media control is created with a placeholder title string - assertThat(backgroundExecutor.runAllReady()).isEqualTo(1) - assertThat(foregroundExecutor.runAllReady()).isEqualTo(1) + testScope.assertRunAllReady(foreground = 1, background = 1) verify(listener) .onMediaDataLoaded( eq(KEY), @@ -627,8 +635,7 @@ class MediaDataProcessorTest : SysuiTestCase() { mediaDataProcessor.onNotificationAdded(KEY, mediaNotification) // Then a media control is created with a placeholder title string - assertThat(backgroundExecutor.runAllReady()).isEqualTo(1) - assertThat(foregroundExecutor.runAllReady()).isEqualTo(1) + testScope.assertRunAllReady(foreground = 1, background = 1) verify(listener) .onMediaDataLoaded( eq(KEY), @@ -669,8 +676,7 @@ class MediaDataProcessorTest : SysuiTestCase() { mediaDataProcessor.onNotificationAdded(KEY, mediaNotification) // Then the media control is added using the notification's title - assertThat(backgroundExecutor.runAllReady()).isEqualTo(1) - assertThat(foregroundExecutor.runAllReady()).isEqualTo(1) + testScope.assertRunAllReady(foreground = 1, background = 1) verify(listener) .onMediaDataLoaded( eq(KEY), @@ -778,8 +784,7 @@ class MediaDataProcessorTest : SysuiTestCase() { // GIVEN that the manager has two notifications with resume actions mediaDataProcessor.onNotificationAdded(KEY, mediaNotification) mediaDataProcessor.onNotificationAdded(KEY_2, mediaNotification) - assertThat(backgroundExecutor.runAllReady()).isEqualTo(2) - assertThat(foregroundExecutor.runAllReady()).isEqualTo(2) + testScope.assertRunAllReady(foreground = 2, background = 2) verify(listener) .onMediaDataLoaded( @@ -866,7 +871,7 @@ class MediaDataProcessorTest : SysuiTestCase() { @Test fun testOnNotificationRemoved_withResumption_isRemoteAndRemoteAllowed() { // With the flag enabled to allow remote media to resume - whenever(mediaFlags.isRemoteResumeAllowed()).thenReturn(true) + fakeFeatureFlags.set(MEDIA_REMOTE_RESUME, true) // GIVEN that the manager has a notification with a resume action, but is not local whenever(controller.metadata).thenReturn(metadataBuilder.build()) @@ -897,7 +902,7 @@ class MediaDataProcessorTest : SysuiTestCase() { @Test fun testOnNotificationRemoved_withResumption_isRcnAndRemoteAllowed() { // With the flag enabled to allow remote media to resume - whenever(mediaFlags.isRemoteResumeAllowed()).thenReturn(true) + fakeFeatureFlags.set(MEDIA_REMOTE_RESUME, true) // GIVEN that the manager has a remote cast notification addNotificationAndLoad(remoteCastNotification) @@ -1016,7 +1021,7 @@ class MediaDataProcessorTest : SysuiTestCase() { @Test fun testAddResumptionControls_hasPartialProgress() { - whenever(mediaFlags.isResumeProgressEnabled()).thenReturn(true) + fakeFeatureFlags.set(MEDIA_RESUME_PROGRESS, true) // WHEN resumption controls are added with partial progress val progress = 0.5 @@ -1043,7 +1048,7 @@ class MediaDataProcessorTest : SysuiTestCase() { @Test fun testAddResumptionControls_hasNotPlayedProgress() { - whenever(mediaFlags.isResumeProgressEnabled()).thenReturn(true) + fakeFeatureFlags.set(MEDIA_RESUME_PROGRESS, true) // WHEN resumption controls are added that have not been played val extras = @@ -1068,7 +1073,7 @@ class MediaDataProcessorTest : SysuiTestCase() { @Test fun testAddResumptionControls_hasFullProgress() { - whenever(mediaFlags.isResumeProgressEnabled()).thenReturn(true) + fakeFeatureFlags.set(MEDIA_RESUME_PROGRESS, true) // WHEN resumption controls are added with progress info val extras = @@ -1094,7 +1099,7 @@ class MediaDataProcessorTest : SysuiTestCase() { @Test fun testAddResumptionControls_hasNoExtras() { - whenever(mediaFlags.isResumeProgressEnabled()).thenReturn(true) + fakeFeatureFlags.set(MEDIA_RESUME_PROGRESS, true) // WHEN resumption controls are added that do not have any extras val desc = @@ -1112,7 +1117,7 @@ class MediaDataProcessorTest : SysuiTestCase() { @Test fun testAddResumptionControls_hasEmptyTitle() { - whenever(mediaFlags.isResumeProgressEnabled()).thenReturn(true) + fakeFeatureFlags.set(MEDIA_RESUME_PROGRESS, true) // WHEN resumption controls are added that have empty title val desc = @@ -1131,8 +1136,7 @@ class MediaDataProcessorTest : SysuiTestCase() { ) // Resumption controls are not added. - assertThat(backgroundExecutor.runAllReady()).isEqualTo(1) - assertThat(foregroundExecutor.runAllReady()).isEqualTo(0) + testScope.assertRunAllReady(foreground = 0, background = 1) verify(listener, never()) .onMediaDataLoaded( eq(PACKAGE_NAME), @@ -1146,7 +1150,7 @@ class MediaDataProcessorTest : SysuiTestCase() { @Test fun testAddResumptionControls_hasBlankTitle() { - whenever(mediaFlags.isResumeProgressEnabled()).thenReturn(true) + fakeFeatureFlags.set(MEDIA_RESUME_PROGRESS, true) // WHEN resumption controls are added that have a blank title val desc = @@ -1165,8 +1169,7 @@ class MediaDataProcessorTest : SysuiTestCase() { ) // Resumption controls are not added. - assertThat(backgroundExecutor.runAllReady()).isEqualTo(1) - assertThat(foregroundExecutor.runAllReady()).isEqualTo(0) + testScope.assertRunAllReady(foreground = 0, background = 1) verify(listener, never()) .onMediaDataLoaded( eq(PACKAGE_NAME), @@ -1233,8 +1236,7 @@ class MediaDataProcessorTest : SysuiTestCase() { mediaDataProcessor.onNotificationAdded(KEY, notif) // THEN it still loads - assertThat(backgroundExecutor.runAllReady()).isEqualTo(1) - assertThat(foregroundExecutor.runAllReady()).isEqualTo(1) + testScope.assertRunAllReady(foreground = 1, background = 1) verify(listener) .onMediaDataLoaded( eq(KEY), @@ -1351,7 +1353,7 @@ class MediaDataProcessorTest : SysuiTestCase() { @Test fun testOnSmartspaceMediaDataLoaded_persistentEnabled_headphoneTrigger_isActive() { - whenever(mediaFlags.isPersistentSsCardEnabled()).thenReturn(true) + fakeFeatureFlags.set(MEDIA_RETAIN_RECOMMENDATIONS, true) smartspaceMediaDataProvider.onTargetsAvailable(listOf(mediaSmartspaceTarget)) val instanceId = instanceIdSequence.lastInstanceId @@ -1377,7 +1379,7 @@ class MediaDataProcessorTest : SysuiTestCase() { @Test fun testOnSmartspaceMediaDataLoaded_persistentEnabled_periodicTrigger_notActive() { - whenever(mediaFlags.isPersistentSsCardEnabled()).thenReturn(true) + fakeFeatureFlags.set(MEDIA_RETAIN_RECOMMENDATIONS, true) val extras = Bundle().apply { putString("package_name", PACKAGE_NAME) @@ -1411,7 +1413,7 @@ class MediaDataProcessorTest : SysuiTestCase() { @Test fun testOnSmartspaceMediaDataLoaded_persistentEnabled_noTargets_inactive() { - whenever(mediaFlags.isPersistentSsCardEnabled()).thenReturn(true) + fakeFeatureFlags.set(MEDIA_RETAIN_RECOMMENDATIONS, true) smartspaceMediaDataProvider.onTargetsAvailable(listOf(mediaSmartspaceTarget)) val instanceId = instanceIdSequence.lastInstanceId @@ -1443,7 +1445,7 @@ class MediaDataProcessorTest : SysuiTestCase() { @Test fun testSetRecommendationInactive_notifiesListeners() { - whenever(mediaFlags.isPersistentSsCardEnabled()).thenReturn(true) + fakeFeatureFlags.set(MEDIA_RETAIN_RECOMMENDATIONS, true) smartspaceMediaDataProvider.onTargetsAvailable(listOf(mediaSmartspaceTarget)) val instanceId = instanceIdSequence.lastInstanceId @@ -1476,6 +1478,7 @@ class MediaDataProcessorTest : SysuiTestCase() { fun testOnSmartspaceMediaDataLoaded_settingDisabled_doesNothing() { // WHEN media recommendation setting is off settings.putInt(Settings.Secure.MEDIA_CONTROLS_RECOMMENDATION, 0) + testScope.runCurrent() smartspaceMediaDataProvider.onTargetsAvailable(listOf(mediaSmartspaceTarget)) @@ -1493,6 +1496,7 @@ class MediaDataProcessorTest : SysuiTestCase() { // WHEN the media recommendation setting is turned off settings.putInt(Settings.Secure.MEDIA_CONTROLS_RECOMMENDATION, 0) + testScope.runCurrent() // THEN listeners are notified uiExecutor.advanceClockToLast() @@ -1513,8 +1517,7 @@ class MediaDataProcessorTest : SysuiTestCase() { fun testOnMediaDataTimedOut_updatesLastActiveTime() { // GIVEN that the manager has a notification mediaDataProcessor.onNotificationAdded(KEY, mediaNotification) - assertThat(backgroundExecutor.runAllReady()).isEqualTo(1) - assertThat(foregroundExecutor.runAllReady()).isEqualTo(1) + testScope.assertRunAllReady(foreground = 1, background = 1) // WHEN the notification times out clock.advanceTime(100) @@ -1622,8 +1625,7 @@ class MediaDataProcessorTest : SysuiTestCase() { // WHEN the notification is loaded mediaDataProcessor.onNotificationAdded(KEY, notif) - assertThat(backgroundExecutor.runAllReady()).isEqualTo(1) - assertThat(foregroundExecutor.runAllReady()).isEqualTo(1) + testScope.assertRunAllReady(foreground = 1, background = 1) // THEN only the first MAX_COMPACT_ACTIONS are actually set verify(listener) @@ -1658,8 +1660,7 @@ class MediaDataProcessorTest : SysuiTestCase() { // WHEN the notification is loaded mediaDataProcessor.onNotificationAdded(KEY, notif) - assertThat(backgroundExecutor.runAllReady()).isEqualTo(1) - assertThat(foregroundExecutor.runAllReady()).isEqualTo(1) + testScope.assertRunAllReady(foreground = 1, background = 1) // THEN only the first MAX_NOTIFICATION_ACTIONS are actually included verify(listener) @@ -1678,7 +1679,7 @@ class MediaDataProcessorTest : SysuiTestCase() { @Test fun testPlaybackActions_noState_usesNotification() { val desc = "Notification Action" - whenever(mediaFlags.areMediaSessionActionsEnabled(any(), any())).thenReturn(true) + fakeFeatureFlags.set(MEDIA_SESSION_ACTIONS, true) whenever(controller.playbackState).thenReturn(null) val notifWithAction = @@ -1693,8 +1694,7 @@ class MediaDataProcessorTest : SysuiTestCase() { } mediaDataProcessor.onNotificationAdded(KEY, notifWithAction) - assertThat(backgroundExecutor.runAllReady()).isEqualTo(1) - assertThat(foregroundExecutor.runAllReady()).isEqualTo(1) + testScope.assertRunAllReady(foreground = 1, background = 1) verify(listener) .onMediaDataLoaded( eq(KEY), @@ -1713,7 +1713,7 @@ class MediaDataProcessorTest : SysuiTestCase() { @Test fun testPlaybackActions_hasPrevNext() { val customDesc = arrayOf("custom 1", "custom 2", "custom 3", "custom 4") - whenever(mediaFlags.areMediaSessionActionsEnabled(any(), any())).thenReturn(true) + fakeFeatureFlags.set(MEDIA_SESSION_ACTIONS, true) val stateActions = PlaybackState.ACTION_PLAY or PlaybackState.ACTION_SKIP_TO_PREVIOUS or @@ -1757,7 +1757,7 @@ class MediaDataProcessorTest : SysuiTestCase() { @Test fun testPlaybackActions_noPrevNext_usesCustom() { val customDesc = arrayOf("custom 1", "custom 2", "custom 3", "custom 4", "custom 5") - whenever(mediaFlags.areMediaSessionActionsEnabled(any(), any())).thenReturn(true) + fakeFeatureFlags.set(MEDIA_SESSION_ACTIONS, true) val stateActions = PlaybackState.ACTION_PLAY val stateBuilder = PlaybackState.Builder().setActions(stateActions) customDesc.forEach { @@ -1789,7 +1789,7 @@ class MediaDataProcessorTest : SysuiTestCase() { @Test fun testPlaybackActions_connecting() { - whenever(mediaFlags.areMediaSessionActionsEnabled(any(), any())).thenReturn(true) + fakeFeatureFlags.set(MEDIA_SESSION_ACTIONS, true) val stateActions = PlaybackState.ACTION_PLAY val stateBuilder = PlaybackState.Builder() @@ -1809,87 +1809,85 @@ class MediaDataProcessorTest : SysuiTestCase() { @Test @EnableFlags(Flags.FLAG_MEDIA_CONTROLS_DRAWABLES_REUSE) - fun postWithPlaybackActions_drawablesReused() = - kosmos.testScope.runTest { - whenever(mediaFlags.areMediaSessionActionsEnabled(any(), any())).thenReturn(true) - whenever(notificationLockscreenUserManager.isCurrentProfile(USER_ID)).thenReturn(true) - whenever(notificationLockscreenUserManager.isProfileAvailable(USER_ID)).thenReturn(true) - val stateActions = - PlaybackState.ACTION_PAUSE or - PlaybackState.ACTION_SKIP_TO_PREVIOUS or - PlaybackState.ACTION_SKIP_TO_NEXT - val stateBuilder = - PlaybackState.Builder() - .setState(PlaybackState.STATE_PLAYING, 0, 10f) - .setActions(stateActions) - whenever(controller.playbackState).thenReturn(stateBuilder.build()) - val userEntries by collectLastValue(mediaFilterRepository.selectedUserEntries) - - mediaDataProcessor.addInternalListener(mediaDataFilter) - mediaDataFilter.mediaDataProcessor = mediaDataProcessor - addNotificationAndLoad() - - assertThat(userEntries).hasSize(1) - val firstSemanticActions = userEntries?.values?.toList()?.get(0)?.semanticActions!! - - addNotificationAndLoad() - - assertThat(userEntries).hasSize(1) - val secondSemanticActions = userEntries?.values?.toList()?.get(0)?.semanticActions!! - assertThat(secondSemanticActions.playOrPause?.icon) - .isEqualTo(firstSemanticActions.playOrPause?.icon) - assertThat(secondSemanticActions.playOrPause?.background) - .isEqualTo(firstSemanticActions.playOrPause?.background) - assertThat(secondSemanticActions.nextOrCustom?.icon) - .isEqualTo(firstSemanticActions.nextOrCustom?.icon) - assertThat(secondSemanticActions.prevOrCustom?.icon) - .isEqualTo(firstSemanticActions.prevOrCustom?.icon) - } + fun postWithPlaybackActions_drawablesReused() { + fakeFeatureFlags.set(MEDIA_SESSION_ACTIONS, true) + whenever(notificationLockscreenUserManager.isCurrentProfile(USER_ID)).thenReturn(true) + whenever(notificationLockscreenUserManager.isProfileAvailable(USER_ID)).thenReturn(true) + val stateActions = + PlaybackState.ACTION_PAUSE or + PlaybackState.ACTION_SKIP_TO_PREVIOUS or + PlaybackState.ACTION_SKIP_TO_NEXT + val stateBuilder = + PlaybackState.Builder() + .setState(PlaybackState.STATE_PLAYING, 0, 10f) + .setActions(stateActions) + whenever(controller.playbackState).thenReturn(stateBuilder.build()) + val userEntries by testScope.collectLastValue(mediaFilterRepository.selectedUserEntries) + + mediaDataProcessor.addInternalListener(mediaDataFilter) + mediaDataFilter.mediaDataProcessor = mediaDataProcessor + addNotificationAndLoad() + + assertThat(userEntries).hasSize(1) + val firstSemanticActions = userEntries?.values?.toList()?.get(0)?.semanticActions!! + + addNotificationAndLoad() + + assertThat(userEntries).hasSize(1) + val secondSemanticActions = userEntries?.values?.toList()?.get(0)?.semanticActions!! + assertThat(secondSemanticActions.playOrPause?.icon) + .isEqualTo(firstSemanticActions.playOrPause?.icon) + assertThat(secondSemanticActions.playOrPause?.background) + .isEqualTo(firstSemanticActions.playOrPause?.background) + assertThat(secondSemanticActions.nextOrCustom?.icon) + .isEqualTo(firstSemanticActions.nextOrCustom?.icon) + assertThat(secondSemanticActions.prevOrCustom?.icon) + .isEqualTo(firstSemanticActions.prevOrCustom?.icon) + } @Test @DisableFlags(Flags.FLAG_MEDIA_CONTROLS_DRAWABLES_REUSE) - fun postWithPlaybackActions_drawablesNotReused() = - kosmos.testScope.runTest { - whenever(mediaFlags.areMediaSessionActionsEnabled(any(), any())).thenReturn(true) - whenever(notificationLockscreenUserManager.isCurrentProfile(USER_ID)).thenReturn(true) - whenever(notificationLockscreenUserManager.isProfileAvailable(USER_ID)).thenReturn(true) - val stateActions = - PlaybackState.ACTION_PAUSE or - PlaybackState.ACTION_SKIP_TO_PREVIOUS or - PlaybackState.ACTION_SKIP_TO_NEXT - val stateBuilder = - PlaybackState.Builder() - .setState(PlaybackState.STATE_PLAYING, 0, 10f) - .setActions(stateActions) - whenever(controller.playbackState).thenReturn(stateBuilder.build()) - val userEntries by collectLastValue(mediaFilterRepository.selectedUserEntries) - - mediaDataProcessor.addInternalListener(mediaDataFilter) - mediaDataFilter.mediaDataProcessor = mediaDataProcessor - addNotificationAndLoad() - - assertThat(userEntries).hasSize(1) - val firstSemanticActions = userEntries?.values?.toList()?.get(0)?.semanticActions!! - - addNotificationAndLoad() - - assertThat(userEntries).hasSize(1) - val secondSemanticActions = userEntries?.values?.toList()?.get(0)?.semanticActions!! - - assertThat(secondSemanticActions.playOrPause?.icon) - .isNotEqualTo(firstSemanticActions.playOrPause?.icon) - assertThat(secondSemanticActions.playOrPause?.background) - .isNotEqualTo(firstSemanticActions.playOrPause?.background) - assertThat(secondSemanticActions.nextOrCustom?.icon) - .isNotEqualTo(firstSemanticActions.nextOrCustom?.icon) - assertThat(secondSemanticActions.prevOrCustom?.icon) - .isNotEqualTo(firstSemanticActions.prevOrCustom?.icon) - } + fun postWithPlaybackActions_drawablesNotReused() { + fakeFeatureFlags.set(MEDIA_SESSION_ACTIONS, true) + whenever(notificationLockscreenUserManager.isCurrentProfile(USER_ID)).thenReturn(true) + whenever(notificationLockscreenUserManager.isProfileAvailable(USER_ID)).thenReturn(true) + val stateActions = + PlaybackState.ACTION_PAUSE or + PlaybackState.ACTION_SKIP_TO_PREVIOUS or + PlaybackState.ACTION_SKIP_TO_NEXT + val stateBuilder = + PlaybackState.Builder() + .setState(PlaybackState.STATE_PLAYING, 0, 10f) + .setActions(stateActions) + whenever(controller.playbackState).thenReturn(stateBuilder.build()) + val userEntries by testScope.collectLastValue(mediaFilterRepository.selectedUserEntries) + + mediaDataProcessor.addInternalListener(mediaDataFilter) + mediaDataFilter.mediaDataProcessor = mediaDataProcessor + addNotificationAndLoad() + + assertThat(userEntries).hasSize(1) + val firstSemanticActions = userEntries?.values?.toList()?.get(0)?.semanticActions!! + + addNotificationAndLoad() + + assertThat(userEntries).hasSize(1) + val secondSemanticActions = userEntries?.values?.toList()?.get(0)?.semanticActions!! + + assertThat(secondSemanticActions.playOrPause?.icon) + .isNotEqualTo(firstSemanticActions.playOrPause?.icon) + assertThat(secondSemanticActions.playOrPause?.background) + .isNotEqualTo(firstSemanticActions.playOrPause?.background) + assertThat(secondSemanticActions.nextOrCustom?.icon) + .isNotEqualTo(firstSemanticActions.nextOrCustom?.icon) + assertThat(secondSemanticActions.prevOrCustom?.icon) + .isNotEqualTo(firstSemanticActions.prevOrCustom?.icon) + } @Test fun testPlaybackActions_reservedSpace() { val customDesc = arrayOf("custom 1", "custom 2", "custom 3", "custom 4") - whenever(mediaFlags.areMediaSessionActionsEnabled(any(), any())).thenReturn(true) + fakeFeatureFlags.set(MEDIA_SESSION_ACTIONS, true) val stateActions = PlaybackState.ACTION_PLAY val stateBuilder = PlaybackState.Builder().setActions(stateActions) customDesc.forEach { @@ -1927,7 +1925,7 @@ class MediaDataProcessorTest : SysuiTestCase() { @Test fun testPlaybackActions_playPause_hasButton() { - whenever(mediaFlags.areMediaSessionActionsEnabled(any(), any())).thenReturn(true) + fakeFeatureFlags.set(MEDIA_SESSION_ACTIONS, true) val stateActions = PlaybackState.ACTION_PLAY_PAUSE val stateBuilder = PlaybackState.Builder().setActions(stateActions) whenever(controller.playbackState).thenReturn(stateBuilder.build()) @@ -1964,8 +1962,7 @@ class MediaDataProcessorTest : SysuiTestCase() { // update to remote cast mediaDataProcessor.onNotificationAdded(KEY, remoteCastNotification) - assertThat(backgroundExecutor.runAllReady()).isEqualTo(1) - assertThat(foregroundExecutor.runAllReady()).isEqualTo(1) + testScope.assertRunAllReady(foreground = 1, background = 1) verify(logger) .logPlaybackLocationChange( anyInt(), @@ -2027,7 +2024,7 @@ class MediaDataProcessorTest : SysuiTestCase() { @Test fun testPlaybackState_PauseWhenFlagTrue_keyExists_callsListener() { - whenever(mediaFlags.areMediaSessionActionsEnabled(any(), any())).thenReturn(true) + fakeFeatureFlags.set(MEDIA_SESSION_ACTIONS, true) val state = PlaybackState.Builder().setState(PlaybackState.STATE_PAUSED, 0L, 1f).build() whenever(controller.playbackState).thenReturn(state) @@ -2071,6 +2068,7 @@ class MediaDataProcessorTest : SysuiTestCase() { pendingIntent, PACKAGE_NAME ) + testScope.runCurrent() backgroundExecutor.runAllReady() foregroundExecutor.runAllReady() @@ -2149,7 +2147,7 @@ class MediaDataProcessorTest : SysuiTestCase() { @Test fun testRetain_notifPlayer_notifRemoved_setToResume() { - whenever(mediaFlags.isRetainingPlayersEnabled()).thenReturn(true) + fakeFeatureFlags.set(MEDIA_RETAIN_SESSIONS, true) // When a media control based on notification is added, times out, and then removed addNotificationAndLoad() @@ -2179,7 +2177,7 @@ class MediaDataProcessorTest : SysuiTestCase() { @Test fun testRetain_notifPlayer_sessionDestroyed_doesNotChange() { - whenever(mediaFlags.isRetainingPlayersEnabled()).thenReturn(true) + fakeFeatureFlags.set(MEDIA_RETAIN_SESSIONS, true) // When a media control based on notification is added and times out addNotificationAndLoad() @@ -2197,7 +2195,7 @@ class MediaDataProcessorTest : SysuiTestCase() { @Test fun testRetain_notifPlayer_removeWhileActive_fullyRemoved() { - whenever(mediaFlags.isRetainingPlayersEnabled()).thenReturn(true) + fakeFeatureFlags.set(MEDIA_RETAIN_SESSIONS, true) // When a media control based on notification is added and then removed, without timing out addNotificationAndLoad() @@ -2214,7 +2212,7 @@ class MediaDataProcessorTest : SysuiTestCase() { @Test fun testRetain_canResume_removeWhileActive_setToResume() { - whenever(mediaFlags.isRetainingPlayersEnabled()).thenReturn(true) + fakeFeatureFlags.set(MEDIA_RETAIN_SESSIONS, true) // When a media control that supports resumption is added addNotificationAndLoad() @@ -2246,8 +2244,8 @@ class MediaDataProcessorTest : SysuiTestCase() { @Test fun testRetain_sessionPlayer_notifRemoved_doesNotChange() { - whenever(mediaFlags.isRetainingPlayersEnabled()).thenReturn(true) - whenever(mediaFlags.areMediaSessionActionsEnabled(any(), any())).thenReturn(true) + fakeFeatureFlags.set(MEDIA_RETAIN_SESSIONS, true) + fakeFeatureFlags.set(MEDIA_SESSION_ACTIONS, true) addPlaybackStateAction() // When a media control with PlaybackState actions is added, times out, @@ -2266,8 +2264,8 @@ class MediaDataProcessorTest : SysuiTestCase() { @Test fun testRetain_sessionPlayer_sessionDestroyed_setToResume() { - whenever(mediaFlags.isRetainingPlayersEnabled()).thenReturn(true) - whenever(mediaFlags.areMediaSessionActionsEnabled(any(), any())).thenReturn(true) + fakeFeatureFlags.set(MEDIA_RETAIN_SESSIONS, true) + fakeFeatureFlags.set(MEDIA_SESSION_ACTIONS, true) addPlaybackStateAction() // When a media control with PlaybackState actions is added, times out, @@ -2300,8 +2298,8 @@ class MediaDataProcessorTest : SysuiTestCase() { @Test fun testRetain_sessionPlayer_destroyedWhileActive_noResume_fullyRemoved() { - whenever(mediaFlags.isRetainingPlayersEnabled()).thenReturn(true) - whenever(mediaFlags.areMediaSessionActionsEnabled(any(), any())).thenReturn(true) + fakeFeatureFlags.set(MEDIA_RETAIN_SESSIONS, true) + fakeFeatureFlags.set(MEDIA_SESSION_ACTIONS, true) addPlaybackStateAction() // When a media control using session actions is added, and then the session is destroyed @@ -2320,8 +2318,8 @@ class MediaDataProcessorTest : SysuiTestCase() { @Test fun testRetain_sessionPlayer_canResume_destroyedWhileActive_setToResume() { - whenever(mediaFlags.isRetainingPlayersEnabled()).thenReturn(true) - whenever(mediaFlags.areMediaSessionActionsEnabled(any(), any())).thenReturn(true) + fakeFeatureFlags.set(MEDIA_RETAIN_SESSIONS, true) + fakeFeatureFlags.set(MEDIA_SESSION_ACTIONS, true) addPlaybackStateAction() // When a media control using session actions and that does allow resumption is added, @@ -2354,7 +2352,7 @@ class MediaDataProcessorTest : SysuiTestCase() { @Test fun testSessionPlayer_sessionDestroyed_noResume_fullyRemoved() { - whenever(mediaFlags.areMediaSessionActionsEnabled(any(), any())).thenReturn(true) + fakeFeatureFlags.set(MEDIA_SESSION_ACTIONS, true) addPlaybackStateAction() // When a media control with PlaybackState actions is added, times out, @@ -2381,7 +2379,7 @@ class MediaDataProcessorTest : SysuiTestCase() { @Test fun testSessionPlayer_destroyedWhileActive_noResume_fullyRemoved() { - whenever(mediaFlags.areMediaSessionActionsEnabled(any(), any())).thenReturn(true) + fakeFeatureFlags.set(MEDIA_SESSION_ACTIONS, true) addPlaybackStateAction() // When a media control using session actions is added, and then the session is destroyed @@ -2400,7 +2398,7 @@ class MediaDataProcessorTest : SysuiTestCase() { @Test fun testSessionPlayer_canResume_destroyedWhileActive_setToResume() { - whenever(mediaFlags.areMediaSessionActionsEnabled(any(), any())).thenReturn(true) + fakeFeatureFlags.set(MEDIA_SESSION_ACTIONS, true) addPlaybackStateAction() // When a media control using session actions and that does allow resumption is added, @@ -2433,8 +2431,8 @@ class MediaDataProcessorTest : SysuiTestCase() { @Test fun testSessionDestroyed_noNotificationKey_stillRemoved() { - whenever(mediaFlags.isRetainingPlayersEnabled()).thenReturn(true) - whenever(mediaFlags.areMediaSessionActionsEnabled(any(), any())).thenReturn(true) + fakeFeatureFlags.set(MEDIA_RETAIN_SESSIONS, true) + fakeFeatureFlags.set(MEDIA_SESSION_ACTIONS, true) // When a notiifcation is added and then removed before it is fully processed mediaDataProcessor.onNotificationAdded(KEY, mediaNotification) @@ -2505,6 +2503,23 @@ class MediaDataProcessorTest : SysuiTestCase() { assertThat(mediaDataCaptor.value.artwork).isNull() } + private fun TestScope.assertRunAllReady(foreground: Int = 0, background: Int = 0) { + runCurrent() + if (Flags.mediaLoadMetadataViaMediaDataLoader()) { + // It doesn't make much sense to count tasks when we use coroutines in loader + // so this check is skipped in that scenario. + backgroundExecutor.runAllReady() + foregroundExecutor.runAllReady() + } else { + if (background > 0) { + assertThat(backgroundExecutor.runAllReady()).isEqualTo(background) + } + if (foreground > 0) { + assertThat(foregroundExecutor.runAllReady()).isEqualTo(foreground) + } + } + } + /** Helper function to add a basic media notification and capture the resulting MediaData */ private fun addNotificationAndLoad() { addNotificationAndLoad(mediaNotification) @@ -2513,8 +2528,7 @@ class MediaDataProcessorTest : SysuiTestCase() { /** Helper function to add the given notification and capture the resulting MediaData */ private fun addNotificationAndLoad(sbn: StatusBarNotification) { mediaDataProcessor.onNotificationAdded(KEY, sbn) - assertThat(backgroundExecutor.runAllReady()).isEqualTo(1) - assertThat(foregroundExecutor.runAllReady()).isEqualTo(1) + testScope.assertRunAllReady(foreground = 1, background = 1) verify(listener) .onMediaDataLoaded( eq(KEY), @@ -2548,8 +2562,7 @@ class MediaDataProcessorTest : SysuiTestCase() { pendingIntent, packageName ) - assertThat(backgroundExecutor.runAllReady()).isEqualTo(1) - assertThat(foregroundExecutor.runAllReady()).isEqualTo(1) + testScope.assertRunAllReady(foreground = 1, background = 1) verify(listener) .onMediaDataLoaded( 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 6a66c4087615..0c8d88065a73 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 @@ -54,6 +54,7 @@ import com.android.systemui.media.muteawait.MediaMuteAwaitConnectionManager import com.android.systemui.media.muteawait.MediaMuteAwaitConnectionManagerFactory import com.android.systemui.res.R import com.android.systemui.statusbar.policy.ConfigurationController +import com.android.systemui.testKosmos import com.android.systemui.util.concurrency.FakeExecutor import com.android.systemui.util.time.FakeSystemClock import com.google.common.truth.Truth.assertThat @@ -125,6 +126,8 @@ public class MediaDeviceManagerTest : SysuiTestCase() { private lateinit var mediaData: MediaData @JvmField @Rule val mockito = MockitoJUnit.rule() + private val kosmos = testKosmos() + @Before fun setUp() { fakeFgExecutor = FakeExecutor(FakeSystemClock()) @@ -141,6 +144,7 @@ public class MediaDeviceManagerTest : SysuiTestCase() { { localBluetoothManager }, fakeFgExecutor, fakeBgExecutor, + kosmos.mediaDeviceLogger, ) manager.addListener(listener) 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 850916be35bf..46c66e03fd9f 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 @@ -27,19 +27,24 @@ import android.util.MathUtils.abs import android.view.View 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.internal.logging.InstanceId import com.android.keyguard.KeyguardUpdateMonitor import com.android.keyguard.KeyguardUpdateMonitorCallback import com.android.systemui.SysuiTestCase import com.android.systemui.dagger.qualifiers.Main +import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor import com.android.systemui.dump.DumpManager import com.android.systemui.flags.DisableSceneContainer import com.android.systemui.flags.EnableSceneContainer import com.android.systemui.flags.Flags import com.android.systemui.flags.fakeFeatureFlagsClassic +import com.android.systemui.keyguard.data.repository.fakeDeviceEntryFingerprintAuthRepository 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.keyguard.shared.model.SuccessFingerprintAuthenticationStatus import com.android.systemui.kosmos.applicationCoroutineScope import com.android.systemui.kosmos.testDispatcher import com.android.systemui.kosmos.testScope @@ -71,14 +76,18 @@ import com.android.systemui.util.settings.FakeSettings import com.android.systemui.util.settings.GlobalSettings import com.android.systemui.util.settings.SecureSettings import com.android.systemui.util.time.FakeSystemClock +import com.google.common.truth.Truth.assertThat import java.util.Locale import javax.inject.Provider import junit.framework.Assert.assertEquals import junit.framework.Assert.assertFalse import junit.framework.Assert.assertTrue import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.test.TestDispatcher +import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.UnconfinedTestDispatcher +import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest import org.junit.After import org.junit.Before @@ -106,6 +115,7 @@ private val SMARTSPACE_KEY = "smartspace" private const val PAUSED_LOCAL = "paused local" private const val PLAYING_LOCAL = "playing local" +@ExperimentalCoroutinesApi @SmallTest @TestableLooper.RunWithLooper(setAsMainLooper = true) @RunWith(AndroidJUnit4::class) @@ -183,7 +193,7 @@ class MediaCarouselControllerTest : SysuiTestCase() { secureSettings = secureSettings, mediaCarouselViewModel = kosmos.mediaCarouselViewModel, mediaViewControllerFactory = mediaViewControllerFactory, - sceneInteractor = kosmos.sceneInteractor, + deviceEntryInteractor = kosmos.deviceEntryInteractor, ) verify(configurationController).addCallback(capture(configListener)) verify(visualStabilityProvider) @@ -868,7 +878,6 @@ class MediaCarouselControllerTest : SysuiTestCase() { } @DisableSceneContainer - @ExperimentalCoroutinesApi @Test fun testKeyguardGone_showMediaCarousel() = kosmos.testScope.runTest { @@ -892,7 +901,6 @@ class MediaCarouselControllerTest : SysuiTestCase() { } @EnableSceneContainer - @ExperimentalCoroutinesApi @Test fun testKeyguardGone_showMediaCarousel_scene_container() = kosmos.testScope.runTest { @@ -910,7 +918,6 @@ class MediaCarouselControllerTest : SysuiTestCase() { job.cancel() } - @ExperimentalCoroutinesApi @Test fun keyguardShowing_notAllowedOnLockscreen_updateVisibility() { kosmos.testScope.runTest { @@ -940,7 +947,6 @@ class MediaCarouselControllerTest : SysuiTestCase() { } } - @ExperimentalCoroutinesApi @Test fun keyguardShowing_allowedOnLockscreen_updateVisibility() { kosmos.testScope.runTest { @@ -970,6 +976,74 @@ class MediaCarouselControllerTest : SysuiTestCase() { } } + @EnableSceneContainer + @Test + fun deviceEntered_mediaAllowed_notLockedAndHidden() { + kosmos.testScope.runTest { + val settingsJob = + mediaCarouselController.listenForLockscreenSettingChanges( + kosmos.applicationCoroutineScope + ) + secureSettings.putBool(Settings.Secure.MEDIA_CONTROLS_LOCK_SCREEN, true) + setDeviceEntered(true) + + assertEquals(false, mediaCarouselController.isLockedAndHidden()) + + settingsJob.cancel() + } + } + + @EnableSceneContainer + @Test + fun deviceEntered_mediaNotAllowed_notLockedAndHidden() { + kosmos.testScope.runTest { + val settingsJob = + mediaCarouselController.listenForLockscreenSettingChanges( + kosmos.applicationCoroutineScope + ) + secureSettings.putBool(Settings.Secure.MEDIA_CONTROLS_LOCK_SCREEN, false) + setDeviceEntered(true) + + assertEquals(false, mediaCarouselController.isLockedAndHidden()) + + settingsJob.cancel() + } + } + + @EnableSceneContainer + @Test + fun deviceNotEntered_mediaAllowed_notLockedAndHidden() { + kosmos.testScope.runTest { + val settingsJob = + mediaCarouselController.listenForLockscreenSettingChanges( + kosmos.applicationCoroutineScope + ) + secureSettings.putBool(Settings.Secure.MEDIA_CONTROLS_LOCK_SCREEN, true) + setDeviceEntered(false) + + assertEquals(false, mediaCarouselController.isLockedAndHidden()) + + settingsJob.cancel() + } + } + + @EnableSceneContainer + @Test + fun deviceNotEntered_mediaNotAllowed_lockedAndHidden() { + kosmos.testScope.runTest { + val settingsJob = + mediaCarouselController.listenForLockscreenSettingChanges( + kosmos.applicationCoroutineScope + ) + secureSettings.putBool(Settings.Secure.MEDIA_CONTROLS_LOCK_SCREEN, false) + setDeviceEntered(false) + + assertEquals(true, mediaCarouselController.isLockedAndHidden()) + + settingsJob.cancel() + } + } + @Test fun testInvisibleToUserAndExpanded_playersNotListening() { // Add players to carousel. @@ -1129,4 +1203,30 @@ class MediaCarouselControllerTest : SysuiTestCase() { mediaCarouselController.mediaCarouselScrollHandler.visibleMediaIndex ) } + + private fun TestScope.setDeviceEntered(isEntered: Boolean) { + if (isEntered) { + // Unlock the device, marking the device as 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) { + kosmos.sceneInteractor.changeScene(key, "test") + kosmos.sceneInteractor.setTransitionState( + MutableStateFlow<ObservableTransitionState>(ObservableTransitionState.Idle(key)) + ) + runCurrent() + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ModesTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ModesTileTest.kt index 19735e29834e..8435b1cb71dc 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ModesTileTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ModesTileTest.kt @@ -25,9 +25,10 @@ import android.testing.TestableLooper.RunWithLooper import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.internal.logging.MetricsLogger -import com.android.settingslib.notification.data.repository.FakeZenModeRepository import com.android.systemui.SysuiTestCase import com.android.systemui.classifier.FalsingManagerFake +import com.android.systemui.kosmos.testDispatcher +import com.android.systemui.kosmos.testScope import com.android.systemui.plugins.ActivityStarter import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.qs.QSHost @@ -41,16 +42,15 @@ import com.android.systemui.qs.tiles.viewmodel.QSTileConfigProvider import com.android.systemui.qs.tiles.viewmodel.QSTileConfigTestBuilder import com.android.systemui.qs.tiles.viewmodel.QSTileUIConfig import com.android.systemui.res.R -import com.android.systemui.shared.notifications.data.repository.NotificationSettingsRepository -import com.android.systemui.statusbar.policy.domain.interactor.ZenModeInteractor +import com.android.systemui.statusbar.policy.data.repository.zenModeRepository +import com.android.systemui.statusbar.policy.domain.interactor.zenModeInteractor import com.android.systemui.statusbar.policy.ui.dialog.ModesDialogDelegate +import com.android.systemui.testKosmos import com.android.systemui.util.mockito.any import com.android.systemui.util.settings.FakeSettings import com.android.systemui.util.settings.SecureSettings import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.test.TestScope -import kotlinx.coroutines.test.UnconfinedTestDispatcher import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest import org.junit.After @@ -59,7 +59,6 @@ import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mock import org.mockito.MockitoAnnotations -import org.mockito.kotlin.mock import org.mockito.kotlin.whenever @OptIn(ExperimentalCoroutinesApi::class) @@ -68,6 +67,9 @@ import org.mockito.kotlin.whenever @RunWith(AndroidJUnit4::class) @RunWithLooper(setAsMainLooper = true) class ModesTileTest : SysuiTestCase() { + private val kosmos = testKosmos() + private val testScope = kosmos.testScope + private val testDispatcher = kosmos.testDispatcher @Mock private lateinit var qsHost: QSHost @@ -85,17 +87,10 @@ class ModesTileTest : SysuiTestCase() { @Mock private lateinit var dialogDelegate: ModesDialogDelegate - private val testDispatcher = UnconfinedTestDispatcher() - private val testScope = TestScope(testDispatcher) - private val inputHandler = FakeQSTileIntentUserInputHandler() - private val zenModeRepository = FakeZenModeRepository() + private val zenModeRepository = kosmos.zenModeRepository private val tileDataInteractor = - ModesTileDataInteractor( - context, - ZenModeInteractor(context, zenModeRepository, mock<NotificationSettingsRepository>()), - testDispatcher - ) + ModesTileDataInteractor(context, kosmos.zenModeInteractor, testDispatcher) private val mapper = ModesTileMapper( context.orCreateTestableResources diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt index 5a5cdcd99054..3ba1447eb406 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt @@ -61,7 +61,6 @@ import com.android.systemui.log.logcatLogBuffer import com.android.systemui.media.controls.controller.keyguardMediaController import com.android.systemui.res.R import com.android.systemui.scene.shared.model.sceneDataSourceDelegator -import com.android.systemui.shade.data.repository.fakeShadeRepository import com.android.systemui.shade.domain.interactor.shadeInteractor import com.android.systemui.statusbar.lockscreen.lockscreenSmartspaceController import com.android.systemui.statusbar.notification.stack.notificationStackScrollLayoutController @@ -417,13 +416,17 @@ class GlanceableHubContainerControllerTest : SysuiTestCase() { // Communal is open. goToScene(CommunalScenes.Communal) - // Shade shows up. - shadeTestUtil.setQsExpansion(0.5f) - testableLooper.processAllMessages() + // Touch starts and ends. assertThat(underTest.onTouchEvent(DOWN_EVENT)).isTrue() assertThat(underTest.onTouchEvent(CANCEL_EVENT)).isTrue() + + // Up event is no longer processed assertThat(underTest.onTouchEvent(UP_EVENT)).isFalse() + + // Move event can still be processed assertThat(underTest.onTouchEvent(MOVE_EVENT)).isTrue() + assertThat(underTest.onTouchEvent(MOVE_EVENT)).isTrue() + assertThat(underTest.onTouchEvent(UP_EVENT)).isTrue() } } @@ -702,7 +705,7 @@ class GlanceableHubContainerControllerTest : SysuiTestCase() { verify(containerView).onTouchEvent(DOWN_EVENT) // User is interacting with shade on lockscreen. - fakeShadeRepository.setLegacyLockscreenShadeTracking(true) + shadeTestUtil.setLockscreenShadeTracking(true) testableLooper.processAllMessages() // A move event is ignored while the user is already interacting. @@ -716,6 +719,30 @@ class GlanceableHubContainerControllerTest : SysuiTestCase() { } @Test + fun onTouchEvent_shadeExpanding_touchesNotDispatched() = + with(kosmos) { + testScope.runTest { + // On lockscreen. + goToScene(CommunalScenes.Blank) + whenever( + notificationStackScrollLayoutController.isBelowLastNotification( + any(), + any() + ) + ) + .thenReturn(true) + + // Shade is open slightly. + shadeTestUtil.setShadeExpansion(0.01f) + testableLooper.processAllMessages() + + // Touches are not consumed. + assertThat(underTest.onTouchEvent(DOWN_EVENT)).isFalse() + verify(containerView, never()).onTouchEvent(DOWN_EVENT) + } + } + + @Test fun onTouchEvent_bouncerInteracting_movesNotDispatched() = with(kosmos) { testScope.runTest { diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java index 523d15c7548f..9481e5a52098 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java @@ -155,7 +155,6 @@ import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinatorLogger; import com.android.systemui.statusbar.notification.data.repository.NotificationsKeyguardViewStateRepository; import com.android.systemui.statusbar.notification.domain.interactor.ActiveNotificationsInteractor; -import com.android.systemui.statusbar.notification.domain.interactor.HeadsUpNotificationInteractor; import com.android.systemui.statusbar.notification.domain.interactor.NotificationsKeyguardInteractor; import com.android.systemui.statusbar.notification.row.NotificationGutsManager; import com.android.systemui.statusbar.notification.stack.AmbientState; @@ -163,7 +162,6 @@ import com.android.systemui.statusbar.notification.stack.NotificationListContain import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout; import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController; import com.android.systemui.statusbar.notification.stack.NotificationStackSizeCalculator; -import com.android.systemui.statusbar.notification.stack.data.repository.FakeHeadsUpNotificationRepository; import com.android.systemui.statusbar.notification.stack.domain.interactor.SharedNotificationContainerInteractor; import com.android.systemui.statusbar.phone.CentralSurfaces; import com.android.systemui.statusbar.phone.ConfigurationControllerImpl; @@ -365,13 +363,6 @@ public class NotificationPanelViewControllerBaseTest extends SysuiTestCase { protected TestScope mTestScope = mKosmos.getTestScope(); protected ShadeInteractor mShadeInteractor; protected PowerInteractor mPowerInteractor; - protected FakeHeadsUpNotificationRepository mFakeHeadsUpNotificationRepository = - new FakeHeadsUpNotificationRepository(); - protected NotificationsKeyguardViewStateRepository mNotificationsKeyguardViewStateRepository = - new NotificationsKeyguardViewStateRepository(); - protected NotificationsKeyguardInteractor mNotificationsKeyguardInteractor = - new NotificationsKeyguardInteractor(mNotificationsKeyguardViewStateRepository); - protected HeadsUpNotificationInteractor mHeadsUpNotificationInteractor; protected NotificationPanelViewController.TouchHandler mTouchHandler; protected ConfigurationController mConfigurationController; protected SysuiStatusBarStateController mStatusBarStateController; @@ -689,12 +680,6 @@ public class NotificationPanelViewControllerBaseTest extends SysuiTestCase { when(longPressHandlingView.getResources()).thenReturn(longPressHandlingViewRes); when(longPressHandlingViewRes.getString(anyInt())).thenReturn(""); - - mHeadsUpNotificationInteractor = - new HeadsUpNotificationInteractor(mFakeHeadsUpNotificationRepository, - mDeviceEntryFaceAuthInteractor, mKeyguardTransitionInteractor, - mNotificationsKeyguardInteractor, mShadeInteractor); - mNotificationPanelViewController = new NotificationPanelViewController( mView, mMainHandler, @@ -769,7 +754,6 @@ public class NotificationPanelViewControllerBaseTest extends SysuiTestCase { mActivityStarter, mSharedNotificationContainerInteractor, mActiveNotificationsInteractor, - mHeadsUpNotificationInteractor, mShadeAnimationInteractor, mKeyguardViewConfigurator, mDeviceEntryFaceAuthInteractor, diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java index 905cc4cd13b2..a7fd1609d1ca 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java @@ -58,13 +58,13 @@ import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.systemui.DejankUtils; +import com.android.systemui.flags.DisableSceneContainer; import com.android.systemui.flags.Flags; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.power.domain.interactor.PowerInteractor; import com.android.systemui.res.R; import com.android.systemui.statusbar.notification.row.ExpandableView; import com.android.systemui.statusbar.notification.row.ExpandableView.OnHeightChangedListener; -import com.android.systemui.statusbar.notification.shared.NotificationsHeadsUpRefactor; import com.android.systemui.statusbar.notification.stack.AmbientState; import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController; import com.android.systemui.statusbar.phone.KeyguardClockPositionAlgorithm; @@ -1375,7 +1375,7 @@ public class NotificationPanelViewControllerTest extends NotificationPanelViewCo } @Test - @DisableFlags(NotificationsHeadsUpRefactor.FLAG_NAME) + @DisableSceneContainer public void shadeExpanded_whenHunIsPresent() { when(mHeadsUpManager.hasPinnedHeadsUp()).thenReturn(true); assertThat(mNotificationPanelViewController.isExpanded()).isTrue(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerWithCoroutinesTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerWithCoroutinesTest.kt index 64eadb7db1e3..90655c3cf4b3 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerWithCoroutinesTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerWithCoroutinesTest.kt @@ -19,7 +19,6 @@ package com.android.systemui.shade import android.platform.test.annotations.DisableFlags -import android.platform.test.annotations.EnableFlags import android.testing.TestableLooper import android.view.HapticFeedbackConstants import android.view.View @@ -35,7 +34,6 @@ import com.android.systemui.statusbar.StatusBarState.KEYGUARD import com.android.systemui.statusbar.StatusBarState.SHADE import com.android.systemui.statusbar.StatusBarState.SHADE_LOCKED import com.android.systemui.statusbar.notification.data.repository.FakeHeadsUpRowRepository -import com.android.systemui.statusbar.notification.shared.NotificationsHeadsUpRefactor import com.android.systemui.util.mockito.eq import com.android.systemui.util.mockito.whenever import com.google.common.truth.Truth.assertThat @@ -243,36 +241,4 @@ class NotificationPanelViewControllerWithCoroutinesTest : val bottomAreaAlpha by collectLastValue(mFakeKeyguardRepository.bottomAreaAlpha) assertThat(bottomAreaAlpha).isEqualTo(1f) } - - @Test - @EnableFlags(NotificationsHeadsUpRefactor.FLAG_NAME) - fun shadeExpanded_whenHunIsPresent() = runTest { - launch(mainDispatcher) { - givenViewAttached() - - // WHEN a pinned heads up is present - mFakeHeadsUpNotificationRepository.setNotifications( - FakeHeadsUpRowRepository("key", isPinned = true) - ) - } - advanceUntilIdle() - - // THEN the panel should be visible - assertThat(mNotificationPanelViewController.isExpanded).isTrue() - } - - @Test - @EnableFlags(NotificationsHeadsUpRefactor.FLAG_NAME) - fun shadeExpanded_whenHunIsAnimatingAway() = runTest { - launch(mainDispatcher) { - givenViewAttached() - - // WHEN a heads up is animating away - mFakeHeadsUpNotificationRepository.isHeadsUpAnimatingAway.value = true - } - advanceUntilIdle() - - // THEN the panel should be visible - assertThat(mNotificationPanelViewController.isExpanded).isTrue() - } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarSignalPolicyTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarSignalPolicyTest.kt new file mode 100644 index 000000000000..593f87382b52 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarSignalPolicyTest.kt @@ -0,0 +1,173 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar + +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.R +import com.android.settingslib.mobile.TelephonyIcons +import com.android.systemui.Flags.FLAG_STATUS_BAR_SIGNAL_POLICY_REFACTOR +import com.android.systemui.SysuiTestCase +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.kosmos.testCase +import com.android.systemui.statusbar.connectivity.IconState +import com.android.systemui.statusbar.connectivity.NetworkController +import com.android.systemui.statusbar.phone.StatusBarSignalPolicy +import com.android.systemui.statusbar.phone.StatusBarSignalPolicy_Factory +import com.android.systemui.statusbar.phone.ui.StatusBarIconController +import com.android.systemui.statusbar.pipeline.airplane.domain.interactor.airplaneModeInteractor +import com.android.systemui.statusbar.policy.SecurityController +import com.android.systemui.tuner.TunerService +import com.android.systemui.util.CarrierConfigTracker +import com.android.systemui.util.kotlin.JavaAdapter +import com.android.systemui.util.mockito.mock +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.runner.RunWith +import org.mockito.Mockito.verify +import org.mockito.kotlin.clearInvocations +import org.mockito.kotlin.verifyZeroInteractions +import kotlin.test.Test + +@OptIn(ExperimentalCoroutinesApi::class) +@SmallTest +@RunWith(AndroidJUnit4::class) +class StatusBarSignalPolicyTest : SysuiTestCase() { + private val kosmos = Kosmos().also { it.testCase = this } + + private lateinit var underTest: StatusBarSignalPolicy + + private val testScope = TestScope() + + private val javaAdapter = JavaAdapter(testScope.backgroundScope) + private val airplaneModeInteractor = kosmos.airplaneModeInteractor + + private val securityController = mock<SecurityController>() + private val tunerService = mock<TunerService>() + private val statusBarIconController = mock<StatusBarIconController>() + private val networkController = mock<NetworkController>() + private val carrierConfigTracker = mock<CarrierConfigTracker>() + + private var slotAirplane: String? = null + + @Before + fun setup() { + underTest = + StatusBarSignalPolicy_Factory.newInstance( + mContext, + statusBarIconController, + carrierConfigTracker, + networkController, + securityController, + tunerService, + javaAdapter, + airplaneModeInteractor, + ) + + slotAirplane = mContext.getString(R.string.status_bar_airplane) + } + + @Test + @EnableFlags(FLAG_STATUS_BAR_SIGNAL_POLICY_REFACTOR) + fun airplaneModeViaInteractor_statusBarSignalPolicyRefactorFlagEnabled_iconUpdated() = + testScope.runTest { + underTest.start() + airplaneModeInteractor.setIsAirplaneMode(true) + runCurrent() + verify(statusBarIconController).setIconVisibility(slotAirplane, true) + + airplaneModeInteractor.setIsAirplaneMode(false) + runCurrent() + verify(statusBarIconController).setIconVisibility(slotAirplane, false) + } + + @Test + @EnableFlags(FLAG_STATUS_BAR_SIGNAL_POLICY_REFACTOR) + fun airplaneModeViaSignalCallback_statusBarSignalPolicyRefactorFlagEnabled_iconNotUpdated() = + testScope.runTest { + underTest.start() + runCurrent() + clearInvocations(statusBarIconController) + + // Make sure the legacy code path does not change airplane mode when the refactor + // flag is enabled. + underTest.setIsAirplaneMode(IconState(true, TelephonyIcons.FLIGHT_MODE_ICON, "")) + runCurrent() + verifyZeroInteractions(statusBarIconController) + + underTest.setIsAirplaneMode(IconState(false, TelephonyIcons.FLIGHT_MODE_ICON, "")) + runCurrent() + verifyZeroInteractions(statusBarIconController) + } + + @Test + @EnableFlags(FLAG_STATUS_BAR_SIGNAL_POLICY_REFACTOR) + fun statusBarSignalPolicyInitialization_statusBarSignalPolicyRefactorFlagEnabled_initNoOp() = + testScope.runTest { + // Make sure StatusBarSignalPolicy.init does no initialization when + // the refactor flag is disabled. + underTest.init() + verifyZeroInteractions(securityController, networkController, tunerService) + } + + @Test + @DisableFlags(FLAG_STATUS_BAR_SIGNAL_POLICY_REFACTOR) + fun airplaneModeViaSignalCallback_statusBarSignalPolicyRefactorFlagDisabled_iconUpdated() = + testScope.runTest { + underTest.init() + + underTest.setIsAirplaneMode(IconState(true, TelephonyIcons.FLIGHT_MODE_ICON, "")) + runCurrent() + verify(statusBarIconController).setIconVisibility(slotAirplane, true) + + underTest.setIsAirplaneMode(IconState(false, TelephonyIcons.FLIGHT_MODE_ICON, "")) + runCurrent() + verify(statusBarIconController).setIconVisibility(slotAirplane, false) + } + + @Test + @DisableFlags(FLAG_STATUS_BAR_SIGNAL_POLICY_REFACTOR) + fun airplaneModeViaInteractor_statusBarSignalPolicyRefactorFlagDisabled_iconNotUpdated() = + testScope.runTest { + underTest.init() + + // Make sure changing airplane mode from airplaneModeRepository does nothing + // if the StatusBarSignalPolicyRefactor is not enabled. + airplaneModeInteractor.setIsAirplaneMode(true) + runCurrent() + verifyZeroInteractions(statusBarIconController) + + airplaneModeInteractor.setIsAirplaneMode(false) + runCurrent() + verifyZeroInteractions(statusBarIconController) + } + + @Test + @DisableFlags(FLAG_STATUS_BAR_SIGNAL_POLICY_REFACTOR) + fun statusBarSignalPolicyInitialization_statusBarSignalPolicyRefactorFlagDisabled_startNoOp() = + testScope.runTest { + // Make sure StatusBarSignalPolicy.start does no initialization when + // the refactor flag is disabled. + underTest.start() + verifyZeroInteractions(securityController, networkController, tunerService) + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModelTest.kt index ce79fbde77a3..7bc6d4ae2816 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModelTest.kt @@ -132,10 +132,10 @@ class CallChipViewModelTest : SysuiTestCase() { ) assertThat((latest as OngoingActivityChipModel.Shown).icon) - .isInstanceOf(OngoingActivityChipModel.ChipIcon.Basic::class.java) + .isInstanceOf(OngoingActivityChipModel.ChipIcon.SingleColorIcon::class.java) val icon = (((latest as OngoingActivityChipModel.Shown).icon) - as OngoingActivityChipModel.ChipIcon.Basic) + as OngoingActivityChipModel.ChipIcon.SingleColorIcon) .impl as Icon.Resource assertThat(icon.res).isEqualTo(com.android.internal.R.drawable.ic_phone) assertThat(icon.contentDescription).isNotNull() @@ -170,10 +170,10 @@ class CallChipViewModelTest : SysuiTestCase() { ) assertThat((latest as OngoingActivityChipModel.Shown).icon) - .isInstanceOf(OngoingActivityChipModel.ChipIcon.Basic::class.java) + .isInstanceOf(OngoingActivityChipModel.ChipIcon.SingleColorIcon::class.java) val icon = (((latest as OngoingActivityChipModel.Shown).icon) - as OngoingActivityChipModel.ChipIcon.Basic) + as OngoingActivityChipModel.ChipIcon.SingleColorIcon) .impl as Icon.Resource assertThat(icon.res).isEqualTo(com.android.internal.R.drawable.ic_phone) assertThat(icon.contentDescription).isNotNull() @@ -206,10 +206,10 @@ class CallChipViewModelTest : SysuiTestCase() { repo.setOngoingCallState(inCallModel(startTimeMs = 1000, notificationIcon = null)) assertThat((latest as OngoingActivityChipModel.Shown).icon) - .isInstanceOf(OngoingActivityChipModel.ChipIcon.Basic::class.java) + .isInstanceOf(OngoingActivityChipModel.ChipIcon.SingleColorIcon::class.java) val icon = (((latest as OngoingActivityChipModel.Shown).icon) - as OngoingActivityChipModel.ChipIcon.Basic) + as OngoingActivityChipModel.ChipIcon.SingleColorIcon) .impl as Icon.Resource assertThat(icon.res).isEqualTo(com.android.internal.R.drawable.ic_phone) assertThat(icon.contentDescription).isNotNull() diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/viewmodel/CastToOtherDeviceChipViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/viewmodel/CastToOtherDeviceChipViewModelTest.kt index a8d2c5b4cdd7..77992dbaecc2 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/viewmodel/CastToOtherDeviceChipViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/viewmodel/CastToOtherDeviceChipViewModelTest.kt @@ -127,7 +127,7 @@ class CastToOtherDeviceChipViewModelTest : SysuiTestCase() { assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown.Timer::class.java) val icon = (((latest as OngoingActivityChipModel.Shown).icon) - as OngoingActivityChipModel.ChipIcon.Basic) + as OngoingActivityChipModel.ChipIcon.SingleColorIcon) .impl as Icon.Resource assertThat(icon.res).isEqualTo(R.drawable.ic_cast_connected) assertThat((icon.contentDescription as ContentDescription.Resource).res) @@ -146,7 +146,7 @@ class CastToOtherDeviceChipViewModelTest : SysuiTestCase() { assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown.Timer::class.java) val icon = (((latest as OngoingActivityChipModel.Shown).icon) - as OngoingActivityChipModel.ChipIcon.Basic) + as OngoingActivityChipModel.ChipIcon.SingleColorIcon) .impl as Icon.Resource assertThat(icon.res).isEqualTo(R.drawable.ic_cast_connected) assertThat((icon.contentDescription as ContentDescription.Resource).res) @@ -184,7 +184,7 @@ class CastToOtherDeviceChipViewModelTest : SysuiTestCase() { assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown.IconOnly::class.java) val icon = (((latest as OngoingActivityChipModel.Shown).icon) - as OngoingActivityChipModel.ChipIcon.Basic) + as OngoingActivityChipModel.ChipIcon.SingleColorIcon) .impl as Icon.Resource assertThat(icon.res).isEqualTo(R.drawable.ic_cast_connected) // This content description is just generic "Casting", not "Casting screen" @@ -214,7 +214,7 @@ class CastToOtherDeviceChipViewModelTest : SysuiTestCase() { assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown.Timer::class.java) val icon = (((latest as OngoingActivityChipModel.Shown).icon) - as OngoingActivityChipModel.ChipIcon.Basic) + as OngoingActivityChipModel.ChipIcon.SingleColorIcon) .impl as Icon.Resource assertThat(icon.res).isEqualTo(R.drawable.ic_cast_connected) // MediaProjection == screen casting, so this content description reflects that we're diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/ron/demo/ui/viewmodel/DemoRonChipViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/ron/demo/ui/viewmodel/DemoRonChipViewModelTest.kt new file mode 100644 index 000000000000..8576893216d0 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/ron/demo/ui/viewmodel/DemoRonChipViewModelTest.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.statusbar.chips.ron.demo.ui.viewmodel + +import android.content.packageManager +import android.graphics.drawable.BitmapDrawable +import android.platform.test.annotations.DisableFlags +import android.platform.test.annotations.EnableFlags +import androidx.test.filters.SmallTest +import com.android.systemui.Flags.FLAG_STATUS_BAR_RON_CHIPS +import com.android.systemui.SysuiTestCase +import com.android.systemui.coroutines.collectLastValue +import com.android.systemui.kosmos.testScope +import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel +import com.android.systemui.statusbar.commandline.CommandRegistry +import com.android.systemui.statusbar.commandline.commandRegistry +import com.android.systemui.testKosmos +import com.google.common.truth.Truth.assertThat +import java.io.PrintWriter +import java.io.StringWriter +import kotlin.test.Test +import kotlinx.coroutines.test.runTest +import org.junit.Before +import org.mockito.kotlin.any +import org.mockito.kotlin.whenever + +@SmallTest +class DemoRonChipViewModelTest : SysuiTestCase() { + private val kosmos = testKosmos() + private val testScope = kosmos.testScope + private val commandRegistry = kosmos.commandRegistry + private val pw = PrintWriter(StringWriter()) + + private val underTest = kosmos.demoRonChipViewModel + + @Before + fun setUp() { + underTest.start() + whenever(kosmos.packageManager.getApplicationIcon(any<String>())) + .thenReturn(BitmapDrawable()) + } + + @Test + @DisableFlags(FLAG_STATUS_BAR_RON_CHIPS) + fun chip_flagOff_hidden() = + testScope.runTest { + val latest by collectLastValue(underTest.chip) + + addDemoRonChip() + + assertThat(latest).isInstanceOf(OngoingActivityChipModel.Hidden::class.java) + } + + @Test + @EnableFlags(FLAG_STATUS_BAR_RON_CHIPS) + fun chip_noPackage_hidden() = + testScope.runTest { + val latest by collectLastValue(underTest.chip) + + commandRegistry.onShellCommand(pw, arrayOf("demo-ron")) + + assertThat(latest).isInstanceOf(OngoingActivityChipModel.Hidden::class.java) + } + + @Test + @EnableFlags(FLAG_STATUS_BAR_RON_CHIPS) + fun chip_hasPackage_shown() = + testScope.runTest { + val latest by collectLastValue(underTest.chip) + + commandRegistry.onShellCommand(pw, arrayOf("demo-ron", "-p", "com.android.systemui")) + + assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown::class.java) + } + + @Test + @EnableFlags(FLAG_STATUS_BAR_RON_CHIPS) + fun chip_hasText_shownWithText() = + testScope.runTest { + val latest by collectLastValue(underTest.chip) + + commandRegistry.onShellCommand( + pw, + arrayOf("demo-ron", "-p", "com.android.systemui", "-t", "test") + ) + + assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown.Text::class.java) + } + + @Test + @EnableFlags(FLAG_STATUS_BAR_RON_CHIPS) + fun chip_hasHideArg_hidden() = + testScope.runTest { + val latest by collectLastValue(underTest.chip) + + // First, show a chip + addDemoRonChip() + assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown::class.java) + + // Then, hide the chip + commandRegistry.onShellCommand(pw, arrayOf("demo-ron", "--hide")) + + assertThat(latest).isInstanceOf(OngoingActivityChipModel.Hidden::class.java) + } + + private fun addDemoRonChip() { + Companion.addDemoRonChip(commandRegistry, pw) + } + + companion object { + fun addDemoRonChip(commandRegistry: CommandRegistry, pw: PrintWriter) { + commandRegistry.onShellCommand(pw, arrayOf("demo-ron", "-p", "com.android.systemui")) + } + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/screenrecord/ui/viewmodel/ScreenRecordChipViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/screenrecord/ui/viewmodel/ScreenRecordChipViewModelTest.kt index 804eb5cf597c..16101bfe387c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/screenrecord/ui/viewmodel/ScreenRecordChipViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/screenrecord/ui/viewmodel/ScreenRecordChipViewModelTest.kt @@ -150,7 +150,7 @@ class ScreenRecordChipViewModelTest : SysuiTestCase() { assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown.Timer::class.java) val icon = (((latest as OngoingActivityChipModel.Shown).icon) - as OngoingActivityChipModel.ChipIcon.Basic) + as OngoingActivityChipModel.ChipIcon.SingleColorIcon) .impl as Icon.Resource assertThat(icon.res).isEqualTo(R.drawable.ic_screenrecord) assertThat(icon.contentDescription).isNotNull() diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/sharetoapp/ui/viewmodel/ShareToAppChipViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/sharetoapp/ui/viewmodel/ShareToAppChipViewModelTest.kt index a2ef59916ff6..791a21d0fb63 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/sharetoapp/ui/viewmodel/ShareToAppChipViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/sharetoapp/ui/viewmodel/ShareToAppChipViewModelTest.kt @@ -135,7 +135,7 @@ class ShareToAppChipViewModelTest : SysuiTestCase() { assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown.Timer::class.java) val icon = (((latest as OngoingActivityChipModel.Shown).icon) - as OngoingActivityChipModel.ChipIcon.Basic) + as OngoingActivityChipModel.ChipIcon.SingleColorIcon) .impl as Icon.Resource assertThat(icon.res).isEqualTo(R.drawable.ic_present_to_all) assertThat(icon.contentDescription).isNotNull() @@ -152,7 +152,7 @@ class ShareToAppChipViewModelTest : SysuiTestCase() { assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown.Timer::class.java) val icon = (((latest as OngoingActivityChipModel.Shown).icon) - as OngoingActivityChipModel.ChipIcon.Basic) + as OngoingActivityChipModel.ChipIcon.SingleColorIcon) .impl as Icon.Resource assertThat(icon.res).isEqualTo(R.drawable.ic_present_to_all) assertThat(icon.contentDescription).isNotNull() diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/ui/viewmodel/ChipTransitionHelperTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/ui/viewmodel/ChipTransitionHelperTest.kt index a724cfaa4798..4977c548fb92 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/ui/viewmodel/ChipTransitionHelperTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/ui/viewmodel/ChipTransitionHelperTest.kt @@ -154,5 +154,7 @@ class ChipTransitionHelperTest : SysuiTestCase() { } private fun createIcon(@DrawableRes drawable: Int) = - OngoingActivityChipModel.ChipIcon.Basic(Icon.Resource(drawable, contentDescription = null)) + OngoingActivityChipModel.ChipIcon.SingleColorIcon( + Icon.Resource(drawable, contentDescription = null) + ) } 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 556ec6a307ab..bd5df07b7ece 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 @@ -19,9 +19,12 @@ package com.android.systemui.statusbar.chips.ui.viewmodel import android.content.DialogInterface import android.content.packageManager import android.content.pm.PackageManager +import android.graphics.drawable.BitmapDrawable +import android.platform.test.annotations.EnableFlags import android.view.View import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest +import com.android.systemui.Flags.FLAG_STATUS_BAR_RON_CHIPS import com.android.systemui.SysuiTestCase import com.android.systemui.common.shared.model.Icon import com.android.systemui.coroutines.collectLastValue @@ -36,8 +39,11 @@ import com.android.systemui.screenrecord.data.model.ScreenRecordModel import com.android.systemui.screenrecord.data.repository.screenRecordRepository 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.ron.demo.ui.viewmodel.DemoRonChipViewModelTest.Companion.addDemoRonChip +import com.android.systemui.statusbar.chips.ron.demo.ui.viewmodel.demoRonChipViewModel import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel import com.android.systemui.statusbar.chips.ui.view.ChipBackgroundContainer +import com.android.systemui.statusbar.commandline.commandRegistry import com.android.systemui.statusbar.phone.SystemUIDialog import com.android.systemui.statusbar.phone.mockSystemUIDialogFactory import com.android.systemui.statusbar.phone.ongoingcall.data.repository.ongoingCallRepository @@ -45,6 +51,8 @@ import com.android.systemui.statusbar.phone.ongoingcall.shared.model.OngoingCall import com.android.systemui.statusbar.phone.ongoingcall.shared.model.inCallModel import com.android.systemui.util.time.fakeSystemClock import com.google.common.truth.Truth.assertThat +import java.io.PrintWriter +import java.io.StringWriter import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach @@ -68,11 +76,14 @@ class OngoingActivityChipsViewModelTest : SysuiTestCase() { private val kosmos = Kosmos().also { it.testCase = this } private val testScope = kosmos.testScope private val systemClock = kosmos.fakeSystemClock + private val commandRegistry = kosmos.commandRegistry private val screenRecordState = kosmos.screenRecordRepository.screenRecordState private val mediaProjectionState = kosmos.fakeMediaProjectionRepository.mediaProjectionState private val callRepo = kosmos.ongoingCallRepository + private val pw = PrintWriter(StringWriter()) + private val mockSystemUIDialog = mock<SystemUIDialog>() private val chipBackgroundView = mock<ChipBackgroundContainer>() private val chipView = @@ -90,6 +101,9 @@ class OngoingActivityChipsViewModelTest : SysuiTestCase() { @Before fun setUp() { setUpPackageManagerForMediaProjection(kosmos) + kosmos.demoRonChipViewModel.start() + whenever(kosmos.packageManager.getApplicationIcon(any<String>())) + .thenReturn(BitmapDrawable()) } @Test @@ -169,15 +183,24 @@ class OngoingActivityChipsViewModelTest : SysuiTestCase() { } @Test + @EnableFlags(FLAG_STATUS_BAR_RON_CHIPS) fun chip_higherPriorityChipAdded_lowerPriorityChipReplaced() = testScope.runTest { - // Start with just the lower priority call chip - callRepo.setOngoingCallState(inCallModel(startTimeMs = 34)) + // Start with just the lowest priority chip shown + addDemoRonChip(commandRegistry, pw) + // And everything else hidden + callRepo.setOngoingCallState(OngoingCallModel.NoCall) mediaProjectionState.value = MediaProjectionState.NotProjecting screenRecordState.value = ScreenRecordModel.DoingNothing val latest by collectLastValue(underTest.chip) + assertIsDemoRonChip(latest) + + // WHEN the higher priority call chip is added + callRepo.setOngoingCallState(inCallModel(startTimeMs = 34)) + + // THEN the higher priority call chip is used assertIsCallChip(latest) // WHEN the higher priority media projection chip is added @@ -199,14 +222,15 @@ class OngoingActivityChipsViewModelTest : SysuiTestCase() { } @Test + @EnableFlags(FLAG_STATUS_BAR_RON_CHIPS) fun chip_highestPriorityChipRemoved_showsNextPriorityChip() = testScope.runTest { // WHEN all chips are active screenRecordState.value = ScreenRecordModel.Recording mediaProjectionState.value = MediaProjectionState.Projecting.EntireScreen(NORMAL_PACKAGE) - callRepo.setOngoingCallState(inCallModel(startTimeMs = 34)) + addDemoRonChip(commandRegistry, pw) val latest by collectLastValue(underTest.chip) @@ -224,6 +248,12 @@ class OngoingActivityChipsViewModelTest : SysuiTestCase() { // THEN the lower priority call is used assertIsCallChip(latest) + + // WHEN the higher priority call is removed + callRepo.setOngoingCallState(OngoingCallModel.NoCall) + + // THEN the lower priority demo RON is used + assertIsDemoRonChip(latest) } /** Regression test for b/347726238. */ @@ -338,7 +368,7 @@ class OngoingActivityChipsViewModelTest : SysuiTestCase() { assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown::class.java) val icon = (((latest as OngoingActivityChipModel.Shown).icon) - as OngoingActivityChipModel.ChipIcon.Basic) + as OngoingActivityChipModel.ChipIcon.SingleColorIcon) .impl as Icon.Resource assertThat(icon.res).isEqualTo(R.drawable.ic_screenrecord) } @@ -347,7 +377,7 @@ class OngoingActivityChipsViewModelTest : SysuiTestCase() { assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown::class.java) val icon = (((latest as OngoingActivityChipModel.Shown).icon) - as OngoingActivityChipModel.ChipIcon.Basic) + as OngoingActivityChipModel.ChipIcon.SingleColorIcon) .impl as Icon.Resource assertThat(icon.res).isEqualTo(R.drawable.ic_present_to_all) } @@ -356,9 +386,15 @@ class OngoingActivityChipsViewModelTest : SysuiTestCase() { assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown::class.java) val icon = (((latest as OngoingActivityChipModel.Shown).icon) - as OngoingActivityChipModel.ChipIcon.Basic) + as OngoingActivityChipModel.ChipIcon.SingleColorIcon) .impl as Icon.Resource assertThat(icon.res).isEqualTo(com.android.internal.R.drawable.ic_phone) } + + fun assertIsDemoRonChip(latest: OngoingActivityChipModel?) { + assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown::class.java) + assertThat((latest as OngoingActivityChipModel.Shown).icon) + .isInstanceOf(OngoingActivityChipModel.ChipIcon.FullColorAppIcon::class.java) + } } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java index 1717f4c069f4..8d1228ca5577 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java @@ -95,7 +95,6 @@ import com.android.systemui.statusbar.notification.footer.ui.view.FooterView; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.row.ExpandableView; import com.android.systemui.statusbar.notification.shared.NotificationThrottleHun; -import com.android.systemui.statusbar.notification.shared.NotificationsHeadsUpRefactor; import com.android.systemui.statusbar.phone.KeyguardBypassController; import com.android.systemui.statusbar.phone.ScreenOffAnimationController; import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager; @@ -1206,7 +1205,7 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase { @Test - @EnableFlags(NotificationsHeadsUpRefactor.FLAG_NAME) + @EnableSceneContainer public void testGenerateHeadsUpDisappearEvent_setsHeadsUpAnimatingAway() { // GIVEN NSSL is ready for HUN animations Consumer<Boolean> headsUpAnimatingAwayListener = mock(BooleanConsumer.class); @@ -1222,7 +1221,7 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase { } @Test - @EnableFlags(NotificationsHeadsUpRefactor.FLAG_NAME) + @EnableSceneContainer public void testGenerateHeadsUpDisappearEvent_stackExpanded_headsUpAnimatingAwayNotSet() { // GIVEN NSSL would be ready for HUN animations, BUT it is expanded Consumer<Boolean> headsUpAnimatingAwayListener = mock(BooleanConsumer.class); @@ -1241,7 +1240,7 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase { } @Test - @EnableFlags(NotificationsHeadsUpRefactor.FLAG_NAME) + @EnableSceneContainer public void testGenerateHeadsUpDisappearEvent_pendingAppearEvent_headsUpAnimatingAwayNotSet() { // GIVEN NSSL is ready for HUN animations Consumer<Boolean> headsUpAnimatingAwayListener = mock(BooleanConsumer.class); @@ -1259,7 +1258,7 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase { } @Test - @EnableFlags(NotificationsHeadsUpRefactor.FLAG_NAME) + @EnableSceneContainer public void testGenerateHeadsUpAppearEvent_headsUpAnimatingAwayNotSet() { // GIVEN NSSL is ready for HUN animations Consumer<Boolean> headsUpAnimatingAwayListener = mock(BooleanConsumer.class); @@ -1295,7 +1294,7 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase { } @Test - @EnableFlags(NotificationsHeadsUpRefactor.FLAG_NAME) + @EnableSceneContainer public void testOnChildAnimationsFinished_resetsheadsUpAnimatingAway() { // GIVEN NSSL is ready for HUN animations Consumer<Boolean> headsUpAnimatingAwayListener = mock(BooleanConsumer.class); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicyTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicyTest.kt index 76dc65cbc915..2ed34735db1f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicyTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicyTest.kt @@ -34,6 +34,7 @@ import android.testing.TestableLooper import android.testing.TestableLooper.RunWithLooper import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest +import com.android.internal.statusbar.StatusBarIcon import com.android.settingslib.notification.modes.TestModeBuilder import com.android.systemui.Flags import com.android.systemui.SysuiTestCase @@ -41,6 +42,7 @@ import com.android.systemui.broadcast.BroadcastDispatcher import com.android.systemui.display.domain.interactor.ConnectedDisplayInteractor import com.android.systemui.display.domain.interactor.ConnectedDisplayInteractor.PendingDisplay import com.android.systemui.display.domain.interactor.ConnectedDisplayInteractor.State +import com.android.systemui.kosmos.testScope import com.android.systemui.privacy.PrivacyItemController import com.android.systemui.privacy.logging.PrivacyLogger import com.android.systemui.screenrecord.RecordingController @@ -71,9 +73,7 @@ import com.android.systemui.util.time.DateFormatUtil import com.android.systemui.util.time.FakeSystemClock import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.MutableSharedFlow -import kotlinx.coroutines.test.TestScope -import kotlinx.coroutines.test.UnconfinedTestDispatcher +import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest import org.junit.Before @@ -145,7 +145,7 @@ class PhoneStatusBarPolicyTest : SysuiTestCase() { private lateinit var alarmCallbackCaptor: ArgumentCaptor<NextAlarmController.NextAlarmChangeCallback> - private val testScope = TestScope(UnconfinedTestDispatcher()) + private val testScope = kosmos.testScope private val fakeConnectedDisplayStateProvider = FakeConnectedDisplayStateProvider() private val zenModeController = FakeZenModeController() @@ -249,7 +249,7 @@ class PhoneStatusBarPolicyTest : SysuiTestCase() { statusBarPolicy.init() clearInvocations(iconController) - fakeConnectedDisplayStateProvider.emit(State.CONNECTED) + fakeConnectedDisplayStateProvider.setState(State.CONNECTED) runCurrent() verify(iconController).setIconVisibility(CONNECTED_DISPLAY_SLOT, true) @@ -261,7 +261,8 @@ class PhoneStatusBarPolicyTest : SysuiTestCase() { statusBarPolicy.init() clearInvocations(iconController) - fakeConnectedDisplayStateProvider.emit(State.DISCONNECTED) + fakeConnectedDisplayStateProvider.setState(State.DISCONNECTED) + runCurrent() verify(iconController).setIconVisibility(CONNECTED_DISPLAY_SLOT, false) } @@ -272,9 +273,12 @@ class PhoneStatusBarPolicyTest : SysuiTestCase() { statusBarPolicy.init() clearInvocations(iconController) - fakeConnectedDisplayStateProvider.emit(State.CONNECTED) - fakeConnectedDisplayStateProvider.emit(State.DISCONNECTED) - fakeConnectedDisplayStateProvider.emit(State.CONNECTED) + fakeConnectedDisplayStateProvider.setState(State.CONNECTED) + runCurrent() + fakeConnectedDisplayStateProvider.setState(State.DISCONNECTED) + runCurrent() + fakeConnectedDisplayStateProvider.setState(State.CONNECTED) + runCurrent() inOrder(iconController).apply { verify(iconController).setIconVisibility(CONNECTED_DISPLAY_SLOT, true) @@ -289,7 +293,8 @@ class PhoneStatusBarPolicyTest : SysuiTestCase() { statusBarPolicy.init() clearInvocations(iconController) - fakeConnectedDisplayStateProvider.emit(State.CONNECTED_SECURE) + fakeConnectedDisplayStateProvider.setState(State.CONNECTED_SECURE) + runCurrent() verify(iconController).setIconVisibility(CONNECTED_DISPLAY_SLOT, true) } @@ -390,7 +395,7 @@ class PhoneStatusBarPolicyTest : SysuiTestCase() { } @Test - @EnableFlags(android.app.Flags.FLAG_MODES_UI_ICONS) + @EnableFlags(android.app.Flags.FLAG_MODES_UI, android.app.Flags.FLAG_MODES_UI_ICONS) fun zenModeInteractorActiveModeChanged_showsModeIcon() = testScope.runTest { statusBarPolicy.init() @@ -403,8 +408,8 @@ class PhoneStatusBarPolicyTest : SysuiTestCase() { .setName("Bedtime Mode") .setType(AutomaticZenRule.TYPE_BEDTIME) .setActive(true) - .setPackage("some.package") - .setIconResId(123) + .setPackage(mContext.packageName) + .setIconResId(android.R.drawable.ic_lock_lock) .build(), TestModeBuilder() .setId("other") @@ -412,7 +417,7 @@ class PhoneStatusBarPolicyTest : SysuiTestCase() { .setType(AutomaticZenRule.TYPE_OTHER) .setActive(true) .setPackage(SystemZenRules.PACKAGE_ANDROID) - .setIconResId(456) + .setIconResId(android.R.drawable.ic_media_play) .build(), ) ) @@ -422,17 +427,25 @@ class PhoneStatusBarPolicyTest : SysuiTestCase() { verify(iconController) .setResourceIcon( eq(ZEN_SLOT), - eq("some.package"), - eq(123), - eq(null), - eq("Bedtime Mode") + eq(mContext.packageName), + eq(android.R.drawable.ic_lock_lock), + any(), // non-null + eq("Bedtime Mode"), + eq(StatusBarIcon.Shape.FIXED_SPACE) ) zenModeRepository.deactivateMode("bedtime") runCurrent() verify(iconController) - .setResourceIcon(eq(ZEN_SLOT), eq(null), eq(456), eq(null), eq("Other Mode")) + .setResourceIcon( + eq(ZEN_SLOT), + eq(null), + eq(android.R.drawable.ic_media_play), + any(), // non-null + eq("Other Mode"), + eq(StatusBarIcon.Shape.FIXED_SPACE) + ) zenModeRepository.deactivateMode("other") runCurrent() @@ -441,7 +454,7 @@ class PhoneStatusBarPolicyTest : SysuiTestCase() { } @Test - @EnableFlags(android.app.Flags.FLAG_MODES_UI_ICONS) + @EnableFlags(android.app.Flags.FLAG_MODES_UI, android.app.Flags.FLAG_MODES_UI_ICONS) fun zenModeControllerOnGlobalZenChanged_doesNotUpdateDndIcon() { statusBarPolicy.init() reset(iconController) @@ -450,7 +463,8 @@ class PhoneStatusBarPolicyTest : SysuiTestCase() { verify(iconController, never()).setIconVisibility(eq(ZEN_SLOT), any()) verify(iconController, never()).setIcon(eq(ZEN_SLOT), anyInt(), any()) - verify(iconController, never()).setResourceIcon(eq(ZEN_SLOT), any(), any(), any(), any()) + verify(iconController, never()) + .setResourceIcon(eq(ZEN_SLOT), any(), any(), any(), any(), any()) } @Test @@ -466,7 +480,7 @@ class PhoneStatusBarPolicyTest : SysuiTestCase() { verify(iconController, never()).setIconVisibility(eq(ZEN_SLOT), any()) verify(iconController, never()).setIcon(eq(ZEN_SLOT), anyInt(), any()) verify(iconController, never()) - .setResourceIcon(eq(ZEN_SLOT), any(), any(), any(), any()) + .setResourceIcon(eq(ZEN_SLOT), any(), any(), any(), any(), any()) } @Test @@ -529,9 +543,11 @@ class PhoneStatusBarPolicyTest : SysuiTestCase() { } private class FakeConnectedDisplayStateProvider : ConnectedDisplayInteractor { - private val flow = MutableSharedFlow<State>() + private val flow = MutableStateFlow(State.DISCONNECTED) - suspend fun emit(value: State) = flow.emit(value) + fun setState(value: State) { + flow.value = value + } override val connectedDisplayState: Flow<State> get() = flow diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java index b75ac2bc9bde..3e3c046ce62e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java @@ -83,10 +83,10 @@ import com.android.systemui.dock.DockManager; import com.android.systemui.dreams.DreamOverlayStateController; import com.android.systemui.flags.DisableSceneContainer; import com.android.systemui.flags.EnableSceneContainer; +import com.android.systemui.keyguard.DismissCallbackRegistry; +import com.android.systemui.keyguard.domain.interactor.KeyguardDismissTransitionInteractor; import com.android.systemui.keyguard.domain.interactor.KeyguardDismissActionInteractor; -import com.android.systemui.keyguard.domain.interactor.KeyguardSurfaceBehindInteractor; import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor; -import com.android.systemui.keyguard.domain.interactor.WindowManagerLockscreenVisibilityInteractor; import com.android.systemui.keyguard.shared.model.KeyguardState; import com.android.systemui.keyguard.shared.model.TransitionState; import com.android.systemui.keyguard.shared.model.TransitionStep; @@ -171,6 +171,7 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase { @Mock private SelectedUserInteractor mSelectedUserInteractor; @Mock private DeviceEntryInteractor mDeviceEntryInteractor; @Mock private SceneInteractor mSceneInteractor; + @Mock private DismissCallbackRegistry mDismissCallbackRegistry; private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager; private PrimaryBouncerCallbackInteractor.PrimaryBouncerExpansionCallback @@ -233,16 +234,16 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase { mUdfpsOverlayInteractor, mActivityStarter, mKeyguardTransitionInteractor, + mock(KeyguardDismissTransitionInteractor.class), StandardTestDispatcher(null, null), - () -> mock(WindowManagerLockscreenVisibilityInteractor.class), () -> mock(KeyguardDismissActionInteractor.class), mSelectedUserInteractor, - () -> mock(KeyguardSurfaceBehindInteractor.class), mock(JavaAdapter.class), () -> mSceneInteractor, mock(StatusBarKeyguardViewManagerInteractor.class), mExecutor, - () -> mDeviceEntryInteractor) { + () -> mDeviceEntryInteractor, + mDismissCallbackRegistry) { @Override public ViewRootImpl getViewRootImpl() { return mViewRootImpl; @@ -756,16 +757,16 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase { mUdfpsOverlayInteractor, mActivityStarter, mock(KeyguardTransitionInteractor.class), + mock(KeyguardDismissTransitionInteractor.class), StandardTestDispatcher(null, null), - () -> mock(WindowManagerLockscreenVisibilityInteractor.class), () -> mock(KeyguardDismissActionInteractor.class), mSelectedUserInteractor, - () -> mock(KeyguardSurfaceBehindInteractor.class), mock(JavaAdapter.class), () -> mSceneInteractor, mock(StatusBarKeyguardViewManagerInteractor.class), mExecutor, - () -> mDeviceEntryInteractor) { + () -> mDeviceEntryInteractor, + mDismissCallbackRegistry) { @Override public ViewRootImpl getViewRootImpl() { return mViewRootImpl; @@ -777,7 +778,11 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase { } @Test + @DisableSceneContainer public void testResetHideBouncerWhenShowing_alternateBouncerHides() { + reset(mDismissCallbackRegistry); + reset(mPrimaryBouncerInteractor); + // GIVEN the keyguard is showing reset(mAlternateBouncerInteractor); when(mKeyguardStateController.isShowing()).thenReturn(true); @@ -785,8 +790,10 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase { // WHEN SBKV is reset with hideBouncerWhenShowing=true mStatusBarKeyguardViewManager.reset(true); - // THEN alternate bouncer is hidden + // THEN alternate bouncer is hidden and dismiss actions reset verify(mAlternateBouncerInteractor).hide(); + verify(mDismissCallbackRegistry).notifyDismissCancelled(); + verify(mPrimaryBouncerInteractor).setDismissAction(eq(null), eq(null)); } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ui/IconManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ui/IconManagerTest.kt new file mode 100644 index 000000000000..90732d0183d2 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ui/IconManagerTest.kt @@ -0,0 +1,122 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.phone.ui + +import android.app.Flags +import android.graphics.drawable.Icon +import android.os.UserHandle +import android.platform.test.annotations.DisableFlags +import android.platform.test.annotations.EnableFlags +import android.view.ViewGroup +import android.widget.ImageView +import android.widget.LinearLayout +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.internal.statusbar.StatusBarIcon +import com.android.systemui.SysuiTestCase +import com.android.systemui.statusbar.StatusBarIconView +import com.android.systemui.statusbar.connectivity.ui.MobileContextProvider +import com.android.systemui.statusbar.phone.StatusBarLocation +import com.android.systemui.statusbar.pipeline.mobile.ui.MobileUiAdapter +import com.android.systemui.statusbar.pipeline.wifi.ui.WifiUiAdapter +import com.android.systemui.util.Assert +import com.google.common.truth.Truth.assertThat +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mockito.RETURNS_DEEP_STUBS +import org.mockito.kotlin.mock + +@SmallTest +@RunWith(AndroidJUnit4::class) +class IconManagerTest : SysuiTestCase() { + + private lateinit var underTest: IconManager + private lateinit var viewGroup: ViewGroup + + @Before + fun setUp() { + Assert.setTestThread(Thread.currentThread()) + viewGroup = LinearLayout(context) + underTest = + IconManager( + viewGroup, + StatusBarLocation.HOME, + mock<WifiUiAdapter>(defaultAnswer = RETURNS_DEEP_STUBS), + mock<MobileUiAdapter>(defaultAnswer = RETURNS_DEEP_STUBS), + mock<MobileContextProvider>(defaultAnswer = RETURNS_DEEP_STUBS), + ) + } + + @Test + @EnableFlags(Flags.FLAG_MODES_UI, Flags.FLAG_MODES_UI_ICONS) + fun addIcon_shapeWrapContent_addsIconViewWithVariableWidth() { + val sbIcon = newStatusBarIcon(StatusBarIcon.Shape.WRAP_CONTENT) + + underTest.addIcon(0, "slot", false, sbIcon) + + assertThat(viewGroup.childCount).isEqualTo(1) + val iconView = viewGroup.getChildAt(0) as StatusBarIconView + assertThat(iconView).isNotNull() + + assertThat(iconView.layoutParams.width).isEqualTo(ViewGroup.LayoutParams.WRAP_CONTENT) + assertThat(iconView.scaleType).isEqualTo(ImageView.ScaleType.CENTER) + } + + @Test + @EnableFlags(Flags.FLAG_MODES_UI, Flags.FLAG_MODES_UI_ICONS) + fun addIcon_shapeFixedSpace_addsIconViewWithFixedWidth() { + val sbIcon = newStatusBarIcon(StatusBarIcon.Shape.FIXED_SPACE) + + underTest.addIcon(0, "slot", false, sbIcon) + + assertThat(viewGroup.childCount).isEqualTo(1) + val iconView = viewGroup.getChildAt(0) as StatusBarIconView + assertThat(iconView).isNotNull() + + assertThat(iconView.layoutParams.width).isNotEqualTo(ViewGroup.LayoutParams.WRAP_CONTENT) + assertThat(iconView.layoutParams.width).isEqualTo(iconView.layoutParams.height) + assertThat(iconView.scaleType).isEqualTo(ImageView.ScaleType.FIT_CENTER) + } + + @Test + @DisableFlags(Flags.FLAG_MODES_UI_ICONS) + fun addIcon_iconsFlagOff_addsIconViewWithVariableWidth() { + val sbIcon = newStatusBarIcon(StatusBarIcon.Shape.FIXED_SPACE) + + underTest.addIcon(0, "slot", false, sbIcon) + + assertThat(viewGroup.childCount).isEqualTo(1) + val iconView = viewGroup.getChildAt(0) as StatusBarIconView + assertThat(iconView).isNotNull() + + assertThat(iconView.layoutParams.width).isEqualTo(ViewGroup.LayoutParams.WRAP_CONTENT) + assertThat(iconView.scaleType).isEqualTo(ImageView.ScaleType.CENTER) + } + + private fun newStatusBarIcon(shape: StatusBarIcon.Shape) = + StatusBarIcon( + UserHandle.CURRENT, + context.packageName, + Icon.createWithResource(context, android.R.drawable.ic_media_next), + 0, + 0, + "", + StatusBarIcon.Type.ResourceIcon, + shape, + ) +} 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 26a57e4c1ca9..50a13b93ea15 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 @@ -424,7 +424,14 @@ class StatusBarIconControllerImplTest : SysuiTestCase() { @EnableFlags(android.app.Flags.FLAG_MODES_UI, android.app.Flags.FLAG_MODES_UI_ICONS) fun setResourceIcon_setsIconAndPreloadedIconInHolder() { val drawable = ColorDrawable(1) - underTest.setResourceIcon("slot", "some.package", 123, drawable, "description") + underTest.setResourceIcon( + "slot", + "some.package", + 123, + drawable, + "description", + StatusBarIcon.Shape.FIXED_SPACE + ) val iconHolder = iconList.getIconHolder("slot", 0) assertThat(iconHolder).isNotNull() @@ -432,6 +439,7 @@ class StatusBarIconControllerImplTest : SysuiTestCase() { assertThat(iconHolder?.icon?.icon?.resId).isEqualTo(123) assertThat(iconHolder?.icon?.icon?.resPackage).isEqualTo("some.package") assertThat(iconHolder?.icon?.contentDescription).isEqualTo("description") + assertThat(iconHolder?.icon?.shape).isEqualTo(StatusBarIcon.Shape.FIXED_SPACE) assertThat(iconHolder?.icon?.preloadedIcon).isEqualTo(drawable) } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/data/repository/ConnectivityRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/data/repository/ConnectivityRepositoryImplTest.kt index f486787e9cc7..0945742fb325 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/data/repository/ConnectivityRepositoryImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/data/repository/ConnectivityRepositoryImplTest.kt @@ -21,60 +21,61 @@ import android.net.Network import android.net.NetworkCapabilities import android.net.NetworkCapabilities.TRANSPORT_CELLULAR import android.net.NetworkCapabilities.TRANSPORT_ETHERNET +import android.net.NetworkCapabilities.TRANSPORT_VPN import android.net.NetworkCapabilities.TRANSPORT_WIFI +import android.net.VpnTransportInfo import android.net.vcn.VcnTransportInfo import android.net.wifi.WifiInfo +import android.platform.test.annotations.DisableFlags +import android.platform.test.annotations.EnableFlags import android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest +import com.android.systemui.Flags.FLAG_STATUS_BAR_ALWAYS_CHECK_UNDERLYING_NETWORKS import com.android.systemui.SysuiTestCase -import com.android.systemui.dump.DumpManager +import com.android.systemui.coroutines.collectLastValue +import com.android.systemui.dump.dumpManager +import com.android.systemui.kosmos.testScope +import com.android.systemui.log.core.FakeLogBuffer import com.android.systemui.statusbar.pipeline.shared.ConnectivityInputLogger import com.android.systemui.statusbar.pipeline.shared.data.model.ConnectivitySlot import com.android.systemui.statusbar.pipeline.shared.data.model.ConnectivitySlots -import com.android.systemui.statusbar.pipeline.shared.data.model.DefaultConnectionModel import com.android.systemui.statusbar.pipeline.shared.data.repository.ConnectivityRepositoryImpl.Companion.DEFAULT_HIDDEN_ICONS_RESOURCE import com.android.systemui.statusbar.pipeline.shared.data.repository.ConnectivityRepositoryImpl.Companion.HIDDEN_ICONS_TUNABLE_KEY import com.android.systemui.statusbar.pipeline.shared.data.repository.ConnectivityRepositoryImpl.Companion.getMainOrUnderlyingWifiInfo +import com.android.systemui.testKosmos import com.android.systemui.tuner.TunerService -import com.android.systemui.util.mockito.any -import com.android.systemui.util.mockito.argumentCaptor -import com.android.systemui.util.mockito.eq -import com.android.systemui.util.mockito.mock import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.flow.launchIn -import kotlinx.coroutines.flow.onEach -import kotlinx.coroutines.test.TestScope -import kotlinx.coroutines.test.UnconfinedTestDispatcher +import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest -import kotlinx.coroutines.yield 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.Mockito.`when` as whenever -import org.mockito.MockitoAnnotations +import org.mockito.kotlin.any +import org.mockito.kotlin.argumentCaptor +import org.mockito.kotlin.eq +import org.mockito.kotlin.mock +import org.mockito.kotlin.verify +import org.mockito.kotlin.whenever @OptIn(ExperimentalCoroutinesApi::class) @SmallTest @RunWith(AndroidJUnit4::class) class ConnectivityRepositoryImplTest : SysuiTestCase() { + private val kosmos = testKosmos() private lateinit var underTest: ConnectivityRepositoryImpl - @Mock private lateinit var connectivityManager: ConnectivityManager - @Mock private lateinit var connectivitySlots: ConnectivitySlots - @Mock private lateinit var dumpManager: DumpManager - @Mock private lateinit var logger: ConnectivityInputLogger - private lateinit var testScope: TestScope - @Mock private lateinit var tunerService: TunerService + private val connectivityManager = mock<ConnectivityManager>() + private val connectivitySlots = mock<ConnectivitySlots>() + private val dumpManager = kosmos.dumpManager + private val logger = ConnectivityInputLogger(FakeLogBuffer.Factory.create()) + private val testScope = kosmos.testScope + private val tunerService = mock<TunerService>() @Before fun setUp() { - MockitoAnnotations.initMocks(this) - testScope = TestScope(UnconfinedTestDispatcher()) createAndSetRepo() } @@ -89,12 +90,10 @@ class ConnectivityRepositoryImplTest : SysuiTestCase() { // config_statusBarIconsToExclude when it's first constructed createAndSetRepo() - var latest: Set<ConnectivitySlot>? = null - val job = underTest.forceHiddenSlots.onEach { latest = it }.launchIn(this) + val latest by collectLastValue(underTest.forceHiddenSlots) + runCurrent() assertThat(latest).containsExactly(ConnectivitySlot.ETHERNET, ConnectivitySlot.WIFI) - - job.cancel() } @Test @@ -102,14 +101,12 @@ class ConnectivityRepositoryImplTest : SysuiTestCase() { testScope.runTest { setUpEthernetWifiMobileSlotNames() - var latest: Set<ConnectivitySlot>? = null - val job = underTest.forceHiddenSlots.onEach { latest = it }.launchIn(this) + val latest by collectLastValue(underTest.forceHiddenSlots) + runCurrent() getTunable().onTuningChanged(HIDDEN_ICONS_TUNABLE_KEY, SLOT_MOBILE) assertThat(latest).containsExactly(ConnectivitySlot.MOBILE) - - job.cancel() } @Test @@ -117,19 +114,16 @@ class ConnectivityRepositoryImplTest : SysuiTestCase() { testScope.runTest { setUpEthernetWifiMobileSlotNames() - var latest: Set<ConnectivitySlot>? = null - val job = underTest.forceHiddenSlots.onEach { latest = it }.launchIn(this) + val latest by collectLastValue(underTest.forceHiddenSlots) + runCurrent() getTunable().onTuningChanged(HIDDEN_ICONS_TUNABLE_KEY, SLOT_MOBILE) // WHEN onTuningChanged with the wrong key getTunable().onTuningChanged("wrongKey", SLOT_WIFI) - yield() // THEN we didn't update our value and still have the old one assertThat(latest).containsExactly(ConnectivitySlot.MOBILE) - - job.cancel() } @Test @@ -143,8 +137,8 @@ class ConnectivityRepositoryImplTest : SysuiTestCase() { // config_statusBarIconsToExclude when it's first constructed createAndSetRepo() - var latest: Set<ConnectivitySlot>? = null - val job = underTest.forceHiddenSlots.onEach { latest = it }.launchIn(this) + val latest by collectLastValue(underTest.forceHiddenSlots) + runCurrent() // First, update the slots getTunable().onTuningChanged(HIDDEN_ICONS_TUNABLE_KEY, SLOT_MOBILE) @@ -152,19 +146,16 @@ class ConnectivityRepositoryImplTest : SysuiTestCase() { // WHEN we update to a null value getTunable().onTuningChanged(HIDDEN_ICONS_TUNABLE_KEY, null) - yield() // THEN we go back to our default value assertThat(latest).containsExactly(ConnectivitySlot.ETHERNET, ConnectivitySlot.WIFI) - - job.cancel() } @Test fun forceHiddenSlots_someInvalidSlotNames_flowHasValidSlotsOnly() = testScope.runTest { - var latest: Set<ConnectivitySlot>? = null - val job = underTest.forceHiddenSlots.onEach { latest = it }.launchIn(this) + val latest by collectLastValue(underTest.forceHiddenSlots) + runCurrent() whenever(connectivitySlots.getSlotFromName(SLOT_WIFI)).thenReturn(ConnectivitySlot.WIFI) whenever(connectivitySlots.getSlotFromName(SLOT_MOBILE)).thenReturn(null) @@ -172,8 +163,6 @@ class ConnectivityRepositoryImplTest : SysuiTestCase() { getTunable().onTuningChanged(HIDDEN_ICONS_TUNABLE_KEY, "$SLOT_WIFI,$SLOT_MOBILE") assertThat(latest).containsExactly(ConnectivitySlot.WIFI) - - job.cancel() } @Test @@ -181,23 +170,21 @@ class ConnectivityRepositoryImplTest : SysuiTestCase() { testScope.runTest { setUpEthernetWifiMobileSlotNames() - var latest: Set<ConnectivitySlot>? = null - val job = underTest.forceHiddenSlots.onEach { latest = it }.launchIn(this) + val latest by collectLastValue(underTest.forceHiddenSlots) + runCurrent() // WHEN there's empty and blank slot names getTunable().onTuningChanged(HIDDEN_ICONS_TUNABLE_KEY, "$SLOT_MOBILE, ,,$SLOT_WIFI") // THEN we skip that slot but still process the other ones assertThat(latest).containsExactly(ConnectivitySlot.WIFI, ConnectivitySlot.MOBILE) - - job.cancel() } @Test fun forceHiddenSlots_allInvalidOrEmptySlotNames_flowHasEmpty() = testScope.runTest { - var latest: Set<ConnectivitySlot>? = null - val job = underTest.forceHiddenSlots.onEach { latest = it }.launchIn(this) + val latest by collectLastValue(underTest.forceHiddenSlots) + runCurrent() whenever(connectivitySlots.getSlotFromName(SLOT_WIFI)).thenReturn(null) whenever(connectivitySlots.getSlotFromName(SLOT_ETHERNET)).thenReturn(null) @@ -210,8 +197,6 @@ class ConnectivityRepositoryImplTest : SysuiTestCase() { ) assertThat(latest).isEmpty() - - job.cancel() } @Test @@ -219,29 +204,25 @@ class ConnectivityRepositoryImplTest : SysuiTestCase() { testScope.runTest { setUpEthernetWifiMobileSlotNames() - var latest1: Set<ConnectivitySlot>? = null - val job1 = underTest.forceHiddenSlots.onEach { latest1 = it }.launchIn(this) + val latest1 by collectLastValue(underTest.forceHiddenSlots) + runCurrent() getTunable().onTuningChanged(HIDDEN_ICONS_TUNABLE_KEY, "$SLOT_WIFI,$SLOT_ETHERNET") assertThat(latest1).containsExactly(ConnectivitySlot.WIFI, ConnectivitySlot.ETHERNET) // WHEN we add a second subscriber after having already emitted a value - var latest2: Set<ConnectivitySlot>? = null - val job2 = underTest.forceHiddenSlots.onEach { latest2 = it }.launchIn(this) + val latest2 by collectLastValue(underTest.forceHiddenSlots) + runCurrent() // THEN the second subscribe receives the already-emitted value assertThat(latest2).containsExactly(ConnectivitySlot.WIFI, ConnectivitySlot.ETHERNET) - - job1.cancel() - job2.cancel() } @Test fun defaultConnections_noTransports_nothingIsDefault() = testScope.runTest { - var latest: DefaultConnectionModel? = null - val job = underTest.defaultConnections.onEach { latest = it }.launchIn(this) + val latest by collectLastValue(underTest.defaultConnections) val capabilities = mock<NetworkCapabilities>().also { @@ -256,15 +237,12 @@ class ConnectivityRepositoryImplTest : SysuiTestCase() { assertThat(latest!!.wifi.isDefault).isFalse() assertThat(latest!!.ethernet.isDefault).isFalse() assertThat(latest!!.carrierMerged.isDefault).isFalse() - - job.cancel() } @Test fun defaultConnections_cellularTransport_mobileIsDefault() = testScope.runTest { - var latest: DefaultConnectionModel? = null - val job = underTest.defaultConnections.onEach { latest = it }.launchIn(this) + val latest by collectLastValue(underTest.defaultConnections) val capabilities = mock<NetworkCapabilities>().also { @@ -279,15 +257,12 @@ class ConnectivityRepositoryImplTest : SysuiTestCase() { assertThat(latest!!.wifi.isDefault).isFalse() assertThat(latest!!.ethernet.isDefault).isFalse() assertThat(latest!!.carrierMerged.isDefault).isFalse() - - job.cancel() } @Test fun defaultConnections_wifiTransport_wifiIsDefault() = testScope.runTest { - var latest: DefaultConnectionModel? = null - val job = underTest.defaultConnections.onEach { latest = it }.launchIn(this) + val latest by collectLastValue(underTest.defaultConnections) val capabilities = mock<NetworkCapabilities>().also { @@ -302,15 +277,12 @@ class ConnectivityRepositoryImplTest : SysuiTestCase() { assertThat(latest!!.ethernet.isDefault).isFalse() assertThat(latest!!.carrierMerged.isDefault).isFalse() assertThat(latest!!.mobile.isDefault).isFalse() - - job.cancel() } @Test fun defaultConnections_ethernetTransport_ethernetIsDefault() = testScope.runTest { - var latest: DefaultConnectionModel? = null - val job = underTest.defaultConnections.onEach { latest = it }.launchIn(this) + val latest by collectLastValue(underTest.defaultConnections) val capabilities = mock<NetworkCapabilities>().also { @@ -325,15 +297,12 @@ class ConnectivityRepositoryImplTest : SysuiTestCase() { assertThat(latest!!.wifi.isDefault).isFalse() assertThat(latest!!.carrierMerged.isDefault).isFalse() assertThat(latest!!.mobile.isDefault).isFalse() - - job.cancel() } @Test fun defaultConnections_carrierMergedViaWifi_wifiAndCarrierMergedDefault() = testScope.runTest { - var latest: DefaultConnectionModel? = null - val job = underTest.defaultConnections.onEach { latest = it }.launchIn(this) + val latest by collectLastValue(underTest.defaultConnections) val carrierMergedInfo = mock<WifiInfo>().apply { whenever(this.isCarrierMerged).thenReturn(true) } @@ -350,15 +319,12 @@ class ConnectivityRepositoryImplTest : SysuiTestCase() { assertThat(latest!!.wifi.isDefault).isTrue() assertThat(latest!!.carrierMerged.isDefault).isTrue() assertThat(latest!!.mobile.isDefault).isFalse() - - job.cancel() } @Test fun defaultConnections_carrierMergedViaMobile_mobileCarrierMergedWifiDefault() = testScope.runTest { - var latest: DefaultConnectionModel? = null - val job = underTest.defaultConnections.onEach { latest = it }.launchIn(this) + val latest by collectLastValue(underTest.defaultConnections) val carrierMergedInfo = mock<WifiInfo>().apply { whenever(this.isCarrierMerged).thenReturn(true) } @@ -375,15 +341,12 @@ class ConnectivityRepositoryImplTest : SysuiTestCase() { assertThat(latest!!.mobile.isDefault).isTrue() assertThat(latest!!.carrierMerged.isDefault).isTrue() assertThat(latest!!.wifi.isDefault).isTrue() - - job.cancel() } @Test fun defaultConnections_carrierMergedViaWifiWithVcnTransport_wifiAndCarrierMergedDefault() = testScope.runTest { - var latest: DefaultConnectionModel? = null - val job = underTest.defaultConnections.onEach { latest = it }.launchIn(this) + val latest by collectLastValue(underTest.defaultConnections) val carrierMergedInfo = mock<WifiInfo>().apply { whenever(this.isCarrierMerged).thenReturn(true) } @@ -400,15 +363,13 @@ class ConnectivityRepositoryImplTest : SysuiTestCase() { assertThat(latest!!.wifi.isDefault).isTrue() assertThat(latest!!.carrierMerged.isDefault).isTrue() assertThat(latest!!.mobile.isDefault).isFalse() - - job.cancel() } + /** VCN over W+ (aka VCN over carrier merged). See b/352162710#comment27 scenario #1. */ @Test fun defaultConnections_carrierMergedViaMobileWithVcnTransport_mobileCarrierMergedWifiDefault() = testScope.runTest { - var latest: DefaultConnectionModel? = null - val job = underTest.defaultConnections.onEach { latest = it }.launchIn(this) + val latest by collectLastValue(underTest.defaultConnections) val carrierMergedInfo = mock<WifiInfo>().apply { whenever(this.isCarrierMerged).thenReturn(true) } @@ -425,15 +386,48 @@ class ConnectivityRepositoryImplTest : SysuiTestCase() { assertThat(latest!!.mobile.isDefault).isTrue() assertThat(latest!!.carrierMerged.isDefault).isTrue() assertThat(latest!!.wifi.isDefault).isTrue() + } + + /** VPN over W+ (aka VPN over carrier merged). See b/352162710#comment27 scenario #2. */ + @Test + @EnableFlags(FLAG_STATUS_BAR_ALWAYS_CHECK_UNDERLYING_NETWORKS) + fun defaultConnections_vpnOverCarrierMerged_carrierMergedDefault() = + testScope.runTest { + val latest by collectLastValue(underTest.defaultConnections) + + // Underlying carrier merged network + val underlyingCarrierMergedNetwork = mock<Network>() + val carrierMergedInfo = + mock<WifiInfo>().apply { whenever(this.isCarrierMerged).thenReturn(true) } + val underlyingCapabilities = + mock<NetworkCapabilities>().also { + whenever(it.hasTransport(TRANSPORT_WIFI)).thenReturn(true) + whenever(it.transportInfo).thenReturn(carrierMergedInfo) + } + whenever(connectivityManager.getNetworkCapabilities(underlyingCarrierMergedNetwork)) + .thenReturn(underlyingCapabilities) - job.cancel() + val mainCapabilities = + mock<NetworkCapabilities>().also { + whenever(it.hasTransport(TRANSPORT_ETHERNET)).thenReturn(false) + // Transports are WIFI|VPN, *not* CELLULAR. + whenever(it.hasTransport(TRANSPORT_CELLULAR)).thenReturn(false) + whenever(it.hasTransport(TRANSPORT_WIFI)).thenReturn(true) + whenever(it.hasTransport(TRANSPORT_VPN)).thenReturn(true) + whenever(it.transportInfo).thenReturn(VpnTransportInfo(0, null, false, false)) + whenever(it.underlyingNetworks) + .thenReturn(listOf(underlyingCarrierMergedNetwork)) + } + + getDefaultNetworkCallback().onCapabilitiesChanged(NETWORK, mainCapabilities) + + assertThat(latest!!.carrierMerged.isDefault).isTrue() } @Test fun defaultConnections_notCarrierMergedViaWifi_carrierMergedNotDefault() = testScope.runTest { - var latest: DefaultConnectionModel? = null - val job = underTest.defaultConnections.onEach { latest = it }.launchIn(this) + val latest by collectLastValue(underTest.defaultConnections) val carrierMergedInfo = mock<WifiInfo>().apply { whenever(this.isCarrierMerged).thenReturn(false) } @@ -448,15 +442,12 @@ class ConnectivityRepositoryImplTest : SysuiTestCase() { getDefaultNetworkCallback().onCapabilitiesChanged(NETWORK, capabilities) assertThat(latest!!.carrierMerged.isDefault).isFalse() - - job.cancel() } @Test fun defaultConnections_notCarrierMergedViaMobile_carrierMergedNotDefault() = testScope.runTest { - var latest: DefaultConnectionModel? = null - val job = underTest.defaultConnections.onEach { latest = it }.launchIn(this) + val latest by collectLastValue(underTest.defaultConnections) val carrierMergedInfo = mock<WifiInfo>().apply { whenever(this.isCarrierMerged).thenReturn(false) } @@ -471,15 +462,12 @@ class ConnectivityRepositoryImplTest : SysuiTestCase() { getDefaultNetworkCallback().onCapabilitiesChanged(NETWORK, capabilities) assertThat(latest!!.carrierMerged.isDefault).isFalse() - - job.cancel() } @Test fun defaultConnections_transportInfoNotWifi_wifiNotDefault() = testScope.runTest { - var latest: DefaultConnectionModel? = null - val job = underTest.defaultConnections.onEach { latest = it }.launchIn(this) + val latest by collectLastValue(underTest.defaultConnections) val capabilities = mock<NetworkCapabilities>().also { @@ -492,8 +480,6 @@ class ConnectivityRepositoryImplTest : SysuiTestCase() { getDefaultNetworkCallback().onCapabilitiesChanged(NETWORK, capabilities) assertThat(latest!!.wifi.isDefault).isFalse() - - job.cancel() } @Test @@ -531,8 +517,7 @@ class ConnectivityRepositoryImplTest : SysuiTestCase() { @Test fun defaultConnections_cellular_underlyingCarrierMergedViaWifi_allDefault() = testScope.runTest { - var latest: DefaultConnectionModel? = null - val job = underTest.defaultConnections.onEach { latest = it }.launchIn(this) + val latest by collectLastValue(underTest.defaultConnections) // Underlying carrier merged network val underlyingCarrierMergedNetwork = mock<Network>() @@ -560,16 +545,17 @@ class ConnectivityRepositoryImplTest : SysuiTestCase() { assertThat(latest!!.mobile.isDefault).isTrue() assertThat(latest!!.carrierMerged.isDefault).isTrue() assertThat(latest!!.wifi.isDefault).isTrue() - - job.cancel() } - /** Test for b/225902574. */ + /** + * Test for b/225902574: VPN over VCN over W+ (aka VPN over VCN over carrier merged). + * + * Also see b/352162710#comment27 scenario #3 and b/352162710#comment30. + */ @Test fun defaultConnections_cellular_underlyingCarrierMergedViaMobileWithVcnTransport_allDefault() = testScope.runTest { - var latest: DefaultConnectionModel? = null - val job = underTest.defaultConnections.onEach { latest = it }.launchIn(this) + val latest by collectLastValue(underTest.defaultConnections) // Underlying carrier merged network val underlyingCarrierMergedNetwork = mock<Network>() @@ -587,6 +573,7 @@ class ConnectivityRepositoryImplTest : SysuiTestCase() { val mainCapabilities = mock<NetworkCapabilities>().also { whenever(it.hasTransport(TRANSPORT_CELLULAR)).thenReturn(true) + whenever(it.hasTransport(TRANSPORT_VPN)).thenReturn(true) whenever(it.transportInfo).thenReturn(null) whenever(it.underlyingNetworks) .thenReturn(listOf(underlyingCarrierMergedNetwork)) @@ -597,15 +584,12 @@ class ConnectivityRepositoryImplTest : SysuiTestCase() { assertThat(latest!!.mobile.isDefault).isTrue() assertThat(latest!!.carrierMerged.isDefault).isTrue() assertThat(latest!!.wifi.isDefault).isTrue() - - job.cancel() } @Test fun defaultConnections_multipleTransports_multipleDefault() = testScope.runTest { - var latest: DefaultConnectionModel? = null - val job = underTest.defaultConnections.onEach { latest = it }.launchIn(this) + val latest by collectLastValue(underTest.defaultConnections) val capabilities = mock<NetworkCapabilities>().also { @@ -619,15 +603,12 @@ class ConnectivityRepositoryImplTest : SysuiTestCase() { assertThat(latest!!.mobile.isDefault).isTrue() assertThat(latest!!.ethernet.isDefault).isTrue() assertThat(latest!!.wifi.isDefault).isTrue() - - job.cancel() } @Test fun defaultConnections_hasValidated_isValidatedTrue() = testScope.runTest { - var latest: DefaultConnectionModel? = null - val job = underTest.defaultConnections.onEach { latest = it }.launchIn(this) + val latest by collectLastValue(underTest.defaultConnections) val capabilities = mock<NetworkCapabilities>().also { @@ -638,14 +619,12 @@ class ConnectivityRepositoryImplTest : SysuiTestCase() { getDefaultNetworkCallback().onCapabilitiesChanged(NETWORK, capabilities) assertThat(latest!!.isValidated).isTrue() - job.cancel() } @Test fun defaultConnections_noValidated_isValidatedFalse() = testScope.runTest { - var latest: DefaultConnectionModel? = null - val job = underTest.defaultConnections.onEach { latest = it }.launchIn(this) + val latest by collectLastValue(underTest.defaultConnections) val capabilities = mock<NetworkCapabilities>().also { @@ -656,7 +635,6 @@ class ConnectivityRepositoryImplTest : SysuiTestCase() { getDefaultNetworkCallback().onCapabilitiesChanged(NETWORK, capabilities) assertThat(latest!!.isValidated).isFalse() - job.cancel() } @Test @@ -669,8 +647,7 @@ class ConnectivityRepositoryImplTest : SysuiTestCase() { testScope.runTest { val vcnInfo = VcnTransportInfo(SUB_1_ID) - var latest: Int? = null - val job = underTest.vcnSubId.onEach { latest = it }.launchIn(this) + val latest by collectLastValue(underTest.vcnSubId) val capabilities = mock<NetworkCapabilities>().also { @@ -681,7 +658,6 @@ class ConnectivityRepositoryImplTest : SysuiTestCase() { getDefaultNetworkCallback().onCapabilitiesChanged(NETWORK, capabilities) assertThat(latest).isEqualTo(SUB_1_ID) - job.cancel() } @Test @@ -689,8 +665,7 @@ class ConnectivityRepositoryImplTest : SysuiTestCase() { testScope.runTest { val vcnInfo = VcnTransportInfo(INVALID_SUBSCRIPTION_ID) - var latest: Int? = null - val job = underTest.vcnSubId.onEach { latest = it }.launchIn(this) + val latest by collectLastValue(underTest.vcnSubId) val capabilities = mock<NetworkCapabilities>().also { @@ -701,14 +676,12 @@ class ConnectivityRepositoryImplTest : SysuiTestCase() { getDefaultNetworkCallback().onCapabilitiesChanged(NETWORK, capabilities) assertThat(latest).isNull() - job.cancel() } @Test fun vcnSubId_nullIfNoTransportInfo() = testScope.runTest { - var latest: Int? = null - val job = underTest.vcnSubId.onEach { latest = it }.launchIn(this) + val latest by collectLastValue(underTest.vcnSubId) val capabilities = mock<NetworkCapabilities>().also { @@ -719,7 +692,6 @@ class ConnectivityRepositoryImplTest : SysuiTestCase() { getDefaultNetworkCallback().onCapabilitiesChanged(NETWORK, capabilities) assertThat(latest).isNull() - job.cancel() } @Test @@ -728,8 +700,7 @@ class ConnectivityRepositoryImplTest : SysuiTestCase() { // If the underlying network of the VCN is a WiFi network, then there is no subId that // could disagree with telephony's active data subscription id. - var latest: Int? = null - val job = underTest.vcnSubId.onEach { latest = it }.launchIn(this) + val latest by collectLastValue(underTest.vcnSubId) val wifiInfo = mock<WifiInfo>() val vcnInfo = VcnTransportInfo(wifiInfo) @@ -742,14 +713,12 @@ class ConnectivityRepositoryImplTest : SysuiTestCase() { getDefaultNetworkCallback().onCapabilitiesChanged(NETWORK, capabilities) assertThat(latest).isNull() - job.cancel() } @Test fun vcnSubId_changingVcnInfoIsTracked() = testScope.runTest { - var latest: Int? = null - val job = underTest.vcnSubId.onEach { latest = it }.launchIn(this) + val latest by collectLastValue(underTest.vcnSubId) val wifiInfo = mock<WifiInfo>() val wifiVcnInfo = VcnTransportInfo(wifiInfo) @@ -788,8 +757,6 @@ class ConnectivityRepositoryImplTest : SysuiTestCase() { getDefaultNetworkCallback().onCapabilitiesChanged(NETWORK, capabilities) assertThat(latest).isNull() - - job.cancel() } @Test @@ -862,6 +829,7 @@ class ConnectivityRepositoryImplTest : SysuiTestCase() { } @Test + @DisableFlags(FLAG_STATUS_BAR_ALWAYS_CHECK_UNDERLYING_NETWORKS) fun getMainOrUnderlyingWifiInfo_notCellular_underlyingWifi_noInfo() { val underlyingNetwork = mock<Network>() val underlyingWifiInfo = mock<WifiInfo>() @@ -916,6 +884,7 @@ class ConnectivityRepositoryImplTest : SysuiTestCase() { } @Test + @DisableFlags(FLAG_STATUS_BAR_ALWAYS_CHECK_UNDERLYING_NETWORKS) fun getMainOrUnderlyingWifiInfo_notCellular_underlyingVcnWithWifi_noInfo() { val underlyingNetwork = mock<Network>() val underlyingVcnInfo = VcnTransportInfo(mock<WifiInfo>()) @@ -1076,12 +1045,13 @@ class ConnectivityRepositoryImplTest : SysuiTestCase() { testScope.backgroundScope, tunerService, ) + testScope.runCurrent() } private fun getTunable(): TunerService.Tunable { val callbackCaptor = argumentCaptor<TunerService.Tunable>() verify(tunerService).addTunable(callbackCaptor.capture(), any()) - return callbackCaptor.value!! + return callbackCaptor.firstValue } private fun setUpEthernetWifiMobileSlotNames() { @@ -1094,7 +1064,7 @@ class ConnectivityRepositoryImplTest : SysuiTestCase() { private fun getDefaultNetworkCallback(): ConnectivityManager.NetworkCallback { val callbackCaptor = argumentCaptor<ConnectivityManager.NetworkCallback>() verify(connectivityManager).registerDefaultNetworkCallback(callbackCaptor.capture()) - return callbackCaptor.value!! + return callbackCaptor.firstValue } private companion object { diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/ui/viewmodel/KeyguardStatusBarViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/ui/viewmodel/KeyguardStatusBarViewModelTest.kt index 12cfdcfa8df5..e396b567ac89 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/ui/viewmodel/KeyguardStatusBarViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/ui/viewmodel/KeyguardStatusBarViewModelTest.kt @@ -16,11 +16,11 @@ package com.android.systemui.statusbar.ui.viewmodel -import android.platform.test.annotations.EnableFlags import android.platform.test.flag.junit.FlagsParameterization import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.collectLastValue +import com.android.systemui.flags.EnableSceneContainer import com.android.systemui.flags.andSceneContainer import com.android.systemui.keyguard.data.repository.fakeDeviceEntryFaceAuthRepository import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository @@ -33,7 +33,6 @@ import com.android.systemui.scene.domain.interactor.sceneInteractor import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.statusbar.domain.interactor.keyguardStatusBarInteractor import com.android.systemui.statusbar.notification.data.repository.FakeHeadsUpRowRepository -import com.android.systemui.statusbar.notification.shared.NotificationsHeadsUpRefactor import com.android.systemui.statusbar.notification.stack.data.repository.headsUpNotificationRepository import com.android.systemui.statusbar.notification.stack.domain.interactor.headsUpNotificationInteractor import com.android.systemui.statusbar.policy.BatteryController @@ -127,7 +126,7 @@ class KeyguardStatusBarViewModelTest(flags: FlagsParameterization) : SysuiTestCa } @Test - @EnableFlags(NotificationsHeadsUpRefactor.FLAG_NAME) + @EnableSceneContainer fun isVisible_headsUpStatusBarShown_false() = testScope.runTest { val latest by collectLastValue(underTest.isVisible) diff --git a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeControllerCollectorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeControllerAdapterTest.kt index dd78e4a1fdaa..c1403649efdd 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeControllerCollectorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeControllerAdapterTest.kt @@ -19,16 +19,17 @@ package com.android.systemui.volume import android.media.IVolumeController import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest -import com.android.settingslib.media.data.repository.VolumeControllerEvent +import com.android.settingslib.volume.data.model.VolumeControllerEvent import com.android.systemui.SysuiTestCase import com.android.systemui.kosmos.applicationCoroutineScope import com.android.systemui.kosmos.testScope import com.android.systemui.testKosmos +import com.android.systemui.volume.data.repository.audioRepository import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.filterNotNull import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest +import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.mockito.kotlin.eq @@ -38,14 +39,20 @@ import org.mockito.kotlin.verify @OptIn(ExperimentalCoroutinesApi::class) @RunWith(AndroidJUnit4::class) @SmallTest -class VolumeControllerCollectorTest : SysuiTestCase() { +class VolumeControllerAdapterTest : SysuiTestCase() { private val kosmos = testKosmos() private val eventsFlow = MutableStateFlow<VolumeControllerEvent?>(null) - private val underTest = VolumeControllerCollector(kosmos.applicationCoroutineScope) + private val underTest = + with(kosmos) { VolumeControllerAdapter(applicationCoroutineScope, audioRepository) } private val volumeController = mock<IVolumeController> {} + @Before + fun setUp() { + kosmos.audioRepository.init() + } + @Test fun volumeControllerEvent_volumeChanged_callsMethod() = testEvent(VolumeControllerEvent.VolumeChanged(3, 0)) { @@ -90,7 +97,8 @@ class VolumeControllerCollectorTest : SysuiTestCase() { private fun testEvent(event: VolumeControllerEvent, verify: () -> Unit) = kosmos.testScope.runTest { - underTest.collectToController(eventsFlow.filterNotNull(), volumeController) + kosmos.audioRepository.sendVolumeControllerEvent(event) + underTest.collectToController(volumeController) eventsFlow.value = event runCurrent() diff --git a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogControllerImplTest.java index 4ea1a0ca9f2b..f62beeb16ae5 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogControllerImplTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogControllerImplTest.java @@ -48,9 +48,11 @@ import androidx.test.filters.SmallTest; import com.android.settingslib.flags.Flags; import com.android.systemui.SysuiTestCase; +import com.android.systemui.SysuiTestCaseExtKt; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.dump.DumpManager; import com.android.systemui.keyguard.WakefulnessLifecycle; +import com.android.systemui.kosmos.Kosmos; import com.android.systemui.plugins.VolumeDialogController; import com.android.systemui.settings.UserTracker; import com.android.systemui.statusbar.VibratorHelper; @@ -78,6 +80,8 @@ import java.util.concurrent.Executor; @TestableLooper.RunWithLooper public class VolumeDialogControllerImplTest extends SysuiTestCase { + private final Kosmos mKosmos = SysuiTestCaseExtKt.testKosmos(this); + TestableVolumeDialogControllerImpl mVolumeController; VolumeDialogControllerImpl.C mCallback; @Mock @@ -146,6 +150,7 @@ public class VolumeDialogControllerImplTest extends SysuiTestCase { mNotificationManager, mVibrator, mIAudioService, + VolumeControllerAdapterKosmosKt.getVolumeControllerAdapter(mKosmos), mAccessibilityManager, mPackageManager, mWakefullnessLifcycle, @@ -323,6 +328,7 @@ public class VolumeDialogControllerImplTest extends SysuiTestCase { NotificationManager notificationManager, VibratorHelper optionalVibrator, IAudioService iAudioService, + VolumeControllerAdapter volumeControllerAdapter, AccessibilityManager accessibilityManager, PackageManager packageManager, WakefulnessLifecycle wakefulnessLifecycle, @@ -342,6 +348,7 @@ public class VolumeDialogControllerImplTest extends SysuiTestCase { notificationManager, optionalVibrator, iAudioService, + volumeControllerAdapter, accessibilityManager, packageManager, wakefulnessLifecycle, diff --git a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogControllerImplTestKt.kt b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogControllerImplTestKt.kt new file mode 100644 index 000000000000..98cea9d92561 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogControllerImplTestKt.kt @@ -0,0 +1,156 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.volume + +import android.app.activityManager +import android.app.keyguardManager +import android.content.applicationContext +import android.content.packageManager +import android.media.AudioManager +import android.media.IVolumeController +import android.os.Handler +import android.os.looper +import android.platform.test.annotations.DisableFlags +import android.platform.test.annotations.EnableFlags +import android.platform.test.flag.junit.SetFlagsRule +import android.testing.TestableLooper +import android.view.accessibility.accessibilityManager +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.settingslib.volume.data.model.VolumeControllerEvent +import com.android.systemui.Flags +import com.android.systemui.SysuiTestCase +import com.android.systemui.dump.dumpManager +import com.android.systemui.keyguard.WakefulnessLifecycle +import com.android.systemui.keyguard.wakefulnessLifecycle +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.kosmos.testScope +import com.android.systemui.plugins.VolumeDialogController +import com.android.systemui.testKosmos +import com.android.systemui.util.RingerModeLiveData +import com.android.systemui.util.concurrency.FakeExecutor +import com.android.systemui.util.concurrency.FakeThreadFactory +import com.android.systemui.util.time.fakeSystemClock +import com.android.systemui.volume.data.repository.audioRepository +import com.android.systemui.volume.domain.interactor.audioSharingInteractor +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.runCurrent +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.kotlin.any +import org.mockito.kotlin.argumentCaptor +import org.mockito.kotlin.mock +import org.mockito.kotlin.verify +import org.mockito.kotlin.whenever + +@OptIn(ExperimentalCoroutinesApi::class) +@RunWith(AndroidJUnit4::class) +@SmallTest +@TestableLooper.RunWithLooper +class VolumeDialogControllerImplTestKt : SysuiTestCase() { + + @get:Rule val setFlagsRule = SetFlagsRule() + + private val kosmos: Kosmos = testKosmos() + private val audioManager: AudioManager = mock {} + private val callbacks: VolumeDialogController.Callbacks = mock {} + + private lateinit var threadFactory: FakeThreadFactory + private lateinit var underTest: VolumeDialogControllerImpl + + @Before + fun setUp() = + with(kosmos) { + audioRepository.init() + threadFactory = + FakeThreadFactory(FakeExecutor(fakeSystemClock)).apply { setLooper(looper) } + underTest = + VolumeDialogControllerImpl( + applicationContext, + mock {}, + mock { + on { ringerMode }.thenReturn(mock<RingerModeLiveData> {}) + on { ringerModeInternal }.thenReturn(mock<RingerModeLiveData> {}) + }, + threadFactory, + audioManager, + mock {}, + mock {}, + mock {}, + volumeControllerAdapter, + accessibilityManager, + packageManager, + wakefulnessLifecycle, + keyguardManager, + activityManager, + mock { on { userContext }.thenReturn(applicationContext) }, + dumpManager, + audioSharingInteractor, + mock {}, + ) + .apply { + setEnableDialogs(true, true) + addCallback(callbacks, Handler(looper)) + } + } + + @Test + @EnableFlags(Flags.FLAG_USE_VOLUME_CONTROLLER) + fun useVolumeControllerEnabled_listensToVolumeController() = + testVolumeController { stream: Int, flags: Int -> + audioRepository.sendVolumeControllerEvent( + VolumeControllerEvent.VolumeChanged(streamType = stream, flags = flags) + ) + } + + @Test + @DisableFlags(Flags.FLAG_USE_VOLUME_CONTROLLER) + fun useVolumeControllerDisabled_listensToVolumeController() = + testVolumeController { stream: Int, flags: Int -> + audioManager.emitVolumeChange(stream, flags) + } + + private fun testVolumeController( + emitVolumeChange: suspend Kosmos.(stream: Int, flags: Int) -> Unit + ) = + with(kosmos) { + testScope.runTest { + whenever(wakefulnessLifecycle.wakefulness) + .thenReturn(WakefulnessLifecycle.WAKEFULNESS_AWAKE) + underTest.setVolumeController() + runCurrent() + + emitVolumeChange(AudioManager.STREAM_SYSTEM, AudioManager.FLAG_SHOW_UI) + runCurrent() + TestableLooper.get(this@VolumeDialogControllerImplTestKt).processAllMessages() + + verify(callbacks) { 1 * { onShowRequested(any(), any(), any()) } } + } + } + + private companion object { + + private fun AudioManager.emitVolumeChange(stream: Int, flags: Int = 0) { + val captor = argumentCaptor<IVolumeController>() + verify(this) { 1 * { volumeController = captor.capture() } } + captor.firstValue.volumeChanged(stream, flags) + } + } +} 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 caa177908db0..1e2648b228f3 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java @@ -257,13 +257,13 @@ public class VolumeDialogImplTest extends SysuiTestCase { private State createShellState() { State state = new VolumeDialogController.State(); - for (int i = AudioManager.STREAM_VOICE_CALL; i <= AudioManager.STREAM_ACCESSIBILITY; i++) { + for (int stream : STREAMS.keySet()) { VolumeDialogController.StreamState ss = new VolumeDialogController.StreamState(); - ss.name = STREAMS.get(i); + ss.name = STREAMS.get(stream); ss.level = 1; ss.levelMin = 0; ss.levelMax = 25; - state.states.append(i, ss); + state.states.append(stream, ss); } return state; } diff --git a/packages/SystemUI/tests/utils/src/com/android/settingslib/notification/modes/ZenIconLoaderKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/settingslib/notification/modes/ZenIconLoaderKosmos.kt new file mode 100644 index 000000000000..8541d7704517 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/settingslib/notification/modes/ZenIconLoaderKosmos.kt @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settingslib.notification.modes + +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.kosmos.Kosmos.Fixture +import com.google.common.util.concurrent.MoreExecutors + +val Kosmos.zenIconLoader by Fixture { ZenIconLoader(MoreExecutors.newDirectExecutorService()) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduInteractorKosmos.kt index 88ab170d72b3..811c6533c656 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduInteractorKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduInteractorKosmos.kt @@ -16,6 +16,7 @@ package com.android.systemui.education.domain.interactor +import android.hardware.input.InputManager import com.android.systemui.education.data.repository.fakeEduClock import com.android.systemui.inputdevice.data.repository.UserInputDeviceRepository import com.android.systemui.keyboard.data.repository.keyboardRepository @@ -24,6 +25,7 @@ import com.android.systemui.kosmos.testDispatcher import com.android.systemui.kosmos.testScope import com.android.systemui.touchpad.data.repository.touchpadRepository import com.android.systemui.user.data.repository.userRepository +import org.mockito.kotlin.mock var Kosmos.keyboardTouchpadEduInteractor by Kosmos.Fixture { @@ -37,10 +39,13 @@ var Kosmos.keyboardTouchpadEduInteractor by touchpadRepository, userRepository ), - clock = fakeEduClock + clock = fakeEduClock, + inputManager = mockEduInputManager ) } +var Kosmos.mockEduInputManager by Kosmos.Fixture { mock<InputManager>() } + var Kosmos.keyboardTouchpadEduStatsInteractor by Kosmos.Fixture { KeyboardTouchpadEduStatsInteractorImpl( diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/EnableSceneContainer.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/flags/EnableSceneContainer.kt index 110797191d37..c252924f4d2d 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/EnableSceneContainer.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/flags/EnableSceneContainer.kt @@ -22,7 +22,7 @@ import com.android.systemui.Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR import com.android.systemui.Flags.FLAG_KEYGUARD_BOTTOM_AREA_REFACTOR import com.android.systemui.Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR import com.android.systemui.Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT -import com.android.systemui.Flags.FLAG_NOTIFICATIONS_HEADS_UP_REFACTOR +import com.android.systemui.Flags.FLAG_NOTIFICATION_AVALANCHE_THROTTLE_HUN import com.android.systemui.Flags.FLAG_PREDICTIVE_BACK_SYSUI import com.android.systemui.Flags.FLAG_SCENE_CONTAINER @@ -35,7 +35,7 @@ import com.android.systemui.Flags.FLAG_SCENE_CONTAINER FLAG_KEYGUARD_BOTTOM_AREA_REFACTOR, FLAG_KEYGUARD_WM_STATE_REFACTOR, FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT, - FLAG_NOTIFICATIONS_HEADS_UP_REFACTOR, + FLAG_NOTIFICATION_AVALANCHE_THROTTLE_HUN, FLAG_PREDICTIVE_BACK_SYSUI, FLAG_SCENE_CONTAINER, FLAG_DEVICE_ENTRY_UDFPS_REFACTOR, diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeDeviceEntryFaceAuthRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeDeviceEntryFaceAuthRepository.kt index e96aeada0212..5753c6c6e8ea 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeDeviceEntryFaceAuthRepository.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeDeviceEntryFaceAuthRepository.kt @@ -27,7 +27,6 @@ import javax.inject.Inject import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow -import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.filterNotNull @SysUISingleton @@ -37,38 +36,41 @@ class FakeDeviceEntryFaceAuthRepository @Inject constructor() : DeviceEntryFaceA private val _authenticationStatus = MutableStateFlow<FaceAuthenticationStatus?>(null) override val authenticationStatus: Flow<FaceAuthenticationStatus> = _authenticationStatus.filterNotNull() + fun setAuthenticationStatus(status: FaceAuthenticationStatus) { _authenticationStatus.value = status } + private val _detectionStatus = MutableStateFlow<FaceDetectionStatus?>(null) override val detectionStatus: Flow<FaceDetectionStatus> get() = _detectionStatus.filterNotNull() + fun setDetectionStatus(status: FaceDetectionStatus) { _detectionStatus.value = status } private val _isLockedOut = MutableStateFlow(false) override val isLockedOut = _isLockedOut - private val _runningAuthRequest = MutableStateFlow<Pair<FaceAuthUiEvent, Boolean>?>(null) - val runningAuthRequest: StateFlow<Pair<FaceAuthUiEvent, Boolean>?> = - _runningAuthRequest.asStateFlow() + val runningAuthRequest: MutableStateFlow<Pair<FaceAuthUiEvent, Boolean>?> = + MutableStateFlow(null) private val _isAuthRunning = MutableStateFlow(false) override val isAuthRunning: StateFlow<Boolean> = _isAuthRunning override val isBypassEnabled = MutableStateFlow(false) + override fun setLockedOut(isLockedOut: Boolean) { _isLockedOut.value = isLockedOut } override fun requestAuthenticate(uiEvent: FaceAuthUiEvent, fallbackToDetection: Boolean) { - _runningAuthRequest.value = uiEvent to fallbackToDetection + runningAuthRequest.value = uiEvent to fallbackToDetection _isAuthRunning.value = true } override fun cancel() { _isAuthRunning.value = false - _runningAuthRequest.value = null + runningAuthRequest.value = null } } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardTransitionRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardTransitionRepository.kt index 616f2b688746..a73c184a1ba8 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardTransitionRepository.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardTransitionRepository.kt @@ -102,6 +102,45 @@ class FakeKeyguardTransitionRepository( } /** + * Sends the provided [step] and makes sure that all previous [TransitionState]'s are sent when + * [fillInSteps] is true. e.g. when a step FINISHED is provided, a step with STARTED and RUNNING + * is also sent. + */ + suspend fun sendTransitionSteps( + step: TransitionStep, + testScope: TestScope, + fillInSteps: Boolean = true, + ) { + if (fillInSteps && step.transitionState != TransitionState.STARTED) { + sendTransitionStep( + step = + TransitionStep( + transitionState = TransitionState.STARTED, + from = step.from, + to = step.to, + value = 0f, + ) + ) + testScope.testScheduler.runCurrent() + + if (step.transitionState != TransitionState.RUNNING) { + sendTransitionStep( + step = + TransitionStep( + transitionState = TransitionState.RUNNING, + from = step.from, + to = step.to, + value = 0.6f, + ) + ) + testScope.testScheduler.runCurrent() + } + } + sendTransitionStep(step = step) + testScope.testScheduler.runCurrent() + } + + /** * Sends TransitionSteps between [from] and [to], calling [runCurrent] after each step. * * By default, sends steps through FINISHED (STARTED, RUNNING, FINISHED) but can be halted part diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/DismissKeyguardInteractor.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/DismissKeyguardInteractor.kt new file mode 100644 index 000000000000..82a5311269f0 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/DismissKeyguardInteractor.kt @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.keyguard.domain.interactor + +import com.android.systemui.keyguard.data.repository.keyguardTransitionRepository +import com.android.systemui.kosmos.Kosmos + +val Kosmos.keyguardDismissTransitionInteractor: KeyguardDismissTransitionInteractor by + Kosmos.Fixture { + KeyguardDismissTransitionInteractor( + repository = keyguardTransitionRepository, + fromLockscreenTransitionInteractor = fromLockscreenTransitionInteractor, + fromPrimaryBouncerTransitionInteractor = fromPrimaryBouncerTransitionInteractor, + fromAodTransitionInteractor = fromAodTransitionInteractor, + fromAlternateBouncerTransitionInteractor = fromAlternateBouncerTransitionInteractor, + fromDozingTransitionInteractor = fromDozingTransitionInteractor, + fromOccludedTransitionInteractor = fromOccludedTransitionInteractor, + ) + } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardEnabledInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardEnabledInteractorKosmos.kt index c6b5ed0b608f..007d2297e387 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardEnabledInteractorKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardEnabledInteractorKosmos.kt @@ -27,7 +27,7 @@ val Kosmos.keyguardEnabledInteractor by applicationCoroutineScope, keyguardRepository, biometricSettingsRepository, - keyguardTransitionInteractor, + keyguardDismissTransitionInteractor, internalTransitionInteractor = internalKeyguardTransitionInteractor, ) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorKosmos.kt index b68d6a0510d5..aa94c368e8f1 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorKosmos.kt @@ -16,7 +16,6 @@ package com.android.systemui.keyguard.domain.interactor -import com.android.systemui.keyguard.data.repository.keyguardRepository import com.android.systemui.keyguard.data.repository.keyguardTransitionRepository import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.applicationCoroutineScope @@ -27,13 +26,6 @@ val Kosmos.keyguardTransitionInteractor: KeyguardTransitionInteractor by KeyguardTransitionInteractor( scope = applicationCoroutineScope, repository = keyguardTransitionRepository, - keyguardRepository = keyguardRepository, - fromLockscreenTransitionInteractor = { fromLockscreenTransitionInteractor }, - fromPrimaryBouncerTransitionInteractor = { fromPrimaryBouncerTransitionInteractor }, - fromAodTransitionInteractor = { fromAodTransitionInteractor }, - fromAlternateBouncerTransitionInteractor = { fromAlternateBouncerTransitionInteractor }, - fromDozingTransitionInteractor = { fromDozingTransitionInteractor }, - fromOccludedTransitionInteractor = { fromOccludedTransitionInteractor }, sceneInteractor = sceneInteractor ) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModelKosmos.kt index 29583153ccc6..f1d87fe3abb7 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModelKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModelKosmos.kt @@ -19,6 +19,7 @@ package com.android.systemui.keyguard.ui.viewmodel import com.android.systemui.bouncer.domain.interactor.alternateBouncerInteractor +import com.android.systemui.bouncer.domain.interactor.primaryBouncerInteractor import com.android.systemui.keyguard.dismissCallbackRegistry import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor import com.android.systemui.kosmos.Kosmos @@ -32,5 +33,6 @@ val Kosmos.alternateBouncerViewModel by Fixture { keyguardTransitionInteractor = keyguardTransitionInteractor, dismissCallbackRegistry = dismissCallbackRegistry, alternateBouncerInteractor = { alternateBouncerInteractor }, + primaryBouncerInteractor = primaryBouncerInteractor, ) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToDozingTransitionViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToDozingTransitionViewModelKosmos.kt index a05e60672ef7..4196e54a085d 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToDozingTransitionViewModelKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToDozingTransitionViewModelKosmos.kt @@ -18,6 +18,7 @@ package com.android.systemui.keyguard.ui.viewmodel +import com.android.systemui.deviceentry.domain.interactor.deviceEntryUdfpsInteractor import com.android.systemui.keyguard.ui.keyguardTransitionAnimationFlow import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.Kosmos.Fixture @@ -25,6 +26,7 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi val Kosmos.occludedToDozingTransitionViewModel by Fixture { OccludedToDozingTransitionViewModel( + deviceEntryUdfpsInteractor = deviceEntryUdfpsInteractor, animationFlow = keyguardTransitionAnimationFlow, ) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/lifecycle/FakeSysUiViewModel.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/lifecycle/FakeSysUiViewModel.kt index 165246284b5f..2eb7ce603597 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/lifecycle/FakeSysUiViewModel.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/lifecycle/FakeSysUiViewModel.kt @@ -26,17 +26,25 @@ import kotlinx.coroutines.flow.flowOf class FakeSysUiViewModel( private val onActivation: () -> Unit = {}, private val onDeactivation: () -> Unit = {}, - private val upstreamFlow: Flow<Boolean> = flowOf(true), - private val upstreamStateFlow: StateFlow<Boolean> = MutableStateFlow(true).asStateFlow(), -) : SysUiViewModel, ExclusiveActivatable() { + upstreamFlow: Flow<Boolean> = flowOf(true), + upstreamStateFlow: StateFlow<Boolean> = MutableStateFlow(true).asStateFlow(), +) : ExclusiveActivatable() { var activationCount = 0 var cancellationCount = 0 - private val hydrator = Hydrator() + private val hydrator = Hydrator("test") val stateBackedByFlow: Boolean by - hydrator.hydratedStateOf(initialValue = true, source = upstreamFlow) - val stateBackedByStateFlow: Boolean by hydrator.hydratedStateOf(source = upstreamStateFlow) + hydrator.hydratedStateOf( + traceName = "test", + initialValue = true, + source = upstreamFlow, + ) + val stateBackedByStateFlow: Boolean by + hydrator.hydratedStateOf( + traceName = "test", + source = upstreamStateFlow, + ) override suspend fun onActivated(): Nothing { activationCount++ diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/domain/pipeline/MediaDataProcessorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/domain/pipeline/MediaDataProcessorKosmos.kt index 2127a88e5a45..632436a4574a 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/domain/pipeline/MediaDataProcessorKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/domain/pipeline/MediaDataProcessorKosmos.kt @@ -18,7 +18,6 @@ package com.android.systemui.media.controls.domain.pipeline import android.app.smartspace.SmartspaceManager import android.content.applicationContext -import android.os.fakeExecutorHandler import com.android.keyguard.keyguardUpdateMonitor import com.android.systemui.broadcast.broadcastDispatcher import com.android.systemui.concurrency.fakeExecutor @@ -45,7 +44,7 @@ val Kosmos.mediaDataProcessor by backgroundExecutor = fakeExecutor, uiExecutor = fakeExecutor, foregroundExecutor = fakeExecutor, - handler = fakeExecutorHandler, + mainDispatcher = testDispatcher, mediaControllerFactory = fakeMediaControllerFactory, broadcastDispatcher = broadcastDispatcher, dumpManager = dumpManager, @@ -60,5 +59,6 @@ val Kosmos.mediaDataProcessor by smartspaceManager = SmartspaceManager(applicationContext), keyguardUpdateMonitor = keyguardUpdateMonitor, mediaDataRepository = mediaDataRepository, + mediaDataLoader = { mediaDataLoader }, ) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceLoggerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceLoggerKosmos.kt new file mode 100644 index 000000000000..76d71dd05edd --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceLoggerKosmos.kt @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.media.controls.domain.pipeline + +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.log.logcatLogBuffer + +var Kosmos.mediaDeviceLogger by + Kosmos.Fixture { MediaDeviceLogger(logcatLogBuffer("MediaDeviceLoggerKosmos")) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManagerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManagerKosmos.kt index c479ce676761..11408d8a4b90 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManagerKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManagerKosmos.kt @@ -41,5 +41,6 @@ val Kosmos.mediaDeviceManager by }, fgExecutor = fakeExecutor, bgExecutor = fakeExecutor, + logger = mediaDeviceLogger, ) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneKosmos.kt index dd931410b003..7dfe8027d783 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneKosmos.kt @@ -1,10 +1,12 @@ package com.android.systemui.scene +import com.android.compose.animation.scene.OverlayKey import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.Kosmos.Fixture import com.android.systemui.scene.shared.model.FakeScene import com.android.systemui.scene.shared.model.SceneContainerConfig import com.android.systemui.scene.shared.model.Scenes +import com.android.systemui.scene.ui.FakeOverlay var Kosmos.sceneKeys by Fixture { listOf( @@ -22,6 +24,17 @@ val Kosmos.fakeScenes by Fixture { sceneKeys.map { key -> FakeScene(key) }.toSet val Kosmos.scenes by Fixture { fakeScenes } val Kosmos.initialSceneKey by Fixture { Scenes.Lockscreen } + +var Kosmos.overlayKeys by Fixture { + listOf<OverlayKey>( + // TODO(b/356596436): Add overlays here when we have them. + ) +} + +val Kosmos.fakeOverlays by Fixture { overlayKeys.map { key -> FakeOverlay(key) }.toSet() } + +val Kosmos.overlays by Fixture { fakeOverlays } + var Kosmos.sceneContainerConfig by Fixture { val navigationDistances = mapOf( @@ -32,5 +45,11 @@ var Kosmos.sceneContainerConfig by Fixture { Scenes.QuickSettings to 3, Scenes.Bouncer to 4, ) - SceneContainerConfig(sceneKeys, initialSceneKey, navigationDistances) + + SceneContainerConfig( + sceneKeys = sceneKeys, + initialSceneKey = initialSceneKey, + overlayKeys = overlayKeys, + navigationDistances = navigationDistances, + ) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/data/repository/SceneContainerRepositoryUtil.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/data/repository/SceneContainerRepositoryUtil.kt index 53d3c0121b1f..59f2b9412413 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/data/repository/SceneContainerRepositoryUtil.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/data/repository/SceneContainerRepositoryUtil.kt @@ -19,6 +19,7 @@ package com.android.systemui.scene.data.repository import com.android.compose.animation.scene.ObservableTransitionState import com.android.compose.animation.scene.SceneKey import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository +import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.shared.model.TransitionStep import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.testScope @@ -36,20 +37,26 @@ private val mutableTransitionState = suspend fun Kosmos.setTransition( sceneTransition: ObservableTransitionState, stateTransition: TransitionStep? = null, + fillInStateSteps: Boolean = true, scope: TestScope = testScope, repository: SceneContainerRepository = sceneContainerRepository ) { + var state: TransitionStep? = stateTransition if (SceneContainerFlag.isEnabled) { setSceneTransition(sceneTransition, scope, repository) - } else { - if (stateTransition == null) throw IllegalArgumentException("No transitionStep provided") - fakeKeyguardTransitionRepository.sendTransitionSteps( - from = stateTransition.from, - to = stateTransition.to, - testScope = scope, - throughTransitionState = stateTransition.transitionState - ) + + if (state != null) { + state = getStateWithUndefined(sceneTransition, state) + } } + + if (state == null) return + fakeKeyguardTransitionRepository.sendTransitionSteps( + step = state, + testScope = scope, + fillInSteps = fillInStateSteps, + ) + scope.testScheduler.runCurrent() } fun Kosmos.setSceneTransition( @@ -59,7 +66,7 @@ fun Kosmos.setSceneTransition( ) { repository.setTransitionState(mutableTransitionState) mutableTransitionState.value = transition - scope.runCurrent() + scope.testScheduler.runCurrent() } fun Transition( @@ -87,3 +94,43 @@ fun Transition( fun Idle(currentScene: SceneKey): ObservableTransitionState.Idle { return ObservableTransitionState.Idle(currentScene) } + +private fun getStateWithUndefined( + sceneTransition: ObservableTransitionState, + state: TransitionStep +): TransitionStep { + return when (sceneTransition) { + is ObservableTransitionState.Idle -> { + TransitionStep( + from = state.from, + to = + if (sceneTransition.currentScene != Scenes.Lockscreen) { + KeyguardState.UNDEFINED + } else { + state.to + }, + value = state.value, + transitionState = state.transitionState + ) + } + is ObservableTransitionState.Transition -> { + TransitionStep( + from = + if (sceneTransition.fromScene != Scenes.Lockscreen) { + KeyguardState.UNDEFINED + } else { + state.from + }, + to = + if (sceneTransition.toScene != Scenes.Lockscreen) { + KeyguardState.UNDEFINED + } else { + state.from + }, + value = state.value, + transitionState = state.transitionState + ) + } + else -> state + } +} diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/shared/model/FakeSceneDataSource.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/shared/model/FakeSceneDataSource.kt index 957a60f83134..f52572a9e42d 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/shared/model/FakeSceneDataSource.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/shared/model/FakeSceneDataSource.kt @@ -16,6 +16,7 @@ package com.android.systemui.scene.shared.model +import com.android.compose.animation.scene.OverlayKey import com.android.compose.animation.scene.SceneKey import com.android.compose.animation.scene.TransitionKey import kotlinx.coroutines.flow.MutableStateFlow @@ -29,11 +30,18 @@ class FakeSceneDataSource( private val _currentScene = MutableStateFlow(initialSceneKey) override val currentScene: StateFlow<SceneKey> = _currentScene.asStateFlow() + private val _currentOverlays = MutableStateFlow<Set<OverlayKey>>(emptySet()) + override val currentOverlays: StateFlow<Set<OverlayKey>> = _currentOverlays.asStateFlow() + var isPaused = false private set + var pendingScene: SceneKey? = null private set + var pendingOverlays: Set<OverlayKey>? = null + private set + override fun changeScene(toScene: SceneKey, transitionKey: TransitionKey?) { if (isPaused) { pendingScene = toScene @@ -46,10 +54,32 @@ class FakeSceneDataSource( changeScene(toScene) } + override fun showOverlay(overlay: OverlayKey, transitionKey: TransitionKey?) { + if (isPaused) { + pendingOverlays = (pendingOverlays ?: currentOverlays.value) + overlay + } else { + _currentOverlays.value += overlay + } + } + + override fun hideOverlay(overlay: OverlayKey, transitionKey: TransitionKey?) { + if (isPaused) { + pendingOverlays = (pendingOverlays ?: currentOverlays.value) - overlay + } else { + _currentOverlays.value -= overlay + } + } + + override fun replaceOverlay(from: OverlayKey, to: OverlayKey, transitionKey: TransitionKey?) { + hideOverlay(from, transitionKey) + showOverlay(to, transitionKey) + } + /** - * Pauses scene changes. + * Pauses scene and overlay changes. * - * Any following calls to [changeScene] will be conflated and the last one will be remembered. + * Any following calls to [changeScene] or overlay changing functions will be conflated and the + * last one will be remembered. */ fun pause() { check(!isPaused) { "Can't pause what's already paused!" } @@ -58,11 +88,14 @@ class FakeSceneDataSource( } /** - * Unpauses scene changes. + * Unpauses scene and overlay changes. * * If there were any calls to [changeScene] since [pause] was called, the latest of the bunch * will be replayed. * + * If there were any calls to show, hide or replace overlays since [pause] was called, they will + * all be applied at once. + * * If [force] is `true`, there will be no check that [isPaused] is true. * * If [expectedScene] is provided, will assert that it's indeed the latest called. @@ -76,6 +109,8 @@ class FakeSceneDataSource( isPaused = false pendingScene?.let { _currentScene.value = it } pendingScene = null + pendingOverlays?.let { _currentOverlays.value = it } + pendingOverlays = null check(expectedScene == null || currentScene.value == expectedScene) { """ diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/ui/FakeOverlay.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/ui/FakeOverlay.kt new file mode 100644 index 000000000000..f4f30cd1d5dd --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/ui/FakeOverlay.kt @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.scene.ui + +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import com.android.compose.animation.scene.ContentScope +import com.android.compose.animation.scene.OverlayKey +import com.android.systemui.lifecycle.ExclusiveActivatable +import com.android.systemui.scene.ui.composable.Overlay +import kotlinx.coroutines.awaitCancellation + +class FakeOverlay( + override val key: OverlayKey, +) : ExclusiveActivatable(), Overlay { + + @Composable override fun ContentScope.Content(modifier: Modifier) = Unit + + override suspend fun onActivated(): Nothing { + awaitCancellation() + } +} diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/ron/demo/ui/viewmodel/DemoRonChipViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/ron/demo/ui/viewmodel/DemoRonChipViewModelKosmos.kt new file mode 100644 index 000000000000..c0d65a076ca0 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/ron/demo/ui/viewmodel/DemoRonChipViewModelKosmos.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.chips.ron.demo.ui.viewmodel + +import android.content.packageManager +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.statusbar.commandline.commandRegistry +import com.android.systemui.util.time.fakeSystemClock + +val Kosmos.demoRonChipViewModel: DemoRonChipViewModel by + Kosmos.Fixture { + DemoRonChipViewModel( + commandRegistry = commandRegistry, + packageManager = packageManager, + systemClock = fakeSystemClock, + ) + } 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 16e288fcf113..5382c1c4b8d0 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 @@ -20,6 +20,7 @@ import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.testScope import com.android.systemui.statusbar.chips.call.ui.viewmodel.callChipViewModel import com.android.systemui.statusbar.chips.casttootherdevice.ui.viewmodel.castToOtherDeviceChipViewModel +import com.android.systemui.statusbar.chips.ron.demo.ui.viewmodel.demoRonChipViewModel import com.android.systemui.statusbar.chips.screenrecord.ui.viewmodel.screenRecordChipViewModel import com.android.systemui.statusbar.chips.sharetoapp.ui.viewmodel.shareToAppChipViewModel import com.android.systemui.statusbar.chips.statusBarChipsLogger @@ -32,6 +33,7 @@ val Kosmos.ongoingActivityChipsViewModel: OngoingActivityChipsViewModel by shareToAppChipViewModel = shareToAppChipViewModel, castToOtherDeviceChipViewModel = castToOtherDeviceChipViewModel, callChipViewModel = callChipViewModel, + demoRonChipViewModel = demoRonChipViewModel, logger = statusBarChipsLogger, ) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/commandline/CommandRegistryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/commandline/CommandRegistryKosmos.kt new file mode 100644 index 000000000000..14777b4bc492 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/commandline/CommandRegistryKosmos.kt @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.commandline + +import android.content.applicationContext +import com.android.systemui.kosmos.Kosmos + +val Kosmos.commandRegistry: CommandRegistry by + Kosmos.Fixture { + CommandRegistry( + context = applicationContext, + // Immediately run anything that comes in + mainExecutor = { command -> command.run() }, + ) + } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowBuilder.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowBuilder.kt index 4dd3ae762db7..2eb1573dc3d7 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowBuilder.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowBuilder.kt @@ -35,7 +35,9 @@ import com.android.systemui.dump.DumpManager import com.android.systemui.flags.FakeFeatureFlagsClassic import com.android.systemui.flags.FeatureFlags import com.android.systemui.flags.Flags +import com.android.systemui.media.controls.util.MediaFeatureFlag import com.android.systemui.media.dialog.MediaOutputDialogManager +import com.android.systemui.plugins.ActivityStarter import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.shared.system.ActivityManagerWrapper import com.android.systemui.shared.system.DevicePolicyManagerWrapper @@ -46,6 +48,7 @@ import com.android.systemui.statusbar.NotificationShadeWindowController import com.android.systemui.statusbar.RankingBuilder import com.android.systemui.statusbar.SmartReplyController import com.android.systemui.statusbar.notification.ColorUpdateLogger +import com.android.systemui.statusbar.notification.ConversationNotificationManager import com.android.systemui.statusbar.notification.ConversationNotificationProcessor import com.android.systemui.statusbar.notification.collection.NotificationEntry import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder @@ -69,6 +72,7 @@ import com.android.systemui.statusbar.notification.row.NotificationRowContentBin 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.phone.KeyguardDismissUtil import com.android.systemui.statusbar.policy.HeadsUpManager import com.android.systemui.statusbar.policy.SmartActionInflaterImpl import com.android.systemui.statusbar.policy.SmartReplyConstants @@ -84,6 +88,7 @@ import com.android.systemui.util.time.FakeSystemClock import com.android.systemui.wmshell.BubblesManager import java.util.Optional import java.util.concurrent.CountDownLatch +import java.util.concurrent.Executor import java.util.concurrent.TimeUnit import kotlinx.coroutines.test.TestScope import org.junit.Assert.assertTrue @@ -128,19 +133,19 @@ class ExpandableNotificationRowBuilder( dependency.injectMockDependency(NotificationShadeWindowController::class.java) dependency.injectMockDependency(MediaOutputDialogManager::class.java) - mMockLogger = Mockito.mock(ExpandableNotificationRowLogger::class.java) - mStatusBarStateController = Mockito.mock(StatusBarStateController::class.java) - mKeyguardBypassController = Mockito.mock(KeyguardBypassController::class.java) + mMockLogger = Mockito.mock(ExpandableNotificationRowLogger::class.java, STUB_ONLY) + mStatusBarStateController = Mockito.mock(StatusBarStateController::class.java, STUB_ONLY) + mKeyguardBypassController = Mockito.mock(KeyguardBypassController::class.java, STUB_ONLY) mGroupMembershipManager = GroupMembershipManagerImpl() - mSmartReplyController = Mockito.mock(SmartReplyController::class.java) + mSmartReplyController = Mockito.mock(SmartReplyController::class.java, STUB_ONLY) val dumpManager = DumpManager() mGroupExpansionManager = GroupExpansionManagerImpl(dumpManager, mGroupMembershipManager) - mHeadsUpManager = Mockito.mock(HeadsUpManager::class.java) + mHeadsUpManager = Mockito.mock(HeadsUpManager::class.java, STUB_ONLY) mIconManager = IconManager( - Mockito.mock(CommonNotifCollection::class.java), - Mockito.mock(LauncherApps::class.java), + Mockito.mock(CommonNotifCollection::class.java, STUB_ONLY), + Mockito.mock(LauncherApps::class.java, STUB_ONLY), IconBuilder(context), mTestScope, mBgCoroutineContext, @@ -173,7 +178,7 @@ class ExpandableNotificationRowBuilder( } ) val remoteViewsFactories = getNotifRemoteViewsFactoryContainer(featureFlags) - val remoteInputManager = Mockito.mock(NotificationRemoteInputManager::class.java) + val remoteInputManager = Mockito.mock(NotificationRemoteInputManager::class.java, STUB_ONLY) val smartReplyStateInflater = SmartReplyStateInflaterImpl( constants = mSmartReplyConstants, @@ -183,7 +188,8 @@ class ExpandableNotificationRowBuilder( smartRepliesInflater = SmartReplyInflaterImpl( constants = mSmartReplyConstants, - keyguardDismissUtil = mock(), + keyguardDismissUtil = + Mockito.mock(KeyguardDismissUtil::class.java, STUB_ONLY), remoteInputManager = remoteInputManager, smartReplyController = mSmartReplyController, context = context @@ -191,7 +197,7 @@ class ExpandableNotificationRowBuilder( smartActionsInflater = SmartActionInflaterImpl( constants = mSmartReplyConstants, - activityStarter = mock(), + activityStarter = Mockito.mock(ActivityStarter::class.java, STUB_ONLY), smartReplyController = mSmartReplyController, headsUpManager = mHeadsUpManager ) @@ -206,41 +212,42 @@ class ExpandableNotificationRowBuilder( } val conversationProcessor = ConversationNotificationProcessor( - mock(), - mock(), + Mockito.mock(LauncherApps::class.java, STUB_ONLY), + Mockito.mock(ConversationNotificationManager::class.java, STUB_ONLY), ) + mContentBinder = if (NotificationRowContentBinderRefactor.isEnabled) NotificationRowContentBinderImpl( - mock(), + Mockito.mock(NotifRemoteViewCache::class.java, STUB_ONLY), remoteInputManager, conversationProcessor, - mock(), - mock(), - mock(), + Mockito.mock(RichOngoingNotificationContentExtractor::class.java, STUB_ONLY), + Mockito.mock(RichOngoingNotificationViewInflater::class.java, STUB_ONLY), + Mockito.mock(Executor::class.java, STUB_ONLY), smartReplyStateInflater, notifLayoutInflaterFactoryProvider, - mock(), - mock(), + Mockito.mock(HeadsUpStyleProvider::class.java, STUB_ONLY), + Mockito.mock(NotificationRowContentBinderLogger::class.java, STUB_ONLY), ) else NotificationContentInflater( - mock(), + Mockito.mock(NotifRemoteViewCache::class.java, STUB_ONLY), remoteInputManager, conversationProcessor, - mock(), - mock(), + Mockito.mock(MediaFeatureFlag::class.java, STUB_ONLY), + Mockito.mock(Executor::class.java, STUB_ONLY), smartReplyStateInflater, notifLayoutInflaterFactoryProvider, - mock(), - mock(), + Mockito.mock(HeadsUpStyleProvider::class.java, STUB_ONLY), + Mockito.mock(NotificationRowContentBinderLogger::class.java, STUB_ONLY), ) mContentBinder.setInflateSynchronously(true) mBindStage = RowContentBindStage( mContentBinder, - mock(), - mock(), + Mockito.mock(NotifInflationErrorManager::class.java, STUB_ONLY), + Mockito.mock(RowContentBindStageLogger::class.java, STUB_ONLY), ) val collection = Mockito.mock(CommonNotifCollection::class.java) @@ -248,7 +255,7 @@ class ExpandableNotificationRowBuilder( mBindPipeline = NotifBindPipeline( collection, - Mockito.mock(NotifBindPipelineLogger::class.java), + Mockito.mock(NotifBindPipelineLogger::class.java, STUB_ONLY), NotificationEntryProcessorFactoryExecutorImpl(mMainExecutor), ) mBindPipeline.setStage(mBindStage) @@ -256,9 +263,11 @@ class ExpandableNotificationRowBuilder( val collectionListenerCaptor = ArgumentCaptor.forClass(NotifCollectionListener::class.java) Mockito.verify(collection).addCollectionListener(collectionListenerCaptor.capture()) mBindPipelineEntryListener = collectionListenerCaptor.value - mPeopleNotificationIdentifier = Mockito.mock(PeopleNotificationIdentifier::class.java) + mPeopleNotificationIdentifier = + Mockito.mock(PeopleNotificationIdentifier::class.java, STUB_ONLY) mOnUserInteractionCallback = Mockito.mock(OnUserInteractionCallback::class.java) - mDismissibilityProvider = Mockito.mock(NotificationDismissibilityProvider::class.java) + mDismissibilityProvider = + Mockito.mock(NotificationDismissibilityProvider::class.java, STUB_ONLY) val mFutureDismissalRunnable = Mockito.mock(Runnable::class.java) whenever( mOnUserInteractionCallback.registerFutureDismissal( @@ -320,7 +329,10 @@ class ExpandableNotificationRowBuilder( // set, but we do not want to override an existing value that is needed by a specific test. val rowInflaterTask = - RowInflaterTask(mFakeSystemClock, Mockito.mock(RowInflaterTaskLogger::class.java)) + RowInflaterTask( + mFakeSystemClock, + Mockito.mock(RowInflaterTaskLogger::class.java, STUB_ONLY) + ) val row = rowInflaterTask.inflateSynchronously(context, null, entry) entry.row = row @@ -329,7 +341,7 @@ class ExpandableNotificationRowBuilder( mBindPipeline.manageRow(entry, row) row.initialize( entry, - Mockito.mock(RemoteInputViewSubcomponent.Factory::class.java), + Mockito.mock(RemoteInputViewSubcomponent.Factory::class.java, STUB_ONLY), APP_NAME, entry.key, mMockLogger, @@ -338,23 +350,23 @@ class ExpandableNotificationRowBuilder( mGroupExpansionManager, mHeadsUpManager, mBindStage, - Mockito.mock(OnExpandClickListener::class.java), - Mockito.mock(CoordinateOnClickListener::class.java), + Mockito.mock(OnExpandClickListener::class.java, STUB_ONLY), + Mockito.mock(CoordinateOnClickListener::class.java, STUB_ONLY), FalsingManagerFake(), mStatusBarStateController, mPeopleNotificationIdentifier, mOnUserInteractionCallback, - Optional.of(Mockito.mock(BubblesManager::class.java)), - Mockito.mock(NotificationGutsManager::class.java), + Optional.of(Mockito.mock(BubblesManager::class.java, STUB_ONLY)), + Mockito.mock(NotificationGutsManager::class.java, STUB_ONLY), mDismissibilityProvider, - Mockito.mock(MetricsLogger::class.java), - Mockito.mock(NotificationChildrenContainerLogger::class.java), - Mockito.mock(ColorUpdateLogger::class.java), + Mockito.mock(MetricsLogger::class.java, STUB_ONLY), + Mockito.mock(NotificationChildrenContainerLogger::class.java, STUB_ONLY), + Mockito.mock(ColorUpdateLogger::class.java, STUB_ONLY), mSmartReplyConstants, mSmartReplyController, featureFlags, - Mockito.mock(IStatusBarService::class.java), - Mockito.mock(UiEventLogger::class.java) + Mockito.mock(IStatusBarService::class.java, STUB_ONLY), + Mockito.mock(UiEventLogger::class.java, STUB_ONLY) ) row.setAboveShelfChangedListener { aboveShelf: Boolean -> } mBindStage.getStageParams(entry).requireContentViews(extraInflationFlags) @@ -381,6 +393,8 @@ class ExpandableNotificationRowBuilder( private val Notification.isConversationStyleNotification get() = extras.getBoolean(IS_CONVERSATION_FLAG, false) + private val STUB_ONLY = Mockito.withSettings().stubOnly() + fun markAsConversation(builder: Notification.Builder) { builder.addExtras(bundleOf(IS_CONVERSATION_FLAG to true)) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/domain/interactor/ZenModeInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/domain/interactor/ZenModeInteractorKosmos.kt index 66be7e7a7a7e..61b53c9a2067 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/domain/interactor/ZenModeInteractorKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/domain/interactor/ZenModeInteractorKosmos.kt @@ -17,8 +17,10 @@ package com.android.systemui.statusbar.policy.domain.interactor import android.content.testableContext +import com.android.settingslib.notification.modes.zenIconLoader import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.Kosmos.Fixture +import com.android.systemui.kosmos.testDispatcher import com.android.systemui.shared.notifications.data.repository.notificationSettingsRepository import com.android.systemui.statusbar.policy.data.repository.zenModeRepository @@ -27,5 +29,7 @@ val Kosmos.zenModeInteractor by Fixture { context = testableContext, zenModeRepository = zenModeRepository, notificationSettingsRepository = notificationSettingsRepository, + bgDispatcher = testDispatcher, + iconLoader = zenIconLoader, ) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeStatusBarIconController.java b/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeStatusBarIconController.java index 2dbac670b298..0089199cfb88 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeStatusBarIconController.java +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeStatusBarIconController.java @@ -64,7 +64,8 @@ public class FakeStatusBarIconController extends BaseLeakChecker<IconManager> @Override public void setResourceIcon(String slot, @Nullable String resPackage, int iconResId, - @Nullable Drawable preloadedIcon, CharSequence contentDescription) { + @Nullable Drawable preloadedIcon, CharSequence contentDescription, + StatusBarIcon.Shape shape) { } @Override diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/VolumeControllerCollectorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/VolumeControllerAdapterKosmos.kt index d60f14cab28f..4045135b928e 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/VolumeControllerCollectorKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/VolumeControllerAdapterKosmos.kt @@ -18,6 +18,7 @@ package com.android.systemui.volume import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.applicationCoroutineScope +import com.android.systemui.volume.data.repository.audioRepository -val Kosmos.volumeControllerCollector by - Kosmos.Fixture { VolumeControllerCollector(applicationCoroutineScope) } +val Kosmos.volumeControllerAdapter by + Kosmos.Fixture { VolumeControllerAdapter(applicationCoroutineScope, audioRepository) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/data/repository/FakeAudioRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/data/repository/FakeAudioRepository.kt index 135cb14a3497..1fa6c3f2327b 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/data/repository/FakeAudioRepository.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/data/repository/FakeAudioRepository.kt @@ -18,12 +18,16 @@ package com.android.systemui.volume.data.repository import android.media.AudioDeviceInfo import android.media.AudioManager +import com.android.settingslib.volume.data.model.VolumeControllerEvent import com.android.settingslib.volume.data.repository.AudioRepository import com.android.settingslib.volume.shared.model.AudioStream import com.android.settingslib.volume.shared.model.AudioStreamModel import com.android.settingslib.volume.shared.model.RingerMode +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asSharedFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.update @@ -39,10 +43,26 @@ class FakeAudioRepository : AudioRepository { override val communicationDevice: StateFlow<AudioDeviceInfo?> = mutableCommunicationDevice.asStateFlow() + private val mutableVolumeControllerEvents = MutableSharedFlow<VolumeControllerEvent>(replay = 1) + override val volumeControllerEvents: Flow<VolumeControllerEvent> + get() = mutableVolumeControllerEvents.asSharedFlow() + private val models: MutableMap<AudioStream, MutableStateFlow<AudioStreamModel>> = mutableMapOf() private val lastAudibleVolumes: MutableMap<AudioStream, Int> = mutableMapOf() private val deviceCategories: MutableMap<String, Int> = mutableMapOf() + private val mutableIsVolumeControllerVisible = MutableStateFlow(false) + val isVolumeControllerVisible: StateFlow<Boolean> + get() = mutableIsVolumeControllerVisible.asStateFlow() + + private var mutableIsInitialized: Boolean = false + val isInitialized: Boolean + get() = mutableIsInitialized + + override fun init() { + mutableIsInitialized = true + } + private fun getAudioStreamModelState( audioStream: AudioStream ): MutableStateFlow<AudioStreamModel> = @@ -111,4 +131,16 @@ class FakeAudioRepository : AudioRepository { override suspend fun getBluetoothAudioDeviceCategory(bluetoothAddress: String): Int { return deviceCategories[bluetoothAddress] ?: AudioManager.AUDIO_DEVICE_CATEGORY_UNKNOWN } + + suspend fun sendVolumeControllerEvent(event: VolumeControllerEvent) { + if (isInitialized) { + mutableVolumeControllerEvents.emit(event) + } + } + + override suspend fun notifyVolumeControllerVisible(isVisible: Boolean) { + if (isInitialized) { + mutableIsVolumeControllerVisible.value = isVisible + } + } } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/volume/domain/interactor/AudioSlidersInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/volume/domain/interactor/AudioSlidersInteractorKosmos.kt index 63386d016a7a..dd5bbf37c0f1 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/volume/domain/interactor/AudioSlidersInteractorKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/volume/domain/interactor/AudioSlidersInteractorKosmos.kt @@ -18,7 +18,6 @@ package com.android.systemui.volume.panel.component.volume.domain.interactor import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.applicationCoroutineScope -import com.android.systemui.volume.data.repository.audioRepository import com.android.systemui.volume.domain.interactor.audioModeInteractor import com.android.systemui.volume.mediaOutputInteractor @@ -27,7 +26,6 @@ val Kosmos.audioSlidersInteractor by AudioSlidersInteractor( applicationCoroutineScope, mediaOutputInteractor, - audioRepository, audioModeInteractor, ) } diff --git a/ravenwood/.gitignore b/ravenwood/.gitignore new file mode 100644 index 000000000000..751553b3acb9 --- /dev/null +++ b/ravenwood/.gitignore @@ -0,0 +1 @@ +*.bak diff --git a/ravenwood/Android.bp b/ravenwood/Android.bp index be4cd761a4ec..9b0c8e554d64 100644 --- a/ravenwood/Android.bp +++ b/ravenwood/Android.bp @@ -160,6 +160,7 @@ java_library { "ravenwood-framework", "services.core.ravenwood", "junit", + "framework-annotations-lib", ], sdk_version: "core_current", visibility: ["//frameworks/base"], diff --git a/ravenwood/TEST_MAPPING b/ravenwood/TEST_MAPPING index 7e2ee3e2a052..b73f2359f909 100644 --- a/ravenwood/TEST_MAPPING +++ b/ravenwood/TEST_MAPPING @@ -1,6 +1,3 @@ -// Keep the following two TEST_MAPPINGs in sync: -// frameworks/base/ravenwood/TEST_MAPPING -// frameworks/base/tools/hoststubgen/TEST_MAPPING { "presubmit": [ { "name": "tiny-framework-dump-test" }, @@ -39,12 +36,71 @@ } ], "ravenwood-presubmit": [ + // AUTO-GENERATED-START + // DO NOT MODIFY MANUALLY + // Use scripts/update-test-mapping.sh to update it. { - "name": "RavenwoodMinimumTest", + "name": "AdServicesSharedLibrariesUnitTestsRavenwood", "host": true }, { - "name": "RavenwoodMockitoTest", + "name": "android.test.mock.ravenwood.tests", + "host": true + }, + { + "name": "CarLibHostUnitTest", + "host": true + }, + { + "name": "CarServiceHostUnitTest", + "host": true + }, + { + "name": "CarSystemUIRavenTests", + "host": true + }, + { + "name": "CtsAccountManagerTestCasesRavenwood", + "host": true + }, + { + "name": "CtsAppTestCasesRavenwood", + "host": true + }, + { + "name": "CtsContentTestCasesRavenwood", + "host": true + }, + { + "name": "CtsDatabaseTestCasesRavenwood", + "host": true + }, + { + "name": "CtsGraphicsTestCasesRavenwood", + "host": true + }, + { + "name": "CtsIcuTestCasesRavenwood", + "host": true + }, + { + "name": "CtsInputMethodTestCasesRavenwood", + "host": true + }, + { + "name": "CtsOsTestCasesRavenwood", + "host": true + }, + { + "name": "CtsProtoTestCasesRavenwood", + "host": true + }, + { + "name": "CtsResourcesTestCasesRavenwood", + "host": true + }, + { + "name": "CtsTextTestCasesRavenwood", "host": true }, { @@ -52,12 +108,65 @@ "host": true }, { - "name": "RavenwoodResApkTest", + "name": "FrameworksCoreSystemPropertiesTestsRavenwood", + "host": true + }, + { + "name": "FrameworksCoreTestsRavenwood", + "host": true + }, + { + "name": "FrameworksInputMethodSystemServerTestsRavenwood", + "host": true + }, + { + "name": "FrameworksMockingServicesTestsRavenwood", + "host": true + }, + { + "name": "FrameworksServicesTestsRavenwood", + "host": true + }, + { + "name": "FrameworksUtilTestsRavenwood", + "host": true + }, + { + "name": "InternalTestsRavenwood", + "host": true + }, + { + "name": "PowerStatsTestsRavenwood", "host": true }, { "name": "RavenwoodBivalentTest", "host": true + }, + { + "name": "RavenwoodMinimumTest", + "host": true + }, + { + "name": "RavenwoodMockitoTest", + "host": true + }, + { + "name": "RavenwoodResApkTest", + "host": true + }, + { + "name": "RavenwoodRuntimeTest", + "host": true + }, + { + "name": "RavenwoodServicesTest", + "host": true + }, + { + "name": "SystemUiRavenTests", + "host": true } + // AUTO-GENERATED-END ] } diff --git a/ravenwood/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/RavenwoodClassRuleDeviceOnlyTest.java b/ravenwood/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/RavenwoodClassRuleDeviceOnlyTest.java index 3a24c0e829a4..e8f59db86901 100644 --- a/ravenwood/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/RavenwoodClassRuleDeviceOnlyTest.java +++ b/ravenwood/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/RavenwoodClassRuleDeviceOnlyTest.java @@ -26,6 +26,10 @@ import org.junit.ClassRule; import org.junit.Test; import org.junit.runner.RunWith; +/** + * Test to ensure @DisabledOnRavenwood works. Note, now the DisabledOnRavenwood annotation + * is handled by the test runner, so it won't really need the class rule. + */ @RunWith(AndroidJUnit4.class) @DisabledOnRavenwood public class RavenwoodClassRuleDeviceOnlyTest { diff --git a/ravenwood/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/ravenizer/RavenwoodImplicitClassRuleDeviceOnlyTest.java b/ravenwood/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/ravenizer/RavenwoodImplicitClassRuleDeviceOnlyTest.java index 0f8be0eeebeb..7ef672e80bee 100644 --- a/ravenwood/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/ravenizer/RavenwoodImplicitClassRuleDeviceOnlyTest.java +++ b/ravenwood/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/ravenizer/RavenwoodImplicitClassRuleDeviceOnlyTest.java @@ -34,7 +34,12 @@ public class RavenwoodImplicitClassRuleDeviceOnlyTest { @BeforeClass public static void beforeClass() { - Assert.assertFalse(RavenwoodRule.isOnRavenwood()); + // This method shouldn't be called -- unless RUN_DISABLED_TESTS is enabled. + + // If we're doing RUN_DISABLED_TESTS, don't throw here, because that'd confuse junit. + if (!RavenwoodRule.private$ravenwood().isRunningDisabledTests()) { + Assert.assertFalse(RavenwoodRule.isOnRavenwood()); + } } @Test @@ -46,7 +51,10 @@ public class RavenwoodImplicitClassRuleDeviceOnlyTest { public static void afterClass() { if (RavenwoodRule.isOnRavenwood()) { Log.e(TAG, "Even @AfterClass shouldn't be executed!"); - System.exit(1); + + if (!RavenwoodRule.private$ravenwood().isRunningDisabledTests()) { + System.exit(1); + } } } } diff --git a/ravenwood/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/ravenizer/RavenwoodRunDisabledTestsReallyDisabledTest.java b/ravenwood/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/ravenizer/RavenwoodRunDisabledTestsReallyDisabledTest.java new file mode 100644 index 000000000000..c77841b1b55a --- /dev/null +++ b/ravenwood/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/ravenizer/RavenwoodRunDisabledTestsReallyDisabledTest.java @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.ravenwoodtest.bivalenttest.ravenizer; + +import static org.junit.Assert.fail; + +import android.platform.test.annotations.DisabledOnRavenwood; +import android.platform.test.ravenwood.RavenwoodAwareTestRunner.RavenwoodTestRunnerInitializing; +import android.platform.test.ravenwood.RavenwoodRule; +import android.util.Log; + +import androidx.test.ext.junit.runners.AndroidJUnit4; + +import org.junit.AfterClass; +import org.junit.Test; +import org.junit.runner.RunWith; + +/** + * Test for "RAVENWOOD_RUN_DISABLED_TESTS" with "REALLY_DISABLED" set. + * + * This test is only executed on Ravenwood. + */ +@RunWith(AndroidJUnit4.class) +public class RavenwoodRunDisabledTestsReallyDisabledTest { + private static final String TAG = "RavenwoodRunDisabledTestsTest"; + + private static final CallTracker sCallTracker = new CallTracker(); + + @RavenwoodTestRunnerInitializing + public static void ravenwoodRunnerInitializing() { + RavenwoodRule.private$ravenwood().overrideRunDisabledTest(true, + "\\#testReallyDisabled$"); + } + + /** + * This test gets to run with RAVENWOOD_RUN_DISABLED_TESTS set. + */ + @Test + @DisabledOnRavenwood + public void testDisabledTestGetsToRun() { + if (!RavenwoodRule.isOnRavenwood()) { + return; + } + sCallTracker.incrementMethodCallCount(); + + fail("This test won't pass on Ravenwood."); + } + + /** + * This will still not be executed due to the "really disabled" pattern. + */ + @Test + @DisabledOnRavenwood + public void testReallyDisabled() { + if (!RavenwoodRule.isOnRavenwood()) { + return; + } + sCallTracker.incrementMethodCallCount(); + + fail("This test won't pass on Ravenwood."); + } + + @AfterClass + public static void afterClass() { + if (!RavenwoodRule.isOnRavenwood()) { + return; + } + Log.i(TAG, "afterClass called"); + + sCallTracker.assertCallsOrDie( + "testDisabledTestGetsToRun", 1, + "testReallyDisabled", 0 + ); + } +} diff --git a/ravenwood/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/ravenizer/RavenwoodRunDisabledTestsTest.java b/ravenwood/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/ravenizer/RavenwoodRunDisabledTestsTest.java new file mode 100644 index 000000000000..ea1a29d57482 --- /dev/null +++ b/ravenwood/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/ravenizer/RavenwoodRunDisabledTestsTest.java @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.ravenwoodtest.bivalenttest.ravenizer; + +import static org.junit.Assert.fail; + +import android.platform.test.annotations.DisabledOnRavenwood; +import android.platform.test.ravenwood.RavenwoodAwareTestRunner.RavenwoodTestRunnerInitializing; +import android.platform.test.ravenwood.RavenwoodRule; +import android.util.Log; + +import androidx.test.ext.junit.runners.AndroidJUnit4; + +import org.junit.AfterClass; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.junit.runner.RunWith; + +/** + * Test for "RAVENWOOD_RUN_DISABLED_TESTS". (with no "REALLY_DISABLED" set.) + * + * This test is only executed on Ravenwood. + */ +@RunWith(AndroidJUnit4.class) +public class RavenwoodRunDisabledTestsTest { + private static final String TAG = "RavenwoodRunDisabledTestsTest"; + + @Rule + public ExpectedException mExpectedException = ExpectedException.none(); + + private static final CallTracker sCallTracker = new CallTracker(); + + @RavenwoodTestRunnerInitializing + public static void ravenwoodRunnerInitializing() { + RavenwoodRule.private$ravenwood().overrideRunDisabledTest(true, null); + } + + @Test + @DisabledOnRavenwood + public void testDisabledTestGetsToRun() { + if (!RavenwoodRule.isOnRavenwood()) { + return; + } + sCallTracker.incrementMethodCallCount(); + + fail("This test won't pass on Ravenwood."); + } + + @Test + @DisabledOnRavenwood + public void testDisabledButPass() { + if (!RavenwoodRule.isOnRavenwood()) { + return; + } + sCallTracker.incrementMethodCallCount(); + + // When a @DisabledOnRavenwood actually passed, the runner should make fail(). + mExpectedException.expectMessage("it actually passed under Ravenwood"); + } + + @AfterClass + public static void afterClass() { + if (!RavenwoodRule.isOnRavenwood()) { + return; + } + Log.i(TAG, "afterClass called"); + + sCallTracker.assertCallsOrDie( + "testDisabledTestGetsToRun", 1, + "testDisabledButPass", 1 + ); + } +} diff --git a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodAwareTestRunnerHook.java b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodAwareTestRunnerHook.java index 03600ad5511f..1da93eba94f7 100644 --- a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodAwareTestRunnerHook.java +++ b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodAwareTestRunnerHook.java @@ -17,14 +17,16 @@ package android.platform.test.ravenwood; import static com.android.ravenwood.common.RavenwoodCommonUtils.RAVENWOOD_VERSION_JAVA_SYSPROP; +import static org.junit.Assert.fail; + import android.os.Bundle; import android.platform.test.ravenwood.RavenwoodAwareTestRunner.Order; import android.platform.test.ravenwood.RavenwoodAwareTestRunner.Scope; +import android.platform.test.ravenwood.RavenwoodTestStats.Result; +import android.util.Log; import androidx.test.platform.app.InstrumentationRegistry; -import com.android.ravenwood.common.RavenwoodCommonUtils; - import org.junit.runner.Description; import org.junit.runner.Runner; import org.junit.runners.model.TestClass; @@ -38,12 +40,24 @@ public class RavenwoodAwareTestRunnerHook { private RavenwoodAwareTestRunnerHook() { } - private static void log(String message) { - RavenwoodCommonUtils.log(TAG, message); + private static RavenwoodTestStats sStats; // lazy initialization. + private static Description sCurrentClassDescription; + + private static RavenwoodTestStats getStats() { + if (sStats == null) { + // We don't want to throw in the static initializer, because tradefed may not report + // it properly, so we initialize it here. + sStats = new RavenwoodTestStats(); + } + return sStats; } + /** + * Called when a runner starts, before the inner runner gets a chance to run. + */ public static void onRunnerInitializing(Runner runner, TestClass testClass) { - log("onRunnerStart: testClass=" + testClass + " runner=" + runner); + // This log call also ensures the framework JNI is loaded. + Log.i(TAG, "onRunnerInitializing: testClass=" + testClass + " runner=" + runner); // TODO: Move the initialization code to a better place. @@ -52,26 +66,97 @@ public class RavenwoodAwareTestRunnerHook { "androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner"); System.setProperty(RAVENWOOD_VERSION_JAVA_SYSPROP, "1"); + // This is needed to make AndroidJUnit4ClassRunner happy. InstrumentationRegistry.registerInstance(null, Bundle.EMPTY); } + /** + * Called when a whole test class is skipped. + */ + public static void onClassSkipped(Description description) { + Log.i(TAG, "onClassSkipped: description=" + description); + getStats().onClassSkipped(description); + } + + /** + * Called before a test / class. + * + * Return false if it should be skipped. + */ public static boolean onBefore(RavenwoodAwareTestRunner runner, Description description, Scope scope, Order order) { - log("onBefore: description=" + description + ", " + scope + ", " + order); + Log.i(TAG, "onBefore: description=" + description + ", " + scope + ", " + order); + + if (scope == Scope.Class && order == Order.First) { + // Keep track of the current class. + sCurrentClassDescription = description; + } // Class-level annotations are checked by the runner already, so we only check // method-level annotations here. if (scope == Scope.Instance && order == Order.First) { - if (!RavenwoodRule.shouldEnableOnRavenwood(description)) { + if (!RavenwoodEnablementChecker.shouldEnableOnRavenwood( + description, true)) { + getStats().onTestFinished(sCurrentClassDescription, description, Result.Skipped); return false; } } return true; } - public static void onAfter(RavenwoodAwareTestRunner runner, Description description, + /** + * Called after a test / class. + * + * Return false if the exception should be ignored. + */ + public static boolean onAfter(RavenwoodAwareTestRunner runner, Description description, Scope scope, Order order, Throwable th) { - log("onAfter: description=" + description + ", " + scope + ", " + order + ", " + th); + Log.i(TAG, "onAfter: description=" + description + ", " + scope + ", " + order + ", " + th); + + if (scope == Scope.Instance && order == Order.First) { + getStats().onTestFinished(sCurrentClassDescription, description, + th == null ? Result.Passed : Result.Failed); + + } else if (scope == Scope.Class && order == Order.Last) { + getStats().onClassFinished(sCurrentClassDescription); + } + + // If RUN_DISABLED_TESTS is set, and the method did _not_ throw, make it an error. + if (RavenwoodRule.private$ravenwood().isRunningDisabledTests() + && scope == Scope.Instance && order == Order.First) { + + boolean isTestEnabled = RavenwoodEnablementChecker.shouldEnableOnRavenwood( + description, false); + if (th == null) { + // Test passed. Is the test method supposed to be enabled? + if (isTestEnabled) { + // Enabled and didn't throw, okay. + return true; + } else { + // Disabled and didn't throw. We should report it. + fail("Test wasn't included under Ravenwood, but it actually " + + "passed under Ravenwood; consider updating annotations"); + return true; // unreachable. + } + } else { + // Test failed. + if (isTestEnabled) { + // Enabled but failed. We should throw the exception. + return true; + } else { + // Disabled and failed. Expected. Don't throw. + return false; + } + } + } + return true; + } + + /** + * Called by {@link RavenwoodAwareTestRunner} to see if it should run a test class or not. + */ + public static boolean shouldRunClassOnRavenwood(Class<?> clazz) { + return RavenwoodEnablementChecker.shouldRunClassOnRavenwood(clazz, true); } } diff --git a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodEnablementChecker.java b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodEnablementChecker.java new file mode 100644 index 000000000000..77275c445dd9 --- /dev/null +++ b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodEnablementChecker.java @@ -0,0 +1,117 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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.platform.test.ravenwood; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.platform.test.annotations.DisabledOnRavenwood; +import android.platform.test.annotations.EnabledOnRavenwood; +import android.platform.test.annotations.IgnoreUnderRavenwood; + +import org.junit.runner.Description; + +/** + * Calculates which tests need to be executed on Ravenwood. + */ +public class RavenwoodEnablementChecker { + private static final String TAG = "RavenwoodDisablementChecker"; + + private RavenwoodEnablementChecker() { + } + + /** + * Determine if the given {@link Description} should be enabled when running on the + * Ravenwood test environment. + * + * A more specific method-level annotation always takes precedence over any class-level + * annotation, and an {@link EnabledOnRavenwood} annotation always takes precedence over + * an {@link DisabledOnRavenwood} annotation. + */ + public static boolean shouldEnableOnRavenwood(Description description, + boolean takeIntoAccountRunDisabledTestsFlag) { + // First, consult any method-level annotations + if (description.isTest()) { + Boolean result = null; + + // Stopgap for http://g/ravenwood/EPAD-N5ntxM + if (description.getMethodName().endsWith("$noRavenwood")) { + result = false; + } else if (description.getAnnotation(EnabledOnRavenwood.class) != null) { + result = true; + } else if (description.getAnnotation(DisabledOnRavenwood.class) != null) { + result = false; + } else if (description.getAnnotation(IgnoreUnderRavenwood.class) != null) { + result = false; + } + if (result != null) { + if (takeIntoAccountRunDisabledTestsFlag + && RavenwoodRule.private$ravenwood().isRunningDisabledTests()) { + result = !shouldStillIgnoreInProbeIgnoreMode( + description.getTestClass(), description.getMethodName()); + } + } + if (result != null) { + return result; + } + } + + // Otherwise, consult any class-level annotations + return shouldRunClassOnRavenwood(description.getTestClass(), + takeIntoAccountRunDisabledTestsFlag); + } + + public static boolean shouldRunClassOnRavenwood(@NonNull Class<?> testClass, + boolean takeIntoAccountRunDisabledTestsFlag) { + boolean result = true; + if (testClass.getAnnotation(EnabledOnRavenwood.class) != null) { + result = true; + } else if (testClass.getAnnotation(DisabledOnRavenwood.class) != null) { + result = false; + } else if (testClass.getAnnotation(IgnoreUnderRavenwood.class) != null) { + result = false; + } + if (!result) { + if (takeIntoAccountRunDisabledTestsFlag + && RavenwoodRule.private$ravenwood().isRunningDisabledTests()) { + result = !shouldStillIgnoreInProbeIgnoreMode(testClass, null); + } + } + return result; + } + + /** + * Check if a test should _still_ disabled even if {@code RUN_DISABLED_TESTS} + * is true, using {@code REALLY_DISABLED_PATTERN}. + * + * This only works on tests, not on classes. + */ + static boolean shouldStillIgnoreInProbeIgnoreMode( + @NonNull Class<?> testClass, @Nullable String methodName) { + if (RavenwoodRule.private$ravenwood().getReallyDisabledPattern().pattern().isEmpty()) { + return false; + } + + final var fullname = testClass.getName() + (methodName != null ? "#" + methodName : ""); + + System.out.println("XXX=" + fullname); + + if (RavenwoodRule.private$ravenwood().getReallyDisabledPattern().matcher(fullname).find()) { + System.out.println("Still ignoring " + fullname); + return true; + } + return false; + } +} diff --git a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuleImpl.java b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuleImpl.java index 7b4c17390942..a2088fd0b77f 100644 --- a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuleImpl.java +++ b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuleImpl.java @@ -40,7 +40,6 @@ import com.android.internal.os.RuntimeInit; import com.android.server.LocalServices; import org.junit.runner.Description; -import org.junit.runners.model.Statement; import java.io.File; import java.io.IOException; @@ -226,11 +225,6 @@ public class RavenwoodRuleImpl { } } - public static void validate(Statement base, Description description, - boolean enableOptionalValidation) { - // Nothing to check, for now. - } - /** * Set the current configuration to the actual SystemProperties. */ diff --git a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodTestStats.java b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodTestStats.java new file mode 100644 index 000000000000..631f68ff1dec --- /dev/null +++ b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodTestStats.java @@ -0,0 +1,163 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.platform.test.ravenwood; + +import android.util.Log; + +import org.junit.runner.Description; + +import java.io.File; +import java.io.IOException; +import java.io.PrintWriter; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.HashMap; +import java.util.Map; + +/** + * Creats a "stats" CSV file containing the test results. + * + * The output file is created as `/tmp/Ravenwood-stats_[TEST-MODULE=NAME]_[TIMESTAMP].csv`. + * A symlink to the latest result will be created as + * `/tmp/Ravenwood-stats_[TEST-MODULE=NAME]_latest.csv`. + */ +public class RavenwoodTestStats { + private static final String TAG = "RavenwoodTestStats"; + private static final String HEADER = "Module,Class,ClassDesc,Passed,Failed,Skipped"; + + public enum Result { + Passed, + Failed, + Skipped, + } + + private final File mOutputFile; + private final PrintWriter mOutputWriter; + private final String mTestModuleName; + + public final Map<Description, Map<Description, Result>> mStats = new HashMap<>(); + + /** Ctor */ + public RavenwoodTestStats() { + mTestModuleName = guessTestModuleName(); + + var basename = "Ravenwood-stats_" + mTestModuleName + "_"; + + // Get the current time + LocalDateTime now = LocalDateTime.now(); + DateTimeFormatter fmt = DateTimeFormatter.ofPattern("yyyyMMdd-HHmmss"); + + var tmpdir = System.getProperty("java.io.tmpdir"); + mOutputFile = new File(tmpdir, basename + now.format(fmt) + ".csv"); + + try { + mOutputWriter = new PrintWriter(mOutputFile); + } catch (IOException e) { + throw new RuntimeException("Failed to crete logfile. File=" + mOutputFile, e); + } + + // Crete the "latest" symlink. + Path symlink = Paths.get(tmpdir, basename + "latest.csv"); + try { + if (Files.exists(symlink)) { + Files.delete(symlink); + } + Files.createSymbolicLink(symlink, Paths.get(mOutputFile.getName())); + + } catch (IOException e) { + throw new RuntimeException("Failed to crete logfile. File=" + mOutputFile, e); + } + + Log.i(TAG, "Test result stats file: " + mOutputFile); + + // Print the header. + mOutputWriter.println(HEADER); + mOutputWriter.flush(); + } + + private String guessTestModuleName() { + // Assume the current directory name is the test module name. + File cwd; + try { + cwd = new File(".").getCanonicalFile(); + } catch (IOException e) { + throw new RuntimeException("Failed to get the current directory", e); + } + return cwd.getName(); + } + + private void addResult(Description classDescription, Description methodDescription, + Result result) { + mStats.compute(classDescription, (classDesc, value) -> { + if (value == null) { + value = new HashMap<>(); + } + value.put(methodDescription, result); + return value; + }); + } + + public void onClassSkipped(Description classDescription) { + addResult(classDescription, Description.EMPTY, Result.Skipped); + onClassFinished(classDescription); + } + + public void onTestFinished(Description classDescription, Description testDescription, + Result result) { + addResult(classDescription, testDescription, result); + } + + public void onClassFinished(Description classDescription) { + int passed = 0; + int skipped = 0; + int failed = 0; + for (var e : mStats.get(classDescription).values()) { + switch (e) { + case Passed: passed++; break; + case Skipped: skipped++; break; + case Failed: failed++; break; + } + } + + var testClass = extractTestClass(classDescription); + + mOutputWriter.printf("%s,%s,%s,%d,%d,%d\n", + mTestModuleName, (testClass == null ? "?" : testClass.getCanonicalName()), + classDescription, passed, failed, skipped); + mOutputWriter.flush(); + } + + /** + * Try to extract the class from a description, which is needed because + * ParameterizedAndroidJunit4's description doesn't contain a class. + */ + private Class<?> extractTestClass(Description desc) { + if (desc.getTestClass() != null) { + return desc.getTestClass(); + } + // Look into the children. + for (var child : desc.getChildren()) { + var fromChild = extractTestClass(child); + if (fromChild != null) { + return fromChild; + } + } + return null; + } +} diff --git a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodAwareTestRunner.java b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodAwareTestRunner.java index a4fa41af26e5..8271039af674 100644 --- a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodAwareTestRunner.java +++ b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodAwareTestRunner.java @@ -15,14 +15,14 @@ */ package android.platform.test.ravenwood; -import static android.platform.test.ravenwood.RavenwoodRule.shouldRunCassOnRavenwood; - import static com.android.ravenwood.common.RavenwoodCommonUtils.ensureIsPublicVoidMethod; import static com.android.ravenwood.common.RavenwoodCommonUtils.isOnRavenwood; import static java.lang.annotation.ElementType.METHOD; import static java.lang.annotation.ElementType.TYPE; +import android.util.Log; + import com.android.ravenwood.common.RavenwoodCommonUtils; import com.android.ravenwood.common.SneakyThrow; @@ -38,6 +38,7 @@ import org.junit.runner.manipulation.Orderable; import org.junit.runner.manipulation.Orderer; import org.junit.runner.manipulation.Sortable; import org.junit.runner.manipulation.Sorter; +import org.junit.runner.notification.Failure; import org.junit.runner.notification.RunNotifier; import org.junit.runners.BlockJUnit4ClassRunner; import org.junit.runners.model.Statement; @@ -136,8 +137,10 @@ public class RavenwoodAwareTestRunner extends Runner implements Filterable, Orde return runner; } - private final TestClass mTestClsas; - private final Runner mRealRunner; + private TestClass mTestClass = null; + private Runner mRealRunner = null; + private Description mDescription = null; + private Throwable mExceptionInConstructor = null; /** Simple logging method. */ private void log(String message) { @@ -151,44 +154,62 @@ public class RavenwoodAwareTestRunner extends Runner implements Filterable, Orde } public TestClass getTestClass() { - return mTestClsas; + return mTestClass; } /** * Constructor. */ public RavenwoodAwareTestRunner(Class<?> testClass) { - mTestClsas = new TestClass(testClass); - - /* - * If the class has @DisabledOnRavenwood, then we'll delegate to ClassSkippingTestRunner, - * which simply skips it. - */ - if (isOnRavenwood() && !shouldRunCassOnRavenwood(mTestClsas.getJavaClass())) { - mRealRunner = new ClassSkippingTestRunner(mTestClsas); - return; - } + try { + mTestClass = new TestClass(testClass); + + /* + * If the class has @DisabledOnRavenwood, then we'll delegate to + * ClassSkippingTestRunner, which simply skips it. + */ + if (isOnRavenwood() && !RavenwoodAwareTestRunnerHook.shouldRunClassOnRavenwood( + mTestClass.getJavaClass())) { + mRealRunner = new ClassSkippingTestRunner(mTestClass); + mDescription = mRealRunner.getDescription(); + return; + } - // Find the real runner. - final Class<? extends Runner> realRunner; - final InnerRunner innerRunnerAnnotation = mTestClsas.getAnnotation(InnerRunner.class); - if (innerRunnerAnnotation != null) { - realRunner = innerRunnerAnnotation.value(); - } else { - // Default runner. - realRunner = BlockJUnit4ClassRunner.class; - } + // Find the real runner. + final Class<? extends Runner> realRunner; + final InnerRunner innerRunnerAnnotation = mTestClass.getAnnotation(InnerRunner.class); + if (innerRunnerAnnotation != null) { + realRunner = innerRunnerAnnotation.value(); + } else { + // Default runner. + realRunner = BlockJUnit4ClassRunner.class; + } - onRunnerInitializing(); + onRunnerInitializing(); - try { - log("Initializing the inner runner: " + realRunner); + try { + log("Initializing the inner runner: " + realRunner); - mRealRunner = realRunner.getConstructor(Class.class).newInstance(testClass); + mRealRunner = realRunner.getConstructor(Class.class).newInstance(testClass); + mDescription = mRealRunner.getDescription(); - } catch (InstantiationException | IllegalAccessException - | InvocationTargetException | NoSuchMethodException e) { - throw logAndFail("Failed to instantiate " + realRunner, e); + } catch (InstantiationException | IllegalAccessException + | InvocationTargetException | NoSuchMethodException e) { + throw logAndFail("Failed to instantiate " + realRunner, e); + } + } catch (Throwable th) { + // If we throw in the constructor, Tradefed may not report it and just ignore the class, + // so record it and throw it when the test actually started. + log("Fatal: Exception detected in constructor: " + th.getMessage() + "\n" + + Log.getStackTraceString(th)); + mExceptionInConstructor = new RuntimeException("Exception detected in constructor", + th); + mDescription = Description.createTestDescription(testClass, "Constructor"); + + // This is for testing if tradefed is fixed. + if ("1".equals(System.getenv("RAVENWOOD_THROW_EXCEPTION_IN_TEST_RUNNER"))) { + throw th; + } } } @@ -203,7 +224,7 @@ public class RavenwoodAwareTestRunner extends Runner implements Filterable, Orde log("onRunnerInitializing"); - RavenwoodAwareTestRunnerHook.onRunnerInitializing(this, mTestClsas); + RavenwoodAwareTestRunnerHook.onRunnerInitializing(this, mTestClass); // Hook point to allow more customization. runAnnotatedMethodsOnRavenwood(RavenwoodTestRunnerInitializing.class, null); @@ -231,13 +252,18 @@ public class RavenwoodAwareTestRunner extends Runner implements Filterable, Orde @Override public Description getDescription() { - return mRealRunner.getDescription(); + return mDescription; } @Override public void run(RunNotifier notifier) { if (mRealRunner instanceof ClassSkippingTestRunner) { mRealRunner.run(notifier); + RavenwoodAwareTestRunnerHook.onClassSkipped(getDescription()); + return; + } + + if (maybeReportExceptionFromConstructor(notifier)) { return; } @@ -250,6 +276,18 @@ public class RavenwoodAwareTestRunner extends Runner implements Filterable, Orde } } + /** Throw the exception detected in the constructor, if any. */ + private boolean maybeReportExceptionFromConstructor(RunNotifier notifier) { + if (mExceptionInConstructor == null) { + return false; + } + notifier.fireTestStarted(mDescription); + notifier.fireTestFailure(new Failure(mDescription, mExceptionInConstructor)); + notifier.fireTestFinished(mDescription); + + return true; + } + @Override public void filter(Filter filter) throws NoTestsRemainException { if (mRealRunner instanceof Filterable r) { @@ -294,19 +332,23 @@ public class RavenwoodAwareTestRunner extends Runner implements Filterable, Orde } private void runWithHooks(Description description, Scope scope, Order order, Statement s) { - Throwable th = null; if (isOnRavenwood()) { Assume.assumeTrue( RavenwoodAwareTestRunnerHook.onBefore(this, description, scope, order)); } try { s.evaluate(); + if (isOnRavenwood()) { + RavenwoodAwareTestRunnerHook.onAfter(this, description, scope, order, null); + } } catch (Throwable t) { - th = t; - SneakyThrow.sneakyThrow(t); - } finally { + boolean shouldThrow = true; if (isOnRavenwood()) { - RavenwoodAwareTestRunnerHook.onAfter(this, description, scope, order, th); + shouldThrow = RavenwoodAwareTestRunnerHook.onAfter( + this, description, scope, order, t); + } + if (shouldThrow) { + SneakyThrow.sneakyThrow(t); } } } diff --git a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodClassRule.java b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodClassRule.java index 6c8d96add4ca..85297fe96d6a 100644 --- a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodClassRule.java +++ b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodClassRule.java @@ -16,37 +16,20 @@ package android.platform.test.ravenwood; -import static android.platform.test.ravenwood.RavenwoodRule.ENABLE_PROBE_IGNORED; -import static android.platform.test.ravenwood.RavenwoodRule.IS_ON_RAVENWOOD; -import static android.platform.test.ravenwood.RavenwoodRule.shouldEnableOnRavenwood; -import static android.platform.test.ravenwood.RavenwoodRule.shouldStillIgnoreInProbeIgnoreMode; - -import android.platform.test.annotations.DisabledOnRavenwood; -import android.platform.test.annotations.EnabledOnRavenwood; - -import org.junit.Assume; import org.junit.rules.TestRule; import org.junit.runner.Description; import org.junit.runners.model.Statement; /** - * {@code @ClassRule} that respects Ravenwood-specific class annotations. This rule has no effect - * when tests are run on non-Ravenwood test environments. + * No longer needed. * - * By default, all tests are executed on Ravenwood, but annotations such as - * {@link DisabledOnRavenwood} and {@link EnabledOnRavenwood} can be used at both the method - * and class level to "ignore" tests that may not be ready. + * @deprecated this class used to be used to handle the class level annotation, which + * is now done by the test runner, so this class is not needed. */ +@Deprecated public class RavenwoodClassRule implements TestRule { @Override public Statement apply(Statement base, Description description) { - if (!IS_ON_RAVENWOOD) { - // No check on a real device. - } else if (ENABLE_PROBE_IGNORED) { - Assume.assumeFalse(shouldStillIgnoreInProbeIgnoreMode(description)); - } else { - Assume.assumeTrue(shouldEnableOnRavenwood(description)); - } return base; } } diff --git a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java index 75faafb7fe58..d569896421eb 100644 --- a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java +++ b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java @@ -20,22 +20,20 @@ import static android.os.Process.FIRST_APPLICATION_UID; import static android.os.Process.SYSTEM_UID; import static android.os.UserHandle.SYSTEM; -import static org.junit.Assert.fail; +import static com.android.ravenwood.common.RavenwoodCommonUtils.log; +import android.annotation.Nullable; import android.app.Instrumentation; import android.content.Context; import android.platform.test.annotations.DisabledOnRavenwood; import android.platform.test.annotations.EnabledOnRavenwood; -import android.platform.test.annotations.IgnoreUnderRavenwood; import com.android.ravenwood.common.RavenwoodCommonUtils; -import org.junit.Assume; import org.junit.rules.TestRule; import org.junit.runner.Description; import org.junit.runners.model.Statement; -import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.Objects; @@ -56,18 +54,18 @@ import java.util.regex.Pattern; * before a test class is fully initialized. */ public class RavenwoodRule implements TestRule { + private static final String TAG = "RavenwoodRule"; + static final boolean IS_ON_RAVENWOOD = RavenwoodCommonUtils.isOnRavenwood(); /** - * When probing is enabled, all tests will be unconditionally run on Ravenwood to detect + * When this flag is enabled, all tests will be unconditionally run on Ravenwood to detect * cases where a test is able to pass despite being marked as {@link DisabledOnRavenwood}. * * This is typically helpful for internal maintainers discovering tests that had previously * been ignored, but now have enough Ravenwood-supported functionality to be enabled. - * - * TODO: Rename it to a more descriptive name. */ - static final boolean ENABLE_PROBE_IGNORED = "1".equals( + private static final boolean RUN_DISABLED_TESTS = "1".equals( System.getenv("RAVENWOOD_RUN_DISABLED_TESTS")); /** @@ -92,23 +90,17 @@ public class RavenwoodRule implements TestRule { * * Because we use a regex-find, setting "." would disable all tests. */ - private static final Pattern REALLY_DISABLE_PATTERN = Pattern.compile( - Objects.requireNonNullElse(System.getenv("RAVENWOOD_REALLY_DISABLE"), "")); - - private static final boolean ENABLE_REALLY_DISABLE_PATTERN = - !REALLY_DISABLE_PATTERN.pattern().isEmpty(); + private static final Pattern REALLY_DISABLED_PATTERN = Pattern.compile( + Objects.requireNonNullElse(System.getenv("RAVENWOOD_REALLY_DISABLED"), "")); - /** - * If true, enable optional validation on running tests. - */ - private static final boolean ENABLE_OPTIONAL_VALIDATION = "1".equals( - System.getenv("RAVENWOOD_OPTIONAL_VALIDATION")); + private static final boolean HAS_REALLY_DISABLE_PATTERN = + !REALLY_DISABLED_PATTERN.pattern().isEmpty(); static { - if (ENABLE_PROBE_IGNORED) { - System.out.println("$RAVENWOOD_RUN_DISABLED_TESTS enabled: force running all tests"); - if (ENABLE_REALLY_DISABLE_PATTERN) { - System.out.println("$RAVENWOOD_REALLY_DISABLE=" + REALLY_DISABLE_PATTERN.pattern()); + if (RUN_DISABLED_TESTS) { + log(TAG, "$RAVENWOOD_RUN_DISABLED_TESTS enabled: force running all tests"); + if (HAS_REALLY_DISABLE_PATTERN) { + log(TAG, "$RAVENWOOD_REALLY_DISABLED=" + REALLY_DISABLED_PATTERN.pattern()); } } } @@ -275,103 +267,18 @@ public class RavenwoodRule implements TestRule { "Instrumentation is only available during @Test execution"); } - /** - * Determine if the given {@link Description} should be enabled when running on the - * Ravenwood test environment. - * - * A more specific method-level annotation always takes precedence over any class-level - * annotation, and an {@link EnabledOnRavenwood} annotation always takes precedence over - * an {@link DisabledOnRavenwood} annotation. - */ - public static boolean shouldEnableOnRavenwood(Description description) { - // First, consult any method-level annotations - if (description.isTest()) { - // Stopgap for http://g/ravenwood/EPAD-N5ntxM - if (description.getMethodName().endsWith("$noRavenwood")) { - return false; - } - if (description.getAnnotation(EnabledOnRavenwood.class) != null) { - return true; - } - if (description.getAnnotation(DisabledOnRavenwood.class) != null) { - return false; - } - if (description.getAnnotation(IgnoreUnderRavenwood.class) != null) { - return false; - } - } - - // Otherwise, consult any class-level annotations - return shouldRunCassOnRavenwood(description.getTestClass()); - } - - public static boolean shouldRunCassOnRavenwood(Class<?> clazz) { - if (clazz != null) { - if (clazz.getAnnotation(EnabledOnRavenwood.class) != null) { - return true; - } - if (clazz.getAnnotation(DisabledOnRavenwood.class) != null) { - return false; - } - if (clazz.getAnnotation(IgnoreUnderRavenwood.class) != null) { - return false; - } - } - return true; - } - - static boolean shouldStillIgnoreInProbeIgnoreMode(Description description) { - if (!ENABLE_REALLY_DISABLE_PATTERN) { - return false; - } - - final var fullname = description.getTestClass().getName() - + (description.isTest() ? "#" + description.getMethodName() : ""); - - if (REALLY_DISABLE_PATTERN.matcher(fullname).find()) { - System.out.println("Still ignoring " + fullname); - return true; - } - return false; - } @Override public Statement apply(Statement base, Description description) { - // No special treatment when running outside Ravenwood; run tests as-is - if (!IS_ON_RAVENWOOD) { - return base; - } - - if (ENABLE_PROBE_IGNORED) { - return applyProbeIgnored(base, description); - } else { - return applyDefault(base, description); - } - } - - private void commonPrologue(Statement base, Description description) throws IOException { - RavenwoodRuleImpl.logTestRunner("started", description); - RavenwoodRuleImpl.validate(base, description, ENABLE_OPTIONAL_VALIDATION); - RavenwoodRuleImpl.init(RavenwoodRule.this); - } - - /** - * Run the given {@link Statement} with no special treatment. - */ - private Statement applyDefault(Statement base, Description description) { + // TODO: Here, we're calling init() / reset() once for each rule. + // That means if a test class has multiple rules -- even if they refer to the same + // rule instance -- we're calling them multiple times. We need to fix it. return new Statement() { @Override public void evaluate() throws Throwable { - Assume.assumeTrue(shouldEnableOnRavenwood(description)); - - commonPrologue(base, description); + RavenwoodRuleImpl.init(RavenwoodRule.this); try { base.evaluate(); - - RavenwoodRuleImpl.logTestRunner("finished", description); - } catch (Throwable t) { - RavenwoodRuleImpl.logTestRunner("failed", description); - throw t; } finally { RavenwoodRuleImpl.reset(RavenwoodRule.this); } @@ -380,44 +287,6 @@ public class RavenwoodRule implements TestRule { } /** - * Run the given {@link Statement} with probing enabled. All tests will be unconditionally - * run on Ravenwood to detect cases where a test is able to pass despite being marked as - * {@code IgnoreUnderRavenwood}. - */ - private Statement applyProbeIgnored(Statement base, Description description) { - return new Statement() { - @Override - public void evaluate() throws Throwable { - Assume.assumeFalse(shouldStillIgnoreInProbeIgnoreMode(description)); - - commonPrologue(base, description); - try { - base.evaluate(); - } catch (Throwable t) { - // If the test isn't included, eat the exception and report the - // assumption failure that test authors expect; otherwise throw - Assume.assumeTrue(shouldEnableOnRavenwood(description)); - throw t; - } finally { - RavenwoodRuleImpl.logTestRunner("finished", description); - RavenwoodRuleImpl.reset(RavenwoodRule.this); - } - - if (!shouldEnableOnRavenwood(description)) { - fail("Test wasn't included under Ravenwood, but it actually " - + "passed under Ravenwood; consider updating annotations"); - } - } - }; - } - - public static class _$RavenwoodPrivate { - public static boolean isOptionalValidationEnabled() { - return ENABLE_OPTIONAL_VALIDATION; - } - } - - /** * Returns the "real" result from {@link System#currentTimeMillis()}. * * Currently, it's the same thing as calling {@link System#currentTimeMillis()}, @@ -427,4 +296,47 @@ public class RavenwoodRule implements TestRule { public long realCurrentTimeMillis() { return System.currentTimeMillis(); } + + // Below are internal to ravenwood. Don't use them from normal tests... + + public static class RavenwoodPrivate { + private RavenwoodPrivate() { + } + + private volatile Boolean mRunDisabledTestsOverride = null; + + private volatile Pattern mReallyDisabledPattern = null; + + public boolean isRunningDisabledTests() { + if (mRunDisabledTestsOverride != null) { + return mRunDisabledTestsOverride; + } + return RUN_DISABLED_TESTS; + } + + public Pattern getReallyDisabledPattern() { + if (mReallyDisabledPattern != null) { + return mReallyDisabledPattern; + } + return REALLY_DISABLED_PATTERN; + } + + public void overrideRunDisabledTest(boolean runDisabledTests, + @Nullable String reallyDisabledPattern) { + mRunDisabledTestsOverride = runDisabledTests; + mReallyDisabledPattern = + reallyDisabledPattern == null ? null : Pattern.compile(reallyDisabledPattern); + } + + public void resetRunDisabledTest() { + mRunDisabledTestsOverride = null; + mReallyDisabledPattern = null; + } + } + + private static final RavenwoodPrivate sRavenwoodPrivate = new RavenwoodPrivate(); + + public static RavenwoodPrivate private$ravenwood() { + return sRavenwoodPrivate; + } } diff --git a/ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodAwareTestRunnerHook.java b/ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodAwareTestRunnerHook.java index 6b80e0cbf91e..1e4889ce0678 100644 --- a/ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodAwareTestRunnerHook.java +++ b/ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodAwareTestRunnerHook.java @@ -23,27 +23,51 @@ import org.junit.runner.Runner; import org.junit.runners.model.TestClass; /** - * Provide hook points created by {@link RavenwoodAwareTestRunner}. + * Provide hook points created by {@link RavenwoodAwareTestRunner}. This is a version + * that's used on a device side test. + * + * All methods are no-op in real device tests. + * + * TODO: Use some kind of factory to provide different implementation for the device test + * and the ravenwood test. */ public class RavenwoodAwareTestRunnerHook { private RavenwoodAwareTestRunnerHook() { } /** - * Called when a runner starts, befre the inner runner gets a chance to run. + * Called when a runner starts, before the inner runner gets a chance to run. */ public static void onRunnerInitializing(Runner runner, TestClass testClass) { - // No-op on a real device. } + /** + * Called when a whole test class is skipped. + */ + public static void onClassSkipped(Description description) { + } + + /** + * Called before a test / class. + * + * Return false if it should be skipped. + */ public static boolean onBefore(RavenwoodAwareTestRunner runner, Description description, Scope scope, Order order) { - // No-op on a real device. return true; } - public static void onAfter(RavenwoodAwareTestRunner runner, Description description, + /** + * Called after a test / class. + * + * Return false if the exception should be ignored. + */ + public static boolean onAfter(RavenwoodAwareTestRunner runner, Description description, Scope scope, Order order, Throwable th) { - // No-op on a real device. + return true; + } + + public static boolean shouldRunClassOnRavenwood(Class<?> clazz) { + return true; } } diff --git a/ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodRuleImpl.java b/ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodRuleImpl.java index 483b98a96034..a470626dcbe7 100644 --- a/ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodRuleImpl.java +++ b/ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodRuleImpl.java @@ -17,7 +17,6 @@ package android.platform.test.ravenwood; import org.junit.runner.Description; -import org.junit.runners.model.Statement; public class RavenwoodRuleImpl { public static void init(RavenwoodRule rule) { @@ -32,10 +31,6 @@ public class RavenwoodRuleImpl { // No-op when running on a real device } - public static void validate(Statement base, Description description, - boolean enableOptionalValidation) { - } - public static long realCurrentTimeMillis() { return System.currentTimeMillis(); } diff --git a/ravenwood/runtime-helper-src/libcore-fake/libcore/util/FP16.java b/ravenwood/runtime-helper-src/libcore-fake/libcore/util/FP16.java new file mode 100644 index 000000000000..478503b699a0 --- /dev/null +++ b/ravenwood/runtime-helper-src/libcore-fake/libcore/util/FP16.java @@ -0,0 +1,814 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package libcore.util; + +/** + * <p>The {@code FP16} class is a wrapper and a utility class to manipulate half-precision 16-bit + * <a href="https://en.wikipedia.org/wiki/Half-precision_floating-point_format">IEEE 754</a> + * floating point data types (also called fp16 or binary16). A half-precision float can be + * created from or converted to single-precision floats, and is stored in a short data type. + * + * <p>The IEEE 754 standard specifies an fp16 as having the following format:</p> + * <ul> + * <li>Sign bit: 1 bit</li> + * <li>Exponent width: 5 bits</li> + * <li>Significand: 10 bits</li> + * </ul> + * + * <p>The format is laid out as follows:</p> + * <pre> + * 1 11111 1111111111 + * ^ --^-- -----^---- + * sign | |_______ significand + * | + * -- exponent + * </pre> + * + * <p>Half-precision floating points can be useful to save memory and/or + * bandwidth at the expense of range and precision when compared to single-precision + * floating points (fp32).</p> + * <p>To help you decide whether fp16 is the right storage type for you need, please + * refer to the table below that shows the available precision throughout the range of + * possible values. The <em>precision</em> column indicates the step size between two + * consecutive numbers in a specific part of the range.</p> + * + * <table summary="Precision of fp16 across the range"> + * <tr><th>Range start</th><th>Precision</th></tr> + * <tr><td>0</td><td>1 ⁄ 16,777,216</td></tr> + * <tr><td>1 ⁄ 16,384</td><td>1 ⁄ 16,777,216</td></tr> + * <tr><td>1 ⁄ 8,192</td><td>1 ⁄ 8,388,608</td></tr> + * <tr><td>1 ⁄ 4,096</td><td>1 ⁄ 4,194,304</td></tr> + * <tr><td>1 ⁄ 2,048</td><td>1 ⁄ 2,097,152</td></tr> + * <tr><td>1 ⁄ 1,024</td><td>1 ⁄ 1,048,576</td></tr> + * <tr><td>1 ⁄ 512</td><td>1 ⁄ 524,288</td></tr> + * <tr><td>1 ⁄ 256</td><td>1 ⁄ 262,144</td></tr> + * <tr><td>1 ⁄ 128</td><td>1 ⁄ 131,072</td></tr> + * <tr><td>1 ⁄ 64</td><td>1 ⁄ 65,536</td></tr> + * <tr><td>1 ⁄ 32</td><td>1 ⁄ 32,768</td></tr> + * <tr><td>1 ⁄ 16</td><td>1 ⁄ 16,384</td></tr> + * <tr><td>1 ⁄ 8</td><td>1 ⁄ 8,192</td></tr> + * <tr><td>1 ⁄ 4</td><td>1 ⁄ 4,096</td></tr> + * <tr><td>1 ⁄ 2</td><td>1 ⁄ 2,048</td></tr> + * <tr><td>1</td><td>1 ⁄ 1,024</td></tr> + * <tr><td>2</td><td>1 ⁄ 512</td></tr> + * <tr><td>4</td><td>1 ⁄ 256</td></tr> + * <tr><td>8</td><td>1 ⁄ 128</td></tr> + * <tr><td>16</td><td>1 ⁄ 64</td></tr> + * <tr><td>32</td><td>1 ⁄ 32</td></tr> + * <tr><td>64</td><td>1 ⁄ 16</td></tr> + * <tr><td>128</td><td>1 ⁄ 8</td></tr> + * <tr><td>256</td><td>1 ⁄ 4</td></tr> + * <tr><td>512</td><td>1 ⁄ 2</td></tr> + * <tr><td>1,024</td><td>1</td></tr> + * <tr><td>2,048</td><td>2</td></tr> + * <tr><td>4,096</td><td>4</td></tr> + * <tr><td>8,192</td><td>8</td></tr> + * <tr><td>16,384</td><td>16</td></tr> + * <tr><td>32,768</td><td>32</td></tr> + * </table> + * + * <p>This table shows that numbers higher than 1024 lose all fractional precision.</p> + * + * @hide + */ + +public final class FP16 { + /** + * The number of bits used to represent a half-precision float value. + * + * @hide + */ + public static final int SIZE = 16; + + /** + * Epsilon is the difference between 1.0 and the next value representable + * by a half-precision floating-point. + * + * @hide + */ + public static final short EPSILON = (short) 0x1400; + + /** + * Maximum exponent a finite half-precision float may have. + * + * @hide + */ + public static final int MAX_EXPONENT = 15; + /** + * Minimum exponent a normalized half-precision float may have. + * + * @hide + */ + public static final int MIN_EXPONENT = -14; + + /** + * Smallest negative value a half-precision float may have. + * + * @hide + */ + public static final short LOWEST_VALUE = (short) 0xfbff; + /** + * Maximum positive finite value a half-precision float may have. + * + * @hide + */ + public static final short MAX_VALUE = (short) 0x7bff; + /** + * Smallest positive normal value a half-precision float may have. + * + * @hide + */ + public static final short MIN_NORMAL = (short) 0x0400; + /** + * Smallest positive non-zero value a half-precision float may have. + * + * @hide + */ + public static final short MIN_VALUE = (short) 0x0001; + /** + * A Not-a-Number representation of a half-precision float. + * + * @hide + */ + public static final short NaN = (short) 0x7e00; + /** + * Negative infinity of type half-precision float. + * + * @hide + */ + public static final short NEGATIVE_INFINITY = (short) 0xfc00; + /** + * Negative 0 of type half-precision float. + * + * @hide + */ + public static final short NEGATIVE_ZERO = (short) 0x8000; + /** + * Positive infinity of type half-precision float. + * + * @hide + */ + public static final short POSITIVE_INFINITY = (short) 0x7c00; + /** + * Positive 0 of type half-precision float. + * + * @hide + */ + public static final short POSITIVE_ZERO = (short) 0x0000; + + /** + * The offset to shift by to obtain the sign bit. + * + * @hide + */ + public static final int SIGN_SHIFT = 15; + + /** + * The offset to shift by to obtain the exponent bits. + * + * @hide + */ + public static final int EXPONENT_SHIFT = 10; + + /** + * The bitmask to AND a number with to obtain the sign bit. + * + * @hide + */ + public static final int SIGN_MASK = 0x8000; + + /** + * The bitmask to AND a number shifted by {@link #EXPONENT_SHIFT} right, to obtain exponent bits. + * + * @hide + */ + public static final int SHIFTED_EXPONENT_MASK = 0x1f; + + /** + * The bitmask to AND a number with to obtain significand bits. + * + * @hide + */ + public static final int SIGNIFICAND_MASK = 0x3ff; + + /** + * The bitmask to AND with to obtain exponent and significand bits. + * + * @hide + */ + public static final int EXPONENT_SIGNIFICAND_MASK = 0x7fff; + + /** + * The offset of the exponent from the actual value. + * + * @hide + */ + public static final int EXPONENT_BIAS = 15; + + private static final int FP32_SIGN_SHIFT = 31; + private static final int FP32_EXPONENT_SHIFT = 23; + private static final int FP32_SHIFTED_EXPONENT_MASK = 0xff; + private static final int FP32_SIGNIFICAND_MASK = 0x7fffff; + private static final int FP32_EXPONENT_BIAS = 127; + private static final int FP32_QNAN_MASK = 0x400000; + private static final int FP32_DENORMAL_MAGIC = 126 << 23; + private static final float FP32_DENORMAL_FLOAT = Float.intBitsToFloat(FP32_DENORMAL_MAGIC); + + /** Hidden constructor to prevent instantiation. */ + private FP16() {} + + /** + * <p>Compares the two specified half-precision float values. The following + * conditions apply during the comparison:</p> + * + * <ul> + * <li>{@link #NaN} is considered by this method to be equal to itself and greater + * than all other half-precision float values (including {@code #POSITIVE_INFINITY})</li> + * <li>{@link #POSITIVE_ZERO} is considered by this method to be greater than + * {@link #NEGATIVE_ZERO}.</li> + * </ul> + * + * @param x The first half-precision float value to compare. + * @param y The second half-precision float value to compare + * + * @return The value {@code 0} if {@code x} is numerically equal to {@code y}, a + * value less than {@code 0} if {@code x} is numerically less than {@code y}, + * and a value greater than {@code 0} if {@code x} is numerically greater + * than {@code y} + * + * @hide + */ + public static int compare(short x, short y) { + if (less(x, y)) return -1; + if (greater(x, y)) return 1; + + // Collapse NaNs, akin to halfToIntBits(), but we want to keep + // (signed) short value types to preserve the ordering of -0.0 + // and +0.0 + short xBits = isNaN(x) ? NaN : x; + short yBits = isNaN(y) ? NaN : y; + + return (xBits == yBits ? 0 : (xBits < yBits ? -1 : 1)); + } + + /** + * Returns the closest integral half-precision float value to the specified + * half-precision float value. Special values are handled in the + * following ways: + * <ul> + * <li>If the specified half-precision float is NaN, the result is NaN</li> + * <li>If the specified half-precision float is infinity (negative or positive), + * the result is infinity (with the same sign)</li> + * <li>If the specified half-precision float is zero (negative or positive), + * the result is zero (with the same sign)</li> + * </ul> + * + * @param h A half-precision float value + * @return The value of the specified half-precision float rounded to the nearest + * half-precision float value + * + * @hide + */ + public static short rint(short h) { + int bits = h & 0xffff; + int abs = bits & EXPONENT_SIGNIFICAND_MASK; + int result = bits; + + if (abs < 0x3c00) { + result &= SIGN_MASK; + if (abs > 0x3800){ + result |= 0x3c00; + } + } else if (abs < 0x6400) { + int exp = 25 - (abs >> 10); + int mask = (1 << exp) - 1; + result += ((1 << (exp - 1)) - (~(abs >> exp) & 1)); + result &= ~mask; + } + if (isNaN((short) result)) { + // if result is NaN mask with qNaN + // (i.e. mask the most significant mantissa bit with 1) + // to comply with hardware implementations (ARM64, Intel, etc). + result |= NaN; + } + + return (short) result; + } + + /** + * Returns the smallest half-precision float value toward negative infinity + * greater than or equal to the specified half-precision float value. + * Special values are handled in the following ways: + * <ul> + * <li>If the specified half-precision float is NaN, the result is NaN</li> + * <li>If the specified half-precision float is infinity (negative or positive), + * the result is infinity (with the same sign)</li> + * <li>If the specified half-precision float is zero (negative or positive), + * the result is zero (with the same sign)</li> + * </ul> + * + * @param h A half-precision float value + * @return The smallest half-precision float value toward negative infinity + * greater than or equal to the specified half-precision float value + * + * @hide + */ + public static short ceil(short h) { + int bits = h & 0xffff; + int abs = bits & EXPONENT_SIGNIFICAND_MASK; + int result = bits; + + if (abs < 0x3c00) { + result &= SIGN_MASK; + result |= 0x3c00 & -(~(bits >> 15) & (abs != 0 ? 1 : 0)); + } else if (abs < 0x6400) { + abs = 25 - (abs >> 10); + int mask = (1 << abs) - 1; + result += mask & ((bits >> 15) - 1); + result &= ~mask; + } + if (isNaN((short) result)) { + // if result is NaN mask with qNaN + // (i.e. mask the most significant mantissa bit with 1) + // to comply with hardware implementations (ARM64, Intel, etc). + result |= NaN; + } + + return (short) result; + } + + /** + * Returns the largest half-precision float value toward positive infinity + * less than or equal to the specified half-precision float value. + * Special values are handled in the following ways: + * <ul> + * <li>If the specified half-precision float is NaN, the result is NaN</li> + * <li>If the specified half-precision float is infinity (negative or positive), + * the result is infinity (with the same sign)</li> + * <li>If the specified half-precision float is zero (negative or positive), + * the result is zero (with the same sign)</li> + * </ul> + * + * @param h A half-precision float value + * @return The largest half-precision float value toward positive infinity + * less than or equal to the specified half-precision float value + * + * @hide + */ + public static short floor(short h) { + int bits = h & 0xffff; + int abs = bits & EXPONENT_SIGNIFICAND_MASK; + int result = bits; + + if (abs < 0x3c00) { + result &= SIGN_MASK; + result |= 0x3c00 & (bits > 0x8000 ? 0xffff : 0x0); + } else if (abs < 0x6400) { + abs = 25 - (abs >> 10); + int mask = (1 << abs) - 1; + result += mask & -(bits >> 15); + result &= ~mask; + } + if (isNaN((short) result)) { + // if result is NaN mask with qNaN + // i.e. (Mask the most significant mantissa bit with 1) + result |= NaN; + } + + return (short) result; + } + + /** + * Returns the truncated half-precision float value of the specified + * half-precision float value. Special values are handled in the following ways: + * <ul> + * <li>If the specified half-precision float is NaN, the result is NaN</li> + * <li>If the specified half-precision float is infinity (negative or positive), + * the result is infinity (with the same sign)</li> + * <li>If the specified half-precision float is zero (negative or positive), + * the result is zero (with the same sign)</li> + * </ul> + * + * @param h A half-precision float value + * @return The truncated half-precision float value of the specified + * half-precision float value + * + * @hide + */ + public static short trunc(short h) { + int bits = h & 0xffff; + int abs = bits & EXPONENT_SIGNIFICAND_MASK; + int result = bits; + + if (abs < 0x3c00) { + result &= SIGN_MASK; + } else if (abs < 0x6400) { + abs = 25 - (abs >> 10); + int mask = (1 << abs) - 1; + result &= ~mask; + } + + return (short) result; + } + + /** + * Returns the smaller of two half-precision float values (the value closest + * to negative infinity). Special values are handled in the following ways: + * <ul> + * <li>If either value is NaN, the result is NaN</li> + * <li>{@link #NEGATIVE_ZERO} is smaller than {@link #POSITIVE_ZERO}</li> + * </ul> + * + * @param x The first half-precision value + * @param y The second half-precision value + * @return The smaller of the two specified half-precision values + * + * @hide + */ + public static short min(short x, short y) { + if (isNaN(x)) return NaN; + if (isNaN(y)) return NaN; + + if ((x & EXPONENT_SIGNIFICAND_MASK) == 0 && (y & EXPONENT_SIGNIFICAND_MASK) == 0) { + return (x & SIGN_MASK) != 0 ? x : y; + } + + return ((x & SIGN_MASK) != 0 ? 0x8000 - (x & 0xffff) : x & 0xffff) < + ((y & SIGN_MASK) != 0 ? 0x8000 - (y & 0xffff) : y & 0xffff) ? x : y; + } + + /** + * Returns the larger of two half-precision float values (the value closest + * to positive infinity). Special values are handled in the following ways: + * <ul> + * <li>If either value is NaN, the result is NaN</li> + * <li>{@link #POSITIVE_ZERO} is greater than {@link #NEGATIVE_ZERO}</li> + * </ul> + * + * @param x The first half-precision value + * @param y The second half-precision value + * + * @return The larger of the two specified half-precision values + * + * @hide + */ + public static short max(short x, short y) { + if (isNaN(x)) return NaN; + if (isNaN(y)) return NaN; + + if ((x & EXPONENT_SIGNIFICAND_MASK) == 0 && (y & EXPONENT_SIGNIFICAND_MASK) == 0) { + return (x & SIGN_MASK) != 0 ? y : x; + } + + return ((x & SIGN_MASK) != 0 ? 0x8000 - (x & 0xffff) : x & 0xffff) > + ((y & SIGN_MASK) != 0 ? 0x8000 - (y & 0xffff) : y & 0xffff) ? x : y; + } + + /** + * Returns true if the first half-precision float value is less (smaller + * toward negative infinity) than the second half-precision float value. + * If either of the values is NaN, the result is false. + * + * @param x The first half-precision value + * @param y The second half-precision value + * + * @return True if x is less than y, false otherwise + * + * @hide + */ + public static boolean less(short x, short y) { + if (isNaN(x)) return false; + if (isNaN(y)) return false; + + return ((x & SIGN_MASK) != 0 ? 0x8000 - (x & 0xffff) : x & 0xffff) < + ((y & SIGN_MASK) != 0 ? 0x8000 - (y & 0xffff) : y & 0xffff); + } + + /** + * Returns true if the first half-precision float value is less (smaller + * toward negative infinity) than or equal to the second half-precision + * float value. If either of the values is NaN, the result is false. + * + * @param x The first half-precision value + * @param y The second half-precision value + * + * @return True if x is less than or equal to y, false otherwise + * + * @hide + */ + public static boolean lessEquals(short x, short y) { + if (isNaN(x)) return false; + if (isNaN(y)) return false; + + return ((x & SIGN_MASK) != 0 ? 0x8000 - (x & 0xffff) : x & 0xffff) <= + ((y & SIGN_MASK) != 0 ? 0x8000 - (y & 0xffff) : y & 0xffff); + } + + /** + * Returns true if the first half-precision float value is greater (larger + * toward positive infinity) than the second half-precision float value. + * If either of the values is NaN, the result is false. + * + * @param x The first half-precision value + * @param y The second half-precision value + * + * @return True if x is greater than y, false otherwise + * + * @hide + */ + public static boolean greater(short x, short y) { + if (isNaN(x)) return false; + if (isNaN(y)) return false; + + return ((x & SIGN_MASK) != 0 ? 0x8000 - (x & 0xffff) : x & 0xffff) > + ((y & SIGN_MASK) != 0 ? 0x8000 - (y & 0xffff) : y & 0xffff); + } + + /** + * Returns true if the first half-precision float value is greater (larger + * toward positive infinity) than or equal to the second half-precision float + * value. If either of the values is NaN, the result is false. + * + * @param x The first half-precision value + * @param y The second half-precision value + * + * @return True if x is greater than y, false otherwise + * + * @hide + */ + public static boolean greaterEquals(short x, short y) { + if (isNaN(x)) return false; + if (isNaN(y)) return false; + + return ((x & SIGN_MASK) != 0 ? 0x8000 - (x & 0xffff) : x & 0xffff) >= + ((y & SIGN_MASK) != 0 ? 0x8000 - (y & 0xffff) : y & 0xffff); + } + + /** + * Returns true if the two half-precision float values are equal. + * If either of the values is NaN, the result is false. {@link #POSITIVE_ZERO} + * and {@link #NEGATIVE_ZERO} are considered equal. + * + * @param x The first half-precision value + * @param y The second half-precision value + * + * @return True if x is equal to y, false otherwise + * + * @hide + */ + public static boolean equals(short x, short y) { + if (isNaN(x)) return false; + if (isNaN(y)) return false; + + return x == y || ((x | y) & EXPONENT_SIGNIFICAND_MASK) == 0; + } + + /** + * Returns true if the specified half-precision float value represents + * infinity, false otherwise. + * + * @param h A half-precision float value + * @return True if the value is positive infinity or negative infinity, + * false otherwise + * + * @hide + */ + public static boolean isInfinite(short h) { + return (h & EXPONENT_SIGNIFICAND_MASK) == POSITIVE_INFINITY; + } + + /** + * Returns true if the specified half-precision float value represents + * a Not-a-Number, false otherwise. + * + * @param h A half-precision float value + * @return True if the value is a NaN, false otherwise + * + * @hide + */ + public static boolean isNaN(short h) { + return (h & EXPONENT_SIGNIFICAND_MASK) > POSITIVE_INFINITY; + } + + /** + * Returns true if the specified half-precision float value is normalized + * (does not have a subnormal representation). If the specified value is + * {@link #POSITIVE_INFINITY}, {@link #NEGATIVE_INFINITY}, + * {@link #POSITIVE_ZERO}, {@link #NEGATIVE_ZERO}, NaN or any subnormal + * number, this method returns false. + * + * @param h A half-precision float value + * @return True if the value is normalized, false otherwise + * + * @hide + */ + public static boolean isNormalized(short h) { + return (h & POSITIVE_INFINITY) != 0 && (h & POSITIVE_INFINITY) != POSITIVE_INFINITY; + } + + /** + * <p>Converts the specified half-precision float value into a + * single-precision float value. The following special cases are handled:</p> + * <ul> + * <li>If the input is {@link #NaN}, the returned value is {@link Float#NaN}</li> + * <li>If the input is {@link #POSITIVE_INFINITY} or + * {@link #NEGATIVE_INFINITY}, the returned value is respectively + * {@link Float#POSITIVE_INFINITY} or {@link Float#NEGATIVE_INFINITY}</li> + * <li>If the input is 0 (positive or negative), the returned value is +/-0.0f</li> + * <li>Otherwise, the returned value is a normalized single-precision float value</li> + * </ul> + * + * @param h The half-precision float value to convert to single-precision + * @return A normalized single-precision float value + * + * @hide + */ + public static float toFloat(short h) { + int bits = h & 0xffff; + int s = bits & SIGN_MASK; + int e = (bits >>> EXPONENT_SHIFT) & SHIFTED_EXPONENT_MASK; + int m = (bits ) & SIGNIFICAND_MASK; + + int outE = 0; + int outM = 0; + + if (e == 0) { // Denormal or 0 + if (m != 0) { + // Convert denorm fp16 into normalized fp32 + float o = Float.intBitsToFloat(FP32_DENORMAL_MAGIC + m); + o -= FP32_DENORMAL_FLOAT; + return s == 0 ? o : -o; + } + } else { + outM = m << 13; + if (e == 0x1f) { // Infinite or NaN + outE = 0xff; + if (outM != 0) { // SNaNs are quieted + outM |= FP32_QNAN_MASK; + } + } else { + outE = e - EXPONENT_BIAS + FP32_EXPONENT_BIAS; + } + } + + int out = (s << 16) | (outE << FP32_EXPONENT_SHIFT) | outM; + return Float.intBitsToFloat(out); + } + + /** + * <p>Converts the specified single-precision float value into a + * half-precision float value. The following special cases are handled:</p> + * <ul> + * <li>If the input is NaN (see {@link Float#isNaN(float)}), the returned + * value is {@link #NaN}</li> + * <li>If the input is {@link Float#POSITIVE_INFINITY} or + * {@link Float#NEGATIVE_INFINITY}, the returned value is respectively + * {@link #POSITIVE_INFINITY} or {@link #NEGATIVE_INFINITY}</li> + * <li>If the input is 0 (positive or negative), the returned value is + * {@link #POSITIVE_ZERO} or {@link #NEGATIVE_ZERO}</li> + * <li>If the input is a less than {@link #MIN_VALUE}, the returned value + * is flushed to {@link #POSITIVE_ZERO} or {@link #NEGATIVE_ZERO}</li> + * <li>If the input is a less than {@link #MIN_NORMAL}, the returned value + * is a denorm half-precision float</li> + * <li>Otherwise, the returned value is rounded to the nearest + * representable half-precision float value</li> + * </ul> + * + * @param f The single-precision float value to convert to half-precision + * @return A half-precision float value + * + * @hide + */ + public static short toHalf(float f) { + int bits = Float.floatToRawIntBits(f); + int s = (bits >>> FP32_SIGN_SHIFT ); + int e = (bits >>> FP32_EXPONENT_SHIFT) & FP32_SHIFTED_EXPONENT_MASK; + int m = (bits ) & FP32_SIGNIFICAND_MASK; + + int outE = 0; + int outM = 0; + + if (e == 0xff) { // Infinite or NaN + outE = 0x1f; + outM = m != 0 ? 0x200 : 0; + } else { + e = e - FP32_EXPONENT_BIAS + EXPONENT_BIAS; + if (e >= 0x1f) { // Overflow + outE = 0x1f; + } else if (e <= 0) { // Underflow + if (e < -10) { + // The absolute fp32 value is less than MIN_VALUE, flush to +/-0 + } else { + // The fp32 value is a normalized float less than MIN_NORMAL, + // we convert to a denorm fp16 + m = m | 0x800000; + int shift = 14 - e; + outM = m >> shift; + + int lowm = m & ((1 << shift) - 1); + int hway = 1 << (shift - 1); + // if above halfway or exactly halfway and outM is odd + if (lowm + (outM & 1) > hway){ + // Round to nearest even + // Can overflow into exponent bit, which surprisingly is OK. + // This increment relies on the +outM in the return statement below + outM++; + } + } + } else { + outE = e; + outM = m >> 13; + // if above halfway or exactly halfway and outM is odd + if ((m & 0x1fff) + (outM & 0x1) > 0x1000) { + // Round to nearest even + // Can overflow into exponent bit, which surprisingly is OK. + // This increment relies on the +outM in the return statement below + outM++; + } + } + } + // The outM is added here as the +1 increments for outM above can + // cause an overflow in the exponent bit which is OK. + return (short) ((s << SIGN_SHIFT) | (outE << EXPONENT_SHIFT) + outM); + } + + /** + * <p>Returns a hexadecimal string representation of the specified half-precision + * float value. If the value is a NaN, the result is <code>"NaN"</code>, + * otherwise the result follows this format:</p> + * <ul> + * <li>If the sign is positive, no sign character appears in the result</li> + * <li>If the sign is negative, the first character is <code>'-'</code></li> + * <li>If the value is inifinity, the string is <code>"Infinity"</code></li> + * <li>If the value is 0, the string is <code>"0x0.0p0"</code></li> + * <li>If the value has a normalized representation, the exponent and + * significand are represented in the string in two fields. The significand + * starts with <code>"0x1."</code> followed by its lowercase hexadecimal + * representation. Trailing zeroes are removed unless all digits are 0, then + * a single zero is used. The significand representation is followed by the + * exponent, represented by <code>"p"</code>, itself followed by a decimal + * string of the unbiased exponent</li> + * <li>If the value has a subnormal representation, the significand starts + * with <code>"0x0."</code> followed by its lowercase hexadecimal + * representation. Trailing zeroes are removed unless all digits are 0, then + * a single zero is used. The significand representation is followed by the + * exponent, represented by <code>"p-14"</code></li> + * </ul> + * + * @param h A half-precision float value + * @return A hexadecimal string representation of the specified value + * + * @hide + */ + public static String toHexString(short h) { + StringBuilder o = new StringBuilder(); + + int bits = h & 0xffff; + int s = (bits >>> SIGN_SHIFT ); + int e = (bits >>> EXPONENT_SHIFT) & SHIFTED_EXPONENT_MASK; + int m = (bits ) & SIGNIFICAND_MASK; + + if (e == 0x1f) { // Infinite or NaN + if (m == 0) { + if (s != 0) o.append('-'); + o.append("Infinity"); + } else { + o.append("NaN"); + } + } else { + if (s == 1) o.append('-'); + if (e == 0) { + if (m == 0) { + o.append("0x0.0p0"); + } else { + o.append("0x0."); + String significand = Integer.toHexString(m); + o.append(significand.replaceFirst("0{2,}$", "")); + o.append("p-14"); + } + } else { + o.append("0x1."); + String significand = Integer.toHexString(m); + o.append(significand.replaceFirst("0{2,}$", "")); + o.append('p'); + o.append(Integer.toString(e - EXPONENT_BIAS)); + } + } + + return o.toString(); + } +} diff --git a/ravenwood/scripts/list-ravenwood-tests.sh b/ravenwood/scripts/list-ravenwood-tests.sh index fb9b823ee93b..05f3fdffdaa7 100755 --- a/ravenwood/scripts/list-ravenwood-tests.sh +++ b/ravenwood/scripts/list-ravenwood-tests.sh @@ -15,4 +15,4 @@ # List all the ravenwood test modules. -jq -r 'to_entries[] | select( .value.compatibility_suites | index("ravenwood-tests") ) | .key' "$OUT/module-info.json" +jq -r 'to_entries[] | select( .value.compatibility_suites | index("ravenwood-tests") ) | .key' "$OUT/module-info.json" | sort diff --git a/ravenwood/scripts/remove-ravenizer-output.sh b/ravenwood/scripts/remove-ravenizer-output.sh new file mode 100755 index 000000000000..be15b711b980 --- /dev/null +++ b/ravenwood/scripts/remove-ravenizer-output.sh @@ -0,0 +1,25 @@ +#!/bin/bash +# Copyright (C) 2024 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Delete all the ravenizer output jar files from Soong's intermediate directory. + +# `-a -prune` is needed because otherwise find would be confused if the directory disappears. + +find "${ANDROID_BUILD_TOP:?}/out/soong/.intermediates/" \ + -type d \ + -name 'ravenizer' \ + -print \ + -exec rm -fr \{\} \; \ + -a -prune diff --git a/ravenwood/scripts/update-test-mapping.sh b/ravenwood/scripts/update-test-mapping.sh new file mode 100755 index 000000000000..b6cf5b857682 --- /dev/null +++ b/ravenwood/scripts/update-test-mapping.sh @@ -0,0 +1,82 @@ +#!/bin/bash +# Copyright (C) 2024 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Update f/b/r/TEST_MAPPING with all the ravenwood tests as presubmit. +# +# Note, before running it, make sure module-info.json is up-to-date by running +# (any) build. + +set -e + +main() { + local script_name="${0##*/}" + local script_dir="${0%/*}" + local test_mapping="$script_dir/../TEST_MAPPING" + local test_mapping_bak="$script_dir/../TEST_MAPPING.bak" + + local header="$(sed -ne '1,/AUTO-GENERATED-START/p' "$test_mapping")" + local footer="$(sed -ne '/AUTO-GENERATED-END/,$p' "$test_mapping")" + + echo "Getting all tests" + local tests=( $("$script_dir/list-ravenwood-tests.sh") ) + + local num_tests="${#tests[@]}" + + if (( $num_tests == 0 )) ; then + echo "Something went wrong. No ravenwood tests detected." 1>&2 + return 1 + fi + + echo "Tests: ${tests[@]}" + + echo "Creating backup at $test_mapping_bak" + cp "$test_mapping" "$test_mapping_bak" + + echo "Updating $test_mapping" + { + echo "$header" + + echo " // DO NOT MODIFY MANUALLY" + echo " // Use scripts/$script_name to update it." + + local i=0 + while (( $i < $num_tests )) ; do + local comma="," + if (( $i == ($num_tests - 1) )); then + comma="" + fi + echo " {" + echo " \"name\": \"${tests[$i]}\"," + echo " \"host\": true" + echo " }$comma" + + i=$(( $i + 1 )) + done + + echo "$footer" + } >"$test_mapping" + + if cmp "$test_mapping_bak" "$test_mapping" ; then + echo "No change detecetd." + return 0 + fi + echo "Updated $test_mapping" + + # `|| true` is needed because of `set -e`. + diff -u "$test_mapping_bak" "$test_mapping" || true + return 0 +} + +main diff --git a/ravenwood/texts/ravenwood-annotation-allowed-classes.txt b/ravenwood/texts/ravenwood-annotation-allowed-classes.txt index d8366c58c50d..34239b826c67 100644 --- a/ravenwood/texts/ravenwood-annotation-allowed-classes.txt +++ b/ravenwood/texts/ravenwood-annotation-allowed-classes.txt @@ -46,6 +46,7 @@ android.util.EmptyArray android.util.EventLog android.util.FloatProperty android.util.FloatMath +android.util.Half android.util.IndentingPrintWriter android.util.IntArray android.util.IntProperty @@ -277,7 +278,11 @@ android.graphics.ColorSpace android.graphics.Insets android.graphics.Interpolator android.graphics.Matrix +android.graphics.Matrix44 +android.graphics.Outline +android.graphics.ParcelableColorSpace android.graphics.Path +android.graphics.PixelFormat android.graphics.Point android.graphics.PointF android.graphics.Rect diff --git a/ravenwood/tools/ravenizer/src/com/android/platform/test/ravenwood/ravenizer/Exceptions.kt b/ravenwood/tools/ravenizer/src/com/android/platform/test/ravenwood/ravenizer/Exceptions.kt index 3a7fab39e4ac..0dcd271562d1 100644 --- a/ravenwood/tools/ravenizer/src/com/android/platform/test/ravenwood/ravenizer/Exceptions.kt +++ b/ravenwood/tools/ravenizer/src/com/android/platform/test/ravenwood/ravenizer/Exceptions.kt @@ -17,7 +17,14 @@ package com.android.platform.test.ravenwood.ravenizer +import com.android.hoststubgen.UserErrorException + /** * Use it for internal exception that really shouldn't happen. */ class RavenizerInternalException(message: String) : Exception(message) + +/** + * Thrown when an invalid test is detected in the target jar. (e.g. JUni3 tests) + */ +class RavenizerInvalidTestException(message: String) : Exception(message), UserErrorException diff --git a/ravenwood/tools/ravenizer/src/com/android/platform/test/ravenwood/ravenizer/Ravenizer.kt b/ravenwood/tools/ravenizer/src/com/android/platform/test/ravenwood/ravenizer/Ravenizer.kt index e92ef7216e25..a38512ec9f2d 100644 --- a/ravenwood/tools/ravenizer/src/com/android/platform/test/ravenwood/ravenizer/Ravenizer.kt +++ b/ravenwood/tools/ravenizer/src/com/android/platform/test/ravenwood/ravenizer/Ravenizer.kt @@ -44,6 +44,9 @@ data class RavenizerStats( /** Time took to build [ClasNodes] */ var loadStructureTime: Double = .0, + /** Time took to validate the classes */ + var validationTime: Double = .0, + /** Total real time spent for converting the jar file */ var totalProcessTime: Double = .0, @@ -67,6 +70,7 @@ data class RavenizerStats( RavenizerStats{ totalTime=$totalTime, loadStructureTime=$loadStructureTime, + validationTime=$validationTime, totalProcessTime=$totalProcessTime, totalConversionTime=$totalConversionTime, totalCopyTime=$totalCopyTime, @@ -84,16 +88,44 @@ data class RavenizerStats( class Ravenizer(val options: RavenizerOptions) { fun run() { val stats = RavenizerStats() + + val fatalValidation = options.fatalValidation.get + stats.totalTime = log.nTime { - process(options.inJar.get, options.outJar.get, stats) + process( + options.inJar.get, + options.outJar.get, + options.enableValidation.get, + fatalValidation, + stats, + ) } log.i(stats.toString()) } - private fun process(inJar: String, outJar: String, stats: RavenizerStats) { + private fun process( + inJar: String, + outJar: String, + enableValidation: Boolean, + fatalValidation: Boolean, + stats: RavenizerStats, + ) { var allClasses = ClassNodes.loadClassStructures(inJar) { time -> stats.loadStructureTime = time } + if (enableValidation) { + stats.validationTime = log.iTime("Validating classes") { + if (!validateClasses(allClasses)) { + var message = "Invalid test class(es) detected." + + " See error log for details." + if (fatalValidation) { + throw RavenizerInvalidTestException(message) + } else { + log.w("Warning: $message") + } + } + } + } stats.totalProcessTime = log.iTime("$executableName processing $inJar") { ZipFile(inJar).use { inZip -> diff --git a/ravenwood/tools/ravenizer/src/com/android/platform/test/ravenwood/ravenizer/RavenizerOptions.kt b/ravenwood/tools/ravenizer/src/com/android/platform/test/ravenwood/ravenizer/RavenizerOptions.kt index e85e3be31b77..e8341e5ceb06 100644 --- a/ravenwood/tools/ravenizer/src/com/android/platform/test/ravenwood/ravenizer/RavenizerOptions.kt +++ b/ravenwood/tools/ravenizer/src/com/android/platform/test/ravenwood/ravenizer/RavenizerOptions.kt @@ -27,6 +27,12 @@ class RavenizerOptions( /** Output jar file */ var outJar: SetOnce<String> = SetOnce(""), + + /** Whether to enable test validation. */ + var enableValidation: SetOnce<Boolean> = SetOnce(true), + + /** Whether the validation failure is fatal or not. */ + var fatalValidation: SetOnce<Boolean> = SetOnce(false), ) { companion object { fun parseArgs(args: Array<String>): RavenizerOptions { @@ -52,6 +58,12 @@ class RavenizerOptions( "--in-jar" -> ret.inJar.set(nextArg()).ensureFileExists() "--out-jar" -> ret.outJar.set(nextArg()) + "--enable-validation" -> ret.enableValidation.set(true) + "--disable-validation" -> ret.enableValidation.set(false) + + "--fatal-validation" -> ret.fatalValidation.set(true) + "--no-fatal-validation" -> ret.fatalValidation.set(false) + else -> throw ArgumentsException("Unknown option: $arg") } } catch (e: SetOnce.SetMoreThanOnceException) { @@ -74,6 +86,8 @@ class RavenizerOptions( RavenizerOptions{ inJar=$inJar, outJar=$outJar, + enableValidation=$enableValidation, + fatalValidation=$fatalValidation, } """.trimIndent() } diff --git a/ravenwood/tools/ravenizer/src/com/android/platform/test/ravenwood/ravenizer/Utils.kt b/ravenwood/tools/ravenizer/src/com/android/platform/test/ravenwood/ravenizer/Utils.kt index e026e7ab3679..059f5a4b4d47 100644 --- a/ravenwood/tools/ravenizer/src/com/android/platform/test/ravenwood/ravenizer/Utils.kt +++ b/ravenwood/tools/ravenizer/src/com/android/platform/test/ravenwood/ravenizer/Utils.kt @@ -87,9 +87,12 @@ fun String.shouldByBypassed(): Boolean { return this.startsWithAny( "java/", // just in case... "javax/", + "junit/", "org/junit/", "org/mockito/", "kotlin/", + "androidx/", + "android/support/", // TODO -- anything else? ) } diff --git a/ravenwood/tools/ravenizer/src/com/android/platform/test/ravenwood/ravenizer/Validator.kt b/ravenwood/tools/ravenizer/src/com/android/platform/test/ravenwood/ravenizer/Validator.kt new file mode 100644 index 000000000000..27092d28ae5e --- /dev/null +++ b/ravenwood/tools/ravenizer/src/com/android/platform/test/ravenwood/ravenizer/Validator.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.platform.test.ravenwood.ravenizer + +import com.android.hoststubgen.asm.ClassNodes +import com.android.hoststubgen.asm.startsWithAny +import com.android.hoststubgen.asm.toHumanReadableClassName +import com.android.hoststubgen.log +import org.objectweb.asm.tree.ClassNode + +fun validateClasses(classes: ClassNodes): Boolean { + var allOk = true + classes.forEach { allOk = checkClass(it, classes) && allOk } + + return allOk +} + +/** + * Validate a class. + * + * - A test class shouldn't extend + * + */ +fun checkClass(cn: ClassNode, classes: ClassNodes): Boolean { + if (cn.name.shouldByBypassed()) { + // Class doesn't need to be checked. + return true + } + var allOk = true + + // See if there's any class that extends a legacy base class. + // But ignore the base classes in android.test. + if (!cn.name.startsWithAny("android/test/")) { + allOk = checkSuperClass(cn, cn, classes) && allOk + } + return allOk +} + +fun checkSuperClass(targetClass: ClassNode, currentClass: ClassNode, classes: ClassNodes): Boolean { + if (currentClass.superName == null || currentClass.superName == "java/lang/Object") { + return true // No parent class + } + if (currentClass.superName.isLegacyTestBaseClass()) { + log.e("Error: Class ${targetClass.name.toHumanReadableClassName()} extends" + + " a legacy test class ${currentClass.superName.toHumanReadableClassName()}.") + return false + } + classes.findClass(currentClass.superName)?.let { + return checkSuperClass(targetClass, it, classes) + } + // Super class not found. + // log.w("Class ${currentClass.superName} not found.") + return true +} + +/** + * Check if a class internal name is a known legacy test base class. + */ +fun String.isLegacyTestBaseClass(): Boolean { + return this.startsWithAny( + "junit/framework/TestCase", + + // In case the test doesn't statically include JUnit, we need + "android/test/AndroidTestCase", + "android/test/InstrumentationTestCase", + "android/test/InstrumentationTestSuite", + ) +} diff --git a/ravenwood/tools/ravenizer/src/com/android/platform/test/ravenwood/ravenizer/adapter/RunnerRewritingAdapter.kt b/ravenwood/tools/ravenizer/src/com/android/platform/test/ravenwood/ravenizer/adapter/RunnerRewritingAdapter.kt index 25cad0213b72..dc934ebabbf2 100644 --- a/ravenwood/tools/ravenizer/src/com/android/platform/test/ravenwood/ravenizer/adapter/RunnerRewritingAdapter.kt +++ b/ravenwood/tools/ravenizer/src/com/android/platform/test/ravenwood/ravenizer/adapter/RunnerRewritingAdapter.kt @@ -183,7 +183,7 @@ class RunnerRewritingAdapter private constructor( av.visit("value", ravenwoodTestRunnerType.type) av.visitEnd() } - log.d("Processed ${classInternalName.toHumanReadableClassName()}") + log.i("Update the @RunWith: ${classInternalName.toHumanReadableClassName()}") } /* diff --git a/services/accessibility/accessibility.aconfig b/services/accessibility/accessibility.aconfig index 8e2e0ad76d15..08cc9c33357b 100644 --- a/services/accessibility/accessibility.aconfig +++ b/services/accessibility/accessibility.aconfig @@ -121,6 +121,13 @@ flag { } flag { + name: "enable_magnification_keyboard_control" + namespace: "accessibility" + description: "Whether to enable keyboard control for magnification" + bug: "355487062" +} + +flag { name: "fix_drag_pointer_when_ending_drag" namespace: "accessibility" description: "Send the correct pointer id when transitioning from dragging to delegating states." diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java index 7cbb97e56b01..f1a8b5a96080 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java @@ -163,6 +163,7 @@ import android.view.accessibility.IAccessibilityInteractionConnectionCallback; import android.view.accessibility.IAccessibilityManager; import android.view.accessibility.IAccessibilityManagerClient; import android.view.accessibility.IMagnificationConnection; +import android.view.accessibility.IUserInitializationCompleteCallback; import android.view.inputmethod.EditorInfo; import com.android.internal.R; @@ -366,6 +367,11 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub private final List<SendWindowStateChangedEventRunnable> mSendWindowStateChangedEventRunnables = new ArrayList<>(); + @VisibleForTesting + final HashSet<IUserInitializationCompleteCallback> + mUserInitializationCompleteCallbacks = + new HashSet<IUserInitializationCompleteCallback>(); + @GuardedBy("mLock") private @UserIdInt int mCurrentUserId = UserHandle.USER_SYSTEM; @@ -2034,6 +2040,17 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub obtainMessage(AccessibilityManagerService::announceNewUserIfNeeded, this), WAIT_FOR_USER_STATE_FULLY_INITIALIZED_MILLIS); } + + for (IUserInitializationCompleteCallback callback + : mUserInitializationCompleteCallbacks) { + try { + callback.onUserInitializationComplete(mCurrentUserId); + } catch (RemoteException re) { + Log.e("AccessibilityManagerService", + "Error while dispatching userInitializationComplete callback: ", + re); + } + } } } @@ -6251,6 +6268,24 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub } @Override + @RequiresNoPermission + public void registerUserInitializationCompleteCallback( + IUserInitializationCompleteCallback callback) { + synchronized (mLock) { + mUserInitializationCompleteCallbacks.add(callback); + } + } + + @Override + @RequiresNoPermission + public void unregisterUserInitializationCompleteCallback( + IUserInitializationCompleteCallback callback) { + synchronized (mLock) { + mUserInitializationCompleteCallbacks.remove(callback); + } + } + + @Override @EnforcePermission(INJECT_EVENTS) public void injectInputEventToInputFilter(InputEvent event) { injectInputEventToInputFilter_enforcePermission(); diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/MultiTap.java b/services/accessibility/java/com/android/server/accessibility/gestures/MultiTap.java index 9555980287e3..7f795564752e 100644 --- a/services/accessibility/java/com/android/server/accessibility/gestures/MultiTap.java +++ b/services/accessibility/java/com/android/server/accessibility/gestures/MultiTap.java @@ -89,7 +89,7 @@ public class MultiTap extends GestureMatcher { @Override protected void onUp(MotionEvent event, MotionEvent rawEvent, int policyFlags) { - cancelAfterDoubleTapTimeout(event, rawEvent, policyFlags); + cancelPendingTransitions(); if (!isInsideSlop(rawEvent, mTouchSlop)) { cancelGesture(event, rawEvent, policyFlags); } diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/MultiTapAndHold.java b/services/accessibility/java/com/android/server/accessibility/gestures/MultiTapAndHold.java index 15e1278a9175..872ade5cb510 100644 --- a/services/accessibility/java/com/android/server/accessibility/gestures/MultiTapAndHold.java +++ b/services/accessibility/java/com/android/server/accessibility/gestures/MultiTapAndHold.java @@ -46,7 +46,6 @@ public class MultiTapAndHold extends MultiTap { @Override protected void onUp(MotionEvent event, MotionEvent rawEvent, int policyFlags) { super.onUp(event, rawEvent, policyFlags); - cancelAfterDoubleTapTimeout(event, rawEvent, policyFlags); } @Override diff --git a/services/appfunctions/java/com/android/server/appfunctions/SyncAppSearchCallHelper.java b/services/appfunctions/java/com/android/server/appfunctions/SyncAppSearchCallHelper.java new file mode 100644 index 000000000000..c01fe311e9ca --- /dev/null +++ b/services/appfunctions/java/com/android/server/appfunctions/SyncAppSearchCallHelper.java @@ -0,0 +1,130 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS 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.appfunctions; + +import static android.app.appfunctions.flags.Flags.FLAG_ENABLE_APP_FUNCTION_MANAGER; + +import android.annotation.FlaggedApi; +import android.annotation.NonNull; +import android.annotation.WorkerThread; +import android.app.appsearch.AppSearchManager; +import android.app.appsearch.AppSearchManager.SearchContext; +import android.app.appsearch.AppSearchResult; +import android.app.appsearch.AppSearchSession; +import android.app.appsearch.GetSchemaResponse; +import android.app.appsearch.SetSchemaRequest; +import android.app.appsearch.SetSchemaResponse; +import android.util.Slog; + +import com.android.internal.infra.AndroidFuture; + +import java.io.Closeable; +import java.io.IOException; +import java.util.Objects; +import java.util.concurrent.Executor; + +/** + * Helper class for interacting with a system server local appsearch session synchronously. + */ +@FlaggedApi(FLAG_ENABLE_APP_FUNCTION_MANAGER) +public class SyncAppSearchCallHelper implements Closeable { + private static final String TAG = SyncAppSearchCallHelper.class.getSimpleName(); + private final Executor mExecutor; + private final AppSearchManager mAppSearchManager; + private final AndroidFuture<AppSearchResult<AppSearchSession>> mSettableSessionFuture; + + public SyncAppSearchCallHelper(@NonNull AppSearchManager appSearchManager, + @NonNull Executor executor, + @NonNull SearchContext appSearchContext) { + Objects.requireNonNull(appSearchManager); + Objects.requireNonNull(executor); + Objects.requireNonNull(appSearchContext); + + mExecutor = executor; + mAppSearchManager = appSearchManager; + mSettableSessionFuture = new AndroidFuture<>(); + mAppSearchManager.createSearchSession( + appSearchContext, mExecutor, mSettableSessionFuture::complete); + } + + /** + * Converts a failed app search result codes into an exception. + */ + @NonNull + private static Exception failedResultToException(@NonNull AppSearchResult appSearchResult) { + return switch (appSearchResult.getResultCode()) { + case AppSearchResult.RESULT_INVALID_ARGUMENT -> new IllegalArgumentException( + appSearchResult.getErrorMessage()); + case AppSearchResult.RESULT_IO_ERROR -> new IOException( + appSearchResult.getErrorMessage()); + case AppSearchResult.RESULT_SECURITY_ERROR -> new SecurityException( + appSearchResult.getErrorMessage()); + default -> new IllegalStateException(appSearchResult.getErrorMessage()); + }; + } + + private AppSearchSession getSession() throws Exception { + AppSearchResult<AppSearchSession> sessionResult = mSettableSessionFuture.get(); + if (!sessionResult.isSuccess()) { + throw failedResultToException(sessionResult); + } + return sessionResult.getResultValue(); + } + + /** + * Gets the schema for a given app search session. + */ + @WorkerThread + public GetSchemaResponse getSchema() throws Exception { + AndroidFuture<AppSearchResult<GetSchemaResponse>> settableSchemaResponse = + new AndroidFuture<>(); + getSession().getSchema(mExecutor, settableSchemaResponse::complete); + AppSearchResult<GetSchemaResponse> schemaResponse = settableSchemaResponse.get(); + if (schemaResponse.isSuccess()) { + return schemaResponse.getResultValue(); + } else { + throw failedResultToException(schemaResponse); + } + } + + /** + * Sets the schema for a given app search session. + */ + @WorkerThread + public SetSchemaResponse setSchema( + @NonNull SetSchemaRequest setSchemaRequest) throws Exception { + AndroidFuture<AppSearchResult<SetSchemaResponse>> settableSchemaResponse = + new AndroidFuture<>(); + getSession().setSchema( + setSchemaRequest, mExecutor, mExecutor, settableSchemaResponse::complete); + AppSearchResult<SetSchemaResponse> schemaResponse = settableSchemaResponse.get(); + if (schemaResponse.isSuccess()) { + return schemaResponse.getResultValue(); + } else { + throw failedResultToException(schemaResponse); + } + } + + @Override + public void close() throws IOException { + try { + getSession().close(); + } catch (Exception ex) { + Slog.e(TAG, "Failed to close app search session", ex); + } + } +} diff --git a/services/companion/Android.bp b/services/companion/Android.bp index 2bfdd0a7c819..77650ebe2698 100644 --- a/services/companion/Android.bp +++ b/services/companion/Android.bp @@ -28,7 +28,6 @@ java_library_static { ], static_libs: [ "ukey2_jni", - "virtualdevice_flags_lib", "virtual_camera_service_aidl-java", ], lint: { diff --git a/services/companion/java/com/android/server/companion/devicepresence/ObservableUuidStore.java b/services/companion/java/com/android/server/companion/devicepresence/ObservableUuidStore.java index 4678a165b83f..5fd282df97ec 100644 --- a/services/companion/java/com/android/server/companion/devicepresence/ObservableUuidStore.java +++ b/services/companion/java/com/android/server/companion/devicepresence/ObservableUuidStore.java @@ -190,7 +190,7 @@ public class ObservableUuidStore { } mCachedPerUser.set(userId, cachedObservableUuids); } - return cachedObservableUuids; + return cachedObservableUuids == null ? new ArrayList<>() : cachedObservableUuids; } /** diff --git a/services/companion/java/com/android/server/companion/virtual/Android.bp b/services/companion/java/com/android/server/companion/virtual/Android.bp deleted file mode 100644 index 66313e6ca957..000000000000 --- a/services/companion/java/com/android/server/companion/virtual/Android.bp +++ /dev/null @@ -1,17 +0,0 @@ -package { - default_team: "trendy_team_xr_framework", -} - -java_aconfig_library { - name: "virtualdevice_flags_lib", - aconfig_declarations: "virtualdevice_flags", -} - -aconfig_declarations { - name: "virtualdevice_flags", - package: "com.android.server.companion.virtual", - container: "system", - srcs: [ - "flags.aconfig", - ], -} diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceLog.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceLog.java index b0bacfd158ed..fed153f0b7fa 100644 --- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceLog.java +++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceLog.java @@ -48,9 +48,6 @@ final class VirtualDeviceLog { void logCreated(int deviceId, int ownerUid) { final long token = Binder.clearCallingIdentity(); try { - if (!Flags.dumpHistory()) { - return; - } addEntry(new LogEntry(TYPE_CREATED, deviceId, System.currentTimeMillis(), ownerUid)); } finally { Binder.restoreCallingIdentity(token); @@ -60,9 +57,6 @@ final class VirtualDeviceLog { void logClosed(int deviceId, int ownerUid) { final long token = Binder.clearCallingIdentity(); try { - if (!Flags.dumpHistory()) { - return; - } addEntry(new LogEntry(TYPE_CLOSED, deviceId, System.currentTimeMillis(), ownerUid)); } finally { Binder.restoreCallingIdentity(token); @@ -79,9 +73,6 @@ final class VirtualDeviceLog { void dump(PrintWriter pw) { final long token = Binder.clearCallingIdentity(); try { - if (!Flags.dumpHistory()) { - return; - } pw.println("VirtualDevice Log:"); UidToPackageNameCache packageNameCache = new UidToPackageNameCache( mContext.getPackageManager()); diff --git a/services/companion/java/com/android/server/companion/virtual/flags.aconfig b/services/companion/java/com/android/server/companion/virtual/flags.aconfig deleted file mode 100644 index 616f5d09e13f..000000000000 --- a/services/companion/java/com/android/server/companion/virtual/flags.aconfig +++ /dev/null @@ -1,11 +0,0 @@ -# OLD PACKAGE, DO NOT USE: Prefer `flags.aconfig` in core/java/android/companion/virtual -# (or other custom files) to define your flags -package: "com.android.server.companion.virtual" -container: "system" - -flag { - name: "dump_history" - namespace: "virtual_devices" - description: "This flag controls if a history of virtual devices is shown in dumpsys virtualdevices" - bug: "293114719" -}
\ No newline at end of file diff --git a/services/core/java/com/android/server/BatteryService.java b/services/core/java/com/android/server/BatteryService.java index 2de44820ec05..1470e9a9d925 100644 --- a/services/core/java/com/android/server/BatteryService.java +++ b/services/core/java/com/android/server/BatteryService.java @@ -23,6 +23,7 @@ import static com.android.internal.logging.nano.MetricsProto.MetricsEvent; import static com.android.server.health.Utils.copyV1Battery; import android.annotation.Nullable; +import android.annotation.SuppressLint; import android.app.ActivityManager; import android.app.ActivityManagerInternal; import android.app.AppOpsManager; @@ -67,6 +68,7 @@ import android.util.proto.ProtoOutputStream; import com.android.internal.app.IBatteryStats; import com.android.internal.logging.MetricsLogger; +import com.android.internal.os.SomeArgs; import com.android.internal.util.DumpUtils; import com.android.server.am.BatteryStatsService; import com.android.server.health.HealthServiceWrapper; @@ -207,18 +209,18 @@ public final class BatteryService extends SystemService { private final CopyOnWriteArraySet<BatteryManagerInternal.ChargingPolicyChangeListener> mChargingPolicyChangeListeners = new CopyOnWriteArraySet<>(); - private Bundle mBatteryChangedOptions = BroadcastOptions.makeBasic() + private static final Bundle BATTERY_CHANGED_OPTIONS = BroadcastOptions.makeBasic() .setDeliveryGroupPolicy(BroadcastOptions.DELIVERY_GROUP_POLICY_MOST_RECENT) .setDeferralPolicy(BroadcastOptions.DEFERRAL_POLICY_UNTIL_ACTIVE) .toBundle(); /** Used for both connected/disconnected, so match using key */ - private Bundle mPowerOptions = BroadcastOptions.makeBasic() + private static final Bundle POWER_OPTIONS = BroadcastOptions.makeBasic() .setDeliveryGroupPolicy(BroadcastOptions.DELIVERY_GROUP_POLICY_MOST_RECENT) .setDeliveryGroupMatchingKey("android", Intent.ACTION_POWER_CONNECTED) .setDeferralPolicy(BroadcastOptions.DEFERRAL_POLICY_UNTIL_ACTIVE) .toBundle(); /** Used for both low/okay, so match using key */ - private Bundle mBatteryOptions = BroadcastOptions.makeBasic() + private static final Bundle BATTERY_OPTIONS = BroadcastOptions.makeBasic() .setDeliveryGroupPolicy(BroadcastOptions.DELIVERY_GROUP_POLICY_MOST_RECENT) .setDeliveryGroupMatchingKey("android", Intent.ACTION_BATTERY_OKAY) .setDeferralPolicy(BroadcastOptions.DEFERRAL_POLICY_UNTIL_ACTIVE) @@ -226,11 +228,60 @@ public final class BatteryService extends SystemService { private MetricsLogger mMetricsLogger; + private static final int MSG_BROADCAST_BATTERY_CHANGED = 1; + private static final int MSG_BROADCAST_POWER_CONNECTION_CHANGED = 2; + private static final int MSG_BROADCAST_BATTERY_LOW_OKAY = 3; + + private final Handler.Callback mLocalCallback = msg -> { + switch (msg.what) { + case MSG_BROADCAST_BATTERY_CHANGED: { + final SomeArgs args = (SomeArgs) msg.obj; + final Context context; + final Intent intent; + try { + context = (Context) args.arg1; + intent = (Intent) args.arg2; + } finally { + args.recycle(); + } + broadcastBatteryChangedIntent(context, intent, BATTERY_CHANGED_OPTIONS); + return true; + } + case MSG_BROADCAST_POWER_CONNECTION_CHANGED: { + final SomeArgs args = (SomeArgs) msg.obj; + final Context context; + final Intent intent; + try { + context = (Context) args.arg1; + intent = (Intent) args.arg2; + } finally { + args.recycle(); + } + sendBroadcastToAllUsers(context, intent, POWER_OPTIONS); + return true; + } + case MSG_BROADCAST_BATTERY_LOW_OKAY: { + final SomeArgs args = (SomeArgs) msg.obj; + final Context context; + final Intent intent; + try { + context = (Context) args.arg1; + intent = (Intent) args.arg2; + } finally { + args.recycle(); + } + sendBroadcastToAllUsers(context, intent, BATTERY_OPTIONS); + return true; + } + } + return false; + }; + public BatteryService(Context context) { super(context); mContext = context; - mHandler = new Handler(true /*async*/); + mHandler = new Handler(mLocalCallback, true /*async*/); mLed = new Led(context, getLocalService(LightsManager.class)); mBatteryStats = BatteryStatsService.getService(); mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class); @@ -660,25 +711,43 @@ public final class BatteryService extends SystemService { final Intent statusIntent = new Intent(Intent.ACTION_POWER_CONNECTED); statusIntent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); statusIntent.putExtra(BatteryManager.EXTRA_SEQUENCE, mSequence); - mHandler.post(new Runnable() { - @Override - public void run() { - mContext.sendBroadcastAsUser(statusIntent, UserHandle.ALL, null, - mPowerOptions); - } - }); + if (com.android.server.flags.Flags.consolidateBatteryChangeEvents()) { + mHandler.removeMessages(MSG_BROADCAST_POWER_CONNECTION_CHANGED); + final SomeArgs args = SomeArgs.obtain(); + args.arg1 = mContext; + args.arg2 = statusIntent; + mHandler.obtainMessage(MSG_BROADCAST_POWER_CONNECTION_CHANGED, args) + .sendToTarget(); + } else { + mHandler.post(new Runnable() { + @Override + public void run() { + mContext.sendBroadcastAsUser(statusIntent, UserHandle.ALL, null, + POWER_OPTIONS); + } + }); + } } else if (mPlugType == 0 && mLastPlugType != 0) { final Intent statusIntent = new Intent(Intent.ACTION_POWER_DISCONNECTED); statusIntent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); statusIntent.putExtra(BatteryManager.EXTRA_SEQUENCE, mSequence); - mHandler.post(new Runnable() { - @Override - public void run() { - mContext.sendBroadcastAsUser(statusIntent, UserHandle.ALL, null, - mPowerOptions); - } - }); + if (com.android.server.flags.Flags.consolidateBatteryChangeEvents()) { + mHandler.removeMessages(MSG_BROADCAST_POWER_CONNECTION_CHANGED); + final SomeArgs args = SomeArgs.obtain(); + args.arg1 = mContext; + args.arg2 = statusIntent; + mHandler.obtainMessage(MSG_BROADCAST_POWER_CONNECTION_CHANGED, args) + .sendToTarget(); + } else { + mHandler.post(new Runnable() { + @Override + public void run() { + mContext.sendBroadcastAsUser(statusIntent, UserHandle.ALL, null, + POWER_OPTIONS); + } + }); + } } if (shouldSendBatteryLowLocked()) { @@ -686,26 +755,44 @@ public final class BatteryService extends SystemService { final Intent statusIntent = new Intent(Intent.ACTION_BATTERY_LOW); statusIntent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); statusIntent.putExtra(BatteryManager.EXTRA_SEQUENCE, mSequence); - mHandler.post(new Runnable() { - @Override - public void run() { - mContext.sendBroadcastAsUser(statusIntent, UserHandle.ALL, null, - mBatteryOptions); - } - }); + if (com.android.server.flags.Flags.consolidateBatteryChangeEvents()) { + mHandler.removeMessages(MSG_BROADCAST_BATTERY_LOW_OKAY); + final SomeArgs args = SomeArgs.obtain(); + args.arg1 = mContext; + args.arg2 = statusIntent; + mHandler.obtainMessage(MSG_BROADCAST_BATTERY_LOW_OKAY, args) + .sendToTarget(); + } else { + mHandler.post(new Runnable() { + @Override + public void run() { + mContext.sendBroadcastAsUser(statusIntent, UserHandle.ALL, null, + BATTERY_OPTIONS); + } + }); + } } else if (mSentLowBatteryBroadcast && mHealthInfo.batteryLevel >= mLowBatteryCloseWarningLevel) { mSentLowBatteryBroadcast = false; final Intent statusIntent = new Intent(Intent.ACTION_BATTERY_OKAY); statusIntent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); statusIntent.putExtra(BatteryManager.EXTRA_SEQUENCE, mSequence); - mHandler.post(new Runnable() { - @Override - public void run() { - mContext.sendBroadcastAsUser(statusIntent, UserHandle.ALL, null, - mBatteryOptions); - } - }); + if (com.android.server.flags.Flags.consolidateBatteryChangeEvents()) { + mHandler.removeMessages(MSG_BROADCAST_BATTERY_LOW_OKAY); + final SomeArgs args = SomeArgs.obtain(); + args.arg1 = mContext; + args.arg2 = statusIntent; + mHandler.obtainMessage(MSG_BROADCAST_BATTERY_LOW_OKAY, args) + .sendToTarget(); + } else { + mHandler.post(new Runnable() { + @Override + public void run() { + mContext.sendBroadcastAsUser(statusIntent, UserHandle.ALL, null, + BATTERY_OPTIONS); + } + }); + } } // We are doing this after sending the above broadcasts, so anything processing @@ -777,8 +864,16 @@ public final class BatteryService extends SystemService { + ", info:" + mHealthInfo.toString()); } - mHandler.post(() -> broadcastBatteryChangedIntent(mContext, - intent, mBatteryChangedOptions)); + if (com.android.server.flags.Flags.consolidateBatteryChangeEvents()) { + mHandler.removeMessages(MSG_BROADCAST_BATTERY_CHANGED); + final SomeArgs args = SomeArgs.obtain(); + args.arg1 = mContext; + args.arg2 = intent; + mHandler.obtainMessage(MSG_BROADCAST_BATTERY_CHANGED, args).sendToTarget(); + } else { + mHandler.post(() -> broadcastBatteryChangedIntent(mContext, + intent, BATTERY_CHANGED_OPTIONS)); + } } private static void broadcastBatteryChangedIntent(Context context, Intent intent, @@ -1307,6 +1402,12 @@ public final class BatteryService extends SystemService { Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER); } + @SuppressLint("AndroidFrameworkRequiresPermission") + private static void sendBroadcastToAllUsers(Context context, Intent intent, + Bundle options) { + context.sendBroadcastAsUser(intent, UserHandle.ALL, null, options); + } + private final class Led { // must match: config_notificationsBatteryLowBehavior in config.xml static final int LOW_BATTERY_BEHAVIOR_DEFAULT = 0; diff --git a/services/core/java/com/android/server/EventLogTags.logtags b/services/core/java/com/android/server/EventLogTags.logtags index 361b818260f1..fd512a64b32c 100644 --- a/services/core/java/com/android/server/EventLogTags.logtags +++ b/services/core/java/com/android/server/EventLogTags.logtags @@ -94,6 +94,8 @@ option java_package com.android.server 275534 notification_unautogrouped (key|3) # when a notification is adjusted via assistant 27535 notification_adjusted (key|3),(adjustment_type|3),(new_value|3) +# when a notification cancellation is prevented by the system +27536 notification_cancel_prevented (key|3) # --------------------------- # Watchdog.java diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index d80b38e32b6c..d12153559b31 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -16248,43 +16248,55 @@ public class ActivityManagerService extends IActivityManager.Stub boolean closeFd = true; try { - synchronized (mProcLock) { - if (fd == null) { - throw new IllegalArgumentException("null fd"); - } - mBinderTransactionTrackingEnabled = false; + Objects.requireNonNull(fd); + + record ProcessToDump(String processName, IApplicationThread thread) { } - PrintWriter pw = new FastPrintWriter(new FileOutputStream(fd.getFileDescriptor())); - pw.println("Binder transaction traces for all processes.\n"); - mProcessList.forEachLruProcessesLOSP(true, process -> { + PrintWriter pw = new FastPrintWriter(new FileOutputStream(fd.getFileDescriptor())); + pw.println("Binder transaction traces for all processes.\n"); + final ArrayList<ProcessToDump> processes = new ArrayList<>(); + synchronized (mProcLock) { + // Since dumping binder transactions is a long-running operation, we can't do it + // with mProcLock held. Do the initial verification here, and save the processes + // to dump later outside the lock. + final ArrayList<ProcessRecord> unverifiedProcesses = + new ArrayList<>(mProcessList.getLruProcessesLOSP()); + for (int i = 0, size = unverifiedProcesses.size(); i < size; i++) { + ProcessRecord process = unverifiedProcesses.get(i); final IApplicationThread thread = process.getThread(); if (!processSanityChecksLPr(process, thread)) { - return; + continue; } + processes.add(new ProcessToDump(process.processName, process.getThread())); + } + mBinderTransactionTrackingEnabled = false; + } + for (int i = 0, size = processes.size(); i < size; i++) { + final String processName = processes.get(i).processName(); + final IApplicationThread thread = processes.get(i).thread(); - pw.println("Traces for process: " + process.processName); - pw.flush(); + pw.println("Traces for process: " + processName); + pw.flush(); + try { + TransferPipe tp = new TransferPipe(); try { - TransferPipe tp = new TransferPipe(); - try { - thread.stopBinderTrackingAndDump(tp.getWriteFd()); - tp.go(fd.getFileDescriptor()); - } finally { - tp.kill(); - } - } catch (IOException e) { - pw.println("Failure while dumping IPC traces from " + process + - ". Exception: " + e); - pw.flush(); - } catch (RemoteException e) { - pw.println("Got a RemoteException while dumping IPC traces from " + - process + ". Exception: " + e); - pw.flush(); + thread.stopBinderTrackingAndDump(tp.getWriteFd()); + tp.go(fd.getFileDescriptor()); + } finally { + tp.kill(); } - }); - closeFd = false; - return true; + } catch (IOException e) { + pw.println("Failure while dumping IPC traces from " + processName + + ". Exception: " + e); + pw.flush(); + } catch (RemoteException e) { + pw.println("Got a RemoteException while dumping IPC traces from " + + processName + ". Exception: " + e); + pw.flush(); + } } + closeFd = false; + return true; } finally { if (fd != null && closeFd) { try { diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java index 5137b4c72fae..8e87342a9569 100644 --- a/services/core/java/com/android/server/am/BatteryStatsService.java +++ b/services/core/java/com/android/server/am/BatteryStatsService.java @@ -687,6 +687,9 @@ public final class BatteryStatsService extends IBatteryStats.Stub mBatteryUsageStatsProvider.setPowerStatsExporterEnabled( BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO, Flags.streamlinedConnectivityBatteryStats()); + mBatteryUsageStatsProvider.setPowerStatsExporterEnabled( + BatteryConsumer.POWER_COMPONENT_PHONE, + Flags.streamlinedConnectivityBatteryStats()); mStats.setPowerStatsCollectorEnabled(BatteryConsumer.POWER_COMPONENT_WIFI, Flags.streamlinedConnectivityBatteryStats()); @@ -737,6 +740,9 @@ public final class BatteryStatsService extends IBatteryStats.Stub // By convention POWER_COMPONENT_ANY represents custom Energy Consumers mStats.setPowerStatsCollectorEnabled(BatteryConsumer.POWER_COMPONENT_ANY, Flags.streamlinedMiscBatteryStats()); + mBatteryUsageStatsProvider.setPowerStatsExporterEnabled( + BatteryConsumer.POWER_COMPONENT_ANY, + Flags.streamlinedMiscBatteryStats()); mWorker.systemServicesReady(); mStats.systemServicesReady(mContext); diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java index ab63e247b556..0e266f5644b6 100644 --- a/services/core/java/com/android/server/am/OomAdjuster.java +++ b/services/core/java/com/android/server/am/OomAdjuster.java @@ -1201,6 +1201,7 @@ public class OomAdjuster { >= UNKNOWN_ADJ) { final ProcessServiceRecord psr = app.mServices; switch (state.getCurProcState()) { + case PROCESS_STATE_LAST_ACTIVITY: case PROCESS_STATE_CACHED_ACTIVITY: case ActivityManager.PROCESS_STATE_CACHED_ACTIVITY_CLIENT: case ActivityManager.PROCESS_STATE_CACHED_RECENT: @@ -2180,7 +2181,6 @@ public class OomAdjuster { procState = PROCESS_STATE_LAST_ACTIVITY; schedGroup = SCHED_GROUP_BACKGROUND; state.setAdjType("previous-expired"); - adj = CACHED_APP_MIN_ADJ; if (DEBUG_OOM_ADJ_REASON || logUid == appUid) { reportOomAdjMessageLocked(TAG_OOM_ADJ, "Expire prev adj: " + app); } diff --git a/services/core/java/com/android/server/appop/TEST_MAPPING b/services/core/java/com/android/server/appop/TEST_MAPPING index 2a9dfa273e87..9317c1eda088 100644 --- a/services/core/java/com/android/server/appop/TEST_MAPPING +++ b/services/core/java/com/android/server/appop/TEST_MAPPING @@ -18,24 +18,7 @@ "name": "FrameworksMockingServicesTests_android_server_appop" }, { - "name": "CtsPermissionTestCases", - "options": [ - { - "exclude-annotation": "androidx.test.filters.FlakyTest" - }, - { - "include-filter": "android.permission.cts.BackgroundPermissionsTest" - }, - { - "include-filter": "android.permission.cts.SplitPermissionTest" - }, - { - "include-filter": "android.permission.cts.PermissionFlagsTest" - }, - { - "include-filter": "android.permission.cts.SharedUidPermissionsTest" - } - ] + "name": "CtsPermissionTestCases_Platform" }, { "name": "CtsAppTestCases", diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index 14dca4e03b07..168ec052e67d 100644 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -790,8 +790,7 @@ public class AudioService extends IAudioService.Stub private final BroadcastReceiver mReceiver = new AudioServiceBroadcastReceiver(); private final Executor mAudioServerLifecycleExecutor; - private final ConcurrentLinkedQueue<Future> mScheduledPermissionTasks = - new ConcurrentLinkedQueue(); + private final List<Future> mScheduledPermissionTasks = new ArrayList(); private IMediaProjectionManager mProjectionService; // to validate projection token @@ -10600,46 +10599,50 @@ public class AudioService extends IAudioService.Stub // instanceof to simplify the construction requirements of AudioService for testing: no // delayed execution during unit tests. if (mAudioServerLifecycleExecutor instanceof ScheduledExecutorService exec) { - // We schedule and add from a this callback thread only (serially), so the task order on - // the serial executor matches the order on the task list. This list should almost - // always have only two elements, except in cases of serious system contention. - Runnable task = () -> mScheduledPermissionTasks.add(exec.schedule(() -> { - try { - // Clean up completed tasks before us to bound the queue length. Cancel any - // pending permission refresh tasks, after our own, since we are about to - // fulfill all of them. We must be the first non-completed task in the - // queue, since the execution order matches the queue order. Note, this - // task is the only writer on elements in the queue, and the task is - // serialized, so - // => no in-flight cancellation - // => exists at least one non-completed task (ourselves) - // => the queue is non-empty (only completed tasks removed) - final var iter = mScheduledPermissionTasks.iterator(); - while (iter.next().isDone()) { - iter.remove(); - } - // iter is on the first element which is not completed (us) - while (iter.hasNext()) { - if (!iter.next().cancel(false)) { - throw new AssertionError( - "Cancel should be infallible since we" + - "cancel from the executor"); + // The order on the task list is an embedding on the scheduling order of the executor, + // since we synchronously add the scheduled task to our local queue. This list should + // almost always have only two elements, except in cases of serious system contention. + Runnable task = () -> { + synchronized (mScheduledPermissionTasks) { + mScheduledPermissionTasks.add(exec.schedule(() -> { + try { + // Our goal is to remove all tasks which don't correspond to ourselves + // on this queue. Either they are already done (ahead of us), or we + // should cancel them (behind us), since their work is redundant after + // we fire. + // We must be the first non-completed task in the queue, since the + // execution order matches the queue order. Note, this task is the only + // writer on elements in the queue, and the task is serialized, so + // => no in-flight cancellation + // => exists at least one non-completed task (ourselves) + // => the queue is non-empty (only completed tasks removed) + synchronized (mScheduledPermissionTasks) { + final var iter = mScheduledPermissionTasks.iterator(); + while (iter.next().isDone()) { + iter.remove(); + } + // iter is on the first element which is not completed (us) + while (iter.hasNext()) { + if (!iter.next().cancel(false)) { + throw new AssertionError( + "Cancel should be infallible since we" + + "cancel from the executor"); + } + iter.remove(); + } } - iter.remove(); + mPermissionProvider.onPermissionStateChanged(); + } catch (Exception e) { + // Handle executor routing exceptions to nowhere + Thread.getDefaultUncaughtExceptionHandler() + .uncaughtException(Thread.currentThread(), e); } - mPermissionProvider.onPermissionStateChanged(); - } catch (Exception e) { - // Handle executor routing exceptions to nowhere - Thread.getDefaultUncaughtExceptionHandler() - .uncaughtException(Thread.currentThread(), e); - } - }, - UPDATE_DELAY_MS, - TimeUnit.MILLISECONDS)); + }, UPDATE_DELAY_MS, TimeUnit.MILLISECONDS)); + } + }; mAudioSystem.listenForSystemPropertyChange( PermissionManager.CACHE_KEY_PACKAGE_INFO, task); - task.run(); } else { mAudioSystem.listenForSystemPropertyChange( PermissionManager.CACHE_KEY_PACKAGE_INFO, @@ -10730,7 +10733,7 @@ public class AudioService extends IAudioService.Stub return true; } - public int requestAudioFocus(AudioAttributes aa, int durationHint, IBinder cb, + public int requestAudioFocus(AudioAttributes aa, int focusReqType, IBinder cb, IAudioFocusDispatcher fd, String clientId, String callingPackageName, String attributionTag, int flags, IAudioPolicyCallback pcb, int sdk) { if ((flags & AudioManager.AUDIOFOCUS_FLAG_TEST) != 0) { @@ -10739,7 +10742,7 @@ public class AudioService extends IAudioService.Stub final int uid = Binder.getCallingUid(); MediaMetrics.Item mmi = new MediaMetrics.Item(mMetricsId + "focus") .setUid(uid) - //.putInt("durationHint", durationHint) + //.putInt("focusReqType", focusReqType) .set(MediaMetrics.Property.CALLING_PACKAGE, callingPackageName) .set(MediaMetrics.Property.CLIENT_NAME, clientId) .set(MediaMetrics.Property.EVENT, "requestAudioFocus") @@ -10802,11 +10805,11 @@ public class AudioService extends IAudioService.Stub //TODO move inside HardeningEnforcer after refactor that moves permission checks // in the blockFocusMethod if (permissionOverridesCheck) { - mHardeningEnforcer.metricsLogFocusReq(/*blocked*/false, durationHint, uid); + mHardeningEnforcer.metricsLogFocusReq(/*blocked*/false, focusReqType, uid); } if (!permissionOverridesCheck && mHardeningEnforcer.blockFocusMethod(uid, HardeningEnforcer.METHOD_AUDIO_MANAGER_REQUEST_AUDIO_FOCUS, - clientId, durationHint, callingPackageName, attributionTag, sdk)) { + clientId, focusReqType, callingPackageName, attributionTag, sdk)) { final String reason = "Audio focus request blocked by hardening"; Log.w(TAG, reason); mmi.set(MediaMetrics.Property.EARLY_RETURN, reason).record(); @@ -10817,14 +10820,14 @@ public class AudioService extends IAudioService.Stub } mmi.record(); - return mMediaFocusControl.requestAudioFocus(aa, durationHint, cb, fd, + return mMediaFocusControl.requestAudioFocus(aa, focusReqType, cb, fd, clientId, callingPackageName, flags, sdk, - forceFocusDuckingForAccessibility(aa, durationHint, uid), -1 /*testUid, ignored*/, + forceFocusDuckingForAccessibility(aa, focusReqType, uid), -1 /*testUid, ignored*/, permissionOverridesCheck); } /** see {@link AudioManager#requestAudioFocusForTest(AudioFocusRequest, String, int, int)} */ - public int requestAudioFocusForTest(AudioAttributes aa, int durationHint, IBinder cb, + public int requestAudioFocusForTest(AudioAttributes aa, int focusReqType, IBinder cb, IAudioFocusDispatcher fd, String clientId, String callingPackageName, int flags, int fakeUid, int sdk) { if (!enforceQueryAudioStateForTest("focus request")) { @@ -10835,7 +10838,7 @@ public class AudioService extends IAudioService.Stub Log.e(TAG, reason); return AudioManager.AUDIOFOCUS_REQUEST_FAILED; } - return mMediaFocusControl.requestAudioFocus(aa, durationHint, cb, fd, + return mMediaFocusControl.requestAudioFocus(aa, focusReqType, cb, fd, clientId, callingPackageName, flags, sdk, false /*forceDuck*/, fakeUid, true /*permissionOverridesCheck*/); } @@ -14710,7 +14713,11 @@ public class AudioService extends IAudioService.Stub @Override /** @see AudioManager#permissionUpdateBarrier() */ public void permissionUpdateBarrier() { - for (var x : List.copyOf(mScheduledPermissionTasks)) { + List<Future> snapshot; + synchronized (mScheduledPermissionTasks) { + snapshot = List.copyOf(mScheduledPermissionTasks); + } + for (var x : snapshot) { try { x.get(); } catch (CancellationException e) { diff --git a/services/core/java/com/android/server/audio/HardeningEnforcer.java b/services/core/java/com/android/server/audio/HardeningEnforcer.java index 3c509bca1b84..faeba5d068fc 100644 --- a/services/core/java/com/android/server/audio/HardeningEnforcer.java +++ b/services/core/java/com/android/server/audio/HardeningEnforcer.java @@ -155,14 +155,14 @@ public class HardeningEnforcer { * Checks whether the call in the current thread should be allowed or blocked * @param focusMethod name of the method to check, for logging purposes * @param clientId id of the requester - * @param durationHint focus type being requested + * @param focusReqType 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, String attributionTag, int targetSdk) { + int focusReqType, @NonNull String packageName, String attributionTag, int targetSdk) { if (packageName.isEmpty()) { packageName = getPackNameForUid(callingUid); } @@ -181,14 +181,14 @@ public class HardeningEnforcer { blocked = false; } - metricsLogFocusReq(blocked, durationHint, callingUid); + metricsLogFocusReq(blocked, focusReqType, callingUid); if (!blocked) { return false; } String errorMssg = "Focus request DENIED for uid:" + callingUid - + " clientId:" + clientId + " req:" + durationHint + + " clientId:" + clientId + " req:" + focusReqType + " procState:" + mActivityManager.getUidProcessState(callingUid); mEventLogger.enqueueAndSlog(errorMssg, EventLogger.Event.ALOGI, TAG); diff --git a/services/core/java/com/android/server/audio/SoundDoseHelper.java b/services/core/java/com/android/server/audio/SoundDoseHelper.java index dc79ab26d3b8..643f3308d8f5 100644 --- a/services/core/java/com/android/server/audio/SoundDoseHelper.java +++ b/services/core/java/com/android/server/audio/SoundDoseHelper.java @@ -900,8 +900,10 @@ public class SoundDoseHelper { try { if (!isAbsoluteVolume) { - mLogger.enqueue( - SoundDoseEvent.getAbsVolumeAttenuationEvent(/*attenuation=*/0.f, device)); + if (mSafeMediaVolumeDevices.indexOfKey(device) >= 0) { + mLogger.enqueue(SoundDoseEvent.getAbsVolumeAttenuationEvent(/*attenuation=*/0.f, + device)); + } // remove any possible previous attenuation soundDose.updateAttenuation(/* attenuationDB= */0.f, device); @@ -912,8 +914,12 @@ public class SoundDoseHelper { && safeDevicesContains(device)) { float attenuationDb = -AudioSystem.getStreamVolumeDB(AudioSystem.STREAM_MUSIC, (newIndex + 5) / 10, device); - mLogger.enqueue( - SoundDoseEvent.getAbsVolumeAttenuationEvent(attenuationDb, device)); + + if (mSafeMediaVolumeDevices.indexOfKey(device) >= 0) { + mLogger.enqueue( + SoundDoseEvent.getAbsVolumeAttenuationEvent(attenuationDb, device)); + } + soundDose.updateAttenuation(attenuationDb, device); } } catch (RemoteException e) { diff --git a/services/core/java/com/android/server/biometrics/AuthSession.java b/services/core/java/com/android/server/biometrics/AuthSession.java index 276ab03d9f4b..abfbddc18e24 100644 --- a/services/core/java/com/android/server/biometrics/AuthSession.java +++ b/services/core/java/com/android/server/biometrics/AuthSession.java @@ -221,7 +221,8 @@ public final class AuthSession implements IBinder.DeathRecipient { mFingerprintSensorProperties = fingerprintSensorProperties; mCancelled = false; mBiometricFrameworkStatsLogger = logger; - mOperationContext = new OperationContextExt(true /* isBP */); + mOperationContext = new OperationContextExt(true /* isBP */, + preAuthInfo.getIsMandatoryBiometricsAuthentication() /* isMandatoryBiometrics */); mBiometricManager = mContext.getSystemService(BiometricManager.class); mSfpsSensorIds = mFingerprintSensorProperties.stream().filter( @@ -285,7 +286,8 @@ public final class AuthSession implements IBinder.DeathRecipient { sensor.goToStateWaitingForCookie(requireConfirmation, mToken, mOperationId, mUserId, mSensorReceiver, mOpPackageName, mRequestId, cookie, mPromptInfo.isAllowBackgroundAuthentication(), - mPromptInfo.isForLegacyFingerprintManager()); + mPromptInfo.isForLegacyFingerprintManager(), + mOperationContext.getIsMandatoryBiometrics()); } } diff --git a/services/core/java/com/android/server/biometrics/BiometricSensor.java b/services/core/java/com/android/server/biometrics/BiometricSensor.java index 42dd36a5c35a..c7532d44fd38 100644 --- a/services/core/java/com/android/server/biometrics/BiometricSensor.java +++ b/services/core/java/com/android/server/biometrics/BiometricSensor.java @@ -107,12 +107,13 @@ public abstract class BiometricSensor { void goToStateWaitingForCookie(boolean requireConfirmation, IBinder token, long sessionId, int userId, IBiometricSensorReceiver sensorReceiver, String opPackageName, long requestId, int cookie, boolean allowBackgroundAuthentication, - boolean isForLegacyFingerprintManager) + boolean isForLegacyFingerprintManager, boolean isMandatoryBiometrics) throws RemoteException { mCookie = cookie; impl.prepareForAuthentication(requireConfirmation, token, sessionId, userId, sensorReceiver, opPackageName, requestId, mCookie, - allowBackgroundAuthentication, isForLegacyFingerprintManager); + allowBackgroundAuthentication, isForLegacyFingerprintManager, + isMandatoryBiometrics); mSensorState = STATE_WAITING_FOR_COOKIE; } diff --git a/services/core/java/com/android/server/biometrics/PreAuthInfo.java b/services/core/java/com/android/server/biometrics/PreAuthInfo.java index f0da67bd9f9a..eaf4f813d13a 100644 --- a/services/core/java/com/android/server/biometrics/PreAuthInfo.java +++ b/services/core/java/com/android/server/biometrics/PreAuthInfo.java @@ -447,6 +447,12 @@ class PreAuthInfo { getInternalStatus().second)); } + /** Returns if mandatory biometrics authentication is in effect */ + boolean getIsMandatoryBiometricsAuthentication() { + return mIsMandatoryBiometricsAuthentication; + } + + /** * For the given request, generate the appropriate reason why authentication cannot be started. * Note that for some errors, modality is intentionally cleared. diff --git a/services/core/java/com/android/server/biometrics/log/BiometricFrameworkStatsLogger.java b/services/core/java/com/android/server/biometrics/log/BiometricFrameworkStatsLogger.java index 2c52e3d8ca67..f5af5ea3eab4 100644 --- a/services/core/java/com/android/server/biometrics/log/BiometricFrameworkStatsLogger.java +++ b/services/core/java/com/android/server/biometrics/log/BiometricFrameworkStatsLogger.java @@ -78,6 +78,7 @@ public class BiometricFrameworkStatsLogger { public void authenticate(OperationContextExt operationContext, int statsModality, int statsAction, int statsClient, boolean isDebug, long latency, int authState, boolean requireConfirmation, int targetUserId, float ambientLightLux) { + Slog.d(TAG, "authenticate logging " + operationContext.getIsMandatoryBiometrics()); FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_AUTHENTICATED, statsModality, targetUserId, @@ -98,7 +99,8 @@ public class BiometricFrameworkStatsLogger { foldType(operationContext.getFoldState()), operationContext.getOrderAndIncrement(), toProtoWakeReason(operationContext), - toProtoWakeReasonDetails(operationContext)); + toProtoWakeReasonDetails(operationContext), + operationContext.getIsMandatoryBiometrics()); } /** {@see FrameworkStatsLog.BIOMETRIC_AUTHENTICATED}. */ @@ -129,6 +131,7 @@ public class BiometricFrameworkStatsLogger { public void error(OperationContextExt operationContext, int statsModality, int statsAction, int statsClient, boolean isDebug, long latency, int error, int vendorCode, int targetUserId) { + Slog.d(TAG, "error logging " + operationContext.getIsMandatoryBiometrics()); FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_ERROR_OCCURRED, statsModality, targetUserId, @@ -149,7 +152,8 @@ public class BiometricFrameworkStatsLogger { foldType(operationContext.getFoldState()), operationContext.getOrderAndIncrement(), toProtoWakeReason(operationContext), - toProtoWakeReasonDetails(operationContext)); + toProtoWakeReasonDetails(operationContext), + operationContext.getIsMandatoryBiometrics()); } @VisibleForTesting diff --git a/services/core/java/com/android/server/biometrics/log/OperationContextExt.java b/services/core/java/com/android/server/biometrics/log/OperationContextExt.java index da4e5154d49e..4df63e2729d2 100644 --- a/services/core/java/com/android/server/biometrics/log/OperationContextExt.java +++ b/services/core/java/com/android/server/biometrics/log/OperationContextExt.java @@ -50,21 +50,33 @@ public class OperationContextExt { @Surface.Rotation private int mOrientation = Surface.ROTATION_0; private int mFoldState = IBiometricContextListener.FoldState.UNKNOWN; private final boolean mIsBP; + private final boolean mIsMandatoryBiometrics; /** Create a context. */ public OperationContextExt(boolean isBP) { this(new OperationContext(), isBP, BiometricAuthenticator.TYPE_NONE); } - public OperationContextExt(boolean isBP, @BiometricAuthenticator.Modality int modality) { - this(new OperationContext(), isBP, modality); + public OperationContextExt(boolean isBP, boolean isMandatoryBiometrics) { + this(new OperationContext(), isBP, BiometricAuthenticator.TYPE_NONE, isMandatoryBiometrics); + } + + public OperationContextExt(boolean isBP, @BiometricAuthenticator.Modality int modality, + boolean isMandatoryBiometrics) { + this(new OperationContext(), isBP, modality, isMandatoryBiometrics); } /** Create a wrapped context. */ public OperationContextExt(@NonNull OperationContext context, boolean isBP, @BiometricAuthenticator.Modality int modality) { + this(context, isBP, modality, false /* isMandatoryBiometrics */); + } + + public OperationContextExt(@NonNull OperationContext context, boolean isBP, + @BiometricAuthenticator.Modality int modality, boolean isMandatoryBiometrics) { mAidlContext = context; mIsBP = isBP; + mIsMandatoryBiometrics = isMandatoryBiometrics; if (modality == BiometricAuthenticator.TYPE_FINGERPRINT) { mAidlContext.operationState = OperationState.fingerprintOperationState( @@ -285,6 +297,11 @@ public class OperationContextExt { return mAidlContext.operationState; } + /** If mandatory biometrics is active. */ + public boolean getIsMandatoryBiometrics() { + return mIsMandatoryBiometrics; + } + /** Update this object with the latest values from the given context. */ OperationContextExt update(@NonNull BiometricContext biometricContext, boolean isCrypto) { mAidlContext.isAod = biometricContext.isAod(); diff --git a/services/core/java/com/android/server/biometrics/sensors/AcquisitionClient.java b/services/core/java/com/android/server/biometrics/sensors/AcquisitionClient.java index fbd32a67fe6c..04522e367272 100644 --- a/services/core/java/com/android/server/biometrics/sensors/AcquisitionClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/AcquisitionClient.java @@ -59,9 +59,10 @@ public abstract class AcquisitionClient<T> extends HalClientMonitor<T> implement public AcquisitionClient(@NonNull Context context, @NonNull Supplier<T> lazyDaemon, @NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener, int userId, @NonNull String owner, int cookie, int sensorId, boolean shouldVibrate, - @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext) { + @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext, + boolean isMandatoryBiometrics) { super(context, lazyDaemon, token, listener, userId, owner, cookie, sensorId, - logger, biometricContext); + logger, biometricContext, isMandatoryBiometrics); mPowerManager = context.getSystemService(PowerManager.class); mShouldVibrate = shouldVibrate; } diff --git a/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java index daaafcb61bc5..09386ae2899d 100644 --- a/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java @@ -99,7 +99,7 @@ public abstract class AuthenticationClient<T, O extends AuthenticateOptions> boolean shouldVibrate, int sensorStrength) { super(context, lazyDaemon, token, listener, options.getUserId(), options.getOpPackageName(), cookie, options.getSensorId(), shouldVibrate, - biometricLogger, biometricContext); + biometricLogger, biometricContext, options.isMandatoryBiometrics()); mIsStrongBiometric = isStrongBiometric; mOperationId = operationId; mRequireConfirmation = requireConfirmation; diff --git a/services/core/java/com/android/server/biometrics/sensors/EnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/EnrollClient.java index 438367d54e1f..32c0bd389e2f 100644 --- a/services/core/java/com/android/server/biometrics/sensors/EnrollClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/EnrollClient.java @@ -60,7 +60,7 @@ public abstract class EnrollClient<T> extends AcquisitionClient<T> implements En @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext, int enrollReason) { super(context, lazyDaemon, token, listener, userId, owner, 0 /* cookie */, sensorId, - shouldVibrate, logger, biometricContext); + shouldVibrate, logger, biometricContext, false /* isMandatoryBiometrics */); mBiometricUtils = utils; mHardwareAuthToken = Arrays.copyOf(hardwareAuthToken, hardwareAuthToken.length); mTimeoutSec = timeoutSec; diff --git a/services/core/java/com/android/server/biometrics/sensors/GenerateChallengeClient.java b/services/core/java/com/android/server/biometrics/sensors/GenerateChallengeClient.java index 2adf0cb3bab4..b573b56626aa 100644 --- a/services/core/java/com/android/server/biometrics/sensors/GenerateChallengeClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/GenerateChallengeClient.java @@ -37,7 +37,7 @@ public abstract class GenerateChallengeClient<T> extends HalClientMonitor<T> { int userId, @NonNull String owner, int sensorId, @NonNull BiometricLogger biometricLogger, @NonNull BiometricContext biometricContext) { super(context, lazyDaemon, token, listener, userId, owner, 0 /* cookie */, sensorId, - biometricLogger, biometricContext); + biometricLogger, biometricContext, false /* isMandatoryBiometrics */); } @Override diff --git a/services/core/java/com/android/server/biometrics/sensors/HalClientMonitor.java b/services/core/java/com/android/server/biometrics/sensors/HalClientMonitor.java index 0f01510bd908..3bc51a94cd2f 100644 --- a/services/core/java/com/android/server/biometrics/sensors/HalClientMonitor.java +++ b/services/core/java/com/android/server/biometrics/sensors/HalClientMonitor.java @@ -41,26 +41,29 @@ public abstract class HalClientMonitor<T> extends BaseClientMonitor { private final OperationContextExt mOperationContext; /** - * @param context system_server context - * @param lazyDaemon pointer for lazy retrieval of the HAL - * @param token a unique token for the client - * @param listener recipient of related events (e.g. authentication) - * @param userId target user id for operation - * @param owner name of the client that owns this - * @param cookie BiometricPrompt authentication cookie (to be moved into a subclass soon) - * @param sensorId ID of the sensor that the operation should be requested of - * @param biometricLogger framework stats logger + * @param context system_server context + * @param lazyDaemon pointer for lazy retrieval of the HAL + * @param token a unique token for the client + * @param listener recipient of related events (e.g. authentication) + * @param userId target user id for operation + * @param owner name of the client that owns this + * @param cookie BiometricPrompt authentication cookie (to be moved into a subclass + * soon) + * @param sensorId ID of the sensor that the operation should be requested of + * @param biometricLogger framework stats logger * @param biometricContext system context metadata */ public HalClientMonitor(@NonNull Context context, @NonNull Supplier<T> lazyDaemon, @Nullable IBinder token, @Nullable ClientMonitorCallbackConverter listener, int userId, @NonNull String owner, int cookie, int sensorId, - @NonNull BiometricLogger biometricLogger, @NonNull BiometricContext biometricContext) { + @NonNull BiometricLogger biometricLogger, @NonNull BiometricContext biometricContext, + boolean isMandatoryBiometrics) { super(context, token, listener, userId, owner, cookie, sensorId, biometricLogger, biometricContext); mLazyDaemon = lazyDaemon; int modality = listener != null ? listener.getModality() : BiometricAuthenticator.TYPE_NONE; - mOperationContext = new OperationContextExt(isBiometricPrompt(), modality); + mOperationContext = new OperationContextExt(isBiometricPrompt(), modality, + isMandatoryBiometrics); } @Nullable diff --git a/services/core/java/com/android/server/biometrics/sensors/InternalCleanupClient.java b/services/core/java/com/android/server/biometrics/sensors/InternalCleanupClient.java index 7bd905b4524a..6c305599c39f 100644 --- a/services/core/java/com/android/server/biometrics/sensors/InternalCleanupClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/InternalCleanupClient.java @@ -143,7 +143,8 @@ public abstract class InternalCleanupClient<S extends BiometricAuthenticator.Ide @NonNull BiometricUtils<S> utils, @NonNull Map<Integer, Long> authenticatorIds) { super(context, lazyDaemon, null /* token */, null /* ClientMonitorCallbackConverter */, - userId, owner, 0 /* cookie */, sensorId, logger, biometricContext); + userId, owner, 0 /* cookie */, sensorId, logger, biometricContext, + false /* isMandatoryBiometrics */); mBiometricUtils = utils; mAuthenticatorIds = authenticatorIds; mHasEnrollmentsBeforeStarting = !utils.getBiometricsForUser(context, userId).isEmpty(); diff --git a/services/core/java/com/android/server/biometrics/sensors/InternalEnumerateClient.java b/services/core/java/com/android/server/biometrics/sensors/InternalEnumerateClient.java index 81ab26dd581e..2c2c685fd442 100644 --- a/services/core/java/com/android/server/biometrics/sensors/InternalEnumerateClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/InternalEnumerateClient.java @@ -55,7 +55,8 @@ public abstract class InternalEnumerateClient<T> extends HalClientMonitor<T> // Internal enumerate does not need to send results to anyone. Cleanup (enumerate + remove) // is all done internally. super(context, lazyDaemon, token, null /* ClientMonitorCallbackConverter */, userId, owner, - 0 /* cookie */, sensorId, logger, biometricContext); + 0 /* cookie */, sensorId, logger, biometricContext, + false /* isMandatoryBiometrics */); mEnrolledList = enrolledList; mInitialEnrolledSize = mEnrolledList.size(); mUtils = utils; diff --git a/services/core/java/com/android/server/biometrics/sensors/InvalidationClient.java b/services/core/java/com/android/server/biometrics/sensors/InvalidationClient.java index d5aa5e2c9db6..6c933665a2e2 100644 --- a/services/core/java/com/android/server/biometrics/sensors/InvalidationClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/InvalidationClient.java @@ -49,7 +49,7 @@ public abstract class InvalidationClient<S extends BiometricAuthenticator.Identi @NonNull IInvalidationCallback callback) { super(context, lazyDaemon, null /* token */, null /* listener */, userId, context.getOpPackageName(), 0 /* cookie */, sensorId, - logger, biometricContext); + logger, biometricContext, false /* isMandatoryBiometrics */); mAuthenticatorIds = authenticatorIds; mInvalidationCallback = callback; } diff --git a/services/core/java/com/android/server/biometrics/sensors/RemovalClient.java b/services/core/java/com/android/server/biometrics/sensors/RemovalClient.java index d2ef2786c252..ad5877aed6da 100644 --- a/services/core/java/com/android/server/biometrics/sensors/RemovalClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/RemovalClient.java @@ -49,7 +49,7 @@ public abstract class RemovalClient<S extends BiometricAuthenticator.Identifier, @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext, @NonNull Map<Integer, Long> authenticatorIds) { super(context, lazyDaemon, token, listener, userId, owner, 0 /* cookie */, sensorId, - logger, biometricContext); + logger, biometricContext, false /* isMandatoryBiometrics */); mBiometricUtils = utils; mAuthenticatorIds = authenticatorIds; mHasEnrollmentsBeforeStarting = !utils.getBiometricsForUser(context, userId).isEmpty(); diff --git a/services/core/java/com/android/server/biometrics/sensors/RevokeChallengeClient.java b/services/core/java/com/android/server/biometrics/sensors/RevokeChallengeClient.java index 88f4da261d62..0c8a2ddffc57 100644 --- a/services/core/java/com/android/server/biometrics/sensors/RevokeChallengeClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/RevokeChallengeClient.java @@ -32,7 +32,8 @@ public abstract class RevokeChallengeClient<T> extends HalClientMonitor<T> { @NonNull IBinder token, int userId, @NonNull String owner, int sensorId, @NonNull BiometricLogger biometricLogger, @NonNull BiometricContext biometricContext) { super(context, lazyDaemon, token, null /* listener */, userId, owner, - 0 /* cookie */, sensorId, biometricLogger, biometricContext); + 0 /* cookie */, sensorId, biometricLogger, biometricContext, + false /* isMandatoryBiometrics */); } @Override diff --git a/services/core/java/com/android/server/biometrics/sensors/StartUserClient.java b/services/core/java/com/android/server/biometrics/sensors/StartUserClient.java index 21c9f64eea5b..ff694cda8812 100644 --- a/services/core/java/com/android/server/biometrics/sensors/StartUserClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/StartUserClient.java @@ -51,7 +51,8 @@ public abstract class StartUserClient<T, U> extends HalClientMonitor<T> { @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext, @NonNull UserStartedCallback<U> callback) { super(context, lazyDaemon, token, null /* listener */, userId, context.getOpPackageName(), - 0 /* cookie */, sensorId, logger, biometricContext); + 0 /* cookie */, sensorId, logger, biometricContext, + false /* isMandatoryBiometrics */); mUserStartedCallback = callback; } diff --git a/services/core/java/com/android/server/biometrics/sensors/StopUserClient.java b/services/core/java/com/android/server/biometrics/sensors/StopUserClient.java index e01c4ec76ed2..9119bc72fdd3 100644 --- a/services/core/java/com/android/server/biometrics/sensors/StopUserClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/StopUserClient.java @@ -54,7 +54,8 @@ public abstract class StopUserClient<T> extends HalClientMonitor<T> { @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext, @NonNull UserStoppedCallback callback) { super(context, lazyDaemon, token, null /* listener */, userId, context.getOpPackageName(), - 0 /* cookie */, sensorId, logger, biometricContext); + 0 /* cookie */, sensorId, logger, biometricContext, + false /* isMandatoryBiometrics */); mUserStoppedCallback = callback; } diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceAuthenticator.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceAuthenticator.java index 22110037890f..67d75052c8d6 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/FaceAuthenticator.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceAuthenticator.java @@ -63,13 +63,14 @@ public final class FaceAuthenticator extends IBiometricAuthenticator.Stub { public void prepareForAuthentication(boolean requireConfirmation, IBinder token, long operationId, int userId, IBiometricSensorReceiver sensorReceiver, String opPackageName, long requestId, int cookie, boolean allowBackgroundAuthentication, - boolean isForLegacyFingerprintManager) + boolean isForLegacyFingerprintManager, boolean isMandatoryBiometrics) throws RemoteException { mFaceService.prepareForAuthentication(requireConfirmation, token, operationId, sensorReceiver, new FaceAuthenticateOptions.Builder() .setUserId(userId) .setSensorId(mSensorId) .setOpPackageName(opPackageName) + .setIsMandatoryBiometrics(isMandatoryBiometrics) .build(), requestId, cookie, allowBackgroundAuthentication); } diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceDetectClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceDetectClient.java index 8b4da3181bcf..8eb62eb114bd 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceDetectClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceDetectClient.java @@ -83,7 +83,8 @@ public class FaceDetectClient extends AcquisitionClient<AidlSession> implements boolean isStrongBiometric, SensorPrivacyManager sensorPrivacyManager) { super(context, lazyDaemon, token, listener, options.getUserId(), options.getOpPackageName(), 0 /* cookie */, options.getSensorId(), - false /* shouldVibrate */, logger, biometricContext); + false /* shouldVibrate */, logger, biometricContext, + false /* isMandatoryBiometrics */); setRequestId(requestId); mAuthenticationStateListeners = authenticationStateListeners; mIsStrongBiometric = isStrongBiometric; diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceGetAuthenticatorIdClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceGetAuthenticatorIdClient.java index 1f4f6127dafd..dbd293cd9f8d 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceGetAuthenticatorIdClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceGetAuthenticatorIdClient.java @@ -42,7 +42,8 @@ class FaceGetAuthenticatorIdClient extends HalClientMonitor<AidlSession> { @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext, Map<Integer, Long> authenticatorIds) { super(context, lazyDaemon, null /* token */, null /* listener */, userId, opPackageName, - 0 /* cookie */, sensorId, logger, biometricContext); + 0 /* cookie */, sensorId, logger, biometricContext, + false /* isMandatoryBiometrics */); mAuthenticatorIds = authenticatorIds; } diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceGetFeatureClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceGetFeatureClient.java index c41b706555e8..8d1336f21ccf 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceGetFeatureClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceGetFeatureClient.java @@ -55,7 +55,7 @@ public class FaceGetFeatureClient extends HalClientMonitor<AidlSession> implemen @NonNull String owner, int sensorId, @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext, int feature) { super(context, lazyDaemon, token, listener, userId, owner, 0 /* cookie */, sensorId, - logger, biometricContext); + logger, biometricContext, false /* isMandatoryBiometrics */); mUserId = userId; mFeature = feature; } diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceResetLockoutClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceResetLockoutClient.java index d02eefaed101..93bc1f263678 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceResetLockoutClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceResetLockoutClient.java @@ -60,7 +60,8 @@ public class FaceResetLockoutClient extends HalClientMonitor<AidlSession> implem @NonNull LockoutResetDispatcher lockoutResetDispatcher, @Authenticators.Types int biometricStrength) { super(context, lazyDaemon, null /* token */, null /* listener */, userId, owner, - 0 /* cookie */, sensorId, logger, biometricContext); + 0 /* cookie */, sensorId, logger, biometricContext, + false /* isMandatoryBiometrics */); mHardwareAuthToken = HardwareAuthTokenUtils.toHardwareAuthToken(hardwareAuthToken); mLockoutTracker = lockoutTracker; mLockoutResetDispatcher = lockoutResetDispatcher; diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceSetFeatureClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceSetFeatureClient.java index f6da8726564f..fc73e58ba577 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceSetFeatureClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceSetFeatureClient.java @@ -52,7 +52,7 @@ public class FaceSetFeatureClient extends HalClientMonitor<AidlSession> implemen @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext, int feature, boolean enabled, byte[] hardwareAuthToken) { super(context, lazyDaemon, token, listener, userId, owner, 0 /* cookie */, sensorId, - logger, biometricContext); + logger, biometricContext, false /* isMandatoryBiometrics */); mFeature = feature; mEnabled = enabled; mHardwareAuthToken = HardwareAuthTokenUtils.toHardwareAuthToken(hardwareAuthToken); diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintAuthenticator.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintAuthenticator.java index b6fa0c126cd6..d50ab8d73e3e 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintAuthenticator.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintAuthenticator.java @@ -63,13 +63,14 @@ public final class FingerprintAuthenticator extends IBiometricAuthenticator.Stub public void prepareForAuthentication(boolean requireConfirmation, IBinder token, long operationId, int userId, IBiometricSensorReceiver sensorReceiver, String opPackageName, long requestId, int cookie, boolean allowBackgroundAuthentication, - boolean isForLegacyFingerprintManager) + boolean isForLegacyFingerprintManager, boolean isMandatoryBiometrics) throws RemoteException { mFingerprintService.prepareForAuthentication(token, operationId, sensorReceiver, new FingerprintAuthenticateOptions.Builder() .setSensorId(mSensorId) .setUserId(userId) .setOpPackageName(opPackageName) + .setIsMandatoryBiometrics(isMandatoryBiometrics) .build(), requestId, cookie, allowBackgroundAuthentication, isForLegacyFingerprintManager); } diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java index fb48053913cf..a81ab8ef4c22 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java @@ -71,7 +71,8 @@ public class FingerprintDetectClient extends AcquisitionClient<AidlSession> boolean isStrongBiometric) { super(context, lazyDaemon, token, listener, options.getUserId(), options.getOpPackageName(), 0 /* cookie */, options.getSensorId(), - true /* shouldVibrate */, biometricLogger, biometricContext); + true /* shouldVibrate */, biometricLogger, biometricContext, + false /* isMandatoryBiometrics */); setRequestId(requestId); mAuthenticationStateListeners = authenticationStateListeners; mIsStrongBiometric = isStrongBiometric; diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintGetAuthenticatorIdClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintGetAuthenticatorIdClient.java index 03539690c0a8..f77e11600502 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintGetAuthenticatorIdClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintGetAuthenticatorIdClient.java @@ -42,7 +42,8 @@ public class FingerprintGetAuthenticatorIdClient extends HalClientMonitor<AidlSe @NonNull BiometricLogger biometricLogger, @NonNull BiometricContext biometricContext, Map<Integer, Long> authenticatorIds) { super(context, lazyDaemon, null /* token */, null /* listener */, userId, owner, - 0 /* cookie */, sensorId, biometricLogger, biometricContext); + 0 /* cookie */, sensorId, biometricLogger, biometricContext, + false /* isMandatoryBiometrics */); mAuthenticatorIds = authenticatorIds; } diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintResetLockoutClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintResetLockoutClient.java index 387ae12147ae..81a3d8394435 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintResetLockoutClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintResetLockoutClient.java @@ -59,7 +59,8 @@ public class FingerprintResetLockoutClient extends HalClientMonitor<AidlSession> @NonNull LockoutResetDispatcher lockoutResetDispatcher, @Authenticators.Types int biometricStrength) { super(context, lazyDaemon, null /* token */, null /* listener */, userId, owner, - 0 /* cookie */, sensorId, biometricLogger, biometricContext); + 0 /* cookie */, sensorId, biometricLogger, biometricContext, + false /* isMandatoryBiometrics */); mHardwareAuthToken = hardwareAuthToken == null ? null : HardwareAuthTokenUtils.toHardwareAuthToken(hardwareAuthToken); mLockoutCache = lockoutTracker; diff --git a/services/core/java/com/android/server/biometrics/sensors/iris/IrisAuthenticator.java b/services/core/java/com/android/server/biometrics/sensors/iris/IrisAuthenticator.java index 01d1e378a735..76b5c4e8f310 100644 --- a/services/core/java/com/android/server/biometrics/sensors/iris/IrisAuthenticator.java +++ b/services/core/java/com/android/server/biometrics/sensors/iris/IrisAuthenticator.java @@ -60,7 +60,7 @@ public final class IrisAuthenticator extends IBiometricAuthenticator.Stub { public void prepareForAuthentication(boolean requireConfirmation, IBinder token, long sessionId, int userId, IBiometricSensorReceiver sensorReceiver, String opPackageName, long requestId, int cookie, boolean allowBackgroundAuthentication, - boolean isForLegacyFingerprintManager) + boolean isForLegacyFingerprintManager, boolean isMandatoryBiometrics) throws RemoteException { } diff --git a/services/core/java/com/android/server/flags/services.aconfig b/services/core/java/com/android/server/flags/services.aconfig index c361aee24916..649678c13a00 100644 --- a/services/core/java/com/android/server/flags/services.aconfig +++ b/services/core/java/com/android/server/flags/services.aconfig @@ -45,3 +45,14 @@ flag { purpose: PURPOSE_BUGFIX } } + +flag { + namespace: "backstage_power" + name: "consolidate_battery_change_events" + description: "Optimize battery status updates by delivering only the most recent battery information" + bug: "361334584" + is_fixed_read_only: true + metadata { + purpose: PURPOSE_BUGFIX + } +} diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java index bbe7b2b038c9..3c7b9d37e4c6 100644 --- a/services/core/java/com/android/server/hdmi/HdmiControlService.java +++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java @@ -4328,6 +4328,7 @@ public class HdmiControlService extends SystemService { HdmiCecLocalDevicePlayback playback = playback(); HdmiCecLocalDeviceAudioSystem audioSystem = audioSystem(); if (playback != null) { + playback.dismissUiOnActiveSourceStatusRecovered(); playback.setActiveSource(playback.getDeviceInfo().getLogicalAddress(), physicalAddress, caller); playback.wakeUpIfActiveSource(); diff --git a/services/core/java/com/android/server/locksettings/RebootEscrowManager.java b/services/core/java/com/android/server/locksettings/RebootEscrowManager.java index d0b8990e37c4..f44b85273af6 100644 --- a/services/core/java/com/android/server/locksettings/RebootEscrowManager.java +++ b/services/core/java/com/android/server/locksettings/RebootEscrowManager.java @@ -142,7 +142,6 @@ class RebootEscrowManager { ERROR_KEYSTORE_FAILURE, ERROR_NO_NETWORK, ERROR_TIMEOUT_EXHAUSTED, - ERROR_NO_REBOOT_ESCROW_DATA, }) @Retention(RetentionPolicy.SOURCE) @interface RebootEscrowErrorCode { @@ -158,7 +157,6 @@ class RebootEscrowManager { static final int ERROR_KEYSTORE_FAILURE = 7; static final int ERROR_NO_NETWORK = 8; static final int ERROR_TIMEOUT_EXHAUSTED = 9; - static final int ERROR_NO_REBOOT_ESCROW_DATA = 10; private @RebootEscrowErrorCode int mLoadEscrowDataErrorCode = ERROR_NONE; @@ -507,9 +505,6 @@ class RebootEscrowManager { if (rebootEscrowUsers.isEmpty()) { Slog.i(TAG, "No reboot escrow data found for users," + " skipping loading escrow data"); - setLoadEscrowDataErrorCode(ERROR_NO_REBOOT_ESCROW_DATA, retryHandler); - reportMetricOnRestoreComplete( - /* success= */ false, /* attemptCount= */ 1, retryHandler); clearMetricsStorage(); return; } diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 54bc6fbfd930..dbe778e4d971 100644 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -11777,6 +11777,8 @@ public class NotificationManagerService extends SystemService { mHandler.post(new EnqueueNotificationRunnable(record.getUser().getIdentifier(), record, isAppForeground, /* isAppProvided= */ false, tracker)); + + EventLogTags.writeNotificationCancelPrevented(record.getKey()); } } diff --git a/services/core/java/com/android/server/pm/BackgroundUserSoundNotifier.java b/services/core/java/com/android/server/pm/BackgroundUserSoundNotifier.java index e3061a7c39ae..41351613331d 100644 --- a/services/core/java/com/android/server/pm/BackgroundUserSoundNotifier.java +++ b/services/core/java/com/android/server/pm/BackgroundUserSoundNotifier.java @@ -69,6 +69,7 @@ public class BackgroundUserSoundNotifier { mUserManager = mSystemUserContext.getSystemService(UserManager.class); NotificationChannel channel = new NotificationChannel(BUSN_CHANNEL_ID, BUSN_CHANNEL_NAME, NotificationManager.IMPORTANCE_HIGH); + channel.setSound(null, null); mNotificationManager.createNotificationChannel(channel); setupFocusControlAudioPolicy(); } diff --git a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java index 95e5b84a8ed3..2bc6d53147fb 100644 --- a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java +++ b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java @@ -503,26 +503,21 @@ public class UserRestrictionsUtils { String restriction, boolean isMainUser, boolean isProfileOwnerOnOrgOwnedDevice) { - if (android.app.admin.flags.Flags.esimManagementEnabled()) { - if (IMMUTABLE_BY_OWNERS.contains(restriction)) { - return false; - } - if (DEVICE_OWNER_ONLY_RESTRICTIONS.contains(restriction)) { - return false; - } - if (!isMainUser && MAIN_USER_ONLY_RESTRICTIONS.contains(restriction)) { - return false; - } - if (!isProfileOwnerOnOrgOwnedDevice - && PROFILE_OWNER_ORGANIZATION_OWNED_PROFILE_RESTRICTIONS.contains( - restriction)) { - return false; - } - return true; + if (IMMUTABLE_BY_OWNERS.contains(restriction)) { + return false; + } + if (DEVICE_OWNER_ONLY_RESTRICTIONS.contains(restriction)) { + return false; + } + if (!isMainUser && MAIN_USER_ONLY_RESTRICTIONS.contains(restriction)) { + return false; + } + if (!isProfileOwnerOnOrgOwnedDevice + && PROFILE_OWNER_ORGANIZATION_OWNED_PROFILE_RESTRICTIONS.contains( + restriction)) { + return false; } - return !IMMUTABLE_BY_OWNERS.contains(restriction) - && !DEVICE_OWNER_ONLY_RESTRICTIONS.contains(restriction) - && !(!isMainUser && MAIN_USER_ONLY_RESTRICTIONS.contains(restriction)); + return true; } /** 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 f518769bdb35..e9cb279439a6 100644 --- a/services/core/java/com/android/server/pm/permission/AccessCheckDelegate.java +++ b/services/core/java/com/android/server/pm/permission/AccessCheckDelegate.java @@ -375,24 +375,34 @@ public interface AccessCheckDelegate extends CheckPermissionDelegate, CheckOpsDe @Nullable String message, boolean shouldCollectMessage, boolean skiProxyOperation, @NonNull HexFunction<Integer, AttributionSource, Boolean, String, Boolean, Boolean, SyncNotedAppOp> superImpl) { - if (attributionSource.getUid() == mDelegateAndOwnerUid && isDelegateOp(code)) { - final int shellUid = UserHandle.getUid( - UserHandle.getUserId(attributionSource.getUid()), Process.SHELL_UID); - final long identity = Binder.clearCallingIdentity(); - try { - return superImpl.apply(code, - new AttributionSource(shellUid, Process.INVALID_PID, SHELL_PKG, - attributionSource.getAttributionTag(), - attributionSource.getToken(), /*renouncedPermissions*/ null, - attributionSource.getDeviceId(), attributionSource.getNext()), - shouldCollectAsyncNotedOp, message, shouldCollectMessage, - skiProxyOperation); - } finally { - Binder.restoreCallingIdentity(identity); - } + if (!isDelegateOp(code)) { + return superImpl.apply(code, attributionSource, shouldCollectAsyncNotedOp, + message, shouldCollectMessage, skiProxyOperation); + } + + final int shellUid = UserHandle.getUid( + UserHandle.getUserId(attributionSource.getUid()), Process.SHELL_UID); + AttributionSource next = attributionSource.getNext(); + if (next != null && next.getUid() == mDelegateAndOwnerUid) { + next = new AttributionSource(shellUid, Process.INVALID_PID, SHELL_PKG, + next.getAttributionTag(), next.getToken(), /*renouncedPermissions*/ null, + next.getDeviceId(), next.getNext()); + attributionSource = new AttributionSource(attributionSource, next); + } + if (attributionSource.getUid() == mDelegateAndOwnerUid) { + attributionSource = new AttributionSource(shellUid, Process.INVALID_PID, SHELL_PKG, + attributionSource.getAttributionTag(), + attributionSource.getToken(), /*renouncedPermissions*/ null, + attributionSource.getDeviceId(), attributionSource.getNext()); + } + final long identity = Binder.clearCallingIdentity(); + try { + return superImpl.apply(code, attributionSource, + shouldCollectAsyncNotedOp, message, shouldCollectMessage, + skiProxyOperation); + } finally { + Binder.restoreCallingIdentity(identity); } - return superImpl.apply(code, attributionSource, shouldCollectAsyncNotedOp, - message, shouldCollectMessage, skiProxyOperation); } @Override diff --git a/services/core/java/com/android/server/pm/permission/TEST_MAPPING b/services/core/java/com/android/server/pm/permission/TEST_MAPPING index 24323c8bfbde..8a3c74b4879c 100644 --- a/services/core/java/com/android/server/pm/permission/TEST_MAPPING +++ b/services/core/java/com/android/server/pm/permission/TEST_MAPPING @@ -1,24 +1,7 @@ { "presubmit": [ { - "name": "CtsPermissionTestCases", - "options": [ - { - "exclude-annotation": "androidx.test.filters.FlakyTest" - }, - { - "include-filter": "android.permission.cts.BackgroundPermissionsTest" - }, - { - "include-filter": "android.permission.cts.SplitPermissionTest" - }, - { - "include-filter": "android.permission.cts.PermissionFlagsTest" - }, - { - "include-filter": "android.permission.cts.SharedUidPermissionsTest" - } - ] + "name": "CtsPermissionTestCases_Platform" }, { "name": "CtsAppSecurityHostTestCases", diff --git a/services/core/java/com/android/server/policy/TEST_MAPPING b/services/core/java/com/android/server/policy/TEST_MAPPING index 338b479f1ad1..bdb174d98137 100644 --- a/services/core/java/com/android/server/policy/TEST_MAPPING +++ b/services/core/java/com/android/server/policy/TEST_MAPPING @@ -46,18 +46,7 @@ ] }, { - "name": "CtsPermissionTestCases", - "options": [ - { - "exclude-annotation": "androidx.test.filters.FlakyTest" - }, - { - "include-filter": "android.permission.cts.SplitPermissionTest" - }, - { - "include-filter": "android.permission.cts.BackgroundPermissionsTest" - } - ] + "name": "CtsPermissionTestCases_Platform" }, { "name": "CtsBackupTestCases", diff --git a/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java b/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java index d0b70c391579..da8b01ac86fb 100644 --- a/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java +++ b/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java @@ -176,8 +176,9 @@ public class KeyguardServiceDelegate { final DreamManagerInternal dreamManager = LocalServices.getService(DreamManagerInternal.class); - - dreamManager.registerDreamManagerStateListener(mDreamManagerStateListener); + if(dreamManager != null){ + dreamManager.registerDreamManagerStateListener(mDreamManagerStateListener); + } } private final ServiceConnection mKeyguardConnection = new ServiceConnection() { diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java index a27360df798a..12e7fd010e3d 100644 --- a/services/core/java/com/android/server/power/PowerManagerService.java +++ b/services/core/java/com/android/server/power/PowerManagerService.java @@ -1379,8 +1379,10 @@ public final class PowerManagerService extends SystemService new DisplayGroupPowerChangeListener(); mDisplayManagerInternal.registerDisplayGroupListener(displayGroupPowerChangeListener); - // This DreamManager method does not acquire a lock, so it should be safe to call. - mDreamManager.registerDreamManagerStateListener(new DreamManagerStateListener()); + if(mDreamManager != null){ + // This DreamManager method does not acquire a lock, so it should be safe to call. + mDreamManager.registerDreamManagerStateListener(new DreamManagerStateListener()); + } mWirelessChargerDetector = mInjector.createWirelessChargerDetector(sensorManager, mInjector.createSuspendBlocker( @@ -3543,7 +3545,7 @@ public final class PowerManagerService extends SystemService } // Stop dream. - if (isDreaming) { + if (isDreaming && mDreamManager != null) { mDreamManager.stopDream(/* immediate= */ false, "power manager request" /*reason*/); } } diff --git a/services/core/java/com/android/server/power/ThermalManagerService.java b/services/core/java/com/android/server/power/ThermalManagerService.java index 7f2476979635..822ec2eb79b0 100644 --- a/services/core/java/com/android/server/power/ThermalManagerService.java +++ b/services/core/java/com/android/server/power/ThermalManagerService.java @@ -1644,8 +1644,7 @@ public class ThermalManagerService extends SystemService { if (Flags.allowThermalHeadroomThresholds()) { for (int severity = ThrottlingSeverity.LIGHT; severity <= ThrottlingSeverity.SHUTDOWN; severity++) { - if (severity != ThrottlingSeverity.SEVERE - && threshold.hotThrottlingThresholds.length > severity) { + if (threshold.hotThrottlingThresholds.length > severity) { updateHeadroomThreshold(severity, threshold.hotThrottlingThresholds[severity], severeThreshold); diff --git a/services/core/java/com/android/server/power/batterysaver/BatterySaverStateMachine.java b/services/core/java/com/android/server/power/batterysaver/BatterySaverStateMachine.java index 9a4c60d7625e..68760aae8d9d 100644 --- a/services/core/java/com/android/server/power/batterysaver/BatterySaverStateMachine.java +++ b/services/core/java/com/android/server/power/batterysaver/BatterySaverStateMachine.java @@ -864,7 +864,8 @@ public class BatterySaverStateMachine { buildNotification(DYNAMIC_MODE_NOTIF_CHANNEL_ID, R.string.dynamic_mode_notification_title, R.string.dynamic_mode_notification_summary, - Settings.ACTION_BATTERY_SAVER_SETTINGS, 0L), + Settings.ACTION_BATTERY_SAVER_SETTINGS, 0L, + R.drawable.ic_settings), UserHandle.ALL); }); } @@ -889,7 +890,8 @@ public class BatterySaverStateMachine { R.string.dynamic_mode_notification_summary_v2, Settings.ACTION_BATTERY_SAVER_SETTINGS, 0L /* timeoutMs */, - highlightBundle), + highlightBundle, + R.drawable.ic_qs_battery_saver), UserHandle.ALL); }); } @@ -911,7 +913,8 @@ public class BatterySaverStateMachine { R.string.battery_saver_off_notification_title, R.string.battery_saver_charged_notification_summary, Settings.ACTION_BATTERY_SAVER_SETTINGS, - STICKY_DISABLED_NOTIFY_TIMEOUT_MS), + STICKY_DISABLED_NOTIFY_TIMEOUT_MS, + R.drawable.ic_settings), UserHandle.ALL); }); } @@ -926,7 +929,7 @@ public class BatterySaverStateMachine { } private Notification buildNotification(@NonNull String channelId, @StringRes int titleId, - @StringRes int summaryId, @NonNull String intentAction, long timeoutMs) { + @StringRes int summaryId, @NonNull String intentAction, long timeoutMs, int iconResId) { Resources res = mContext.getResources(); Intent intent = new Intent(intentAction); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); @@ -937,7 +940,7 @@ public class BatterySaverStateMachine { final String summary = res.getString(summaryId); return new Notification.Builder(mContext, channelId) - .setSmallIcon(R.drawable.ic_battery) + .setSmallIcon(iconResId) .setContentTitle(title) .setContentText(summary) .setContentIntent(batterySaverIntent) @@ -950,7 +953,7 @@ public class BatterySaverStateMachine { private Notification buildNotificationV2(@NonNull String channelId, @StringRes int titleId, @StringRes int summaryId, @NonNull String intentAction, long timeoutMs, - @NonNull Bundle highlightBundle) { + @NonNull Bundle highlightBundle, int iconResId) { Resources res = mContext.getResources(); Intent intent = new Intent(intentAction) .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK) @@ -963,7 +966,7 @@ public class BatterySaverStateMachine { final String summary = res.getString(summaryId); return new Notification.Builder(mContext, channelId) - .setSmallIcon(R.drawable.ic_battery) + .setSmallIcon(iconResId) .setContentTitle(title) .setContentText(summary) .setContentIntent(batterySaverIntent) diff --git a/services/core/java/com/android/server/power/stats/BatteryExternalStatsWorker.java b/services/core/java/com/android/server/power/stats/BatteryExternalStatsWorker.java index 46e779fb0c45..8311034c0298 100644 --- a/services/core/java/com/android/server/power/stats/BatteryExternalStatsWorker.java +++ b/services/core/java/com/android/server/power/stats/BatteryExternalStatsWorker.java @@ -120,7 +120,7 @@ public class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStat private int mScreenState; @GuardedBy("this") - private int[] mPerDisplayScreenStates = null; + private int[] mPerDisplayScreenStates; @GuardedBy("this") private boolean mUseLatestStates = true; @@ -243,6 +243,7 @@ public class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStat } synchronized (mStats) { mStats.initEnergyConsumerStatsLocked(supportedStdBuckets, customBucketNames); + mPerDisplayScreenStates = new int[mStats.getDisplayCount()]; } } } @@ -490,6 +491,12 @@ public class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStat onBatteryScreenOff, screenState, displayScreenStates, useLatestStates); } finally { + if ((updateFlags & UPDATE_ALL) == UPDATE_ALL) { + synchronized (mStats) { + // This helps mStats deal with ignoring data from prior to resets. + mStats.informThatAllExternalStatsAreFlushed(); + } + } if (DEBUG) { Slog.d(TAG, "end updateExternalStatsSync"); } @@ -767,7 +774,6 @@ public class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStat // WiFi and Modem state are updated without the mStats lock held, because they // do some network stats retrieval before internally grabbing the mStats lock. - if (wifiInfo != null) { if (wifiInfo.isValid()) { final long wifiChargeUC = @@ -790,11 +796,6 @@ public class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStat mStats.noteModemControllerActivity(modemInfo, mobileRadioChargeUC, elapsedRealtime, uptime, networkStatsManager); } - - if ((updateFlags & UPDATE_ALL) == UPDATE_ALL) { - // This helps mStats deal with ignoring data from prior to resets. - mStats.informThatAllExternalStatsAreFlushed(); - } } /** 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 b45651d7aafc..385561d8c1a8 100644 --- a/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java +++ b/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java @@ -15300,15 +15300,6 @@ public class BatteryStatsImpl extends BatteryStats { mHistory.writeHistoryItem(elapsedRealtimeMs, uptimeMs); } } - if (!onBattery && - (status == BatteryManager.BATTERY_STATUS_FULL || - status == BatteryManager.BATTERY_STATUS_UNKNOWN)) { - // We don't record history while we are plugged in and fully charged - // (or when battery is not present). The next time we are - // unplugged, history will be cleared. - mHistory.setHistoryRecordingEnabled(DEBUG); - } - mLastLearnedBatteryCapacityUah = chargeFullUah; if (mMinLearnedBatteryCapacityUah == -1) { mMinLearnedBatteryCapacityUah = chargeFullUah; diff --git a/services/core/java/com/android/server/statusbar/StatusBarShellCommand.java b/services/core/java/com/android/server/statusbar/StatusBarShellCommand.java index d6bf02fcdc47..6466519356b7 100644 --- a/services/core/java/com/android/server/statusbar/StatusBarShellCommand.java +++ b/services/core/java/com/android/server/statusbar/StatusBarShellCommand.java @@ -190,6 +190,9 @@ public class StatusBarShellCommand extends ShellCommand { case "notification-icons": info.setNotificationIconsDisabled(true); break; + case "quick-settings": + info.setQuickSettingsDisabled(true); + break; default: break; } @@ -277,6 +280,7 @@ public class StatusBarShellCommand extends ShellCommand { pw.println(" system-icons - disable system icons appearing in status bar"); pw.println(" clock - disable clock appearing in status bar"); pw.println(" notification-icons - disable notification icons from status bar"); + pw.println(" quick-settings - disable Quick Settings"); pw.println(""); pw.println(" tracing (start | stop)"); pw.println(" Start or stop SystemUI tracing"); diff --git a/services/core/java/com/android/server/tv/tunerresourcemanager/CasResource.java b/services/core/java/com/android/server/tv/tunerresourcemanager/CasResource.java index 440d2514537c..eb5361c84b89 100644 --- a/services/core/java/com/android/server/tv/tunerresourcemanager/CasResource.java +++ b/services/core/java/com/android/server/tv/tunerresourcemanager/CasResource.java @@ -26,6 +26,11 @@ import java.util.Set; * @hide */ public class CasResource { + /** + * Handle of the current resource. Should not be changed and should be aligned with the driver + * level implementation. + */ + final int mHandle; private final int mSystemId; @@ -39,11 +44,16 @@ public class CasResource { private Map<Integer, Integer> mOwnerClientIdsToSessionNum = new HashMap<>(); CasResource(Builder builder) { + this.mHandle = builder.mHandle; this.mSystemId = builder.mSystemId; this.mMaxSessionNum = builder.mMaxSessionNum; this.mAvailableSessionNum = builder.mMaxSessionNum; } + public int getHandle() { + return mHandle; + } + public int getSystemId() { return mSystemId; } @@ -136,10 +146,12 @@ public class CasResource { */ public static class Builder { + private final int mHandle; private int mSystemId; protected int mMaxSessionNum; - Builder(int systemId) { + Builder(int handle, int systemId) { + this.mHandle = handle; this.mSystemId = systemId; } diff --git a/services/core/java/com/android/server/tv/tunerresourcemanager/CiCamResource.java b/services/core/java/com/android/server/tv/tunerresourcemanager/CiCamResource.java index 31149f3590b8..5cef729627f0 100644 --- a/services/core/java/com/android/server/tv/tunerresourcemanager/CiCamResource.java +++ b/services/core/java/com/android/server/tv/tunerresourcemanager/CiCamResource.java @@ -42,8 +42,8 @@ public final class CiCamResource extends CasResource { * Builder class for {@link CiCamResource}. */ public static class Builder extends CasResource.Builder { - Builder(int systemId) { - super(systemId); + Builder(int handle, int systemId) { + super(handle, systemId); } /** diff --git a/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java b/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java index 0afb049d31c7..9229f7f016bc 100644 --- a/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java +++ b/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java @@ -203,13 +203,7 @@ public class TunerResourceManagerService extends SystemService implements IBinde @Override public void unregisterClientProfile(int clientId) throws RemoteException { enforceTrmAccessPermission("unregisterClientProfile"); - synchronized (mLock) { - if (!checkClientExists(clientId)) { - Slog.e(TAG, "Unregistering non exists client:" + clientId); - return; - } - unregisterClientProfileInternal(clientId); - } + unregisterClientProfileInternal(clientId); } @Override @@ -291,20 +285,7 @@ public class TunerResourceManagerService extends SystemService implements IBinde Slog.e(TAG, "frontendHandle can't be null"); return false; } - synchronized (mLock) { - if (!checkClientExists(request.clientId)) { - Slog.e(TAG, "Request frontend from unregistered client: " - + request.clientId); - return false; - } - // If the request client is holding or sharing a frontend, throw an exception. - if (!getClientProfile(request.clientId).getInUseFrontendHandles().isEmpty()) { - Slog.e(TAG, "Release frontend before requesting another one. Client id: " - + request.clientId); - return false; - } - return requestFrontendInternal(request, frontendHandle); - } + return requestFrontendInternal(request, frontendHandle); } @Override @@ -376,13 +357,7 @@ public class TunerResourceManagerService extends SystemService implements IBinde if (demuxHandle == null) { throw new RemoteException("demuxHandle can't be null"); } - synchronized (mLock) { - if (!checkClientExists(request.clientId)) { - throw new RemoteException("Request demux from unregistered client:" - + request.clientId); - } - return requestDemuxInternal(request, demuxHandle); - } + return requestDemuxInternal(request, demuxHandle); } @Override @@ -409,13 +384,7 @@ public class TunerResourceManagerService extends SystemService implements IBinde if (casSessionHandle == null) { throw new RemoteException("casSessionHandle can't be null"); } - synchronized (mLock) { - if (!checkClientExists(request.clientId)) { - throw new RemoteException("Request cas from unregistered client:" - + request.clientId); - } - return requestCasSessionInternal(request, casSessionHandle); - } + return requestCasSessionInternal(request, casSessionHandle); } @Override @@ -425,13 +394,7 @@ public class TunerResourceManagerService extends SystemService implements IBinde if (ciCamHandle == null) { throw new RemoteException("ciCamHandle can't be null"); } - synchronized (mLock) { - if (!checkClientExists(request.clientId)) { - throw new RemoteException("Request ciCam from unregistered client:" - + request.clientId); - } - return requestCiCamInternal(request, ciCamHandle); - } + return requestCiCamInternal(request, ciCamHandle); } @Override @@ -442,42 +405,14 @@ public class TunerResourceManagerService extends SystemService implements IBinde if (lnbHandle == null) { throw new RemoteException("lnbHandle can't be null"); } - synchronized (mLock) { - if (!checkClientExists(request.clientId)) { - throw new RemoteException("Request lnb from unregistered client:" - + request.clientId); - } - return requestLnbInternal(request, lnbHandle); - } + return requestLnbInternal(request, lnbHandle); } @Override public void releaseFrontend(int frontendHandle, int clientId) throws RemoteException { enforceTunerAccessPermission("releaseFrontend"); enforceTrmAccessPermission("releaseFrontend"); - if (!validateResourceHandle(TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND, - frontendHandle)) { - throw new RemoteException("frontendHandle can't be invalid"); - } - synchronized (mLock) { - if (!checkClientExists(clientId)) { - throw new RemoteException("Release frontend from unregistered client:" - + clientId); - } - FrontendResource fe = getFrontendResource(frontendHandle); - if (fe == null) { - throw new RemoteException("Releasing frontend does not exist."); - } - int ownerClientId = fe.getOwnerClientId(); - ClientProfile ownerProfile = getClientProfile(ownerClientId); - if (ownerClientId != clientId - && (ownerProfile != null - && !ownerProfile.getShareFeClientIds().contains(clientId))) { - throw new RemoteException( - "Client is not the current owner of the releasing fe."); - } - releaseFrontendInternal(fe, clientId); - } + releaseFrontendInternal(frontendHandle, clientId); } @Override @@ -746,17 +681,23 @@ public class TunerResourceManagerService extends SystemService implements IBinde @VisibleForTesting protected void unregisterClientProfileInternal(int clientId) { - if (DEBUG) { - Slog.d(TAG, "unregisterClientProfile(clientId=" + clientId + ")"); - } - removeClientProfile(clientId); - // Remove the Media Resource Manager callingPid to tvAppId mapping - if (mMediaResourceManager != null) { - try { - mMediaResourceManager.overridePid(Binder.getCallingPid(), -1); - } catch (RemoteException e) { - Slog.e(TAG, "Could not overridePid in resourceManagerSercice when unregister," - + " remote exception: " + e); + synchronized (mLock) { + if (!checkClientExists(clientId)) { + Slog.e(TAG, "Unregistering non exists client:" + clientId); + return; + } + if (DEBUG) { + Slog.d(TAG, "unregisterClientProfile(clientId=" + clientId + ")"); + } + removeClientProfile(clientId); + // Remove the Media Resource Manager callingPid to tvAppId mapping + if (mMediaResourceManager != null) { + try { + mMediaResourceManager.overridePid(Binder.getCallingPid(), -1); + } catch (RemoteException e) { + Slog.e(TAG, "Could not overridePid in resourceManagerSercice when unregister," + + " remote exception: " + e); + } } } } @@ -992,10 +933,14 @@ public class TunerResourceManagerService extends SystemService implements IBinde return; } // Add the new Cas Resource. - cas = new CasResource.Builder(casSystemId) + int casSessionHandle = generateResourceHandle( + TunerResourceManager.TUNER_RESOURCE_TYPE_CAS_SESSION, casSystemId); + cas = new CasResource.Builder(casSessionHandle, casSystemId) .maxSessionNum(maxSessionNum) .build(); - ciCam = new CiCamResource.Builder(casSystemId) + int ciCamHandle = generateResourceHandle( + TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND_CICAM, casSystemId); + ciCam = new CiCamResource.Builder(ciCamHandle, casSystemId) .maxSessionNum(maxSessionNum) .build(); addCasResource(cas); @@ -1007,86 +952,120 @@ public class TunerResourceManagerService extends SystemService implements IBinde if (DEBUG) { Slog.d(TAG, "requestFrontend(request=" + request + ")"); } - - frontendHandle[0] = TunerResourceManager.INVALID_RESOURCE_HANDLE; - ClientProfile requestClient = getClientProfile(request.clientId); - // TODO: check if this is really needed - if (requestClient == null) { + int[] reclaimOwnerId = new int[1]; + if (!claimFrontend(request, frontendHandle, reclaimOwnerId)) { return false; } - clientPriorityUpdateOnRequest(requestClient); - int grantingFrontendHandle = TunerResourceManager.INVALID_RESOURCE_HANDLE; - int inUseLowestPriorityFrHandle = TunerResourceManager.INVALID_RESOURCE_HANDLE; - // Priority max value is 1000 - int currentLowestPriority = MAX_CLIENT_PRIORITY + 1; - boolean isRequestFromSameProcess = false; - // If the desired frontend id was specified, we only need to check the frontend. - boolean hasDesiredFrontend = request.desiredId != TunerFrontendRequest.DEFAULT_DESIRED_ID; - for (FrontendResource fr : getFrontendResources().values()) { - int frontendId = getResourceIdFromHandle(fr.getHandle()); - if (fr.getType() == request.frontendType - && (!hasDesiredFrontend || frontendId == request.desiredId)) { - if (!fr.isInUse()) { - // Unused resource cannot be acquired if the max is already reached, but - // TRM still has to look for the reclaim candidate - if (isFrontendMaxNumUseReached(request.frontendType)) { - continue; - } - // Grant unused frontend with no exclusive group members first. - if (fr.getExclusiveGroupMemberFeHandles().isEmpty()) { - grantingFrontendHandle = fr.getHandle(); - break; - } else if (grantingFrontendHandle - == TunerResourceManager.INVALID_RESOURCE_HANDLE) { - // Grant the unused frontend with lower id first if all the unused - // frontends have exclusive group members. - grantingFrontendHandle = fr.getHandle(); - } - } else if (grantingFrontendHandle == TunerResourceManager.INVALID_RESOURCE_HANDLE) { - // Record the frontend id with the lowest client priority among all the - // in use frontends when no available frontend has been found. - int priority = getFrontendHighestClientPriority(fr.getOwnerClientId()); - if (currentLowestPriority > priority) { - // we need to check the max used num if the target frontend type is not - // currently in primary use (and simply blocked due to exclusive group) - ClientProfile targetOwnerProfile = getClientProfile(fr.getOwnerClientId()); - int primaryFeId = targetOwnerProfile.getPrimaryFrontend(); - FrontendResource primaryFe = getFrontendResource(primaryFeId); - if (fr.getType() != primaryFe.getType() - && isFrontendMaxNumUseReached(fr.getType())) { + if (frontendHandle[0] == TunerResourceManager.INVALID_RESOURCE_HANDLE) { + return false; + } + if (reclaimOwnerId[0] != INVALID_CLIENT_ID) { + if (!reclaimResource(reclaimOwnerId[0], TunerResourceManager + .TUNER_RESOURCE_TYPE_FRONTEND)) { + return false; + } + synchronized (mLock) { + if (getFrontendResource(frontendHandle[0]).isInUse()) { + Slog.e(TAG, "Reclaimed frontend still in use"); + return false; + } + updateFrontendClientMappingOnNewGrant(frontendHandle[0], request.clientId); + } + } + return true; + } + + protected boolean claimFrontend( + TunerFrontendRequest request, + int[] frontendHandle, + int[] reclaimOwnerId + ) { + frontendHandle[0] = TunerResourceManager.INVALID_RESOURCE_HANDLE; + reclaimOwnerId[0] = INVALID_CLIENT_ID; + synchronized (mLock) { + if (!checkClientExists(request.clientId)) { + Slog.e(TAG, "Request frontend from unregistered client: " + + request.clientId); + return false; + } + // If the request client is holding or sharing a frontend, throw an exception. + if (!getClientProfile(request.clientId).getInUseFrontendHandles().isEmpty()) { + Slog.e(TAG, "Release frontend before requesting another one. Client id: " + + request.clientId); + return false; + } + ClientProfile requestClient = getClientProfile(request.clientId); + clientPriorityUpdateOnRequest(requestClient); + FrontendResource grantingFrontend = null; + FrontendResource inUseLowestPriorityFrontend = null; + // Priority max value is 1000 + int currentLowestPriority = MAX_CLIENT_PRIORITY + 1; + boolean isRequestFromSameProcess = false; + // If the desired frontend id was specified, we only need to check the frontend. + boolean hasDesiredFrontend = request.desiredId != TunerFrontendRequest + .DEFAULT_DESIRED_ID; + for (FrontendResource fr : getFrontendResources().values()) { + int frontendId = getResourceIdFromHandle(fr.getHandle()); + if (fr.getType() == request.frontendType + && (!hasDesiredFrontend || frontendId == request.desiredId)) { + if (!fr.isInUse()) { + // Unused resource cannot be acquired if the max is already reached, but + // TRM still has to look for the reclaim candidate + if (isFrontendMaxNumUseReached(request.frontendType)) { continue; } - // update the target frontend - inUseLowestPriorityFrHandle = fr.getHandle(); - currentLowestPriority = priority; - isRequestFromSameProcess = (requestClient.getProcessId() - == (getClientProfile(fr.getOwnerClientId())).getProcessId()); + // Grant unused frontend with no exclusive group members first. + if (fr.getExclusiveGroupMemberFeHandles().isEmpty()) { + grantingFrontend = fr; + break; + } else if (grantingFrontend == null) { + // Grant the unused frontend with lower id first if all the unused + // frontends have exclusive group members. + grantingFrontend = fr; + } + } else if (grantingFrontend == null) { + // Record the frontend id with the lowest client priority among all the + // in use frontends when no available frontend has been found. + int priority = getFrontendHighestClientPriority(fr.getOwnerClientId()); + if (currentLowestPriority > priority) { + // we need to check the max used num if the target frontend type is not + // currently in primary use (and simply blocked due to exclusive group) + ClientProfile targetOwnerProfile = + getClientProfile(fr.getOwnerClientId()); + int primaryFeId = targetOwnerProfile.getPrimaryFrontend(); + FrontendResource primaryFe = getFrontendResource(primaryFeId); + if (fr.getType() != primaryFe.getType() + && isFrontendMaxNumUseReached(fr.getType())) { + continue; + } + // update the target frontend + inUseLowestPriorityFrontend = fr; + currentLowestPriority = priority; + isRequestFromSameProcess = (requestClient.getProcessId() + == (getClientProfile(fr.getOwnerClientId())).getProcessId()); + } } } } - } - // Grant frontend when there is unused resource. - if (grantingFrontendHandle != TunerResourceManager.INVALID_RESOURCE_HANDLE) { - frontendHandle[0] = grantingFrontendHandle; - updateFrontendClientMappingOnNewGrant(grantingFrontendHandle, request.clientId); - return true; - } + // Grant frontend when there is unused resource. + if (grantingFrontend != null) { + updateFrontendClientMappingOnNewGrant(grantingFrontend.getHandle(), + request.clientId); + frontendHandle[0] = grantingFrontend.getHandle(); + return true; + } - // When all the resources are occupied, grant the lowest priority resource if the - // request client has higher priority. - if (inUseLowestPriorityFrHandle != TunerResourceManager.INVALID_RESOURCE_HANDLE - && ((requestClient.getPriority() > currentLowestPriority) || ( - (requestClient.getPriority() == currentLowestPriority) && isRequestFromSameProcess))) { - if (!reclaimResource( - getFrontendResource(inUseLowestPriorityFrHandle).getOwnerClientId(), - TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND)) { - return false; + // When all the resources are occupied, grant the lowest priority resource if the + // request client has higher priority. + if (inUseLowestPriorityFrontend != null + && ((requestClient.getPriority() > currentLowestPriority) + || ((requestClient.getPriority() == currentLowestPriority) + && isRequestFromSameProcess))) { + frontendHandle[0] = inUseLowestPriorityFrontend.getHandle(); + reclaimOwnerId[0] = inUseLowestPriorityFrontend.getOwnerClientId(); + return true; } - frontendHandle[0] = inUseLowestPriorityFrHandle; - updateFrontendClientMappingOnNewGrant( - inUseLowestPriorityFrHandle, request.clientId); - return true; } return false; @@ -1192,165 +1171,257 @@ public class TunerResourceManagerService extends SystemService implements IBinde } @VisibleForTesting - protected boolean requestLnbInternal(TunerLnbRequest request, int[] lnbHandle) { + protected boolean requestLnbInternal(TunerLnbRequest request, int[] lnbHandle) + throws RemoteException { if (DEBUG) { Slog.d(TAG, "requestLnb(request=" + request + ")"); } + int[] reclaimOwnerId = new int[1]; + if (!claimLnb(request, lnbHandle, reclaimOwnerId)) { + return false; + } + if (lnbHandle[0] == TunerResourceManager.INVALID_RESOURCE_HANDLE) { + return false; + } + if (reclaimOwnerId[0] != INVALID_CLIENT_ID) { + if (!reclaimResource(reclaimOwnerId[0], + TunerResourceManager.TUNER_RESOURCE_TYPE_LNB)) { + return false; + } + synchronized (mLock) { + if (getLnbResource(lnbHandle[0]).isInUse()) { + Slog.e(TAG, "Reclaimed lnb still in use"); + return false; + } + updateLnbClientMappingOnNewGrant(lnbHandle[0], request.clientId); + } + } + return true; + } + protected boolean claimLnb(TunerLnbRequest request, int[] lnbHandle, int[] reclaimOwnerId) + throws RemoteException { lnbHandle[0] = TunerResourceManager.INVALID_RESOURCE_HANDLE; - ClientProfile requestClient = getClientProfile(request.clientId); - clientPriorityUpdateOnRequest(requestClient); - int grantingLnbHandle = TunerResourceManager.INVALID_RESOURCE_HANDLE; - int inUseLowestPriorityLnbHandle = TunerResourceManager.INVALID_RESOURCE_HANDLE; - // Priority max value is 1000 - int currentLowestPriority = MAX_CLIENT_PRIORITY + 1; - boolean isRequestFromSameProcess = false; - for (LnbResource lnb : getLnbResources().values()) { - if (!lnb.isInUse()) { - // Grant the unused lnb with lower handle first - grantingLnbHandle = lnb.getHandle(); - break; - } else { - // Record the lnb id with the lowest client priority among all the - // in use lnb when no available lnb has been found. - int priority = updateAndGetOwnerClientPriority(lnb.getOwnerClientId()); - if (currentLowestPriority > priority) { - inUseLowestPriorityLnbHandle = lnb.getHandle(); - currentLowestPriority = priority; - isRequestFromSameProcess = (requestClient.getProcessId() - == (getClientProfile(lnb.getOwnerClientId())).getProcessId()); + reclaimOwnerId[0] = INVALID_CLIENT_ID; + synchronized (mLock) { + if (!checkClientExists(request.clientId)) { + throw new RemoteException("Request lnb from unregistered client:" + + request.clientId); + } + ClientProfile requestClient = getClientProfile(request.clientId); + clientPriorityUpdateOnRequest(requestClient); + LnbResource grantingLnb = null; + LnbResource inUseLowestPriorityLnb = null; + // Priority max value is 1000 + int currentLowestPriority = MAX_CLIENT_PRIORITY + 1; + boolean isRequestFromSameProcess = false; + for (LnbResource lnb : getLnbResources().values()) { + if (!lnb.isInUse()) { + // Grant the unused lnb with lower handle first + grantingLnb = lnb; + break; + } else { + // Record the lnb id with the lowest client priority among all the + // in use lnb when no available lnb has been found. + int priority = updateAndGetOwnerClientPriority(lnb.getOwnerClientId()); + if (currentLowestPriority > priority) { + inUseLowestPriorityLnb = lnb; + currentLowestPriority = priority; + isRequestFromSameProcess = (requestClient.getProcessId() + == (getClientProfile(lnb.getOwnerClientId())).getProcessId()); + } } } - } - // Grant Lnb when there is unused resource. - if (grantingLnbHandle > -1) { - lnbHandle[0] = grantingLnbHandle; - updateLnbClientMappingOnNewGrant(grantingLnbHandle, request.clientId); - return true; - } + // Grant Lnb when there is unused resource. + if (grantingLnb != null) { + updateLnbClientMappingOnNewGrant(grantingLnb.getHandle(), request.clientId); + lnbHandle[0] = grantingLnb.getHandle(); + return true; + } - // When all the resources are occupied, grant the lowest priority resource if the - // request client has higher priority. - if (inUseLowestPriorityLnbHandle > TunerResourceManager.INVALID_RESOURCE_HANDLE - && ((requestClient.getPriority() > currentLowestPriority) || ( - (requestClient.getPriority() == currentLowestPriority) && isRequestFromSameProcess))) { - if (!reclaimResource(getLnbResource(inUseLowestPriorityLnbHandle).getOwnerClientId(), - TunerResourceManager.TUNER_RESOURCE_TYPE_LNB)) { - return false; + // When all the resources are occupied, grant the lowest priority resource if the + // request client has higher priority. + if (inUseLowestPriorityLnb != null + && ((requestClient.getPriority() > currentLowestPriority) || ( + (requestClient.getPriority() == currentLowestPriority) + && isRequestFromSameProcess))) { + lnbHandle[0] = inUseLowestPriorityLnb.getHandle(); + reclaimOwnerId[0] = inUseLowestPriorityLnb.getOwnerClientId(); + return true; } - lnbHandle[0] = inUseLowestPriorityLnbHandle; - updateLnbClientMappingOnNewGrant(inUseLowestPriorityLnbHandle, request.clientId); - return true; } return false; } @VisibleForTesting - protected boolean requestCasSessionInternal(CasSessionRequest request, int[] casSessionHandle) { + protected boolean requestCasSessionInternal(CasSessionRequest request, int[] casSessionHandle) + throws RemoteException { if (DEBUG) { Slog.d(TAG, "requestCasSession(request=" + request + ")"); } - CasResource cas = getCasResource(request.casSystemId); - // Unregistered Cas System is treated as having unlimited sessions. - if (cas == null) { - cas = new CasResource.Builder(request.casSystemId) - .maxSessionNum(Integer.MAX_VALUE) - .build(); - addCasResource(cas); + int[] reclaimOwnerId = new int[1]; + if (!claimCasSession(request, casSessionHandle, reclaimOwnerId)) { + return false; } - casSessionHandle[0] = TunerResourceManager.INVALID_RESOURCE_HANDLE; - ClientProfile requestClient = getClientProfile(request.clientId); - clientPriorityUpdateOnRequest(requestClient); - int lowestPriorityOwnerId = -1; - // Priority max value is 1000 - int currentLowestPriority = MAX_CLIENT_PRIORITY + 1; - boolean isRequestFromSameProcess = false; - if (!cas.isFullyUsed()) { - casSessionHandle[0] = generateResourceHandle( - TunerResourceManager.TUNER_RESOURCE_TYPE_CAS_SESSION, cas.getSystemId()); - updateCasClientMappingOnNewGrant(request.casSystemId, request.clientId); - return true; + if (casSessionHandle[0] == TunerResourceManager.INVALID_RESOURCE_HANDLE) { + return false; } - for (int ownerId : cas.getOwnerClientIds()) { - // Record the client id with lowest priority that is using the current Cas system. - int priority = updateAndGetOwnerClientPriority(ownerId); - if (currentLowestPriority > priority) { - lowestPriorityOwnerId = ownerId; - currentLowestPriority = priority; - isRequestFromSameProcess = (requestClient.getProcessId() - == (getClientProfile(ownerId)).getProcessId()); + if (reclaimOwnerId[0] != INVALID_CLIENT_ID) { + if (!reclaimResource(reclaimOwnerId[0], + TunerResourceManager.TUNER_RESOURCE_TYPE_CAS_SESSION)) { + return false; + } + synchronized (mLock) { + if (getCasResource(request.casSystemId).isFullyUsed()) { + Slog.e(TAG, "Reclaimed cas still fully used"); + return false; + } + updateCasClientMappingOnNewGrant(request.casSystemId, request.clientId); } } + return true; + } - // When all the Cas sessions are occupied, reclaim the lowest priority client if the - // request client has higher priority. - if (lowestPriorityOwnerId > -1 && ((requestClient.getPriority() > currentLowestPriority) - || ((requestClient.getPriority() == currentLowestPriority) && isRequestFromSameProcess))) { - if (!reclaimResource(lowestPriorityOwnerId, - TunerResourceManager.TUNER_RESOURCE_TYPE_CAS_SESSION)) { - return false; + protected boolean claimCasSession(CasSessionRequest request, int[] casSessionHandle, + int[] reclaimOwnerId) throws RemoteException { + casSessionHandle[0] = TunerResourceManager.INVALID_RESOURCE_HANDLE; + reclaimOwnerId[0] = INVALID_CLIENT_ID; + synchronized (mLock) { + if (!checkClientExists(request.clientId)) { + throw new RemoteException("Request cas from unregistered client:" + + request.clientId); + } + CasResource cas = getCasResource(request.casSystemId); + // Unregistered Cas System is treated as having unlimited sessions. + if (cas == null) { + int resourceHandle = generateResourceHandle( + TunerResourceManager.TUNER_RESOURCE_TYPE_CAS_SESSION, request.clientId); + cas = new CasResource.Builder(resourceHandle, request.casSystemId) + .maxSessionNum(Integer.MAX_VALUE) + .build(); + addCasResource(cas); + } + ClientProfile requestClient = getClientProfile(request.clientId); + clientPriorityUpdateOnRequest(requestClient); + int lowestPriorityOwnerId = INVALID_CLIENT_ID; + // Priority max value is 1000 + int currentLowestPriority = MAX_CLIENT_PRIORITY + 1; + boolean isRequestFromSameProcess = false; + if (!cas.isFullyUsed()) { + updateCasClientMappingOnNewGrant(request.casSystemId, request.clientId); + casSessionHandle[0] = cas.getHandle(); + return true; + } + for (int ownerId : cas.getOwnerClientIds()) { + // Record the client id with lowest priority that is using the current Cas system. + int priority = updateAndGetOwnerClientPriority(ownerId); + if (currentLowestPriority > priority) { + lowestPriorityOwnerId = ownerId; + currentLowestPriority = priority; + isRequestFromSameProcess = (requestClient.getProcessId() + == (getClientProfile(ownerId)).getProcessId()); + } + } + + // When all the Cas sessions are occupied, reclaim the lowest priority client if the + // request client has higher priority. + if (lowestPriorityOwnerId != INVALID_CLIENT_ID + && ((requestClient.getPriority() > currentLowestPriority) + || ((requestClient.getPriority() == currentLowestPriority) + && isRequestFromSameProcess))) { + casSessionHandle[0] = cas.getHandle(); + reclaimOwnerId[0] = lowestPriorityOwnerId; + return true; } - casSessionHandle[0] = generateResourceHandle( - TunerResourceManager.TUNER_RESOURCE_TYPE_CAS_SESSION, cas.getSystemId()); - updateCasClientMappingOnNewGrant(request.casSystemId, request.clientId); - return true; } + return false; } @VisibleForTesting - protected boolean requestCiCamInternal(TunerCiCamRequest request, int[] ciCamHandle) { + protected boolean requestCiCamInternal(TunerCiCamRequest request, int[] ciCamHandle) + throws RemoteException { if (DEBUG) { Slog.d(TAG, "requestCiCamInternal(TunerCiCamRequest=" + request + ")"); } - CiCamResource ciCam = getCiCamResource(request.ciCamId); - // Unregistered Cas System is treated as having unlimited sessions. - if (ciCam == null) { - ciCam = new CiCamResource.Builder(request.ciCamId) - .maxSessionNum(Integer.MAX_VALUE) - .build(); - addCiCamResource(ciCam); + int[] reclaimOwnerId = new int[1]; + if (!claimCiCam(request, ciCamHandle, reclaimOwnerId)) { + return false; } - ciCamHandle[0] = TunerResourceManager.INVALID_RESOURCE_HANDLE; - ClientProfile requestClient = getClientProfile(request.clientId); - clientPriorityUpdateOnRequest(requestClient); - int lowestPriorityOwnerId = -1; - // Priority max value is 1000 - int currentLowestPriority = MAX_CLIENT_PRIORITY + 1; - boolean isRequestFromSameProcess = false; - if (!ciCam.isFullyUsed()) { - ciCamHandle[0] = generateResourceHandle( - TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND_CICAM, ciCam.getCiCamId()); - updateCiCamClientMappingOnNewGrant(request.ciCamId, request.clientId); - return true; + if (ciCamHandle[0] == TunerResourceManager.INVALID_RESOURCE_HANDLE) { + return false; } - for (int ownerId : ciCam.getOwnerClientIds()) { - // Record the client id with lowest priority that is using the current Cas system. - int priority = updateAndGetOwnerClientPriority(ownerId); - if (currentLowestPriority > priority) { - lowestPriorityOwnerId = ownerId; - currentLowestPriority = priority; - isRequestFromSameProcess = (requestClient.getProcessId() - == (getClientProfile(ownerId)).getProcessId()); - } - } - - // When all the CiCam sessions are occupied, reclaim the lowest priority client if the - // request client has higher priority. - if (lowestPriorityOwnerId > -1 && ((requestClient.getPriority() > currentLowestPriority) - || ((requestClient.getPriority() == currentLowestPriority) - && isRequestFromSameProcess))) { - if (!reclaimResource(lowestPriorityOwnerId, + if (reclaimOwnerId[0] != INVALID_CLIENT_ID) { + if (!reclaimResource(reclaimOwnerId[0], TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND_CICAM)) { return false; } - ciCamHandle[0] = generateResourceHandle( - TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND_CICAM, ciCam.getCiCamId()); - updateCiCamClientMappingOnNewGrant(request.ciCamId, request.clientId); - return true; + synchronized (mLock) { + if (getCiCamResource(request.ciCamId).isFullyUsed()) { + Slog.e(TAG, "Reclaimed ciCam still fully used"); + return false; + } + updateCiCamClientMappingOnNewGrant(request.ciCamId, request.clientId); + } + } + return true; + } + + protected boolean claimCiCam(TunerCiCamRequest request, int[] ciCamHandle, + int[] reclaimOwnerId) throws RemoteException { + ciCamHandle[0] = TunerResourceManager.INVALID_RESOURCE_HANDLE; + reclaimOwnerId[0] = INVALID_CLIENT_ID; + synchronized (mLock) { + if (!checkClientExists(request.clientId)) { + throw new RemoteException("Request ciCam from unregistered client:" + + request.clientId); + } + CiCamResource ciCam = getCiCamResource(request.ciCamId); + // Unregistered CiCam is treated as having unlimited sessions. + if (ciCam == null) { + int resourceHandle = generateResourceHandle( + TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND_CICAM, request.ciCamId); + ciCam = new CiCamResource.Builder(resourceHandle, request.ciCamId) + .maxSessionNum(Integer.MAX_VALUE) + .build(); + addCiCamResource(ciCam); + } + ClientProfile requestClient = getClientProfile(request.clientId); + clientPriorityUpdateOnRequest(requestClient); + int lowestPriorityOwnerId = INVALID_CLIENT_ID; + // Priority max value is 1000 + int currentLowestPriority = MAX_CLIENT_PRIORITY + 1; + boolean isRequestFromSameProcess = false; + if (!ciCam.isFullyUsed()) { + updateCiCamClientMappingOnNewGrant(request.ciCamId, request.clientId); + ciCamHandle[0] = ciCam.getHandle(); + return true; + } + for (int ownerId : ciCam.getOwnerClientIds()) { + // Record the client id with lowest priority that is using the current CiCam. + int priority = updateAndGetOwnerClientPriority(ownerId); + if (currentLowestPriority > priority) { + lowestPriorityOwnerId = ownerId; + currentLowestPriority = priority; + isRequestFromSameProcess = (requestClient.getProcessId() + == (getClientProfile(ownerId)).getProcessId()); + } + } + + // When all the CiCam sessions are occupied, reclaim the lowest priority client if the + // request client has higher priority. + if (lowestPriorityOwnerId != INVALID_CLIENT_ID + && ((requestClient.getPriority() > currentLowestPriority) + || ((requestClient.getPriority() == currentLowestPriority) + && isRequestFromSameProcess))) { + ciCamHandle[0] = ciCam.getHandle(); + reclaimOwnerId[0] = lowestPriorityOwnerId; + return true; + } } + return false; } @@ -1383,20 +1454,49 @@ public class TunerResourceManagerService extends SystemService implements IBinde } @VisibleForTesting - protected void releaseFrontendInternal(FrontendResource fe, int clientId) { + protected void releaseFrontendInternal(int frontendHandle, int clientId) + throws RemoteException { if (DEBUG) { - Slog.d(TAG, "releaseFrontend(id=" + fe.getHandle() + ", clientId=" + clientId + " )"); + Slog.d(TAG, "releaseFrontend(id=" + frontendHandle + ", clientId=" + clientId + " )"); } - if (clientId == fe.getOwnerClientId()) { - ClientProfile ownerClient = getClientProfile(fe.getOwnerClientId()); - if (ownerClient != null) { - for (int shareOwnerId : ownerClient.getShareFeClientIds()) { - reclaimResource(shareOwnerId, - TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND); + if (!validateResourceHandle(TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND, + frontendHandle)) { + throw new RemoteException("frontendHandle can't be invalid"); + } + Set<Integer> reclaimedResourceOwnerIds = unclaimFrontend(frontendHandle, clientId); + if (reclaimedResourceOwnerIds != null) { + for (int shareOwnerId : reclaimedResourceOwnerIds) { + reclaimResource(shareOwnerId, + TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND); + } + } + synchronized (mLock) { + clearFrontendAndClientMapping(getClientProfile(clientId)); + } + } + + private Set<Integer> unclaimFrontend(int frontendHandle, int clientId) throws RemoteException { + Set<Integer> reclaimedResourceOwnerIds = null; + synchronized (mLock) { + if (!checkClientExists(clientId)) { + throw new RemoteException("Release frontend from unregistered client:" + + clientId); + } + FrontendResource fe = getFrontendResource(frontendHandle); + if (fe == null) { + throw new RemoteException("Releasing frontend does not exist."); + } + int ownerClientId = fe.getOwnerClientId(); + ClientProfile ownerProfile = getClientProfile(ownerClientId); + if (ownerClientId == clientId) { + reclaimedResourceOwnerIds = ownerProfile.getShareFeClientIds(); + } else { + if (!ownerProfile.getShareFeClientIds().contains(clientId)) { + throw new RemoteException("Client is not a sharee of the releasing fe."); } } } - clearFrontendAndClientMapping(getClientProfile(clientId)); + return reclaimedResourceOwnerIds; } @VisibleForTesting @@ -1432,103 +1532,129 @@ public class TunerResourceManagerService extends SystemService implements IBinde } @VisibleForTesting - protected boolean requestDemuxInternal(TunerDemuxRequest request, int[] demuxHandle) { + public boolean requestDemuxInternal(@NonNull TunerDemuxRequest request, + @NonNull int[] demuxHandle) throws RemoteException { if (DEBUG) { Slog.d(TAG, "requestDemux(request=" + request + ")"); } - - // For Tuner 2.0 and below or any HW constraint devices that are unable to support - // ITuner.openDemuxById(), demux resources are not really managed under TRM and - // mDemuxResources.size() will be zero - if (mDemuxResources.size() == 0) { - // There are enough Demux resources, so we don't manage Demux in R. - demuxHandle[0] = - generateResourceHandle(TunerResourceManager.TUNER_RESOURCE_TYPE_DEMUX, 0); - return true; + int[] reclaimOwnerId = new int[1]; + if (!claimDemux(request, demuxHandle, reclaimOwnerId)) { + return false; } - - demuxHandle[0] = TunerResourceManager.INVALID_RESOURCE_HANDLE; - ClientProfile requestClient = getClientProfile(request.clientId); - - if (requestClient == null) { + if (demuxHandle[0] == TunerResourceManager.INVALID_RESOURCE_HANDLE) { return false; } + if (reclaimOwnerId[0] != INVALID_CLIENT_ID) { + if (!reclaimResource(reclaimOwnerId[0], + TunerResourceManager.TUNER_RESOURCE_TYPE_DEMUX)) { + return false; + } + synchronized (mLock) { + if (getDemuxResource(demuxHandle[0]).isInUse()) { + Slog.e(TAG, "Reclaimed demux still in use"); + return false; + } + updateDemuxClientMappingOnNewGrant(demuxHandle[0], request.clientId); + } + } + return true; + } - clientPriorityUpdateOnRequest(requestClient); - int grantingDemuxHandle = TunerResourceManager.INVALID_RESOURCE_HANDLE; - int inUseLowestPriorityDrHandle = TunerResourceManager.INVALID_RESOURCE_HANDLE; - // Priority max value is 1000 - int currentLowestPriority = MAX_CLIENT_PRIORITY + 1; - boolean isRequestFromSameProcess = false; - // If the desired demux id was specified, we only need to check the demux. - boolean hasDesiredDemuxCap = request.desiredFilterTypes - != DemuxFilterMainType.UNDEFINED; - int smallestNumOfSupportedCaps = Integer.SIZE + 1; - int smallestNumOfSupportedCapsInUse = Integer.SIZE + 1; - for (DemuxResource dr : getDemuxResources().values()) { - if (!hasDesiredDemuxCap || dr.hasSufficientCaps(request.desiredFilterTypes)) { - if (!dr.isInUse()) { - int numOfSupportedCaps = dr.getNumOfCaps(); - - // look for the demux with minimum caps supporting the desired caps - if (smallestNumOfSupportedCaps > numOfSupportedCaps) { - smallestNumOfSupportedCaps = numOfSupportedCaps; - grantingDemuxHandle = dr.getHandle(); - } - } else if (grantingDemuxHandle == TunerResourceManager.INVALID_RESOURCE_HANDLE) { - // Record the demux id with the lowest client priority among all the - // in use demuxes when no availabledemux has been found. - int priority = updateAndGetOwnerClientPriority(dr.getOwnerClientId()); - if (currentLowestPriority >= priority) { - int numOfSupportedCaps = dr.getNumOfCaps(); - boolean shouldUpdate = false; - // update lowest priority - if (currentLowestPriority > priority) { - currentLowestPriority = priority; - isRequestFromSameProcess = (requestClient.getProcessId() - == (getClientProfile(dr.getOwnerClientId())).getProcessId()); + protected boolean claimDemux(TunerDemuxRequest request, int[] demuxHandle, int[] reclaimOwnerId) + throws RemoteException { + demuxHandle[0] = TunerResourceManager.INVALID_RESOURCE_HANDLE; + reclaimOwnerId[0] = INVALID_CLIENT_ID; + synchronized (mLock) { + if (!checkClientExists(request.clientId)) { + throw new RemoteException("Request demux from unregistered client:" + + request.clientId); + } + + // For Tuner 2.0 and below or any HW constraint devices that are unable to support + // ITuner.openDemuxById(), demux resources are not really managed under TRM and + // mDemuxResources.size() will be zero + if (mDemuxResources.size() == 0) { + // There are enough Demux resources, so we don't manage Demux in R. + demuxHandle[0] = + generateResourceHandle(TunerResourceManager.TUNER_RESOURCE_TYPE_DEMUX, 0); + return true; + } - // reset the smallest caps when lower priority resource is found - smallestNumOfSupportedCapsInUse = numOfSupportedCaps; + ClientProfile requestClient = getClientProfile(request.clientId); + if (requestClient == null) { + return false; + } + clientPriorityUpdateOnRequest(requestClient); + DemuxResource grantingDemux = null; + DemuxResource inUseLowestPriorityDemux = null; + // Priority max value is 1000 + int currentLowestPriority = MAX_CLIENT_PRIORITY + 1; + boolean isRequestFromSameProcess = false; + // If the desired demux id was specified, we only need to check the demux. + boolean hasDesiredDemuxCap = request.desiredFilterTypes + != DemuxFilterMainType.UNDEFINED; + int smallestNumOfSupportedCaps = Integer.SIZE + 1; + int smallestNumOfSupportedCapsInUse = Integer.SIZE + 1; + for (DemuxResource dr : getDemuxResources().values()) { + if (!hasDesiredDemuxCap || dr.hasSufficientCaps(request.desiredFilterTypes)) { + if (!dr.isInUse()) { + int numOfSupportedCaps = dr.getNumOfCaps(); - shouldUpdate = true; - } else { - // This is the case when the priority is the same as previously found - // one. Update smallest caps when priority. - if (smallestNumOfSupportedCapsInUse > numOfSupportedCaps) { + // look for the demux with minimum caps supporting the desired caps + if (smallestNumOfSupportedCaps > numOfSupportedCaps) { + smallestNumOfSupportedCaps = numOfSupportedCaps; + grantingDemux = dr; + } + } else if (grantingDemux == null) { + // Record the demux id with the lowest client priority among all the + // in use demuxes when no availabledemux has been found. + int priority = updateAndGetOwnerClientPriority(dr.getOwnerClientId()); + if (currentLowestPriority >= priority) { + int numOfSupportedCaps = dr.getNumOfCaps(); + boolean shouldUpdate = false; + // update lowest priority + if (currentLowestPriority > priority) { + currentLowestPriority = priority; + isRequestFromSameProcess = (requestClient.getProcessId() + == (getClientProfile(dr.getOwnerClientId())).getProcessId()); + + // reset the smallest caps when lower priority resource is found smallestNumOfSupportedCapsInUse = numOfSupportedCaps; + shouldUpdate = true; + } else { + // This is the case when the priority is the same as previously + // found one. Update smallest caps when priority. + if (smallestNumOfSupportedCapsInUse > numOfSupportedCaps) { + smallestNumOfSupportedCapsInUse = numOfSupportedCaps; + shouldUpdate = true; + } + } + if (shouldUpdate) { + inUseLowestPriorityDemux = dr; } - } - if (shouldUpdate) { - inUseLowestPriorityDrHandle = dr.getHandle(); } } } } - } - // Grant demux when there is unused resource. - if (grantingDemuxHandle != TunerResourceManager.INVALID_RESOURCE_HANDLE) { - demuxHandle[0] = grantingDemuxHandle; - updateDemuxClientMappingOnNewGrant(grantingDemuxHandle, request.clientId); - return true; - } + // Grant demux when there is unused resource. + if (grantingDemux != null) { + updateDemuxClientMappingOnNewGrant(grantingDemux.getHandle(), request.clientId); + demuxHandle[0] = grantingDemux.getHandle(); + return true; + } - // When all the resources are occupied, grant the lowest priority resource if the - // request client has higher priority. - if (inUseLowestPriorityDrHandle != TunerResourceManager.INVALID_RESOURCE_HANDLE - && ((requestClient.getPriority() > currentLowestPriority) || ( - (requestClient.getPriority() == currentLowestPriority) && isRequestFromSameProcess))) { - if (!reclaimResource( - getDemuxResource(inUseLowestPriorityDrHandle).getOwnerClientId(), - TunerResourceManager.TUNER_RESOURCE_TYPE_DEMUX)) { - return false; + // When all the resources are occupied, grant the lowest priority resource if the + // request client has higher priority. + if (inUseLowestPriorityDemux != null + && ((requestClient.getPriority() > currentLowestPriority) || ( + (requestClient.getPriority() == currentLowestPriority) + && isRequestFromSameProcess))) { + demuxHandle[0] = inUseLowestPriorityDemux.getHandle(); + reclaimOwnerId[0] = inUseLowestPriorityDemux.getOwnerClientId(); + return true; } - demuxHandle[0] = inUseLowestPriorityDrHandle; - updateDemuxClientMappingOnNewGrant( - inUseLowestPriorityDrHandle, request.clientId); - return true; } return false; @@ -1792,7 +1918,9 @@ public class TunerResourceManagerService extends SystemService implements IBinde return; } - mListeners.put(clientId, record); + synchronized (mLock) { + mListeners.put(clientId, record); + } } @VisibleForTesting @@ -1808,33 +1936,44 @@ public class TunerResourceManagerService extends SystemService implements IBinde // Reclaim all the resources of the share owners of the frontend that is used by the current // resource reclaimed client. - ClientProfile profile = getClientProfile(reclaimingClientId); - // TODO: check if this check is really needed. - if (profile == null) { - return true; + Set<Integer> shareFeClientIds; + synchronized (mLock) { + ClientProfile profile = getClientProfile(reclaimingClientId); + if (profile == null) { + return true; + } + shareFeClientIds = profile.getShareFeClientIds(); } - Set<Integer> shareFeClientIds = profile.getShareFeClientIds(); + ResourcesReclaimListenerRecord listenerRecord = null; for (int clientId : shareFeClientIds) { - try { - mListeners.get(clientId).getListener().onReclaimResources(); - } catch (RemoteException e) { - Slog.e(TAG, "Failed to reclaim resources on client " + clientId, e); - return false; + synchronized (mLock) { + listenerRecord = mListeners.get(clientId); + } + if (listenerRecord != null) { + try { + listenerRecord.getListener().onReclaimResources(); + } catch (RemoteException e) { + Slog.e(TAG, "Failed to reclaim resources on client " + clientId, e); + } } - clearAllResourcesAndClientMapping(getClientProfile(clientId)); } if (DEBUG) { Slog.d(TAG, "Reclaiming resources because higher priority client request resource type " + resourceType + ", clientId:" + reclaimingClientId); } - try { - mListeners.get(reclaimingClientId).getListener().onReclaimResources(); - } catch (RemoteException e) { - Slog.e(TAG, "Failed to reclaim resources on client " + reclaimingClientId, e); - return false; + + synchronized (mLock) { + listenerRecord = mListeners.get(reclaimingClientId); + } + if (listenerRecord != null) { + try { + listenerRecord.getListener().onReclaimResources(); + } catch (RemoteException e) { + Slog.e(TAG, "Failed to reclaim resources on client " + reclaimingClientId, e); + } } - clearAllResourcesAndClientMapping(profile); + return true; } @@ -2258,6 +2397,7 @@ public class TunerResourceManagerService extends SystemService implements IBinde addResourcesReclaimListener(clientId, listener); } + @SuppressWarnings("GuardedBy") // Lock is held on mListeners private void removeClientProfile(int clientId) { for (int shareOwnerId : getClientProfile(clientId).getShareFeClientIds()) { clearFrontendAndClientMapping(getClientProfile(shareOwnerId)); @@ -2265,12 +2405,9 @@ public class TunerResourceManagerService extends SystemService implements IBinde clearAllResourcesAndClientMapping(getClientProfile(clientId)); mClientProfiles.remove(clientId); - // it may be called by unregisterClientProfileInternal under test - synchronized (mLock) { - ResourcesReclaimListenerRecord record = mListeners.remove(clientId); - if (record != null) { - record.getListener().asBinder().unlinkToDeath(record, 0); - } + ResourcesReclaimListenerRecord record = mListeners.remove(clientId); + if (record != null) { + record.getListener().asBinder().unlinkToDeath(record, 0); } } @@ -2304,7 +2441,8 @@ public class TunerResourceManagerService extends SystemService implements IBinde profile.releaseFrontend(); } - private void clearAllResourcesAndClientMapping(ClientProfile profile) { + @VisibleForTesting + protected void clearAllResourcesAndClientMapping(ClientProfile profile) { // TODO: check if this check is really needed. Maybe needed for reclaimResource path. if (profile == null) { return; diff --git a/services/core/java/com/android/server/vibrator/ExternalVibrationSession.java b/services/core/java/com/android/server/vibrator/ExternalVibrationSession.java new file mode 100644 index 000000000000..b4d3862431f1 --- /dev/null +++ b/services/core/java/com/android/server/vibrator/ExternalVibrationSession.java @@ -0,0 +1,146 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.vibrator; + +import android.annotation.Nullable; +import android.content.Context; +import android.os.ExternalVibration; +import android.os.ExternalVibrationScale; +import android.os.IBinder; +import android.os.VibrationAttributes; +import android.os.vibrator.Flags; + +import com.android.internal.util.FrameworkStatsLog; + +/** + * A vibration session holding a single {@link ExternalVibration} request. + */ +final class ExternalVibrationSession extends Vibration + implements VibrationSession, IBinder.DeathRecipient { + + private final ExternalVibration mExternalVibration; + private final ExternalVibrationScale mScale = new ExternalVibrationScale(); + + @Nullable + private Runnable mBinderDeathCallback; + + ExternalVibrationSession(ExternalVibration externalVibration) { + super(externalVibration.getToken(), new CallerInfo( + externalVibration.getVibrationAttributes(), externalVibration.getUid(), + // TODO(b/249785241): Find a way to link ExternalVibration to a VirtualDevice + // instead of using DEVICE_ID_INVALID here and relying on the UID checks. + Context.DEVICE_ID_INVALID, externalVibration.getPackage(), null)); + mExternalVibration = externalVibration; + } + + public ExternalVibrationScale getScale() { + return mScale; + } + + @Override + public CallerInfo getCallerInfo() { + return callerInfo; + } + + @Override + public VibrationSession.DebugInfo getDebugInfo() { + return new Vibration.DebugInfoImpl(getStatus(), stats, /* playedEffect= */ null, + /* originalEffect= */ null, mScale.scaleLevel, mScale.adaptiveHapticsScale, + callerInfo); + } + + @Override + public VibrationStats.StatsInfo getStatsInfo(long completionUptimeMillis) { + return new VibrationStats.StatsInfo( + mExternalVibration.getUid(), + FrameworkStatsLog.VIBRATION_REPORTED__VIBRATION_TYPE__EXTERNAL, + mExternalVibration.getVibrationAttributes().getUsage(), getStatus(), stats, + completionUptimeMillis); + } + + @Override + public boolean isRepeating() { + // We don't currently know if the external vibration is repeating, so we just use a + // heuristic based on the usage. Ideally this would be propagated in the ExternalVibration. + int usage = mExternalVibration.getVibrationAttributes().getUsage(); + return usage == VibrationAttributes.USAGE_RINGTONE + || usage == VibrationAttributes.USAGE_ALARM; + } + + @Override + public void linkToDeath(Runnable callback) { + synchronized (this) { + mBinderDeathCallback = callback; + } + mExternalVibration.linkToDeath(this); + } + + @Override + public void unlinkToDeath() { + mExternalVibration.unlinkToDeath(this); + synchronized (this) { + mBinderDeathCallback = null; + } + } + + @Override + public void binderDied() { + synchronized (this) { + if (mBinderDeathCallback != null) { + mBinderDeathCallback.run(); + } + } + } + + @Override + void end(EndInfo endInfo) { + super.end(endInfo); + if (stats.hasStarted()) { + // External vibration doesn't have feedback from total time the vibrator was playing + // with non-zero amplitude, so we use the duration between start and end times of + // the vibration as the time the vibrator was ON, since the haptic channels are + // open for this duration and can receive vibration waveform data. + stats.reportVibratorOn(stats.getEndUptimeMillis() - stats.getStartUptimeMillis()); + } + } + + @Override + public void notifyEnded() { + // Notify external client that this vibration should stop sending data to the vibrator. + mExternalVibration.mute(); + } + + boolean isHoldingSameVibration(ExternalVibration vib) { + return mExternalVibration.equals(vib); + } + + void muteScale() { + mScale.scaleLevel = ExternalVibrationScale.ScaleLevel.SCALE_MUTE; + if (Flags.hapticsScaleV2Enabled()) { + mScale.scaleFactor = 0; + } + } + + void scale(VibrationScaler scaler, int usage) { + mScale.scaleLevel = scaler.getScaleLevel(usage); + if (Flags.hapticsScaleV2Enabled()) { + mScale.scaleFactor = scaler.getScaleFactor(usage); + } + mScale.adaptiveHapticsScale = scaler.getAdaptiveHapticsScale(usage); + stats.reportAdaptiveScale(mScale.adaptiveHapticsScale); + } +} diff --git a/services/core/java/com/android/server/vibrator/HalVibration.java b/services/core/java/com/android/server/vibrator/HalVibration.java index fe0cf5909970..ea4bd0182ea3 100644 --- a/services/core/java/com/android/server/vibrator/HalVibration.java +++ b/services/core/java/com/android/server/vibrator/HalVibration.java @@ -51,37 +51,22 @@ final class HalVibration extends Vibration { @NonNull private volatile CombinedVibration mEffectToPlay; - /** Vibration status. */ - private Vibration.Status mStatus; - /** Reported scale values applied to the vibration effects. */ private int mScaleLevel; private float mAdaptiveScale; HalVibration(@NonNull IBinder token, @NonNull CombinedVibration effect, - @NonNull CallerInfo callerInfo) { + @NonNull VibrationSession.CallerInfo callerInfo) { super(token, callerInfo); mOriginalEffect = effect; mEffectToPlay = effect; - mStatus = Vibration.Status.RUNNING; mScaleLevel = VibrationScaler.SCALE_NONE; mAdaptiveScale = VibrationScaler.ADAPTIVE_SCALE_NONE; } - /** - * Set the {@link Status} of this vibration and reports the current system time as this - * vibration end time, for debugging purposes. - * - * <p>This method will only accept given value if the current status is {@link - * Status#RUNNING}. - */ - public void end(EndInfo info) { - if (hasEnded()) { - // Vibration already ended, keep first ending status set and ignore this one. - return; - } - mStatus = info.status; - stats.reportEnded(info.endedBy); + @Override + public void end(EndInfo endInfo) { + super.end(endInfo); mCompletionLatch.countDown(); } @@ -144,11 +129,6 @@ final class HalVibration extends Vibration { // No need to update fallback effects, they are already configured per device. } - /** Return true is current status is different from {@link Status#RUNNING}. */ - public boolean hasEnded() { - return mStatus != Status.RUNNING; - } - @Override public boolean isRepeating() { return mOriginalEffect.getDuration() == Long.MAX_VALUE; @@ -159,16 +139,16 @@ final class HalVibration extends Vibration { return mEffectToPlay; } - /** Return {@link Vibration.DebugInfo} with read-only debug information about this vibration. */ - public Vibration.DebugInfo getDebugInfo() { + @Override + public VibrationSession.DebugInfo getDebugInfo() { // Clear the original effect if it's the same as the effect that was played, for simplicity CombinedVibration originalEffect = Objects.equals(mOriginalEffect, mEffectToPlay) ? null : mOriginalEffect; - return new Vibration.DebugInfo(mStatus, stats, mEffectToPlay, originalEffect, + return new Vibration.DebugInfoImpl(getStatus(), stats, mEffectToPlay, originalEffect, mScaleLevel, mAdaptiveScale, callerInfo); } - /** Return {@link VibrationStats.StatsInfo} with read-only metrics about this vibration. */ + @Override public VibrationStats.StatsInfo getStatsInfo(long completionUptimeMillis) { int vibrationType = mEffectToPlay.hasVendorEffects() ? FrameworkStatsLog.VIBRATION_REPORTED__VIBRATION_TYPE__VENDOR @@ -176,7 +156,7 @@ final class HalVibration extends Vibration { ? FrameworkStatsLog.VIBRATION_REPORTED__VIBRATION_TYPE__REPEATED : FrameworkStatsLog.VIBRATION_REPORTED__VIBRATION_TYPE__SINGLE; return new VibrationStats.StatsInfo( - callerInfo.uid, vibrationType, callerInfo.attrs.getUsage(), mStatus, + callerInfo.uid, vibrationType, callerInfo.attrs.getUsage(), getStatus(), stats, completionUptimeMillis); } diff --git a/services/core/java/com/android/server/vibrator/InputDeviceDelegate.java b/services/core/java/com/android/server/vibrator/InputDeviceDelegate.java index 4e58b9a35243..83e05f4eff9f 100644 --- a/services/core/java/com/android/server/vibrator/InputDeviceDelegate.java +++ b/services/core/java/com/android/server/vibrator/InputDeviceDelegate.java @@ -26,6 +26,7 @@ import android.util.SparseArray; import android.view.InputDevice; import com.android.internal.annotations.GuardedBy; +import com.android.server.vibrator.VibrationSession.CallerInfo; /** Delegates vibrations to all connected {@link InputDevice} with one or more vibrators. */ final class InputDeviceDelegate implements InputManager.InputDeviceListener { @@ -93,7 +94,7 @@ final class InputDeviceDelegate implements InputManager.InputDeviceListener { * * @return {@link #isAvailable()} */ - public boolean vibrateIfAvailable(Vibration.CallerInfo callerInfo, CombinedVibration effect) { + public boolean vibrateIfAvailable(CallerInfo callerInfo, CombinedVibration effect) { synchronized (mLock) { for (int i = 0; i < mInputDeviceVibrators.size(); i++) { mInputDeviceVibrators.valueAt(i).vibrate(callerInfo.uid, callerInfo.opPkg, effect, diff --git a/services/core/java/com/android/server/vibrator/Vibration.java b/services/core/java/com/android/server/vibrator/Vibration.java index aa4b9f3e9d7d..9a0479337471 100644 --- a/services/core/java/com/android/server/vibrator/Vibration.java +++ b/services/core/java/com/android/server/vibrator/Vibration.java @@ -31,7 +31,6 @@ import android.os.vibrator.VibrationEffectSegment; import android.util.IndentingPrintWriter; import android.util.proto.ProtoOutputStream; -import java.io.PrintWriter; import java.time.Instant; import java.time.ZoneId; import java.time.format.DateTimeFormatter; @@ -52,131 +51,69 @@ abstract class Vibration { private static final AtomicInteger sNextVibrationId = new AtomicInteger(1); // 0 = no callback public final long id; - public final CallerInfo callerInfo; + public final VibrationSession.CallerInfo callerInfo; public final VibrationStats stats = new VibrationStats(); public final IBinder callerToken; - /** Vibration status with reference to values from vibratormanagerservice.proto for logging. */ - enum Status { - UNKNOWN(VibrationProto.UNKNOWN), - RUNNING(VibrationProto.RUNNING), - FINISHED(VibrationProto.FINISHED), - FINISHED_UNEXPECTED(VibrationProto.FINISHED_UNEXPECTED), - FORWARDED_TO_INPUT_DEVICES(VibrationProto.FORWARDED_TO_INPUT_DEVICES), - CANCELLED_BINDER_DIED(VibrationProto.CANCELLED_BINDER_DIED), - CANCELLED_BY_SCREEN_OFF(VibrationProto.CANCELLED_BY_SCREEN_OFF), - CANCELLED_BY_SETTINGS_UPDATE(VibrationProto.CANCELLED_BY_SETTINGS_UPDATE), - CANCELLED_BY_USER(VibrationProto.CANCELLED_BY_USER), - CANCELLED_BY_FOREGROUND_USER(VibrationProto.CANCELLED_BY_FOREGROUND_USER), - CANCELLED_BY_UNKNOWN_REASON(VibrationProto.CANCELLED_BY_UNKNOWN_REASON), - CANCELLED_SUPERSEDED(VibrationProto.CANCELLED_SUPERSEDED), - CANCELLED_BY_APP_OPS(VibrationProto.CANCELLED_BY_APP_OPS), - IGNORED_ERROR_APP_OPS(VibrationProto.IGNORED_ERROR_APP_OPS), - IGNORED_ERROR_CANCELLING(VibrationProto.IGNORED_ERROR_CANCELLING), - IGNORED_ERROR_SCHEDULING(VibrationProto.IGNORED_ERROR_SCHEDULING), - IGNORED_ERROR_TOKEN(VibrationProto.IGNORED_ERROR_TOKEN), - IGNORED_APP_OPS(VibrationProto.IGNORED_APP_OPS), - IGNORED_BACKGROUND(VibrationProto.IGNORED_BACKGROUND), - IGNORED_MISSING_PERMISSION(VibrationProto.IGNORED_MISSING_PERMISSION), - IGNORED_UNSUPPORTED(VibrationProto.IGNORED_UNSUPPORTED), - IGNORED_FOR_EXTERNAL(VibrationProto.IGNORED_FOR_EXTERNAL), - IGNORED_FOR_HIGHER_IMPORTANCE(VibrationProto.IGNORED_FOR_HIGHER_IMPORTANCE), - IGNORED_FOR_ONGOING(VibrationProto.IGNORED_FOR_ONGOING), - IGNORED_FOR_POWER(VibrationProto.IGNORED_FOR_POWER), - IGNORED_FOR_RINGER_MODE(VibrationProto.IGNORED_FOR_RINGER_MODE), - IGNORED_FOR_SETTINGS(VibrationProto.IGNORED_FOR_SETTINGS), - IGNORED_SUPERSEDED(VibrationProto.IGNORED_SUPERSEDED), - IGNORED_FROM_VIRTUAL_DEVICE(VibrationProto.IGNORED_FROM_VIRTUAL_DEVICE), - IGNORED_ON_WIRELESS_CHARGER(VibrationProto.IGNORED_ON_WIRELESS_CHARGER); - - private final int mProtoEnumValue; - - Status(int value) { - mProtoEnumValue = value; - } - - public int getProtoEnumValue() { - return mProtoEnumValue; - } - } + private VibrationSession.Status mStatus; - Vibration(@NonNull IBinder token, @NonNull CallerInfo callerInfo) { + Vibration(@NonNull IBinder token, @NonNull VibrationSession.CallerInfo callerInfo) { Objects.requireNonNull(token); Objects.requireNonNull(callerInfo); + mStatus = VibrationSession.Status.RUNNING; this.id = sNextVibrationId.getAndIncrement(); this.callerToken = token; this.callerInfo = callerInfo; } - /** Return true if vibration is a repeating vibration. */ - abstract boolean isRepeating(); + VibrationSession.Status getStatus() { + return mStatus; + } + + /** Return true is current status is different from {@link VibrationSession.Status#RUNNING}. */ + boolean hasEnded() { + return mStatus != VibrationSession.Status.RUNNING; + } /** - * Holds lightweight immutable info on the process that triggered the vibration. This data - * could potentially be kept in memory for a long time for bugreport dumpsys operations. + * Set the {@link VibrationSession} of this vibration and reports the current system time as + * this vibration end time, for debugging purposes. * - * Since CallerInfo can be kept in memory for a long time, it shouldn't hold any references to - * potentially expensive or resource-linked objects, such as {@link IBinder}. + * <p>This method will only accept given value if the current status is {@link + * VibrationSession.Status#RUNNING}. */ - static final class CallerInfo { - public final VibrationAttributes attrs; - public final int uid; - public final int deviceId; - public final String opPkg; - public final String reason; - - CallerInfo(@NonNull VibrationAttributes attrs, int uid, int deviceId, String opPkg, - String reason) { - Objects.requireNonNull(attrs); - this.attrs = attrs; - this.uid = uid; - this.deviceId = deviceId; - this.opPkg = opPkg; - this.reason = reason; + void end(Vibration.EndInfo endInfo) { + if (hasEnded()) { + // Vibration already ended, keep first ending status set and ignore this one. + return; } + mStatus = endInfo.status; + stats.reportEnded(endInfo.endedBy); + } - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (!(o instanceof CallerInfo)) return false; - CallerInfo that = (CallerInfo) o; - return Objects.equals(attrs, that.attrs) - && uid == that.uid - && deviceId == that.deviceId - && Objects.equals(opPkg, that.opPkg) - && Objects.equals(reason, that.reason); - } + /** Return true if vibration is a repeating vibration. */ + abstract boolean isRepeating(); - @Override - public int hashCode() { - return Objects.hash(attrs, uid, deviceId, opPkg, reason); - } + /** Return {@link VibrationSession.DebugInfo} with read-only debug data about this vibration. */ + abstract VibrationSession.DebugInfo getDebugInfo(); - @Override - public String toString() { - return "CallerInfo{" - + " uid=" + uid - + ", opPkg=" + opPkg - + ", deviceId=" + deviceId - + ", attrs=" + attrs - + ", reason=" + reason - + '}'; - } - } + /** Return {@link VibrationStats.StatsInfo} with read-only metrics about this vibration. */ + abstract VibrationStats.StatsInfo getStatsInfo(long completionUptimeMillis); /** Immutable info passed as a signal to end a vibration. */ static final class EndInfo { - /** The {@link Status} to be set to the vibration when it ends with this info. */ + /** The vibration status to be set when it ends with this info. */ @NonNull - public final Status status; + public final VibrationSession.Status status; /** Info about the process that ended the vibration. */ - public final CallerInfo endedBy; + public final VibrationSession.CallerInfo endedBy; - EndInfo(@NonNull Vibration.Status status) { + EndInfo(@NonNull VibrationSession.Status status) { this(status, null); } - EndInfo(@NonNull Vibration.Status status, @Nullable CallerInfo endedBy) { + EndInfo(@NonNull VibrationSession.Status status, + @Nullable VibrationSession.CallerInfo endedBy) { this.status = status; this.endedBy = endedBy; } @@ -211,10 +148,10 @@ abstract class Vibration { * Since DebugInfo can be kept in memory for a long time, it shouldn't hold any references to * potentially expensive or resource-linked objects, such as {@link IBinder}. */ - static final class DebugInfo { - final Status mStatus; + static final class DebugInfoImpl implements VibrationSession.DebugInfo { + final VibrationSession.Status mStatus; final long mCreateTime; - final CallerInfo mCallerInfo; + final VibrationSession.CallerInfo mCallerInfo; @Nullable final CombinedVibration mPlayedEffect; @@ -226,9 +163,10 @@ abstract class Vibration { private final int mScaleLevel; private final float mAdaptiveScale; - DebugInfo(Status status, VibrationStats stats, @Nullable CombinedVibration playedEffect, + DebugInfoImpl(VibrationSession.Status status, VibrationStats stats, + @Nullable CombinedVibration playedEffect, @Nullable CombinedVibration originalEffect, int scaleLevel, - float adaptiveScale, @NonNull CallerInfo callerInfo) { + float adaptiveScale, @NonNull VibrationSession.CallerInfo callerInfo) { Objects.requireNonNull(callerInfo); mCreateTime = stats.getCreateTimeDebug(); mStartTime = stats.getStartTimeDebug(); @@ -243,6 +181,27 @@ abstract class Vibration { } @Override + public VibrationSession.Status getStatus() { + return mStatus; + } + + @Override + public long getCreateUptimeMillis() { + return mCreateTime; + } + + @Override + public VibrationSession.CallerInfo getCallerInfo() { + return mCallerInfo; + } + + @Nullable + @Override + public Object getDumpAggregationKey() { + return mPlayedEffect; + } + + @Override public String toString() { return "createTime: " + formatTime(mCreateTime, /*includeDate=*/ true) + ", startTime: " + formatTime(mStartTime, /*includeDate=*/ true) @@ -257,17 +216,13 @@ abstract class Vibration { + ", callerInfo: " + mCallerInfo; } - void logMetrics(VibratorFrameworkStatsLogger statsLogger) { + @Override + public void logMetrics(VibratorFrameworkStatsLogger statsLogger) { statsLogger.logVibrationAdaptiveHapticScale(mCallerInfo.uid, mAdaptiveScale); } - /** - * Write this info in a compact way into given {@link PrintWriter}. - * - * <p>This is used by dumpsys to log multiple vibration records in single lines that are - * easy to skim through by the sorted created time. - */ - void dumpCompact(IndentingPrintWriter pw) { + @Override + public void dumpCompact(IndentingPrintWriter pw) { boolean isExternalVibration = mPlayedEffect == null; String timingsStr = String.format(Locale.ROOT, "%s | %8s | %20s | duration: %5dms | start: %12s | end: %12s", @@ -299,8 +254,8 @@ abstract class Vibration { pw.println(timingsStr + paramStr + audioUsageStr + callerStr + effectStr); } - /** Write this info into given {@link PrintWriter}. */ - void dump(IndentingPrintWriter pw) { + @Override + public void dump(IndentingPrintWriter pw) { pw.println("Vibration:"); pw.increaseIndent(); pw.println("status = " + mStatus.name().toLowerCase(Locale.ROOT)); @@ -317,8 +272,8 @@ abstract class Vibration { pw.decreaseIndent(); } - /** Write this info into given {@code fieldId} on {@link ProtoOutputStream}. */ - void dump(ProtoOutputStream proto, long fieldId) { + @Override + public void dump(ProtoOutputStream proto, long fieldId) { final long token = proto.start(fieldId); proto.write(VibrationProto.START_TIME, mStartTime); proto.write(VibrationProto.END_TIME, mEndTime); diff --git a/services/core/java/com/android/server/vibrator/VibrationSession.java b/services/core/java/com/android/server/vibrator/VibrationSession.java new file mode 100644 index 000000000000..5640b49b28d8 --- /dev/null +++ b/services/core/java/com/android/server/vibrator/VibrationSession.java @@ -0,0 +1,208 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.vibrator; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.os.CombinedVibration; +import android.os.IBinder; +import android.os.VibrationAttributes; +import android.util.IndentingPrintWriter; +import android.util.proto.ProtoOutputStream; + +import java.io.PrintWriter; +import java.util.Objects; + +/** + * Represents a generic vibration session that plays one or more vibration requests. + * + * <p>This might represent: + * + * <ol> + * <li>A single {@link CombinedVibration} playback. + * <li>An {@link android.os.ExternalVibration} playback. + * </ol> + */ +interface VibrationSession { + + /** Returns data about the client app that triggered this vibration session. */ + CallerInfo getCallerInfo(); + + /** Returns debug data for logging and metric reports. */ + DebugInfo getDebugInfo(); + + /** + * Links this session to the app process death with given callback to handle it. + * + * <p>This can be used by the service to end the vibration session when the app process dies. + */ + void linkToDeath(Runnable callback); + + /** Removes link to the app process death. */ + void unlinkToDeath(); + + /** Notify the session end was requested, which might be acted upon asynchronously. */ + void notifyEnded(); + + /** + * Session status with reference to values from vibratormanagerservice.proto for logging. + */ + enum Status { + UNKNOWN(VibrationProto.UNKNOWN), + RUNNING(VibrationProto.RUNNING), + FINISHED(VibrationProto.FINISHED), + FINISHED_UNEXPECTED(VibrationProto.FINISHED_UNEXPECTED), + FORWARDED_TO_INPUT_DEVICES(VibrationProto.FORWARDED_TO_INPUT_DEVICES), + CANCELLED_BINDER_DIED(VibrationProto.CANCELLED_BINDER_DIED), + CANCELLED_BY_SCREEN_OFF(VibrationProto.CANCELLED_BY_SCREEN_OFF), + CANCELLED_BY_SETTINGS_UPDATE(VibrationProto.CANCELLED_BY_SETTINGS_UPDATE), + CANCELLED_BY_USER(VibrationProto.CANCELLED_BY_USER), + CANCELLED_BY_FOREGROUND_USER(VibrationProto.CANCELLED_BY_FOREGROUND_USER), + CANCELLED_BY_UNKNOWN_REASON(VibrationProto.CANCELLED_BY_UNKNOWN_REASON), + CANCELLED_SUPERSEDED(VibrationProto.CANCELLED_SUPERSEDED), + CANCELLED_BY_APP_OPS(VibrationProto.CANCELLED_BY_APP_OPS), + IGNORED_ERROR_APP_OPS(VibrationProto.IGNORED_ERROR_APP_OPS), + IGNORED_ERROR_CANCELLING(VibrationProto.IGNORED_ERROR_CANCELLING), + IGNORED_ERROR_SCHEDULING(VibrationProto.IGNORED_ERROR_SCHEDULING), + IGNORED_ERROR_TOKEN(VibrationProto.IGNORED_ERROR_TOKEN), + IGNORED_APP_OPS(VibrationProto.IGNORED_APP_OPS), + IGNORED_BACKGROUND(VibrationProto.IGNORED_BACKGROUND), + IGNORED_MISSING_PERMISSION(VibrationProto.IGNORED_MISSING_PERMISSION), + IGNORED_UNSUPPORTED(VibrationProto.IGNORED_UNSUPPORTED), + IGNORED_FOR_EXTERNAL(VibrationProto.IGNORED_FOR_EXTERNAL), + IGNORED_FOR_HIGHER_IMPORTANCE(VibrationProto.IGNORED_FOR_HIGHER_IMPORTANCE), + IGNORED_FOR_ONGOING(VibrationProto.IGNORED_FOR_ONGOING), + IGNORED_FOR_POWER(VibrationProto.IGNORED_FOR_POWER), + IGNORED_FOR_RINGER_MODE(VibrationProto.IGNORED_FOR_RINGER_MODE), + IGNORED_FOR_SETTINGS(VibrationProto.IGNORED_FOR_SETTINGS), + IGNORED_SUPERSEDED(VibrationProto.IGNORED_SUPERSEDED), + IGNORED_FROM_VIRTUAL_DEVICE(VibrationProto.IGNORED_FROM_VIRTUAL_DEVICE), + IGNORED_ON_WIRELESS_CHARGER(VibrationProto.IGNORED_ON_WIRELESS_CHARGER); + + private final int mProtoEnumValue; + + Status(int value) { + mProtoEnumValue = value; + } + + public int getProtoEnumValue() { + return mProtoEnumValue; + } + } + + /** + * Holds lightweight immutable info on the process that triggered the vibration session. + * + * <p>This data could potentially be kept in memory for a long time for bugreport dumpsys + * operations. It shouldn't hold any references to potentially expensive or resource-linked + * objects, such as {@link IBinder}. + */ + final class CallerInfo { + public final VibrationAttributes attrs; + public final int uid; + public final int deviceId; + public final String opPkg; + public final String reason; + + CallerInfo(@NonNull VibrationAttributes attrs, int uid, int deviceId, String opPkg, + String reason) { + Objects.requireNonNull(attrs); + this.attrs = attrs; + this.uid = uid; + this.deviceId = deviceId; + this.opPkg = opPkg; + this.reason = reason; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof CallerInfo)) return false; + CallerInfo that = (CallerInfo) o; + return Objects.equals(attrs, that.attrs) + && uid == that.uid + && deviceId == that.deviceId + && Objects.equals(opPkg, that.opPkg) + && Objects.equals(reason, that.reason); + } + + @Override + public int hashCode() { + return Objects.hash(attrs, uid, deviceId, opPkg, reason); + } + + @Override + public String toString() { + return "CallerInfo{" + + " uid=" + uid + + ", opPkg=" + opPkg + + ", deviceId=" + deviceId + + ", attrs=" + attrs + + ", reason=" + reason + + '}'; + } + } + + /** + * Interface for lightweight debug information about the vibration session for debugging. + * + * <p>This data could potentially be kept in memory for a long time for bugreport dumpsys + * operations. It shouldn't hold any references to potentially expensive or resource-linked + * objects, such as {@link IBinder}. + */ + interface DebugInfo { + + /** Return the vibration session status. */ + Status getStatus(); + + /** Returns the session creation time from {@link android.os.SystemClock#uptimeMillis()}. */ + long getCreateUptimeMillis(); + + /** Returns information about the process that created the session. */ + CallerInfo getCallerInfo(); + + /** + * Returns the aggregation key for log records. + * + * <p>This is used to aggregate similar vibration sessions triggered in quick succession + * (e.g. multiple keyboard vibrations when the user is typing). + * + * <p>This does not need to include data from {@link CallerInfo} or {@link Status}. + * + * @see GroupedAggregatedLogRecords + */ + @Nullable + Object getDumpAggregationKey(); + + /** Logs vibration session fields for metric reports. */ + void logMetrics(VibratorFrameworkStatsLogger statsLogger); + + /** Write this info into given {@code fieldId} on {@link ProtoOutputStream}. */ + void dump(ProtoOutputStream proto, long fieldId); + + /** Write this info into given {@link PrintWriter}. */ + void dump(IndentingPrintWriter pw); + + /** + * Write this info in a compact way into given {@link PrintWriter}. + * + * <p>This is used by dumpsys to log multiple records in single lines that are easy to skim + * through by the sorted created time. + */ + void dumpCompact(IndentingPrintWriter pw); + } +} diff --git a/services/core/java/com/android/server/vibrator/VibrationSettings.java b/services/core/java/com/android/server/vibrator/VibrationSettings.java index 0d6778c18759..69cdcf47680d 100644 --- a/services/core/java/com/android/server/vibrator/VibrationSettings.java +++ b/services/core/java/com/android/server/vibrator/VibrationSettings.java @@ -66,6 +66,8 @@ import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.server.LocalServices; import com.android.server.companion.virtual.VirtualDeviceManagerInternal; +import com.android.server.vibrator.VibrationSession.CallerInfo; +import com.android.server.vibrator.VibrationSession.Status; import java.io.PrintWriter; import java.util.ArrayList; @@ -416,46 +418,46 @@ final class VibrationSettings { /** * Check if given vibration should be ignored by the service. * - * @return One of Vibration.Status.IGNORED_* values if the vibration should be ignored, + * @return One of VibrationSession.Status.IGNORED_* values if the vibration should be ignored, * null otherwise. */ @Nullable - public Vibration.Status shouldIgnoreVibration(@NonNull Vibration.CallerInfo callerInfo) { + public Status shouldIgnoreVibration(@NonNull CallerInfo callerInfo) { final int usage = callerInfo.attrs.getUsage(); synchronized (mLock) { if (!mUidObserver.isUidForeground(callerInfo.uid) && !BACKGROUND_PROCESS_USAGE_ALLOWLIST.contains(usage)) { - return Vibration.Status.IGNORED_BACKGROUND; + return Status.IGNORED_BACKGROUND; } if (callerInfo.deviceId != Context.DEVICE_ID_DEFAULT && callerInfo.deviceId != Context.DEVICE_ID_INVALID) { - return Vibration.Status.IGNORED_FROM_VIRTUAL_DEVICE; + return Status.IGNORED_FROM_VIRTUAL_DEVICE; } if (callerInfo.deviceId == Context.DEVICE_ID_INVALID && isAppRunningOnAnyVirtualDevice(callerInfo.uid)) { - return Vibration.Status.IGNORED_FROM_VIRTUAL_DEVICE; + return Status.IGNORED_FROM_VIRTUAL_DEVICE; } if (mBatterySaverMode && !BATTERY_SAVER_USAGE_ALLOWLIST.contains(usage)) { - return Vibration.Status.IGNORED_FOR_POWER; + return Status.IGNORED_FOR_POWER; } if (!callerInfo.attrs.isFlagSet( VibrationAttributes.FLAG_BYPASS_USER_VIBRATION_INTENSITY_OFF) && !shouldVibrateForUserSetting(callerInfo)) { - return Vibration.Status.IGNORED_FOR_SETTINGS; + return Status.IGNORED_FOR_SETTINGS; } if (!callerInfo.attrs.isFlagSet(VibrationAttributes.FLAG_BYPASS_INTERRUPTION_POLICY)) { if (!shouldVibrateForRingerModeLocked(usage)) { - return Vibration.Status.IGNORED_FOR_RINGER_MODE; + return Status.IGNORED_FOR_RINGER_MODE; } } if (mVibrationConfig.ignoreVibrationsOnWirelessCharger() && mOnWirelessCharger) { - return Vibration.Status.IGNORED_ON_WIRELESS_CHARGER; + return Status.IGNORED_ON_WIRELESS_CHARGER; } } return null; @@ -471,7 +473,7 @@ final class VibrationSettings { * * @return true if the vibration should be cancelled when the screen goes off, false otherwise. */ - public boolean shouldCancelVibrationOnScreenOff(@NonNull Vibration.CallerInfo callerInfo, + public boolean shouldCancelVibrationOnScreenOff(@NonNull CallerInfo callerInfo, long vibrationStartUptimeMillis) { PowerManagerInternal pm; synchronized (mLock) { @@ -483,8 +485,8 @@ final class VibrationSettings { // ignored here and not cancel a vibration, and those are usually triggered by timeout // or inactivity, so it's unlikely that it will override a more active goToSleep reason. PowerManager.SleepData sleepData = pm.getLastGoToSleep(); - if ((sleepData.goToSleepUptimeMillis < vibrationStartUptimeMillis) - || POWER_MANAGER_SLEEP_REASON_ALLOWLIST.contains(sleepData.goToSleepReason)) { + if (sleepData != null && (sleepData.goToSleepUptimeMillis < vibrationStartUptimeMillis + || POWER_MANAGER_SLEEP_REASON_ALLOWLIST.contains(sleepData.goToSleepReason))) { // Ignore screen off events triggered before the vibration started, and all // automatic "go to sleep" events from allowlist. Slog.d(TAG, "Ignoring screen off event triggered at uptime " @@ -522,7 +524,7 @@ final class VibrationSettings { * {@code false} to ignore the vibration. */ @GuardedBy("mLock") - private boolean shouldVibrateForUserSetting(Vibration.CallerInfo callerInfo) { + private boolean shouldVibrateForUserSetting(CallerInfo callerInfo) { final int usage = callerInfo.attrs.getUsage(); if (!mVibrateOn && (VIBRATE_ON_DISABLED_USAGE_ALLOWED != usage)) { // Main setting disabled. diff --git a/services/core/java/com/android/server/vibrator/VibrationStats.java b/services/core/java/com/android/server/vibrator/VibrationStats.java index 8179d6aea9ca..fc0c6e7bf05e 100644 --- a/services/core/java/com/android/server/vibrator/VibrationStats.java +++ b/services/core/java/com/android/server/vibrator/VibrationStats.java @@ -166,7 +166,7 @@ final class VibrationStats { * @return true if the status was accepted. This method will only accept given values if * the end timestamp was never set. */ - boolean reportEnded(@Nullable Vibration.CallerInfo endedBy) { + boolean reportEnded(@Nullable VibrationSession.CallerInfo endedBy) { if (hasEnded()) { // Vibration already ended, keep first ending stats set and ignore this one. return false; @@ -187,7 +187,7 @@ final class VibrationStats { * <p>This method will only accept the first value as the one that was interrupted by this * vibration, and will ignore all successive calls. */ - void reportInterruptedAnotherVibration(@NonNull Vibration.CallerInfo callerInfo) { + void reportInterruptedAnotherVibration(@NonNull VibrationSession.CallerInfo callerInfo) { if (mInterruptedUsage < 0) { mInterruptedUsage = callerInfo.attrs.getUsage(); } @@ -330,7 +330,7 @@ final class VibrationStats { public final int[] halUnsupportedEffectsUsed; private boolean mIsWritten; - StatsInfo(int uid, int vibrationType, int usage, Vibration.Status status, + StatsInfo(int uid, int vibrationType, int usage, VibrationSession.Status status, VibrationStats stats, long completionUptimeMillis) { this.uid = uid; this.vibrationType = vibrationType; diff --git a/services/core/java/com/android/server/vibrator/VibrationStepConductor.java b/services/core/java/com/android/server/vibrator/VibrationStepConductor.java index 7152844cc772..5137d1938332 100644 --- a/services/core/java/com/android/server/vibrator/VibrationStepConductor.java +++ b/services/core/java/com/android/server/vibrator/VibrationStepConductor.java @@ -32,6 +32,7 @@ import android.util.Slog; import android.util.SparseArray; import com.android.internal.annotations.GuardedBy; +import com.android.server.vibrator.VibrationSession.Status; import java.util.ArrayList; import java.util.Iterator; @@ -217,7 +218,7 @@ final class VibrationStepConductor implements IBinder.DeathRecipient { } /** - * Calculate the {@link Vibration.Status} based on the current queue state and the expected + * Calculate the {@link Vibration.EndInfo} based on the current queue state and the expected * number of {@link StartSequentialEffectStep} to be played. */ @Nullable @@ -235,10 +236,10 @@ final class VibrationStepConductor implements IBinder.DeathRecipient { } // No pending steps, and something happened. if (mSuccessfulVibratorOnSteps > 0) { - return new Vibration.EndInfo(Vibration.Status.FINISHED); + return new Vibration.EndInfo(Status.FINISHED); } // If no step was able to turn the vibrator ON successfully. - return new Vibration.EndInfo(Vibration.Status.IGNORED_UNSUPPORTED); + return new Vibration.EndInfo(Status.IGNORED_UNSUPPORTED); } /** @@ -352,7 +353,7 @@ final class VibrationStepConductor implements IBinder.DeathRecipient { if (DEBUG) { Slog.d(TAG, "Binder died, cancelling vibration..."); } - notifyCancelled(new Vibration.EndInfo(Vibration.Status.CANCELLED_BINDER_DIED), + notifyCancelled(new Vibration.EndInfo(Status.CANCELLED_BINDER_DIED), /* immediate= */ false); } @@ -377,7 +378,7 @@ final class VibrationStepConductor implements IBinder.DeathRecipient { if ((cancelInfo == null) || !cancelInfo.status.name().startsWith("CANCEL")) { Slog.w(TAG, "Vibration cancel requested with bad signal=" + cancelInfo + ", using CANCELLED_UNKNOWN_REASON to ensure cancellation."); - cancelInfo = new Vibration.EndInfo(Vibration.Status.CANCELLED_BY_UNKNOWN_REASON); + cancelInfo = new Vibration.EndInfo(Status.CANCELLED_BY_UNKNOWN_REASON); } synchronized (mLock) { if ((immediate && mSignalCancelImmediate) || (mSignalCancel != null)) { diff --git a/services/core/java/com/android/server/vibrator/VibrationThread.java b/services/core/java/com/android/server/vibrator/VibrationThread.java index cfb4c74fcbfc..ab4a4d8fc08d 100644 --- a/services/core/java/com/android/server/vibrator/VibrationThread.java +++ b/services/core/java/com/android/server/vibrator/VibrationThread.java @@ -29,6 +29,7 @@ import android.util.Slog; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; +import com.android.server.vibrator.VibrationSession.Status; import java.util.NoSuchElementException; import java.util.Objects; @@ -240,7 +241,7 @@ final class VibrationThread extends Thread { runCurrentVibrationWithWakeLockAndDeathLink(); } finally { clientVibrationCompleteIfNotAlready( - new Vibration.EndInfo(Vibration.Status.FINISHED_UNEXPECTED)); + new Vibration.EndInfo(Status.FINISHED_UNEXPECTED)); } } finally { mWakeLock.release(); @@ -259,7 +260,7 @@ final class VibrationThread extends Thread { } catch (RemoteException e) { Slog.e(TAG, "Error linking vibration to token death", e); clientVibrationCompleteIfNotAlready( - new Vibration.EndInfo(Vibration.Status.IGNORED_ERROR_TOKEN)); + new Vibration.EndInfo(Status.IGNORED_ERROR_TOKEN)); return; } // Ensure that the unlink always occurs now. diff --git a/services/core/java/com/android/server/vibrator/VibratorControlService.java b/services/core/java/com/android/server/vibrator/VibratorControlService.java index de5e662f2a71..3a814cd80836 100644 --- a/services/core/java/com/android/server/vibrator/VibratorControlService.java +++ b/services/core/java/com/android/server/vibrator/VibratorControlService.java @@ -559,8 +559,8 @@ final class VibratorControlService extends IVibratorControlService.Stub { } /** - * Record for a single {@link Vibration.DebugInfo}, that can be grouped by usage and aggregated - * by UID, {@link VibrationAttributes} and {@link VibrationEffect}. + * Record for a single {@link VibrationSession.DebugInfo}, that can be grouped by usage and + * aggregated by UID, {@link VibrationAttributes} and {@link VibrationEffect}. */ private static final class VibrationScaleParamRecord implements GroupedAggregatedLogRecords.SingleLogRecord { diff --git a/services/core/java/com/android/server/vibrator/VibratorManagerService.java b/services/core/java/com/android/server/vibrator/VibratorManagerService.java index c143bebdd4a5..799934af54c0 100644 --- a/services/core/java/com/android/server/vibrator/VibratorManagerService.java +++ b/services/core/java/com/android/server/vibrator/VibratorManagerService.java @@ -73,9 +73,11 @@ import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.app.IBatteryStats; import com.android.internal.util.DumpUtils; -import com.android.internal.util.FrameworkStatsLog; import com.android.server.SystemService; import com.android.server.pm.BackgroundUserSoundNotifier; +import com.android.server.vibrator.VibrationSession.CallerInfo; +import com.android.server.vibrator.VibrationSession.DebugInfo; +import com.android.server.vibrator.VibrationSession.Status; import libcore.util.NativeAllocationRegistry; @@ -160,11 +162,12 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { @GuardedBy("mLock") private VibrationStepConductor mNextVibration; @GuardedBy("mLock") - private ExternalVibrationHolder mCurrentExternalVibration; + private ExternalVibrationSession mCurrentExternalVibration; @GuardedBy("mLock") private boolean mServiceReady; - private final VibrationSettings mVibrationSettings; + @VisibleForTesting + final VibrationSettings mVibrationSettings; private final VibrationScaler mVibrationScaler; private final VibratorControlService mVibratorControlService; private final InputDeviceDelegate mInputDeviceDelegate; @@ -184,13 +187,12 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { // When the system is entering a non-interactive state, we want to cancel // vibrations in case a misbehaving app has abandoned them. if (shouldCancelOnScreenOffLocked(mNextVibration)) { - clearNextVibrationLocked( - new Vibration.EndInfo(Vibration.Status.CANCELLED_BY_SCREEN_OFF)); + clearNextVibrationLocked(new Vibration.EndInfo( + Status.CANCELLED_BY_SCREEN_OFF)); } if (shouldCancelOnScreenOffLocked(mCurrentVibration)) { - mCurrentVibration.notifyCancelled( - new Vibration.EndInfo(Vibration.Status.CANCELLED_BY_SCREEN_OFF), - /* immediate= */ false); + mCurrentVibration.notifyCancelled(new Vibration.EndInfo( + Status.CANCELLED_BY_SCREEN_OFF), /* immediate= */ false); } } } else if (android.multiuser.Flags.addUiForSoundsFromBackgroundUsers() @@ -198,12 +200,11 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { synchronized (mLock) { if (shouldCancelOnFgUserRequest(mNextVibration)) { clearNextVibrationLocked(new Vibration.EndInfo( - Vibration.Status.CANCELLED_BY_FOREGROUND_USER)); + Status.CANCELLED_BY_FOREGROUND_USER)); } if (shouldCancelOnFgUserRequest(mCurrentVibration)) { mCurrentVibration.notifyCancelled(new Vibration.EndInfo( - Vibration.Status.CANCELLED_BY_FOREGROUND_USER), - /* immediate= */ false); + Status.CANCELLED_BY_FOREGROUND_USER), /* immediate= */ false); } } } @@ -220,12 +221,12 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { } synchronized (mLock) { if (shouldCancelAppOpModeChangedLocked(mNextVibration)) { - clearNextVibrationLocked( - new Vibration.EndInfo(Vibration.Status.CANCELLED_BY_APP_OPS)); + clearNextVibrationLocked(new Vibration.EndInfo( + Status.CANCELLED_BY_APP_OPS)); } if (shouldCancelAppOpModeChangedLocked(mCurrentVibration)) { mCurrentVibration.notifyCancelled(new Vibration.EndInfo( - Vibration.Status.CANCELLED_BY_APP_OPS), /* immediate= */ false); + Status.CANCELLED_BY_APP_OPS), /* immediate= */ false); } } } @@ -441,8 +442,8 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { return false; } AlwaysOnVibration alwaysOnVibration = new AlwaysOnVibration(alwaysOnId, - new Vibration.CallerInfo(attrs, uid, Context.DEVICE_ID_DEFAULT, opPkg, - null), effects); + new CallerInfo(attrs, uid, Context.DEVICE_ID_DEFAULT, opPkg, null), + effects); mAlwaysOnEffects.put(alwaysOnId, alwaysOnVibration); updateAlwaysOnLocked(alwaysOnVibration); } @@ -490,8 +491,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { // Make sure we report the constant id in the requested haptic feedback reason. reason = "performHapticFeedback(constant=" + constant + "): " + reason; HapticFeedbackVibrationProvider hapticVibrationProvider = getHapticVibrationProvider(); - Vibration.Status ignoreStatus = shouldIgnoreHapticFeedback(constant, reason, - hapticVibrationProvider); + Status ignoreStatus = shouldIgnoreHapticFeedback(constant, reason, hapticVibrationProvider); if (ignoreStatus != null) { logAndRecordPerformHapticFeedbackAttempt(uid, deviceId, opPkg, reason, ignoreStatus); return null; @@ -516,8 +516,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { reason = "performHapticFeedbackForInputDevice(constant=" + constant + ", inputDeviceId=" + inputDeviceId + ", inputSource=" + inputSource + "): " + reason; HapticFeedbackVibrationProvider hapticVibrationProvider = getHapticVibrationProvider(); - Vibration.Status ignoreStatus = shouldIgnoreHapticFeedback(constant, reason, - hapticVibrationProvider); + Status ignoreStatus = shouldIgnoreHapticFeedback(constant, reason, hapticVibrationProvider); if (ignoreStatus != null) { logAndRecordPerformHapticFeedbackAttempt(uid, deviceId, opPkg, reason, ignoreStatus); return null; @@ -533,7 +532,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { VibrationAttributes attrs) { if (effect == null) { logAndRecordPerformHapticFeedbackAttempt(uid, deviceId, opPkg, reason, - Vibration.Status.IGNORED_UNSUPPORTED); + Status.IGNORED_UNSUPPORTED); Slog.w(TAG, "performHapticFeedbackWithEffect; vibration absent for constant " + constant); return null; @@ -578,23 +577,21 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { private HalVibration vibrateInternal(int uid, int deviceId, String opPkg, @NonNull CombinedVibration effect, @NonNull VibrationAttributes attrs, String reason, IBinder token) { - Vibration.CallerInfo callerInfo = - new Vibration.CallerInfo(attrs, uid, deviceId, opPkg, reason); + CallerInfo callerInfo = new CallerInfo(attrs, uid, deviceId, opPkg, reason); if (token == null) { Slog.e(TAG, "token must not be null"); - logAndRecordVibrationAttempt(effect, callerInfo, Vibration.Status.IGNORED_ERROR_TOKEN); + logAndRecordVibrationAttempt(effect, callerInfo, Status.IGNORED_ERROR_TOKEN); return null; } if (effect.hasVendorEffects() && !hasPermission(android.Manifest.permission.VIBRATE_VENDOR_EFFECTS)) { Slog.e(TAG, "vibrate; no permission for vendor effects"); - logAndRecordVibrationAttempt(effect, callerInfo, - Vibration.Status.IGNORED_MISSING_PERMISSION); + logAndRecordVibrationAttempt(effect, callerInfo, Status.IGNORED_MISSING_PERMISSION); return null; } enforceUpdateAppOpsStatsPermission(uid); if (!isEffectValid(effect)) { - logAndRecordVibrationAttempt(effect, callerInfo, Vibration.Status.IGNORED_UNSUPPORTED); + logAndRecordVibrationAttempt(effect, callerInfo, Status.IGNORED_UNSUPPORTED); return null; } // Create Vibration.Stats as close to the received request as possible, for tracking. @@ -625,11 +622,11 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { final long ident = Binder.clearCallingIdentity(); try { if (mCurrentExternalVibration != null) { - mCurrentExternalVibration.mute(); + mCurrentExternalVibration.notifyEnded(); vib.stats.reportInterruptedAnotherVibration( mCurrentExternalVibration.callerInfo); endExternalVibrateLocked( - new Vibration.EndInfo(Vibration.Status.CANCELLED_SUPERSEDED, + new Vibration.EndInfo(Status.CANCELLED_SUPERSEDED, vib.callerInfo), /* continueExternalControl= */ false); } else if (mCurrentVibration != null) { @@ -645,7 +642,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { vib.stats.reportInterruptedAnotherVibration( mCurrentVibration.getVibration().callerInfo); mCurrentVibration.notifyCancelled( - new Vibration.EndInfo(Vibration.Status.CANCELLED_SUPERSEDED, + new Vibration.EndInfo(Status.CANCELLED_SUPERSEDED, vib.callerInfo), /* immediate= */ false); } @@ -677,7 +674,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { Slog.d(TAG, "Canceling vibration"); } Vibration.EndInfo cancelledByUserInfo = - new Vibration.EndInfo(Vibration.Status.CANCELLED_BY_USER); + new Vibration.EndInfo(Status.CANCELLED_BY_USER); final long ident = Binder.clearCallingIdentity(); try { if (mNextVibration != null @@ -693,9 +690,9 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { } if (mCurrentExternalVibration != null && shouldCancelVibration( - mCurrentExternalVibration.externalVibration.getVibrationAttributes(), + mCurrentExternalVibration.getCallerInfo().attrs, usageFilter)) { - mCurrentExternalVibration.mute(); + mCurrentExternalVibration.notifyEnded(); endExternalVibrateLocked( cancelledByUserInfo, /* continueExternalControl= */ false); } @@ -860,7 +857,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { : vibrationEndInfo.status)); } mCurrentVibration.notifyCancelled( - new Vibration.EndInfo(Vibration.Status.CANCELLED_BY_SETTINGS_UPDATE), + new Vibration.EndInfo(Status.CANCELLED_BY_SETTINGS_UPDATE), /* immediate= */ false); } } @@ -911,7 +908,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { // Note that we don't consider pipelining here, because new pipelined ones should // replace pending non-executing pipelined ones anyway. clearNextVibrationLocked( - new Vibration.EndInfo(Vibration.Status.IGNORED_SUPERSEDED, vib.callerInfo)); + new Vibration.EndInfo(Status.IGNORED_SUPERSEDED, vib.callerInfo)); mNextVibration = conductor; return null; } finally { @@ -934,15 +931,15 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { if (!mVibrationThread.runVibrationOnVibrationThread(mCurrentVibration)) { // Shouldn't happen. The method call already logs a wtf. mCurrentVibration = null; // Aborted. - return new Vibration.EndInfo(Vibration.Status.IGNORED_ERROR_SCHEDULING); + return new Vibration.EndInfo(Status.IGNORED_ERROR_SCHEDULING); } return null; case AppOpsManager.MODE_ERRORED: Slog.w(TAG, "Start AppOpsManager operation errored for uid " + vib.callerInfo.uid); - return new Vibration.EndInfo(Vibration.Status.IGNORED_ERROR_APP_OPS); + return new Vibration.EndInfo(Status.IGNORED_ERROR_APP_OPS); default: - return new Vibration.EndInfo(Vibration.Status.IGNORED_APP_OPS); + return new Vibration.EndInfo(Status.IGNORED_APP_OPS); } } finally { Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR); @@ -950,7 +947,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { } @GuardedBy("mLock") - private void endVibrationLocked(HalVibration vib, Vibration.EndInfo vibrationEndInfo, + private void endVibrationLocked(Vibration vib, Vibration.EndInfo vibrationEndInfo, boolean shouldWriteStats) { vib.end(vibrationEndInfo); logAndRecordVibration(vib.getDebugInfo()); @@ -960,15 +957,6 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { } } - @GuardedBy("mLock") - private void endVibrationAndWriteStatsLocked(ExternalVibrationHolder vib, - Vibration.EndInfo vibrationEndInfo) { - vib.end(vibrationEndInfo); - logAndRecordVibration(vib.getDebugInfo()); - mFrameworkStatsLogger.writeVibrationReportedAsync( - vib.getStatsInfo(/* completionUptimeMillis= */ SystemClock.uptimeMillis())); - } - private VibrationStepConductor createVibrationStepConductor(HalVibration vib) { CompletableFuture<Void> requestVibrationParamsFuture = null; @@ -990,33 +978,32 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { vib.scaleEffects(mVibrationScaler); mInputDeviceDelegate.vibrateIfAvailable(vib.callerInfo, vib.getEffectToPlay()); - return new Vibration.EndInfo(Vibration.Status.FORWARDED_TO_INPUT_DEVICES); + return new Vibration.EndInfo(Status.FORWARDED_TO_INPUT_DEVICES); } private void logAndRecordPerformHapticFeedbackAttempt(int uid, int deviceId, String opPkg, - String reason, Vibration.Status status) { - Vibration.CallerInfo callerInfo = new Vibration.CallerInfo( + String reason, Status status) { + CallerInfo callerInfo = new CallerInfo( VibrationAttributes.createForUsage(VibrationAttributes.USAGE_UNKNOWN), uid, deviceId, opPkg, reason); logAndRecordVibrationAttempt(/* effect= */ null, callerInfo, status); } private void logAndRecordVibrationAttempt(@Nullable CombinedVibration effect, - Vibration.CallerInfo callerInfo, Vibration.Status status) { + CallerInfo callerInfo, Status status) { logAndRecordVibration( - new Vibration.DebugInfo(status, new VibrationStats(), + new Vibration.DebugInfoImpl(status, new VibrationStats(), effect, /* originalEffect= */ null, VibrationScaler.SCALE_NONE, VibrationScaler.ADAPTIVE_SCALE_NONE, callerInfo)); } - private void logAndRecordVibration(Vibration.DebugInfo info) { + private void logAndRecordVibration(DebugInfo info) { info.logMetrics(mFrameworkStatsLogger); - logVibrationStatus(info.mCallerInfo.uid, info.mCallerInfo.attrs, info.mStatus); + logVibrationStatus(info.getCallerInfo().uid, info.getCallerInfo().attrs, info.getStatus()); mVibratorManagerRecords.record(info); } - private void logVibrationStatus(int uid, VibrationAttributes attrs, - Vibration.Status status) { + private void logVibrationStatus(int uid, VibrationAttributes attrs, Status status) { switch (status) { case IGNORED_BACKGROUND: Slog.e(TAG, "Ignoring incoming vibration as process with" @@ -1161,15 +1148,14 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { if (ongoingVibrationImportance > newVibrationImportance) { // Existing vibration has higher importance and should not be cancelled. - return new Vibration.EndInfo(Vibration.Status.IGNORED_FOR_HIGHER_IMPORTANCE, + return new Vibration.EndInfo(Status.IGNORED_FOR_HIGHER_IMPORTANCE, ongoingVibration.callerInfo); } // Same importance, use repeating as a tiebreaker. if (ongoingVibration.isRepeating() && !newVibration.isRepeating()) { // Ongoing vibration is repeating and new one is not, give priority to ongoing - return new Vibration.EndInfo(Vibration.Status.IGNORED_FOR_ONGOING, - ongoingVibration.callerInfo); + return new Vibration.EndInfo(Status.IGNORED_FOR_ONGOING, ongoingVibration.callerInfo); } // New vibration is repeating or this is a complete tie between them, // give priority to new vibration. @@ -1220,8 +1206,8 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { */ @GuardedBy("mLock") @Nullable - private Vibration.EndInfo shouldIgnoreVibrationLocked(Vibration.CallerInfo callerInfo) { - Vibration.Status statusFromSettings = mVibrationSettings.shouldIgnoreVibration(callerInfo); + private Vibration.EndInfo shouldIgnoreVibrationLocked(CallerInfo callerInfo) { + Status statusFromSettings = mVibrationSettings.shouldIgnoreVibration(callerInfo); if (statusFromSettings != null) { return new Vibration.EndInfo(statusFromSettings); } @@ -1231,9 +1217,9 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { if (mode == AppOpsManager.MODE_ERRORED) { // We might be getting calls from within system_server, so we don't actually // want to throw a SecurityException here. - return new Vibration.EndInfo(Vibration.Status.IGNORED_ERROR_APP_OPS); + return new Vibration.EndInfo(Status.IGNORED_ERROR_APP_OPS); } else { - return new Vibration.EndInfo(Vibration.Status.IGNORED_APP_OPS); + return new Vibration.EndInfo(Status.IGNORED_APP_OPS); } } @@ -1241,16 +1227,16 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { } @Nullable - private Vibration.Status shouldIgnoreHapticFeedback(int constant, String reason, + private Status shouldIgnoreHapticFeedback(int constant, String reason, HapticFeedbackVibrationProvider hapticVibrationProvider) { if (hapticVibrationProvider == null) { Slog.e(TAG, reason + "; haptic vibration provider not ready."); - return Vibration.Status.IGNORED_ERROR_SCHEDULING; + return Status.IGNORED_ERROR_SCHEDULING; } if (hapticVibrationProvider.isRestrictedHapticFeedback(constant) && !hasPermission(android.Manifest.permission.VIBRATE_SYSTEM_CONSTANTS)) { Slog.w(TAG, reason + "; no permission for system constant " + constant); - return Vibration.Status.IGNORED_MISSING_PERMISSION; + return Status.IGNORED_MISSING_PERMISSION; } return null; } @@ -1291,7 +1277,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { * {@code attrs}. This will return one of the AppOpsManager.MODE_*. */ @GuardedBy("mLock") - private int checkAppOpModeLocked(Vibration.CallerInfo callerInfo) { + private int checkAppOpModeLocked(CallerInfo callerInfo) { int mode = mAppOps.checkAudioOpNoThrow(AppOpsManager.OP_VIBRATE, callerInfo.attrs.getAudioUsage(), callerInfo.uid, callerInfo.opPkg); int fixedMode = fixupAppOpModeLocked(mode, callerInfo.attrs); @@ -1306,7 +1292,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { /** Start an operation in {@link AppOpsManager}, if allowed. */ @GuardedBy("mLock") - private int startAppOpModeLocked(Vibration.CallerInfo callerInfo) { + private int startAppOpModeLocked(CallerInfo callerInfo) { return fixupAppOpModeLocked( mAppOps.startOpNoThrow(AppOpsManager.OP_VIBRATE, callerInfo.uid, callerInfo.opPkg), callerInfo.attrs); @@ -1317,7 +1303,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { * operation with same uid was previously started. */ @GuardedBy("mLock") - private void finishAppOpModeLocked(Vibration.CallerInfo callerInfo) { + private void finishAppOpModeLocked(CallerInfo callerInfo) { mAppOps.finishOp(AppOpsManager.OP_VIBRATE, callerInfo.uid, callerInfo.opPkg); } @@ -1735,10 +1721,10 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { */ private static final class AlwaysOnVibration { public final int alwaysOnId; - public final Vibration.CallerInfo callerInfo; + public final CallerInfo callerInfo; public final SparseArray<PrebakedSegment> effects; - AlwaysOnVibration(int alwaysOnId, Vibration.CallerInfo callerInfo, + AlwaysOnVibration(int alwaysOnId, CallerInfo callerInfo, SparseArray<PrebakedSegment> effects) { this.alwaysOnId = alwaysOnId; this.callerInfo = callerInfo; @@ -1746,113 +1732,6 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { } } - /** Holder for a {@link ExternalVibration}. */ - private final class ExternalVibrationHolder extends Vibration implements - IBinder.DeathRecipient { - - public final ExternalVibration externalVibration; - public final ExternalVibrationScale scale = new ExternalVibrationScale(); - - private Vibration.Status mStatus; - - private ExternalVibrationHolder(ExternalVibration externalVibration) { - super(externalVibration.getToken(), new Vibration.CallerInfo( - externalVibration.getVibrationAttributes(), externalVibration.getUid(), - // TODO(b/249785241): Find a way to link ExternalVibration to a VirtualDevice - // instead of using DEVICE_ID_INVALID here and relying on the UID checks. - Context.DEVICE_ID_INVALID, externalVibration.getPackage(), null)); - this.externalVibration = externalVibration; - mStatus = Vibration.Status.RUNNING; - } - - public void muteScale() { - scale.scaleLevel = ExternalVibrationScale.ScaleLevel.SCALE_MUTE; - if (Flags.hapticsScaleV2Enabled()) { - scale.scaleFactor = 0; - } - } - - public void scale(VibrationScaler scaler, int usage) { - scale.scaleLevel = scaler.getScaleLevel(usage); - if (Flags.hapticsScaleV2Enabled()) { - scale.scaleFactor = scaler.getScaleFactor(usage); - } - scale.adaptiveHapticsScale = scaler.getAdaptiveHapticsScale(usage); - stats.reportAdaptiveScale(scale.adaptiveHapticsScale); - } - - public void mute() { - externalVibration.mute(); - } - - public void linkToDeath() { - externalVibration.linkToDeath(this); - } - - public void unlinkToDeath() { - externalVibration.unlinkToDeath(this); - } - - public boolean isHoldingSameVibration(ExternalVibration externalVibration) { - return this.externalVibration.equals(externalVibration); - } - - public void end(Vibration.EndInfo info) { - if (mStatus != Vibration.Status.RUNNING) { - // Already ended, ignore this call - return; - } - mStatus = info.status; - stats.reportEnded(info.endedBy); - - if (stats.hasStarted()) { - // External vibration doesn't have feedback from total time the vibrator was playing - // with non-zero amplitude, so we use the duration between start and end times of - // the vibration as the time the vibrator was ON, since the haptic channels are - // open for this duration and can receive vibration waveform data. - stats.reportVibratorOn( - stats.getEndUptimeMillis() - stats.getStartUptimeMillis()); - } - } - - public void binderDied() { - synchronized (mLock) { - if (mCurrentExternalVibration != null) { - if (DEBUG) { - Slog.d(TAG, "External vibration finished because binder died"); - } - endExternalVibrateLocked( - new Vibration.EndInfo(Vibration.Status.CANCELLED_BINDER_DIED), - /* continueExternalControl= */ false); - } - } - } - - public Vibration.DebugInfo getDebugInfo() { - return new Vibration.DebugInfo(mStatus, stats, /* playedEffect= */ null, - /* originalEffect= */ null, scale.scaleLevel, scale.adaptiveHapticsScale, - callerInfo); - } - - public VibrationStats.StatsInfo getStatsInfo(long completionUptimeMillis) { - return new VibrationStats.StatsInfo( - externalVibration.getUid(), - FrameworkStatsLog.VIBRATION_REPORTED__VIBRATION_TYPE__EXTERNAL, - externalVibration.getVibrationAttributes().getUsage(), mStatus, stats, - completionUptimeMillis); - } - - @Override - boolean isRepeating() { - // We don't currently know if the external vibration is repeating, so we just use a - // heuristic based on the usage. Ideally this would be propagated in the - // ExternalVibration. - int usage = externalVibration.getVibrationAttributes().getUsage(); - return usage == VibrationAttributes.USAGE_RINGTONE - || usage == VibrationAttributes.USAGE_ALARM; - } - } - /** Wrapper around the static-native methods of {@link VibratorManagerService} for tests. */ @VisibleForTesting public static class NativeWrapper { @@ -1912,7 +1791,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { new VibrationRecords(recentVibrationSizeLimit, /* aggregationTimeLimit= */ 0); } - synchronized void record(Vibration.DebugInfo info) { + synchronized void record(DebugInfo info) { GroupedAggregatedLogRecords.AggregatedLogRecord<VibrationRecord> droppedRecord = mRecentVibrations.add(new VibrationRecord(info)); if (droppedRecord != null) { @@ -1969,25 +1848,25 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { } /** - * Record for a single {@link Vibration.DebugInfo}, that can be grouped by usage and aggregated - * by UID, {@link VibrationAttributes} and {@link VibrationEffect}. + * Record for a single {@link DebugInfo}, that can be grouped by usage and aggregated by UID, + * {@link VibrationAttributes} and {@link CombinedVibration}. */ private static final class VibrationRecord implements GroupedAggregatedLogRecords.SingleLogRecord { - private final Vibration.DebugInfo mInfo; + private final DebugInfo mInfo; - VibrationRecord(Vibration.DebugInfo info) { + VibrationRecord(DebugInfo info) { mInfo = info; } @Override public int getGroupKey() { - return mInfo.mCallerInfo.attrs.getUsage(); + return mInfo.getCallerInfo().attrs.getUsage(); } @Override public long getCreateUptimeMs() { - return mInfo.mCreateTime; + return mInfo.getCreateUptimeMillis(); } @Override @@ -1995,10 +1874,10 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { if (!(record instanceof VibrationRecord)) { return false; } - Vibration.DebugInfo info = ((VibrationRecord) record).mInfo; - return mInfo.mCallerInfo.uid == info.mCallerInfo.uid - && Objects.equals(mInfo.mCallerInfo.attrs, info.mCallerInfo.attrs) - && Objects.equals(mInfo.mPlayedEffect, info.mPlayedEffect); + DebugInfo info = ((VibrationRecord) record).mInfo; + return mInfo.getCallerInfo().uid == info.getCallerInfo().uid + && Objects.equals(mInfo.getCallerInfo().attrs, info.getCallerInfo().attrs) + && Objects.equals(mInfo.getDumpAggregationKey(), info.getDumpAggregationKey()); } @Override @@ -2048,7 +1927,8 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { setExternalControl(false, mCurrentExternalVibration.stats); } // The external control was turned off, end it and report metrics right away. - endVibrationAndWriteStatsLocked(mCurrentExternalVibration, vibrationEndInfo); + endVibrationLocked(mCurrentExternalVibration, vibrationEndInfo, + /* shouldWriteStats= */ true); mCurrentExternalVibration = null; } finally { Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR); @@ -2108,17 +1988,18 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { @Override public ExternalVibrationScale onExternalVibrationStart(ExternalVibration vib) { // Create Vibration.Stats as close to the received request as possible, for tracking. - ExternalVibrationHolder vibHolder = new ExternalVibrationHolder(vib); + ExternalVibrationSession externalVibration = new ExternalVibrationSession(vib); // Mute the request until we run all the checks and accept the vibration. - vibHolder.muteScale(); + externalVibration.muteScale(); boolean alreadyUnderExternalControl = false; boolean waitForCompletion = false; synchronized (mLock) { if (!hasExternalControlCapability()) { - endVibrationAndWriteStatsLocked(vibHolder, - new Vibration.EndInfo(Vibration.Status.IGNORED_UNSUPPORTED)); - return vibHolder.scale; + endVibrationLocked(externalVibration, + new Vibration.EndInfo(Status.IGNORED_UNSUPPORTED), + /* shouldWriteStats= */ true); + return externalVibration.getScale(); } if (ActivityManager.checkComponentPermission(android.Manifest.permission.VIBRATE, @@ -2127,44 +2008,46 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { Slog.w(TAG, "pkg=" + vib.getPackage() + ", uid=" + vib.getUid() + " tried to play externally controlled vibration" + " without VIBRATE permission, ignoring."); - endVibrationAndWriteStatsLocked(vibHolder, - new Vibration.EndInfo(Vibration.Status.IGNORED_MISSING_PERMISSION)); - return vibHolder.scale; + endVibrationLocked(externalVibration, + new Vibration.EndInfo(Status.IGNORED_MISSING_PERMISSION), + /* shouldWriteStats= */ true); + return externalVibration.getScale(); } Vibration.EndInfo vibrationEndInfo = shouldIgnoreVibrationLocked( - vibHolder.callerInfo); + externalVibration.callerInfo); if (vibrationEndInfo == null && mCurrentExternalVibration != null && mCurrentExternalVibration.isHoldingSameVibration(vib)) { // We are already playing this external vibration, so we can return the same // scale calculated in the previous call to this method. - return mCurrentExternalVibration.scale; + return mCurrentExternalVibration.getScale(); } if (vibrationEndInfo == null) { // Check if ongoing vibration is more important than this vibration. - vibrationEndInfo = shouldIgnoreVibrationForOngoingLocked(vibHolder); + vibrationEndInfo = shouldIgnoreVibrationForOngoingLocked(externalVibration); } if (vibrationEndInfo != null) { - endVibrationAndWriteStatsLocked(vibHolder, vibrationEndInfo); - return vibHolder.scale; + endVibrationLocked(externalVibration, vibrationEndInfo, + /* shouldWriteStats= */ true); + return externalVibration.getScale(); } if (mCurrentExternalVibration == null) { // If we're not under external control right now, then cancel any normal // vibration that may be playing and ready the vibrator for external control. if (mCurrentVibration != null) { - vibHolder.stats.reportInterruptedAnotherVibration( + externalVibration.stats.reportInterruptedAnotherVibration( mCurrentVibration.getVibration().callerInfo); clearNextVibrationLocked( - new Vibration.EndInfo(Vibration.Status.IGNORED_FOR_EXTERNAL, - vibHolder.callerInfo)); + new Vibration.EndInfo(Status.IGNORED_FOR_EXTERNAL, + externalVibration.callerInfo)); mCurrentVibration.notifyCancelled( - new Vibration.EndInfo(Vibration.Status.CANCELLED_SUPERSEDED, - vibHolder.callerInfo), + new Vibration.EndInfo(Status.CANCELLED_SUPERSEDED, + externalVibration.callerInfo), /* immediate= */ true); waitForCompletion = true; } @@ -2178,12 +2061,12 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { // Note that this doesn't support multiple concurrent external controls, as we // would need to mute the old one still if it came from a different controller. alreadyUnderExternalControl = true; - mCurrentExternalVibration.mute(); - vibHolder.stats.reportInterruptedAnotherVibration( + mCurrentExternalVibration.notifyEnded(); + externalVibration.stats.reportInterruptedAnotherVibration( mCurrentExternalVibration.callerInfo); endExternalVibrateLocked( - new Vibration.EndInfo(Vibration.Status.CANCELLED_SUPERSEDED, - vibHolder.callerInfo), + new Vibration.EndInfo(Status.CANCELLED_SUPERSEDED, + externalVibration.callerInfo), /* continueExternalControl= */ true); } @@ -2195,9 +2078,9 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { mVibrationSettings.update(); } - mCurrentExternalVibration = vibHolder; - vibHolder.linkToDeath(); - vibHolder.scale(mVibrationScaler, attrs.getUsage()); + mCurrentExternalVibration = externalVibration; + externalVibration.linkToDeath(this::onExternalVibrationBinderDied); + externalVibration.scale(mVibrationScaler, attrs.getUsage()); } if (waitForCompletion) { @@ -2206,27 +2089,27 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { synchronized (mLock) { // Trigger endExternalVibrateLocked to unlink to death recipient. endExternalVibrateLocked( - new Vibration.EndInfo(Vibration.Status.IGNORED_ERROR_CANCELLING), + new Vibration.EndInfo(Status.IGNORED_ERROR_CANCELLING), /* continueExternalControl= */ false); // Mute the request, vibration will be ignored. - vibHolder.muteScale(); + externalVibration.muteScale(); } - return vibHolder.scale; + return externalVibration.getScale(); } } if (!alreadyUnderExternalControl) { if (DEBUG) { Slog.d(TAG, "Vibrator going under external control."); } - setExternalControl(true, vibHolder.stats); + setExternalControl(true, externalVibration.stats); } if (DEBUG) { Slog.d(TAG, "Playing external vibration: " + vib); } // Vibrator will start receiving data from external channels after this point. // Report current time as the vibration start time, for debugging. - vibHolder.stats.reportStarted(); - return vibHolder.scale; + externalVibration.stats.reportStarted(); + return externalVibration.getScale(); } @Override @@ -2238,7 +2121,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { Slog.d(TAG, "Stopping external vibration: " + vib); } endExternalVibrateLocked( - new Vibration.EndInfo(Vibration.Status.FINISHED), + new Vibration.EndInfo(Status.FINISHED), /* continueExternalControl= */ false); } } @@ -2252,6 +2135,19 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { } return false; } + + private void onExternalVibrationBinderDied() { + synchronized (mLock) { + if (mCurrentExternalVibration != null) { + if (DEBUG) { + Slog.d(TAG, "External vibration finished because binder died"); + } + endExternalVibrateLocked( + new Vibration.EndInfo(Status.CANCELLED_BINDER_DIED), + /* continueExternalControl= */ false); + } + } + } } /** Provide limited functionality from {@link VibratorManagerService} as shell commands. */ diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java index ba2594abd4d4..f53dda6ee35b 100644 --- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java +++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java @@ -42,6 +42,7 @@ import static com.android.server.wallpaper.WallpaperUtils.WALLPAPER_INFO; import static com.android.server.wallpaper.WallpaperUtils.WALLPAPER_LOCK_ORIG; import static com.android.server.wallpaper.WallpaperUtils.getWallpaperDir; import static com.android.server.wallpaper.WallpaperUtils.makeWallpaperIdLocked; +import static com.android.window.flags.Flags.avoidRebindingIntentionallyDisconnectedWallpaper; import static com.android.window.flags.Flags.multiCrop; import static com.android.window.flags.Flags.offloadColorExtraction; @@ -897,6 +898,12 @@ public class WallpaperManagerService extends IWallpaperManager.Stub return; } + if (avoidRebindingIntentionallyDisconnectedWallpaper() + && mWallpaper.connection == null) { + Slog.w(TAG, "Trying to reset an intentionally disconnected wallpaper!"); + return; + } + if (!mWallpaper.wallpaperUpdating && mWallpaper.userId == mCurrentUserId) { Slog.w(TAG, "Wallpaper reconnect timed out for " + mWallpaper.wallpaperComponent + ", reverting to built-in wallpaper!"); @@ -1066,6 +1073,13 @@ public class WallpaperManagerService extends IWallpaperManager.Stub if (mWallpaper.wallpaperUpdating) { return; } + + if (avoidRebindingIntentionallyDisconnectedWallpaper() + && mWallpaper.connection == null) { + Slog.w(TAG, "Trying to rebind an intentionally disconnected wallpaper!"); + return; + } + final ComponentName wpService = mWallpaper.wallpaperComponent; // The broadcast of package update could be delayed after service disconnected. Try // to re-bind the service for 10 seconds. diff --git a/services/core/java/com/android/server/wm/ActionChain.java b/services/core/java/com/android/server/wm/ActionChain.java new file mode 100644 index 000000000000..d63044a85c1d --- /dev/null +++ b/services/core/java/com/android/server/wm/ActionChain.java @@ -0,0 +1,240 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS 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 android.annotation.IntDef; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.util.Slog; + +import com.android.window.flags.Flags; + +/** + * Represents a chain of WM actions where each action is "caused by" the prior action (except the + * first one of course). A whole chain is associated with one Transition (in fact, the purpose + * of this object is to communicate, to all callees, which transition they are part of). + * + * A single action is defined as "one logical thing requested of WM". This usually corresponds to + * each ingress-point into the process. For example, when starting an activity: + * * the first action is to pause the current/top activity. + * At this point, control leaves the process while the activity pauses. + * * Then WM receives completePause (a new ingress). This is a new action that gets linked + * to the prior action. This action involves resuming the next activity, at which point, + * control leaves the process again. + * * Eventually, when everything is done, we will have formed a chain of actions. + * + * We don't technically need to hold onto each prior action in the chain once a new action has + * been linked to the same transition; however, keeping the whole chain enables improved + * debugging and the ability to detect anomalies. + */ +public class ActionChain { + private static final String TAG = "TransitionChain"; + + /** + * Normal link type. This means the action was expected and is properly linked to the + * current chain. + */ + static final int TYPE_NORMAL = 0; + + /** + * This is the "default" link. It means we haven't done anything to properly track this case + * so it may or may not be correct. It represents the behavior as if there was no tracking. + * + * Any type that has "default" behavior uses the global "collecting transition" if it exists, + * otherwise it doesn't use any transition. + */ + static final int TYPE_DEFAULT = 1; + + /** + * This means the action was performed via a legacy code-path. These should be removed + * eventually. This will have the "default" behavior. + */ + static final int TYPE_LEGACY = 2; + + /** This is for a test. */ + static final int TYPE_TEST = 3; + + /** This is finishing a transition. Collection isn't supported during this. */ + static final int TYPE_FINISH = 4; + + /** + * Something unexpected happened so this action was started to recover from the unexpected + * state. This means that a "real" chain-link couldn't be determined. For now, the behavior of + * this is the same as "default". + */ + static final int TYPE_FAILSAFE = 5; + + /** + * Types of chain links (ie. how is this action associated with the chain it is linked to). + * @hide + */ + @IntDef(prefix = { "TYPE_" }, value = { + TYPE_NORMAL, + TYPE_DEFAULT, + TYPE_LEGACY, + TYPE_TEST, + TYPE_FINISH, + TYPE_FAILSAFE + }) + public @interface LinkType {} + + /** Identifies the entry-point of this action. */ + @NonNull + final String mSource; + + /** Reference to ATMS. TEMPORARY! ONLY USE THIS WHEN tracker_plumbing flag is DISABLED! */ + @Nullable + ActivityTaskManagerService mTmpAtm; + + /** The transition that this chain's changes belong to. */ + @Nullable + Transition mTransition; + + /** The previous action in the chain. */ + @Nullable + ActionChain mPrevious = null; + + /** Classification of how this action is connected to the chain. */ + @LinkType int mType = TYPE_NORMAL; + + /** When this Action started. */ + long mCreateTimeMs; + + private ActionChain(String source, @LinkType int type, Transition transit) { + mSource = source; + mCreateTimeMs = System.currentTimeMillis(); + mType = type; + mTransition = transit; + if (mTransition != null) { + mTransition.recordChain(this); + } + } + + private Transition getTransition() { + if (!Flags.transitTrackerPlumbing()) { + return mTmpAtm.getTransitionController().getCollectingTransition(); + } + return mTransition; + } + + boolean isFinishing() { + return mType == TYPE_FINISH; + } + + /** + * Some common checks to determine (and report) whether this chain has a collecting transition. + */ + private boolean expectCollecting() { + final Transition transition = getTransition(); + if (transition == null) { + Slog.e(TAG, "Can't collect into a chain with no transition"); + return false; + } + if (isFinishing()) { + Slog.e(TAG, "Trying to collect into a finished transition"); + return false; + } + if (transition.mController.getCollectingTransition() != mTransition) { + Slog.e(TAG, "Mismatch between current collecting (" + + transition.mController.getCollectingTransition() + ") and chain (" + + transition + ")"); + return false; + } + return true; + } + + /** + * Helper to collect a container into the associated transition. This will automatically do + * nothing if the chain isn't associated with a collecting transition. + */ + void collect(@NonNull WindowContainer wc) { + if (!wc.mTransitionController.isShellTransitionsEnabled()) return; + if (!expectCollecting()) return; + getTransition().collect(wc); + } + + /** + * An interface for creating and tracking action chains. + */ + static class Tracker { + private final ActivityTaskManagerService mAtm; + + Tracker(ActivityTaskManagerService atm) { + mAtm = atm; + } + + private ActionChain makeChain(String source, @LinkType int type, Transition transit) { + final ActionChain out = new ActionChain(source, type, transit); + if (!Flags.transitTrackerPlumbing()) { + out.mTmpAtm = mAtm; + } + return out; + } + + private ActionChain makeChain(String source, @LinkType int type) { + return makeChain(source, type, + mAtm.getTransitionController().getCollectingTransition()); + } + + /** + * Starts tracking a normal action. + * @see #TYPE_NORMAL + */ + @NonNull + ActionChain start(String source, Transition transit) { + return makeChain(source, TYPE_NORMAL, transit); + } + + /** @see #TYPE_DEFAULT */ + @NonNull + ActionChain startDefault(String source) { + return makeChain(source, TYPE_DEFAULT); + } + + /** + * Starts tracking an action that finishes a transition. + * @see #TYPE_NORMAL + */ + @NonNull + ActionChain startFinish(String source, Transition finishTransit) { + return makeChain(source, TYPE_FINISH, finishTransit); + } + + /** @see #TYPE_LEGACY */ + @NonNull + ActionChain startLegacy(String source) { + return makeChain(source, TYPE_LEGACY, null); + } + + /** @see #TYPE_FAILSAFE */ + @NonNull + ActionChain startFailsafe(String source) { + return makeChain(source, TYPE_FAILSAFE); + } + } + + /** Helpers for usage in tests. */ + @NonNull + static ActionChain test() { + return new ActionChain("test", TYPE_TEST, null /* transition */); + } + + @NonNull + static ActionChain testFinish(Transition toFinish) { + return new ActionChain("test", TYPE_FINISH, toFinish); + } +} diff --git a/services/core/java/com/android/server/wm/ActivityClientController.java b/services/core/java/com/android/server/wm/ActivityClientController.java index e27b2beb3d5a..c1e859ddcccf 100644 --- a/services/core/java/com/android/server/wm/ActivityClientController.java +++ b/services/core/java/com/android/server/wm/ActivityClientController.java @@ -864,8 +864,9 @@ class ActivityClientController extends IActivityClientController.Stub { if (transition != null) { if (changed) { // Always set as scene transition because it expects to be a jump-cut. - transition.setOverrideAnimation(TransitionInfo.AnimationOptions - .makeSceneTransitionAnimOptions(), null, null); + transition.setOverrideAnimation( + TransitionInfo.AnimationOptions.makeSceneTransitionAnimOptions(), r, + null, null); r.mTransitionController.requestStartTransition(transition, null /*startTask */, null /* remoteTransition */, null /* displayChange */); @@ -910,8 +911,9 @@ class ActivityClientController extends IActivityClientController.Stub { && under.returningOptions.getAnimationType() == ANIM_SCENE_TRANSITION) { // Pass along the scene-transition animation-type - transition.setOverrideAnimation(TransitionInfo.AnimationOptions - .makeSceneTransitionAnimOptions(), null, null); + transition.setOverrideAnimation(TransitionInfo + .AnimationOptions.makeSceneTransitionAnimOptions(), r, + null, null); } } else { transition.abort(); @@ -1508,7 +1510,7 @@ class ActivityClientController extends IActivityClientController.Stub { r.mOverrideTaskTransition); r.mTransitionController.setOverrideAnimation( TransitionInfo.AnimationOptions.makeCustomAnimOptions(packageName, - enterAnim, exitAnim, backgroundColor, r.mOverrideTaskTransition), + enterAnim, exitAnim, backgroundColor, r.mOverrideTaskTransition), r, null /* startCallback */, null /* finishCallback */); } } diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index 21908d95c605..235a2115a964 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -353,6 +353,7 @@ import android.window.SplashScreen; import android.window.SplashScreenView; import android.window.SplashScreenView.SplashScreenViewParcelable; import android.window.TaskSnapshot; +import android.window.TransitionInfo; import android.window.TransitionInfo.AnimationOptions; import android.window.WindowContainerToken; import android.window.WindowOnBackInvokedDispatcher; @@ -5034,7 +5035,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // controller but don't clear the animation information from the options since they // need to be sent to the animating activity. mTransitionController.setOverrideAnimation( - AnimationOptions.makeSceneTransitionAnimOptions(), null, null); + TransitionInfo.AnimationOptions.makeSceneTransitionAnimOptions(), this, + null, null); return; } applyOptionsAnimation(mPendingOptions, intent); @@ -5157,7 +5159,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } if (options != null) { - mTransitionController.setOverrideAnimation(options, startCallback, finishCallback); + mTransitionController.setOverrideAnimation(options, this, startCallback, + finishCallback); } } @@ -5890,6 +5893,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A mAtmService.updateBatteryStats(this, false); } mAtmService.updateActivityUsageStats(this, Event.ACTIVITY_DESTROYED); + idle = false; // Fall through. case DESTROYING: if (app != null && !app.hasActivities()) { diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java index 64791112a129..bf18a438f9af 100644 --- a/services/core/java/com/android/server/wm/ActivityStarter.java +++ b/services/core/java/com/android/server/wm/ActivityStarter.java @@ -1945,7 +1945,7 @@ class ActivityStarter { && sourceRecord != null && sourceRecord.getTask() == mStartActivity.getTask() && balVerdict.allows()) { mRootWindowContainer.moveActivityToPinnedRootTask(mStartActivity, - sourceRecord, "launch-into-pip"); + sourceRecord, "launch-into-pip", null /* bounds */); } mSupervisor.getBackgroundActivityLaunchController() diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java index 0f108c5ed5d7..f5476f29849a 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java @@ -795,6 +795,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { WindowOrganizerController mWindowOrganizerController; TaskOrganizerController mTaskOrganizerController; TaskFragmentOrganizerController mTaskFragmentOrganizerController; + ActionChain.Tracker mChainTracker; @Nullable private BackgroundActivityStartCallback mBackgroundActivityStartCallback; @@ -869,6 +870,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { mInternal = new LocalService(); GL_ES_VERSION = SystemProperties.getInt("ro.opengles.version", GL_ES_VERSION_UNDEFINED); mWindowOrganizerController = new WindowOrganizerController(this); + mChainTracker = new ActionChain.Tracker(this); mTaskOrganizerController = mWindowOrganizerController.mTaskOrganizerController; mTaskFragmentOrganizerController = mWindowOrganizerController.mTaskFragmentOrganizerController; @@ -3794,9 +3796,22 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { r.shortComponentName, Boolean.toString(isAutoEnter)); r.setPictureInPictureParams(params); r.mAutoEnteringPip = isAutoEnter; - mRootWindowContainer.moveActivityToPinnedRootTask(r, - null /* launchIntoPipHostActivity */, "enterPictureInPictureMode", - transition); + + if (transition != null) { + mRootWindowContainer.moveActivityToPinnedRootTaskAndRequestStart(r, + "enterPictureInPictureMode"); + } else if (getTransitionController().isCollecting() + || !getTransitionController().isShellTransitionsEnabled()) { + mRootWindowContainer.moveActivityToPinnedRootTask(r, + null /* launchIntoPipHostActivity */, "enterPictureInPictureMode", + null /* bounds */); + } else { + // Need to make a transition just for this. This shouldn't really happen + // though because if transition == null, we should be part of an existing one. + getTransitionController().createTransition(TRANSIT_PIP); + mRootWindowContainer.moveActivityToPinnedRootTaskAndRequestStart(r, + "enterPictureInPictureMode"); + } // Continue the pausing process after entering pip. if (r.isState(PAUSING) && r.mPauseSchedulePendingForPip) { r.getTask().schedulePauseActivity(r, false /* userLeaving */, @@ -4755,6 +4770,10 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } } + boolean mayBeLaunchingApp() { + return (mPowerModeReasons & POWER_MODE_REASON_START_ACTIVITY) != 0; + } + void startPowerMode(@PowerModeReason int reason) { final int prevReasons = mPowerModeReasons; mPowerModeReasons |= reason; diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java index 1446c353d9d4..e90a2c90bfab 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java +++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java @@ -202,6 +202,12 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks { */ private static final int KILL_TASK_PROCESSES_TIMEOUT_MS = 1000; + /** + * The delay to run idle check. It may give a chance to keep launch power mode if an activity + * is starting while the device is sleeping and then the device is unlocked in a short time. + */ + private static final int IDLE_NOW_DELAY_WHILE_SLEEPING_MS = 100; + private static final int IDLE_TIMEOUT_MSG = FIRST_SUPERVISOR_TASK_MSG; private static final int IDLE_NOW_MSG = FIRST_SUPERVISOR_TASK_MSG + 1; private static final int RESUME_TOP_ACTIVITY_MSG = FIRST_SUPERVISOR_TASK_MSG + 2; @@ -2252,7 +2258,9 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks { final void scheduleIdle() { if (!mHandler.hasMessages(IDLE_NOW_MSG)) { if (DEBUG_IDLE) Slog.d(TAG_IDLE, "scheduleIdle: Callers=" + Debug.getCallers(4)); - mHandler.sendEmptyMessage(IDLE_NOW_MSG); + final long delayMs = mService.isSleepingLocked() && mService.mayBeLaunchingApp() + ? IDLE_NOW_DELAY_WHILE_SLEEPING_MS : 0; + mHandler.sendEmptyMessageDelayed(IDLE_NOW_MSG, delayMs); } } diff --git a/services/core/java/com/android/server/wm/BackNavigationController.java b/services/core/java/com/android/server/wm/BackNavigationController.java index 2cbd7f22fcba..3710f7fcb7cd 100644 --- a/services/core/java/com/android/server/wm/BackNavigationController.java +++ b/services/core/java/com/android/server/wm/BackNavigationController.java @@ -574,10 +574,15 @@ class BackNavigationController { private void scheduleAnimation(@NonNull AnimationHandler.ScheduleAnimationBuilder builder) { mPendingAnimation = builder.build(); - mWindowManagerService.mWindowPlacerLocked.requestTraversal(); - if (mShowWallpaper) { - mWindowManagerService.getDefaultDisplayContentLocked().mWallpaperController - .adjustWallpaperWindows(); + if (mAnimationHandler.mOpenAnimAdaptor != null + && mAnimationHandler.mOpenAnimAdaptor.mPreparedOpenTransition != null) { + startAnimation(); + } else { + mWindowManagerService.mWindowPlacerLocked.requestTraversal(); + if (mShowWallpaper) { + mWindowManagerService.getDefaultDisplayContentLocked().mWallpaperController + .adjustWallpaperWindows(); + } } } @@ -1203,7 +1208,7 @@ class BackNavigationController { } void markWindowHasDrawn(ActivityRecord activity) { - if (!mComposed || mWaitTransition || mOpenAnimAdaptor.mPreparedOpenTransition == null + if (!mComposed || mWaitTransition || mOpenAnimAdaptor.mRequestedStartingSurfaceId == INVALID_TASK_ID) { return; } @@ -1215,6 +1220,10 @@ class BackNavigationController { } allWindowDrawn &= next.mAppWindowDrawn; } + // Do not remove until transition ready. + if (!activity.isVisible()) { + return; + } if (allWindowDrawn) { mOpenAnimAdaptor.cleanUpWindowlessSurface(true); } @@ -1289,6 +1298,14 @@ class BackNavigationController { if (mOpenAnimAdaptor.mRequestedStartingSurfaceId == INVALID_TASK_ID) { return; } + boolean allWindowDrawn = true; + for (int i = mOpenAnimAdaptor.mAdaptors.length - 1; i >= 0; --i) { + final BackWindowAnimationAdaptor next = mOpenAnimAdaptor.mAdaptors[i]; + allWindowDrawn &= next.mAppWindowDrawn; + } + if (!allWindowDrawn) { + return; + } final SurfaceControl startingSurface = mOpenAnimAdaptor.mStartingSurface; if (startingSurface != null && startingSurface.isValid()) { startTransaction.addTransactionCommittedListener(Runnable::run, () -> { diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java index a6d965955272..866dcd56ea91 100644 --- a/services/core/java/com/android/server/wm/RootWindowContainer.java +++ b/services/core/java/com/android/server/wm/RootWindowContainer.java @@ -2032,33 +2032,39 @@ class RootWindowContainer extends WindowContainer<DisplayContent> onTop); } - void moveActivityToPinnedRootTask(@NonNull ActivityRecord r, - @Nullable ActivityRecord launchIntoPipHostActivity, String reason) { - moveActivityToPinnedRootTask(r, launchIntoPipHostActivity, reason, null /* transition */); + /** Wrapper/Helper for tests */ + void moveActivityToPinnedRootTask(@NonNull ActivityRecord r, String reason) { + Transition newTransit = (r.mTransitionController.isCollecting() + || !r.mTransitionController.isShellTransitionsEnabled()) + ? null : r.mTransitionController.createTransition(TRANSIT_PIP); + moveActivityToPinnedRootTaskInner(r, null /* launchIntoPipHostActivity */, reason, + null /* bounds */, newTransit != null); } void moveActivityToPinnedRootTask(@NonNull ActivityRecord r, @Nullable ActivityRecord launchIntoPipHostActivity, String reason, - @Nullable Transition transition) { - moveActivityToPinnedRootTask(r, launchIntoPipHostActivity, reason, transition, - null /* bounds */); + @Nullable Rect bounds) { + moveActivityToPinnedRootTaskInner(r, launchIntoPipHostActivity, reason, bounds, + false /* requestStart */); } - void moveActivityToPinnedRootTask(@NonNull ActivityRecord r, + /** + * Moves activity to pinned in the provided transition and also requests start on that + * Transition at an appropriate time. + */ + void moveActivityToPinnedRootTaskAndRequestStart(@NonNull ActivityRecord r, String reason) { + moveActivityToPinnedRootTaskInner(r, null /* launchIntoPipHostActivity */, reason, + null /* bounds */, true /* requestStart */); + } + + private void moveActivityToPinnedRootTaskInner(@NonNull ActivityRecord r, @Nullable ActivityRecord launchIntoPipHostActivity, String reason, - @Nullable Transition transition, @Nullable Rect bounds) { + @Nullable Rect bounds, boolean requestStart) { final TaskDisplayArea taskDisplayArea = r.getDisplayArea(); final Task task = r.getTask(); final Task rootTask; - Transition newTransition = transition; - // Create a transition now (if not provided) to collect the current pinned Task dismiss. - // Only do the create here as the Task (trigger) to enter PIP is not ready yet. final TransitionController transitionController = task.mTransitionController; - if (newTransition == null && !transitionController.isCollecting() - && transitionController.getTransitionPlayer() != null) { - newTransition = transitionController.createTransition(TRANSIT_PIP); - } transitionController.deferTransitionReady(); Transition.ReadyCondition pipChangesApplied = new Transition.ReadyCondition("movedToPip"); @@ -2273,14 +2279,16 @@ class RootWindowContainer extends WindowContainer<DisplayContent> } } - if (newTransition != null) { + // can be null (for now) if shell transitions are disabled or inactive at this time + final Transition transit = transitionController.getCollectingTransition(); + if (requestStart && transit != null) { // Request at end since we want task-organizer events from ensureActivitiesVisible // to be recognized. - transitionController.requestStartTransition(newTransition, rootTask, + transitionController.requestStartTransition(transit, rootTask, null /* remoteTransition */, null /* displayChange */); // A new transition was created just for this operations. Since the operation is // complete, mark it as ready. - newTransition.setReady(rootTask, true /* ready */); + transit.setReady(rootTask, true /* ready */); } resumeFocusedTasksTopActivities(); diff --git a/services/core/java/com/android/server/wm/SnapshotController.java b/services/core/java/com/android/server/wm/SnapshotController.java index 0f9c001dffa8..52994c70f183 100644 --- a/services/core/java/com/android/server/wm/SnapshotController.java +++ b/services/core/java/com/android/server/wm/SnapshotController.java @@ -31,6 +31,8 @@ import android.util.ArrayMap; import android.view.WindowManager; import android.window.TaskSnapshot; +import com.android.window.flags.Flags; + import java.io.PrintWriter; import java.util.ArrayList; @@ -150,6 +152,9 @@ class SnapshotController { if (mOpenActivities.isEmpty()) { return false; } + if (Flags.alwaysCaptureActivitySnapshot()) { + return true; + } for (int i = mOpenActivities.size() - 1; i >= 0; --i) { if (!mOpenActivities.get(i).mOptInOnBackInvoked) { return false; diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java index e25db7e2a3ec..7f6dc8472813 100644 --- a/services/core/java/com/android/server/wm/Transition.java +++ b/services/core/java/com/android/server/wm/Transition.java @@ -181,7 +181,7 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener { final @TransitionType int mType; private int mSyncId = -1; private @TransitionFlags int mFlags; - private final TransitionController mController; + final TransitionController mController; private final BLASTSyncEngine mSyncEngine; private final Token mToken; @@ -329,6 +329,9 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener { */ ArrayList<ActivityRecord> mConfigAtEndActivities = null; + /** The current head of the chain of actions related to this transition. */ + ActionChain mChainHead = null; + @VisibleForTesting Transition(@TransitionType int type, @TransitionFlags int flags, TransitionController controller, BLASTSyncEngine syncEngine) { @@ -950,10 +953,13 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener { * Set animation options for collecting transition by ActivityRecord. * @param options AnimationOptions captured from ActivityOptions */ - void setOverrideAnimation(@Nullable AnimationOptions options, + void setOverrideAnimation(@Nullable AnimationOptions options, @NonNull ActivityRecord r, @Nullable IRemoteCallback startCallback, @Nullable IRemoteCallback finishCallback) { if (!isCollecting()) return; mOverrideOptions = options; + if (mOverrideOptions != null) { + mOverrideOptions.setUserId(r.mUserId); + } sendRemoteCallback(mClientAnimationStartCallback); mClientAnimationStartCallback = startCallback; mClientAnimationFinishCallback = finishCallback; @@ -1051,9 +1057,8 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener { * needs to be passed/applied in shell because until finish is called, shell owns the surfaces. * Additionally, this gives shell the ability to better deal with merged transitions. */ - private void buildFinishTransaction(SurfaceControl.Transaction t, TransitionInfo info) { - // usually only size 1 - final ArraySet<DisplayContent> displays = new ArraySet<>(); + private void buildFinishTransaction(SurfaceControl.Transaction t, TransitionInfo info, + DisplayContent[] participantDisplays) { for (int i = mTargets.size() - 1; i >= 0; --i) { final WindowContainer<?> target = mTargets.get(i).mContainer; if (target.getParent() == null) continue; @@ -1065,7 +1070,6 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener { t.setCornerRadius(targetLeash, 0); t.setShadowRadius(targetLeash, 0); t.setAlpha(targetLeash, 1); - displays.add(target.getDisplayContent()); // For config-at-end, the end-transform will be reset after the config is actually // applied in the client (since the transform depends on config). The other properties // remain here because shell might want to persistently override them. @@ -1079,9 +1083,8 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener { } // Need to update layers on involved displays since they were all paused while // 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; - assignLayers(displays.valueAt(i), t); + for (int i = participantDisplays.length - 1; i >= 0; --i) { + assignLayers(participantDisplays[i], t); } for (int i = 0; i < info.getRootCount(); ++i) { @@ -1207,10 +1210,14 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener { * The transition has finished animating and is ready to finalize WM state. This should not * be called directly; use {@link TransitionController#finishTransition} instead. */ - void finishTransition() { + void finishTransition(@NonNull ActionChain chain) { if (Trace.isTagEnabled(TRACE_TAG_WINDOW_MANAGER) && mIsPlayerEnabled) { asyncTraceEnd(System.identityHashCode(this)); } + if (!chain.isFinishing()) { + throw new IllegalStateException("Can't finish on a non-finishing transition " + + chain.mTransition); + } mLogger.mFinishTimeNs = SystemClock.elapsedRealtimeNanos(); mController.mLoggerHandler.post(mLogger::logOnFinish); mController.mTransitionTracer.logFinishedTransition(this); @@ -1790,6 +1797,8 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener { mController.moveToPlaying(this); // Repopulate the displays based on the resolved targets. + final DisplayContent[] participantDisplays = mTargetDisplays.toArray( + new DisplayContent[mTargetDisplays.size()]); mTargetDisplays.clear(); for (int i = 0; i < info.getRootCount(); ++i) { final DisplayContent dc = mController.mAtm.mRootWindowContainer.getDisplayContent( @@ -1883,7 +1892,9 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener { controller.setupStartTransaction(transaction); } } - buildFinishTransaction(mFinishTransaction, info); + // Use participant displays here (rather than just targets) because it's possible for + // there to be order changes between non-top tasks in an otherwise no-op transition. + buildFinishTransaction(mFinishTransaction, info, participantDisplays); mCleanupTransaction = mController.mAtm.mWindowManager.mTransactionFactory.get(); buildCleanupTransaction(mCleanupTransaction, info); if (mController.getTransitionPlayer() != null && mIsPlayerEnabled) { @@ -2163,7 +2174,7 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener { if (mFinishTransaction != null) { mFinishTransaction.apply(); } - mController.finishTransition(this); + mController.finishTransition(mController.mAtm.mChainTracker.startFinish("clean-up", this)); } private void cleanUpInternal() { @@ -2811,7 +2822,7 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener { } } final SurfaceControl rootLeash = leashReference.makeAnimationLeash().setName( - "Transition Root: " + leashReference.getName()) + "Transition Root: " + leashReference.getName()) .setCallsite("Transition.calculateTransitionRoots").build(); rootLeash.setUnreleasedWarningCallSite("Transition.calculateTransitionRoots"); // Update layers to start transaction because we prevent assignment during collect, so @@ -2982,7 +2993,8 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener { // Create the options based on this change's custom animations and layout // parameters animOptions = getOptions(activityRecord /* customAnimActivity */, - activityRecord /* animLpActivity */); + activityRecord /* animLpActivity */); + animOptions.setUserId(activityRecord.mUserId); if (!change.hasFlags(FLAG_TRANSLUCENT)) { // If this change is not translucent, its options are going to be // inherited by the changes below @@ -2992,6 +3004,7 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener { } else if (activityRecord != null && animOptionsForActivityTransition != null) { // Use the same options from the top activity for all the activities animOptions = animOptionsForActivityTransition; + animOptions.setUserId(activityRecord.mUserId); } else if (Flags.activityEmbeddingOverlayPresentationFlag() && isEmbeddedTaskFragment) { final TaskFragmentAnimationParams params = taskFragment.getAnimationParams(); @@ -3004,6 +3017,7 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener { params.getOpenAnimationResId(), params.getChangeAnimationResId(), params.getCloseAnimationResId(), 0 /* backgroundColor */, false /* overrideTaskTransition */); + animOptions.setUserId(taskFragment.getTask().mUserId); } } if (animOptions != null) { @@ -3067,6 +3081,9 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener { animOptions); animOptions = addCustomActivityTransition(customAnimActivity, false /* open */, animOptions); + if (animOptions != null) { + animOptions.setUserId(customAnimActivity.mUserId); + } } // Layout parameters @@ -3080,10 +3097,12 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener { // are running an app starting animation, in which case we don't want the app to be // able to change its animation directly. if (animOptions != null) { + animOptions.setUserId(animLpActivity.mUserId); animOptions.addOptionsFromLayoutParameters(animLp); } else { animOptions = TransitionInfo.AnimationOptions .makeAnimOptionsFromLayoutParameters(animLp); + animOptions.setUserId(animLpActivity.mUserId); } } return animOptions; @@ -3109,6 +3128,7 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener { if (animOptions == null) { animOptions = TransitionInfo.AnimationOptions .makeCommonAnimOptions(activity.packageName); + animOptions.setUserId(activity.mUserId); } animOptions.addCustomActivityTransition(open, customAnim.mEnterAnim, customAnim.mExitAnim, customAnim.mBackgroundColor); @@ -3237,7 +3257,7 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener { // Remote animations always win, but fullscreen windows override non-fullscreen windows. ActivityRecord result = lookForTopWindowWithFilter(sortedTargets, w -> w.getRemoteAnimationDefinition() != null - && w.getRemoteAnimationDefinition().hasTransition(transit, activityTypes)); + && w.getRemoteAnimationDefinition().hasTransition(transit, activityTypes)); if (result != null) { return result; } @@ -3284,7 +3304,7 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener { private void validateKeyguardOcclusion() { if ((mFlags & KEYGUARD_VISIBILITY_TRANSIT_FLAGS) != 0) { mController.mStateValidators.add( - mController.mAtm.mWindowManager.mPolicy::applyKeyguardOcclusionChange); + mController.mAtm.mWindowManager.mPolicy::applyKeyguardOcclusionChange); } } @@ -3379,6 +3399,11 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener { return false; } + void recordChain(@NonNull ActionChain chain) { + chain.mPrevious = mChainHead; + mChainHead = chain; + } + @VisibleForTesting static class ChangeInfo { private static final int FLAG_NONE = 0; @@ -3888,9 +3913,9 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener { /** @return true if all tracked subtrees are ready. */ boolean allReady() { - ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, " allReady query: used=%b " - + "override=%b defer=%d states=[%s]", mUsed, mReadyOverride, mDeferReadyDepth, - groupsToString()); + ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, + " allReady query: used=%b " + "override=%b defer=%d states=[%s]", mUsed, + mReadyOverride, mDeferReadyDepth, groupsToString()); // If the readiness has never been touched, mUsed will be false. We never want to // consider a transition ready if nothing has been reported on it. if (!mUsed) return false; diff --git a/services/core/java/com/android/server/wm/TransitionController.java b/services/core/java/com/android/server/wm/TransitionController.java index 9bbf10243133..1d2a605e8dae 100644 --- a/services/core/java/com/android/server/wm/TransitionController.java +++ b/services/core/java/com/android/server/wm/TransitionController.java @@ -52,8 +52,8 @@ import android.window.WindowContainerTransaction; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.protolog.ProtoLogGroup; import com.android.internal.protolog.ProtoLog; +import com.android.internal.protolog.ProtoLogGroup; import com.android.server.FgThread; import com.android.window.flags.Flags; @@ -880,10 +880,10 @@ class TransitionController { } /** @see Transition#setOverrideAnimation */ - void setOverrideAnimation(TransitionInfo.AnimationOptions options, + void setOverrideAnimation(TransitionInfo.AnimationOptions options, ActivityRecord r, @Nullable IRemoteCallback startCallback, @Nullable IRemoteCallback finishCallback) { if (mCollectingTransition == null) return; - mCollectingTransition.setOverrideAnimation(options, startCallback, finishCallback); + mCollectingTransition.setOverrideAnimation(options, r, startCallback, finishCallback); } void setNoAnimation(WindowContainer wc) { @@ -921,7 +921,12 @@ class TransitionController { } /** @see Transition#finishTransition */ - void finishTransition(Transition record) { + void finishTransition(@NonNull ActionChain chain) { + if (!chain.isFinishing()) { + throw new IllegalStateException("Can't finish on a non-finishing transition " + + chain.mTransition); + } + final Transition record = chain.mTransition; // It is usually a no-op but make sure that the metric consumer is removed. mTransitionMetricsReporter.reportAnimationStart(record.getToken(), 0 /* startTime */); // It is a no-op if the transition did not change the display. @@ -937,7 +942,7 @@ class TransitionController { mTrackCount = 0; } updateRunningRemoteAnimation(record, false /* isPlaying */); - record.finishTransition(); + record.finishTransition(chain); for (int i = mAnimatingExitWindows.size() - 1; i >= 0; i--) { final WindowState w = mAnimatingExitWindows.get(i); if (w.mAnimatingExit && w.mHasSurface && !w.inTransition()) { diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 74931781e752..979b3a5697cf 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -8977,9 +8977,8 @@ public class WindowManagerService extends IWindowManager.Stub } private boolean shouldDelayTouchOutside(InputTarget t) { - final WindowState w = t.getWindowState(); - final ActivityRecord activity = w != null ? w.getActivityRecord() : null; - final Task task = w != null ? w.getRootTask() : null; + final ActivityRecord activity = t.getActivityRecord(); + final Task task = activity != null ? activity.getTask() : null; final boolean isInputTargetNotFocused = mFocusedInputTarget != t && mFocusedInputTarget != null; diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java index e1e64ee10245..476443aa2050 100644 --- a/services/core/java/com/android/server/wm/WindowOrganizerController.java +++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java @@ -223,7 +223,8 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub final long ident = Binder.clearCallingIdentity(); try { synchronized (mGlobalLock) { - applyTransaction(t, -1 /*syncId*/, null /*transition*/, caller); + final ActionChain chain = mService.mChainTracker.startLegacy("applyTransactLegacy"); + applyTransaction(t, -1 /*syncId*/, chain, caller); } } finally { Binder.restoreCallingIdentity(ident); @@ -242,7 +243,8 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub try { synchronized (mGlobalLock) { if (callback == null) { - applyTransaction(t, -1 /* syncId*/, null /*transition*/, caller); + final ActionChain chain = mService.mChainTracker.startLegacy("applySyncLegacy"); + applyTransaction(t, -1 /* syncId*/, chain, caller); return -1; } @@ -262,13 +264,15 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub final int syncId = syncGroup.mSyncId; if (mTransitionController.isShellTransitionsEnabled()) { mTransitionController.startLegacySyncOrQueue(syncGroup, (deferred) -> { - applyTransaction(t, syncId, null /* transition */, caller, deferred); + applyTransaction(t, syncId, mService.mChainTracker.startLegacy( + "applySyncLegacy"), caller, deferred); setSyncReady(syncId); }); } else { if (!mService.mWindowManager.mSyncEngine.hasActiveSync()) { mService.mWindowManager.mSyncEngine.startSyncSet(syncGroup); - applyTransaction(t, syncId, null /*transition*/, caller); + applyTransaction(t, syncId, mService.mChainTracker.startLegacy( + "applySyncLegacy"), caller); setSyncReady(syncId); } else { // Because the BLAST engine only supports one sync at a time, queue the @@ -276,7 +280,8 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub mService.mWindowManager.mSyncEngine.queueSyncSet( () -> mService.mWindowManager.mSyncEngine.startSyncSet(syncGroup), () -> { - applyTransaction(t, syncId, null /*transition*/, caller); + applyTransaction(t, syncId, mService.mChainTracker.startLegacy( + "applySyncLegacy"), caller); setSyncReady(syncId); }); } @@ -313,7 +318,8 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub throw new IllegalArgumentException("Can't use legacy transitions in" + " compatibility mode with no WCT."); } - applyTransaction(t, -1 /* syncId */, null, caller); + applyTransaction(t, -1 /* syncId */, + mService.mChainTracker.startLegacy("wrongLegacyTransit"), caller); return null; } final WindowContainerTransaction wct = @@ -334,10 +340,11 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub nextTransition.calcParallelCollectType(wct); mTransitionController.startCollectOrQueue(nextTransition, (deferred) -> { + final ActionChain chain = mService.mChainTracker.start( + "startNewTransit", nextTransition); nextTransition.start(); nextTransition.mLogger.mStartWCT = wct; - applyTransaction(wct, -1 /* syncId */, nextTransition, caller, - deferred); + applyTransaction(wct, -1 /* syncId */, chain, caller, deferred); wctApplied.meet(); if (needsSetReady) { setAllReadyIfNeeded(nextTransition, wct); @@ -351,7 +358,9 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub Slog.e(TAG, "Trying to start a transition that isn't collecting. This probably" + " means Shell took too long to respond to a request. WM State may be" + " incorrect now, please file a bug"); - applyTransaction(wct, -1 /*syncId*/, null /*transition*/, caller); + final ActionChain chain = mService.mChainTracker.startFailsafe("startTransit"); + chain.mTransition = null; + applyTransaction(wct, -1 /*syncId*/, chain, caller); return transition.getToken(); } // Currently, application of wct can span multiple looper loops (ie. @@ -367,16 +376,20 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub if (transition.shouldApplyOnDisplayThread()) { mService.mH.post(() -> { synchronized (mService.mGlobalLock) { + final ActionChain chain = mService.mChainTracker.start( + "startTransit", transition); transition.start(); - applyTransaction(wct, -1 /* syncId */, transition, caller); + applyTransaction(wct, -1 /* syncId */, chain, caller); if (wctApplied != null) { wctApplied.meet(); } } }); } else { + final ActionChain chain = mService.mChainTracker.start("startTransit", + transition); transition.start(); - applyTransaction(wct, -1 /* syncId */, transition, caller); + applyTransaction(wct, -1 /* syncId */, chain, caller); if (wctApplied != null) { wctApplied.meet(); } @@ -475,7 +488,8 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub dc.mAppTransition.overridePendingAppTransitionRemote(adapter, true /* sync */, false /* isActivityEmbedding */); syncId = startSyncWithOrganizer(callback); - applyTransaction(t, syncId, null /* transition */, caller); + applyTransaction(t, syncId, mService.mChainTracker.startLegacy("legacyTransit"), + caller); setSyncReady(syncId); } } finally { @@ -493,6 +507,8 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub try { synchronized (mGlobalLock) { final Transition transition = Transition.fromBinder(transitionToken); + final ActionChain chain = + mService.mChainTracker.startFinish("finishTransit", transition); // apply the incoming transaction before finish in case it alters the visibility // of the participants. if (t != null) { @@ -500,9 +516,9 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub // changes of the transition participants will only set visible-requested // and still let finishTransition handle the participants. mTransitionController.mFinishingTransition = transition; - applyTransaction(t, -1 /* syncId */, null /*transition*/, caller, transition); + applyTransaction(t, -1 /* syncId */, chain, caller); } - mTransitionController.finishTransition(transition); + mTransitionController.finishTransition(chain); mTransitionController.mFinishingTransition = null; } } finally { @@ -537,9 +553,10 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub final CallerInfo caller = new CallerInfo(); final long ident = Binder.clearCallingIdentity(); try { - if (mTransitionController.getTransitionPlayer() == null) { + if (!mTransitionController.isShellTransitionsEnabled()) { // No need to worry about transition when Shell transition is not enabled. - applyTransaction(wct, -1 /* syncId */, null /* transition */, caller); + applyTransaction(wct, -1 /* syncId */, + mService.mChainTracker.startLegacy("legacyTFTransact"), caller); return; } @@ -548,8 +565,8 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub // Although there is an active sync, we want to apply the transaction now. // TODO(b/232042367) Redesign the organizer update on activity callback so that we // we will know about the transition explicitly. - final Transition transition = mTransitionController.getCollectingTransition(); - if (transition == null) { + final ActionChain chain = mService.mChainTracker.startDefault("tfTransact"); + if (chain.mTransition == null) { // This should rarely happen, and we should try to avoid using // {@link #applySyncTransaction} with Shell transition. // We still want to apply and merge the transaction to the active sync @@ -559,7 +576,7 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub + " because there is an ongoing sync for" + " applySyncTransaction()."); } - applyTransaction(wct, -1 /* syncId */, transition, caller); + applyTransaction(wct, -1 /* syncId */, chain, caller); return; } @@ -570,8 +587,9 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub transition.abort(); return; } - if (applyTransaction(wct, -1 /* syncId */, transition, caller, deferred) - == TRANSACT_EFFECTS_NONE && transition.mParticipants.isEmpty()) { + final ActionChain chain = mService.mChainTracker.start("tfTransact", transition); + final int effects = applyTransaction(wct, -1 /* syncId */, chain, caller, deferred); + if (effects == TRANSACT_EFFECTS_NONE && transition.mParticipants.isEmpty()) { transition.abort(); return; } @@ -586,15 +604,10 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub } private int applyTransaction(@NonNull WindowContainerTransaction t, int syncId, - @Nullable Transition transition, @NonNull CallerInfo caller) { - return applyTransaction(t, syncId, transition, caller, null /* finishTransition */); - } - - private int applyTransaction(@NonNull WindowContainerTransaction t, int syncId, - @Nullable Transition transition, @NonNull CallerInfo caller, boolean deferred) { + @NonNull ActionChain chain, @NonNull CallerInfo caller, boolean deferred) { if (deferred) { try { - return applyTransaction(t, syncId, transition, caller); + return applyTransaction(t, syncId, chain, caller); } catch (RuntimeException e) { // If the transaction is deferred, the caller could be from TransitionController // #tryStartCollectFromQueue that executes on system's worker thread rather than @@ -604,19 +617,17 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub } return TRANSACT_EFFECTS_NONE; } - return applyTransaction(t, syncId, transition, caller); + return applyTransaction(t, syncId, chain, caller); } /** * @param syncId If non-null, this will be a sync-transaction. - * @param transition A transition to collect changes into. + * @param chain A lifecycle-chain to acculumate changes into. * @param caller Info about the calling process. - * @param finishTransition The transition that is currently being finished. * @return The effects of the window container transaction. */ private int applyTransaction(@NonNull WindowContainerTransaction t, int syncId, - @Nullable Transition transition, @NonNull CallerInfo caller, - @Nullable Transition finishTransition) { + @NonNull ActionChain chain, @NonNull CallerInfo caller) { int effects = TRANSACT_EFFECTS_NONE; ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Apply window transaction, syncId=%d", syncId); mService.deferWindowLayout(); @@ -624,20 +635,21 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub boolean deferResume = true; mService.mTaskSupervisor.setDeferRootVisibilityUpdate(true /* deferUpdate */); boolean deferTransitionReady = false; - if (transition != null && !t.isEmpty()) { - if (transition.isCollecting()) { + if (chain.mTransition != null && !t.isEmpty() && !chain.isFinishing()) { + if (chain.mTransition.isCollecting()) { deferTransitionReady = true; - transition.deferTransitionReady(); + chain.mTransition.deferTransitionReady(); } else { Slog.w(TAG, "Transition is not collecting when applyTransaction." - + " transition=" + transition + " state=" + transition.getState()); - transition = null; + + " transition=" + chain.mTransition + " state=" + + chain.mTransition.getState()); + chain.mTransition = null; } } try { final ArraySet<WindowContainer<?>> haveConfigChanges = new ArraySet<>(); - if (transition != null) { - transition.applyDisplayChangeIfNeeded(haveConfigChanges); + if (chain.mTransition != null) { + chain.mTransition.applyDisplayChangeIfNeeded(haveConfigChanges); if (!haveConfigChanges.isEmpty()) { effects |= TRANSACT_EFFECTS_CLIENT_CONFIG; } @@ -645,7 +657,7 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub final List<WindowContainerTransaction.HierarchyOp> hops = t.getHierarchyOps(); final int hopSize = hops.size(); Iterator<Map.Entry<IBinder, WindowContainerTransaction.Change>> entries; - if (transition != null) { + if (chain.mTransition != null) { // Mark any config-at-end containers before applying config changes so that // the config changes don't dispatch to client. entries = t.getChanges().entrySet().iterator(); @@ -655,7 +667,7 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub if (!entry.getValue().getConfigAtTransitionEnd()) continue; final WindowContainer wc = WindowContainer.fromBinder(entry.getKey()); if (wc == null || !wc.isAttached()) continue; - transition.setConfigAtEnd(wc); + chain.mTransition.setConfigAtEnd(wc); } } entries = t.getChanges().entrySet().iterator(); @@ -672,15 +684,13 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub if (syncId >= 0) { addToSyncSet(syncId, wc); } - if (transition != null) transition.collect(wc); + chain.collect(wc); if ((entry.getValue().getChangeMask() & WindowContainerTransaction.Change.CHANGE_FORCE_NO_PIP) != 0) { // Disable entering pip (eg. when recents pretends to finish itself) - if (finishTransition != null) { - finishTransition.setCanPipOnFinish(false /* canPipOnFinish */); - } else if (transition != null) { - transition.setCanPipOnFinish(false /* canPipOnFinish */); + if (chain.mTransition != null) { + chain.mTransition.setCanPipOnFinish(false /* canPipOnFinish */); } } // A bit hacky, but we need to detect "remove PiP" so that we can "wrap" the @@ -728,9 +738,9 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub if (hopSize > 0) { final boolean isInLockTaskMode = mService.isInLockTaskMode(); for (int i = 0; i < hopSize; ++i) { - effects |= applyHierarchyOp(hops.get(i), effects, syncId, transition, + effects |= applyHierarchyOp(hops.get(i), effects, syncId, chain, isInLockTaskMode, caller, t.getErrorCallbackToken(), - t.getTaskFragmentOrganizer(), finishTransition); + t.getTaskFragmentOrganizer()); } } // Queue-up bounds-change transactions for tasks which are now organized. Do @@ -789,7 +799,7 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub } } finally { if (deferTransitionReady) { - transition.continueTransitionReady(); + chain.mTransition.continueTransitionReady(); } mService.mTaskSupervisor.setDeferRootVisibilityUpdate(false /* deferUpdate */); if (deferResume) { @@ -1079,9 +1089,9 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub } private int applyHierarchyOp(WindowContainerTransaction.HierarchyOp hop, int effects, - int syncId, @Nullable Transition transition, boolean isInLockTaskMode, + int syncId, @NonNull ActionChain chain, boolean isInLockTaskMode, @NonNull CallerInfo caller, @Nullable IBinder errorCallbackToken, - @Nullable ITaskFragmentOrganizer organizer, @Nullable Transition finishTransition) { + @Nullable ITaskFragmentOrganizer organizer) { final int type = hop.getType(); switch (type) { case HIERARCHY_OP_TYPE_REMOVE_TASK: { @@ -1151,7 +1161,7 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub break; } case HIERARCHY_OP_TYPE_CHILDREN_TASKS_REPARENT: { - effects |= reparentChildrenTasksHierarchyOp(hop, transition, syncId, + effects |= reparentChildrenTasksHierarchyOp(hop, chain.mTransition, syncId, isInLockTaskMode); break; } @@ -1204,13 +1214,13 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub if (syncId >= 0) { addToSyncSet(syncId, wc); } - if (transition != null) { - transition.collect(wc); + if (chain.mTransition != null) { + chain.mTransition.collect(wc); if (hop.isReparent()) { if (wc.getParent() != null) { // Collect the current parent. It's visibility may change as // a result of this reparenting. - transition.collect(wc.getParent()); + chain.mTransition.collect(wc.getParent()); } if (hop.getNewParent() != null) { final WindowContainer parentWc = @@ -1219,7 +1229,7 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub Slog.e(TAG, "Can't resolve parent window from token"); break; } - transition.collect(parentWc); + chain.mTransition.collect(parentWc); } } } @@ -1233,8 +1243,8 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub break; } case HIERARCHY_OP_TYPE_ADD_TASK_FRAGMENT_OPERATION: { - effects |= applyTaskFragmentOperation(hop, transition, isInLockTaskMode, caller, - errorCallbackToken, organizer); + effects |= applyTaskFragmentOperation(hop, chain, isInLockTaskMode, + caller, errorCallbackToken, organizer); break; } case HIERARCHY_OP_TYPE_PENDING_INTENT: { @@ -1329,7 +1339,7 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub Rect entryBounds = hop.getBounds(); mService.mRootWindowContainer.moveActivityToPinnedRootTask( pipActivity, null /* launchIntoPipHostActivity */, - "moveActivityToPinnedRootTask", null /* transition */, entryBounds); + "moveActivityToPinnedRootTask", entryBounds); if (pipActivity.isState(PAUSING) && pipActivity.mPauseSchedulePendingForPip) { // Continue the pausing process. This must be done after moving PiP activity to @@ -1348,13 +1358,13 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub break; } case HIERARCHY_OP_TYPE_RESTORE_TRANSIENT_ORDER: { - if (finishTransition == null) break; + if (!chain.isFinishing()) break; final WindowContainer container = WindowContainer.fromBinder(hop.getContainer()); if (container == null) break; final Task thisTask = container.asActivityRecord() != null ? container.asActivityRecord().getTask() : container.asTask(); if (thisTask == null) break; - final Task restoreAt = finishTransition.getTransientLaunchRestoreTarget(container); + final Task restoreAt = chain.mTransition.getTransientLaunchRestoreTarget(container); if (restoreAt == null) break; final TaskDisplayArea taskDisplayArea = thisTask.getTaskDisplayArea(); taskDisplayArea.moveRootTaskBehindRootTask(thisTask.getRootTask(), restoreAt); @@ -1444,7 +1454,7 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub * {@link #TRANSACT_EFFECTS_LIFECYCLE} or {@link #TRANSACT_EFFECTS_CLIENT_CONFIG}. */ private int applyTaskFragmentOperation(@NonNull WindowContainerTransaction.HierarchyOp hop, - @Nullable Transition transition, boolean isInLockTaskMode, @NonNull CallerInfo caller, + @NonNull ActionChain chain, boolean isInLockTaskMode, @NonNull CallerInfo caller, @Nullable IBinder errorCallbackToken, @Nullable ITaskFragmentOrganizer organizer) { if (!validateTaskFragmentOperation(hop, errorCallbackToken, organizer)) { return TRANSACT_EFFECTS_NONE; @@ -1467,7 +1477,7 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub break; } createTaskFragment(taskFragmentCreationParams, errorCallbackToken, caller, - transition); + chain.mTransition); break; } case OP_TYPE_DELETE_TASK_FRAGMENT: { @@ -1484,7 +1494,7 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub break; } } - effects |= deleteTaskFragment(taskFragment, transition); + effects |= deleteTaskFragment(taskFragment, chain.mTransition); break; } case OP_TYPE_START_ACTIVITY_IN_TASK_FRAGMENT: { @@ -1533,14 +1543,14 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub opType, exception); break; } - if (transition != null) { - transition.collect(activity); + if (chain.mTransition != null) { + chain.collect(activity); if (activity.getParent() != null) { // Collect the current parent. Its visibility may change as a result of // this reparenting. - transition.collect(activity.getParent()); + chain.collect(activity.getParent()); } - transition.collect(taskFragment); + chain.collect(taskFragment); } activity.reparent(taskFragment, POSITION_TOP); effects |= TRANSACT_EFFECTS_LIFECYCLE; @@ -1696,8 +1706,8 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub // 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) + final TaskFragment syncTaskFragment = chain.mTransition != null + ? task.getTaskFragment(chain.mTransition.mParticipants::contains) : null; if (syncTaskFragment != null) { @@ -1749,7 +1759,7 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub // 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); + runAfterTransition(chain.mTransition, task::commitDecorSurfaceBoostedState); } break; } diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index e5e153a41dbc..7c05c29baf28 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -1704,18 +1704,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP return mActivityRecord != null ? mActivityRecord.getTaskFragment() : null; } - @Nullable Task getRootTask() { - final Task task = getTask(); - if (task != null) { - return task.getRootTask(); - } - // Some system windows (e.g. "Power off" dialog) don't have a task, but we would still - // associate them with some root task to enable dimming. - final DisplayContent dc = getDisplayContent(); - return mAttrs.type >= FIRST_SYSTEM_WINDOW - && dc != null ? dc.getDefaultTaskDisplayArea().getRootHomeTask() : null; - } - /** * Retrieves the visible bounds of the window. * @param bounds The rect which gets the bounds. @@ -2570,10 +2558,9 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP return false; } - final Task rootTask = getRootTask(); - if (rootTask != null && !rootTask.isFocusable()) { - // Ignore when the root task shouldn't receive input event. - // (i.e. the minimized root task in split screen mode.) + final Task task = getTask(); + if (task != null && !task.isFocusable()) { + // The task can be set as non-focusable, e.g. swapping split-screen sides. return false; } @@ -2599,7 +2586,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP } // Don't allow transient-launch activities to take IME. - if (rootTask != null && mActivityRecord != null + if (task != null && mActivityRecord != null && mTransitionController.isTransientLaunch(mActivityRecord)) { return false; } @@ -2785,11 +2772,9 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP // means we need to intercept touches outside of that window. The dim layer // user associated with the window (task or root task) will give us the good // bounds, as they would be used to display the dim layer. - final TaskFragment taskFragment = getTaskFragment(); + final TaskFragment taskFragment = mActivityRecord.getTaskFragment(); if (taskFragment != null) { taskFragment.getDimBounds(mTmpRect); - } else if (getRootTask() != null) { - getRootTask().getDimBounds(mTmpRect); } } } @@ -3934,14 +3919,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP } } - private int getRootTaskId() { - final Task rootTask = getRootTask(); - if (rootTask == null) { - return INVALID_TASK_ID; - } - return rootTask.mTaskId; - } - public void registerFocusObserver(IWindowFocusObserver observer) { synchronized (mWmService.mGlobalLock) { if (mFocusCallbacks == null) { @@ -4077,7 +4054,12 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP final long token = proto.start(fieldId); super.dumpDebug(proto, WINDOW_CONTAINER, logLevel); proto.write(DISPLAY_ID, getDisplayId()); - proto.write(STACK_ID, getRootTaskId()); + int rootTaskId = INVALID_TASK_ID; + final Task task = getTask(); + if (task != null) { + rootTaskId = task.getRootTaskId(); + } + proto.write(STACK_ID, rootTaskId); mAttrs.dumpDebug(proto, ATTRIBUTES); mGivenContentInsets.dumpDebug(proto, GIVEN_CONTENT_INSETS); mWindowFrames.dumpDebug(proto, WINDOW_FRAMES); @@ -4135,8 +4117,9 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP @Override void dump(PrintWriter pw, String prefix, boolean dumpAll) { pw.print(prefix + "mDisplayId=" + getDisplayId()); - if (getRootTask() != null) { - pw.print(" rootTaskId=" + getRootTaskId()); + final Task task = getTask(); + if (task != null) { + pw.print(" taskId=" + task.mTaskId); } pw.println(" mSession=" + mSession + " mClient=" + mClient.asBinder()); diff --git a/services/core/java/com/android/server/wm/utils/DesktopModeFlagsUtil.java b/services/core/java/com/android/server/wm/utils/DesktopModeFlagsUtil.java index 70c66de22ddf..d33313e08742 100644 --- a/services/core/java/com/android/server/wm/utils/DesktopModeFlagsUtil.java +++ b/services/core/java/com/android/server/wm/utils/DesktopModeFlagsUtil.java @@ -43,7 +43,7 @@ public enum DesktopModeFlagsUtil { // All desktop mode related flags to be overridden by developer option toggle will be added here DESKTOP_WINDOWING_MODE( Flags::enableDesktopWindowingMode, /* shouldOverrideByDevOption= */ true), - DYNAMIC_INITIAL_BOUNDS(Flags::enableWindowingDynamicInitialBounds, true); + DYNAMIC_INITIAL_BOUNDS(Flags::enableWindowingDynamicInitialBounds, false); private static final String TAG = "DesktopModeFlagsUtil"; // Function called to obtain aconfig flag value. diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java b/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java index 6a0dd5a04f82..5eec0124a9e3 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java @@ -1325,27 +1325,7 @@ class ActiveAdmin { pw.print("encryptionRequested="); pw.println(encryptionRequested); - if (!Flags.dumpsysPolicyEngineMigrationEnabled()) { - pw.print("disableCamera="); - pw.println(disableCamera); - - pw.print("disableScreenCapture="); - pw.println(disableScreenCapture); - - pw.print("requireAutoTime="); - pw.println(requireAutoTime); - - if (permittedInputMethods != null) { - pw.print("permittedInputMethods="); - pw.println(permittedInputMethods); - } - - pw.println("userRestrictions:"); - UserRestrictionsUtils.dumpRestrictions(pw, " ", userRestrictions); - } - - if (!Flags.policyEngineMigrationV2Enabled() - || !Flags.dumpsysPolicyEngineMigrationEnabled()) { + if (!Flags.policyEngineMigrationV2Enabled()) { pw.print("mUsbDataSignaling="); pw.println(mUsbDataSignalingEnabled); } diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index a80ee0f66742..886ae7ad7e50 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -871,6 +871,16 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { EXEMPT_FROM_POWER_RESTRICTIONS, OPSTR_SYSTEM_EXEMPT_FROM_POWER_RESTRICTIONS); } + private static final Set<String> METERED_DATA_RESTRICTION_EXEMPT_ROLES = + new ArraySet<>(); + static { + // TODO(b/362545319): reference role name from role manager once it's exposed. + final String roleDeviceLockController = + "android.app.role.SYSTEM_FINANCED_DEVICE_CONTROLLER"; + METERED_DATA_RESTRICTION_EXEMPT_ROLES.add(roleDeviceLockController); + METERED_DATA_RESTRICTION_EXEMPT_ROLES.add(RoleManager.ROLE_FINANCED_DEVICE_KIOSK); + } + /** * Admin apps targeting Android S+ may not use * {@link android.app.admin.DevicePolicyManager#setPasswordQuality} to set password quality @@ -1959,6 +1969,10 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { return UserManager.isHeadlessSystemUserMode(); } + List<String> roleManagerGetRoleHoldersAsUser(String role, UserHandle userHandle) { + return getRoleManager().getRoleHoldersAsUser(role, userHandle); + } + @SuppressWarnings("AndroidFrameworkPendingIntentMutability") PendingIntent pendingIntentGetActivityAsUser(Context context, int requestCode, @NonNull Intent intent, int flags, Bundle options, UserHandle user) { @@ -11479,10 +11493,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { pw.println(); mStatLogger.dump(pw); pw.println(); - if (Flags.dumpsysPolicyEngineMigrationEnabled()) { - mDevicePolicyEngine.dump(pw); - pw.println(); - } + mDevicePolicyEngine.dump(pw); + pw.println(); pw.println("Encryption Status: " + getEncryptionStatusName(getEncryptionStatus())); pw.println("Logout user: " + getLogoutUserIdUnchecked()); pw.println(); @@ -12682,14 +12694,12 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { Preconditions.checkCallAuthorization(isDefaultDeviceOwner(caller)); checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_CREATE_AND_MANAGE_USER); - if (Flags.headlessDeviceOwnerSingleUserEnabled()) { - // Block this method if the device is in headless main user mode - Preconditions.checkCallAuthorization( - !mInjector.userManagerIsHeadlessSystemUserMode() - || getHeadlessDeviceOwnerModeForDeviceOwner() - != HEADLESS_DEVICE_OWNER_MODE_SINGLE_USER, - "createAndManageUser was called while in headless single user mode"); - } + // Block this method if the device is in headless main user mode + Preconditions.checkCallAuthorization( + !mInjector.userManagerIsHeadlessSystemUserMode() + || getHeadlessDeviceOwnerModeForDeviceOwner() + != HEADLESS_DEVICE_OWNER_MODE_SINGLE_USER, + "createAndManageUser was called while in headless single user mode"); // Only allow the system user to use this method Preconditions.checkCallAuthorization(caller.getUserHandle().isSystem(), @@ -13976,11 +13986,9 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { UserManager.DISALLOW_THREAD_NETWORK, new String[]{MANAGE_DEVICE_POLICY_THREAD_NETWORK}); } - if (Flags.assistContentUserRestrictionEnabled()) { - USER_RESTRICTION_PERMISSIONS.put( - UserManager.DISALLOW_ASSIST_CONTENT, - new String[]{MANAGE_DEVICE_POLICY_ASSIST_CONTENT}); - } + USER_RESTRICTION_PERMISSIONS.put( + UserManager.DISALLOW_ASSIST_CONTENT, + new String[]{MANAGE_DEVICE_POLICY_ASSIST_CONTENT}); USER_RESTRICTION_PERMISSIONS.put( UserManager.DISALLOW_ULTRA_WIDEBAND_RADIO, new String[]{MANAGE_DEVICE_POLICY_NEARBY_COMMUNICATION}); USER_RESTRICTION_PERMISSIONS.put( @@ -17315,7 +17323,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { return STATUS_HEADLESS_SYSTEM_USER_MODE_NOT_SUPPORTED; } - if (Flags.headlessDeviceOwnerSingleUserEnabled() && isHeadlessModeSingleUser) { + if (isHeadlessModeSingleUser) { ensureSetUpUser = mUserManagerInternal.getMainUserId(); if (ensureSetUpUser == UserHandle.USER_NULL) { return STATUS_HEADLESS_ONLY_SYSTEM_USER; @@ -17906,15 +17914,28 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { }); } + private Set<String> getMeteredDataRestrictionExemptPackages(int userId) { + final Set<String> exemptPkgs = new ArraySet<>(); + for (String role: METERED_DATA_RESTRICTION_EXEMPT_ROLES) { + String pkg = getRoleHolderPackageNameOnUser(role, userId); + if (pkg != null) { + exemptPkgs.add(pkg); + } + } + + return exemptPkgs; + } + private List<String> removeInvalidPkgsForMeteredDataRestriction( int userId, List<String> pkgNames) { + final Set<String> exemptRolePkgs = getMeteredDataRestrictionExemptPackages(userId); synchronized (getLockObject()) { final Set<String> activeAdmins = getActiveAdminPackagesLocked(userId); final List<String> excludedPkgs = new ArrayList<>(); for (int i = pkgNames.size() - 1; i >= 0; --i) { final String pkgName = pkgNames.get(i); - // If the package is an active admin, don't restrict it. - if (activeAdmins.contains(pkgName)) { + // If the package is an active admin or exempt role, don't restrict it. + if (activeAdmins.contains(pkgName) || exemptRolePkgs.contains(pkgName)) { excludedPkgs.add(pkgName); continue; } @@ -19759,16 +19780,14 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } private void transferSubscriptionOwnership(ComponentName admin, ComponentName target) { - if (Flags.esimManagementEnabled()) { - SubscriptionManager subscriptionManager = mContext.getSystemService( - SubscriptionManager.class); - for (int subId : getSubscriptionIdsInternal(admin.getPackageName()).toArray()) { - try { - subscriptionManager.setGroupOwner(subId, target.getPackageName()); - } catch (Exception e) { - // Shouldn't happen. - Slogf.e(LOG_TAG, e, "Error setting group owner for subId: " + subId); - } + SubscriptionManager subscriptionManager = mContext.getSystemService( + SubscriptionManager.class); + for (int subId : getSubscriptionIdsInternal(admin.getPackageName()).toArray()) { + try { + subscriptionManager.setGroupOwner(subId, target.getPackageName()); + } catch (Exception e) { + // Shouldn't happen. + Slogf.e(LOG_TAG, e, "Error setting group owner for subId: " + subId); } } } @@ -20666,9 +20685,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { // have OP_RUN_ANY_IN_BACKGROUND app op and won't execute in the background. The // code below grants that app op, and once the exemption is in place, the user // won't be able to disable background usage anymore. - if (Flags.powerExemptionBgUsageFix() - && exemption == EXEMPT_FROM_POWER_RESTRICTIONS - && newMode == MODE_ALLOWED) { + if (exemption == EXEMPT_FROM_POWER_RESTRICTIONS && newMode == MODE_ALLOWED) { setBgUsageAppOp(appOpsMgr, appInfo); } } @@ -21759,8 +21776,6 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { */ @Nullable private String getRoleHolderPackageNameOnUser(String role, int userId) { - RoleManager roleManager = mContext.getSystemService(RoleManager.class); - // Clear calling identity as the RoleManager APIs require privileged permissions. return mInjector.binderWithCleanCallingIdentity(() -> { List<UserInfo> users; @@ -21772,7 +21787,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } for (UserInfo user : users) { List<String> roleHolders = - roleManager.getRoleHoldersAsUser(role, user.getUserHandle()); + mInjector.roleManagerGetRoleHoldersAsUser(role, user.getUserHandle()); if (!roleHolders.isEmpty()) { return roleHolders.get(0); } @@ -22065,8 +22080,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { setTimeAndTimezone(provisioningParams.getTimeZone(), provisioningParams.getLocalTime()); setLocale(provisioningParams.getLocale()); - int deviceOwnerUserId = Flags.headlessDeviceOwnerSingleUserEnabled() - && isSingleUserMode && mInjector.userManagerIsHeadlessSystemUserMode() + int deviceOwnerUserId = + isSingleUserMode && mInjector.userManagerIsHeadlessSystemUserMode() ? mUserManagerInternal.getMainUserId() : UserHandle.USER_SYSTEM; if (!removeNonRequiredAppsForManagedDevice( diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyDefinition.java b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyDefinition.java index 19a942cd2eed..24ee46fbcd6f 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyDefinition.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyDefinition.java @@ -536,7 +536,6 @@ final class PolicyDefinition<V> { USER_RESTRICTION_FLAGS.put( UserManager.DISALLOW_THREAD_NETWORK, POLICY_FLAG_GLOBAL_ONLY_POLICY); } - USER_RESTRICTION_FLAGS.put(UserManager.DISALLOW_ASSIST_CONTENT, /* flags= */ 0); for (String key : USER_RESTRICTION_FLAGS.keySet()) { createAndAddUserRestrictionPolicyDefinition(key, USER_RESTRICTION_FLAGS.get(key)); } diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyEnforcerCallbacks.java b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyEnforcerCallbacks.java index e1cb37dbeef5..8068d46d6a9d 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyEnforcerCallbacks.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyEnforcerCallbacks.java @@ -238,9 +238,7 @@ final class PolicyEnforcerCallbacks { } for (int user : resolveUsers(userId)) { - if (Flags.disallowUserControlBgUsageFix()) { - setBgUsageAppOp(packages, pmi, user, appOpsManager); - } + setBgUsageAppOp(packages, pmi, user, appOpsManager); if (Flags.disallowUserControlStoppedStateFix()) { for (String packageName : packages) { pmi.setPackageStoppedState(packageName, false, user); diff --git a/services/tests/dreamservicetests/src/com/android/server/dreams/DreamOverlayServiceTest.java b/services/tests/dreamservicetests/src/com/android/server/dreams/DreamOverlayServiceTest.java index 698ce13aa6db..1abc557c8cce 100644 --- a/services/tests/dreamservicetests/src/com/android/server/dreams/DreamOverlayServiceTest.java +++ b/services/tests/dreamservicetests/src/com/android/server/dreams/DreamOverlayServiceTest.java @@ -140,7 +140,7 @@ public class DreamOverlayServiceTest { // Start the dream. client.startDream(mLayoutParams, mOverlayCallback, - FIRST_DREAM_COMPONENT.flattenToString(), false); + FIRST_DREAM_COMPONENT.flattenToString(), false, false); // The callback should not have run yet. verify(monitor, never()).onStartDream(); @@ -198,22 +198,24 @@ public class DreamOverlayServiceTest { // Start a dream with the first client and ensure the dream is now active from the // overlay's perspective. firstClient.startDream(mLayoutParams, mOverlayCallback, - FIRST_DREAM_COMPONENT.flattenToString(), false); + FIRST_DREAM_COMPONENT.flattenToString(), true, false); verify(monitor).onStartDream(); assertThat(service.getDreamComponent()).isEqualTo(FIRST_DREAM_COMPONENT); + assertThat(service.isDreamInPreviewMode()).isTrue(); Mockito.clearInvocations(monitor); // Start a dream from the second client and verify that the overlay has both cycled to // the new dream (ended/started). secondClient.startDream(mLayoutParams, mOverlayCallback, - SECOND_DREAM_COMPONENT.flattenToString(), false); + SECOND_DREAM_COMPONENT.flattenToString(), false, false); verify(monitor).onEndDream(); verify(monitor).onStartDream(); assertThat(service.getDreamComponent()).isEqualTo(SECOND_DREAM_COMPONENT); + assertThat(service.isDreamInPreviewMode()).isFalse(); Mockito.clearInvocations(monitor); @@ -248,7 +250,7 @@ public class DreamOverlayServiceTest { // Start the dream. client.startDream(mLayoutParams, mOverlayCallback, - FIRST_DREAM_COMPONENT.flattenToString(), false); + FIRST_DREAM_COMPONENT.flattenToString(), false, false); // Make sure redirect state is set on dream. verify(mOverlayCallback).onRedirectWake(eq(true)); @@ -262,7 +264,7 @@ public class DreamOverlayServiceTest { service.redirectWake(true); clearInvocations(mOverlayCallback); client.startDream(mLayoutParams, mOverlayCallback, - FIRST_DREAM_COMPONENT.flattenToString(), false); + FIRST_DREAM_COMPONENT.flattenToString(), false, false); verify(mOverlayCallback).onRedirectWake(eq(true)); } diff --git a/services/tests/dreamservicetests/src/com/android/server/dreams/TestDreamEnvironment.java b/services/tests/dreamservicetests/src/com/android/server/dreams/TestDreamEnvironment.java index 43aa7fe46ca5..7c239ef02e58 100644 --- a/services/tests/dreamservicetests/src/com/android/server/dreams/TestDreamEnvironment.java +++ b/services/tests/dreamservicetests/src/com/android/server/dreams/TestDreamEnvironment.java @@ -385,7 +385,8 @@ public class TestDreamEnvironment { final ArgumentCaptor<IDreamOverlayCallback> overlayCallbackCaptor = ArgumentCaptor.forClass(IDreamOverlayCallback.class); verify(mDreamOverlayClient, description("dream client not informed of dream start")) - .startDream(any(), overlayCallbackCaptor.capture(), any(), anyBoolean()); + .startDream(any(), overlayCallbackCaptor.capture(), any(), anyBoolean(), + anyBoolean()); mDreamOverlayCallback = overlayCallbackCaptor.getValue(); } 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 8656b991b5fc..51aa5284f5f2 100644 --- a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java +++ b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java @@ -46,6 +46,7 @@ import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentat 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_IMPORTANCE_LEVELS; import static com.android.server.am.ProcessList.CACHED_APP_MAX_ADJ; import static com.android.server.am.ProcessList.CACHED_APP_MIN_ADJ; import static com.android.server.am.ProcessList.FOREGROUND_APP_ADJ; @@ -844,6 +845,49 @@ public class MockingOomAdjusterTests { @SuppressWarnings("GuardedBy") @Test + public void testUpdateOomAdj_DoAll_PreviousApp() { + final int numberOfApps = 15; + final ProcessRecord[] apps = new ProcessRecord[numberOfApps]; + for (int i = 0; i < numberOfApps; i++) { + apps[i] = spy(makeDefaultProcessRecord(MOCKAPP_PID + i, MOCKAPP_UID + i, + MOCKAPP_PROCESSNAME + i, MOCKAPP_PACKAGENAME + i, true)); + final WindowProcessController wpc = apps[i].getWindowProcessController(); + doReturn(true).when(wpc).isPreviousProcess(); + doReturn(true).when(wpc).hasActivities(); + } + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + setProcessesToLru(apps); + mService.mOomAdjuster.updateOomAdjLocked(OOM_ADJ_REASON_NONE); + + for (int i = 0; i < numberOfApps; i++) { + assertProcStates(apps[i], PROCESS_STATE_LAST_ACTIVITY, PREVIOUS_APP_ADJ, + SCHED_GROUP_BACKGROUND, "previous"); + } + + if (!Flags.followUpOomadjUpdates()) return; + + for (int i = 0; i < numberOfApps; i++) { + 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(); + + for (int i = 0; i < numberOfApps; i++) { + final int mruIndex = numberOfApps - i - 1; + int expectedAdj = CACHED_APP_MIN_ADJ + (mruIndex * 2 * CACHED_APP_IMPORTANCE_LEVELS); + if (expectedAdj > CACHED_APP_MAX_ADJ) { + expectedAdj = CACHED_APP_MAX_ADJ; + } + assertProcStates(apps[i], PROCESS_STATE_LAST_ACTIVITY, expectedAdj, + SCHED_GROUP_BACKGROUND, "previous-expired"); + } + } + + @SuppressWarnings("GuardedBy") + @Test public void testUpdateOomAdj_DoOne_Backup() { ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID, MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true)); diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java index c8cbbb5957ec..2e6c93cb92aa 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java @@ -102,6 +102,7 @@ import android.view.WindowManager; import android.view.accessibility.AccessibilityManager; import android.view.accessibility.AccessibilityWindowAttributes; import android.view.accessibility.IAccessibilityManager; +import android.view.accessibility.IUserInitializationCompleteCallback; import androidx.test.core.app.ApplicationProvider; import androidx.test.filters.SmallTest; @@ -137,6 +138,7 @@ import org.mockito.ArgumentMatchers; import org.mockito.Captor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import org.mockito.Spy; import org.mockito.internal.util.reflection.FieldReader; import org.mockito.internal.util.reflection.FieldSetter; import org.mockito.stubbing.Answer; @@ -209,6 +211,7 @@ public class AccessibilityManagerServiceTest { @Mock private FullScreenMagnificationController mMockFullScreenMagnificationController; @Mock private ProxyManager mProxyManager; @Mock private StatusBarManagerInternal mStatusBarManagerInternal; + @Spy private IUserInitializationCompleteCallback mUserInitializationCompleteCallback; @Captor private ArgumentCaptor<Intent> mIntentArgumentCaptor; private IAccessibilityManager mA11yManagerServiceOnDevice; private AccessibilityServiceConnection mAccessibilityServiceConnection; @@ -2042,6 +2045,36 @@ public class AccessibilityManagerServiceTest { .isEqualTo(ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR); } + @Test + public void registerUserInitializationCompleteCallback_isRegistered() { + mA11yms.mUserInitializationCompleteCallbacks.clear(); + + mA11yms.registerUserInitializationCompleteCallback(mUserInitializationCompleteCallback); + + assertThat(mA11yms.mUserInitializationCompleteCallbacks).containsExactly( + mUserInitializationCompleteCallback); + } + + @Test + public void unregisterUserInitializationCompleteCallback_isUnregistered() { + mA11yms.mUserInitializationCompleteCallbacks.clear(); + mA11yms.mUserInitializationCompleteCallbacks.add(mUserInitializationCompleteCallback); + + mA11yms.unregisterUserInitializationCompleteCallback(mUserInitializationCompleteCallback); + + assertThat(mA11yms.mUserInitializationCompleteCallbacks).isEmpty(); + } + + @Test + public void switchUser_callsUserInitializationCompleteCallback() throws RemoteException { + mA11yms.mUserInitializationCompleteCallbacks.add(mUserInitializationCompleteCallback); + + mA11yms.switchUser(UserHandle.MIN_SECONDARY_USER_ID); + + verify(mUserInitializationCompleteCallback).onUserInitializationComplete( + UserHandle.MIN_SECONDARY_USER_ID); + } + private Set<String> readStringsFromSetting(String setting) { final Set<String> result = new ArraySet<>(); mA11yms.readColonDelimitedSettingToSet( diff --git a/services/tests/servicestests/src/com/android/server/biometrics/AuthSessionTest.java b/services/tests/servicestests/src/com/android/server/biometrics/AuthSessionTest.java index bc2fd73f22ae..2f7b8d26bdd9 100644 --- a/services/tests/servicestests/src/com/android/server/biometrics/AuthSessionTest.java +++ b/services/tests/servicestests/src/com/android/server/biometrics/AuthSessionTest.java @@ -200,7 +200,8 @@ public class AuthSessionTest { eq(TEST_REQUEST_ID), eq(sensor.getCookie()), anyBoolean() /* allowBackgroundAuthentication */, - anyBoolean() /* isForLegacyFingerprintManager */); + anyBoolean() /* isForLegacyFingerprintManager */, + eq(false) /* isMandatoryBiometrics */); } final int cookie1 = session.mPreAuthInfo.eligibleSensors.get(0).getCookie(); diff --git a/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java b/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java index d2961bc8a90f..6b8e414255cd 100644 --- a/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java @@ -636,7 +636,8 @@ public class BiometricServiceTest { eq(TEST_REQUEST_ID), cookieCaptor.capture() /* cookie */, anyBoolean() /* allowBackgroundAuthentication */, - anyBoolean() /* isForLegacyFingerprintManager */); + anyBoolean() /* isForLegacyFingerprintManager */, + eq(false) /* isMandatoryBiometrics */); // onReadyForAuthentication, mAuthSession state OK mBiometricService.mImpl.onReadyForAuthentication(TEST_REQUEST_ID, cookieCaptor.getValue()); diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/AcquisitionClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/AcquisitionClientTest.java index 4604b310edf7..613cb2019a46 100644 --- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/AcquisitionClientTest.java +++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/AcquisitionClientTest.java @@ -98,7 +98,8 @@ public class AcquisitionClientTest { @NonNull ClientMonitorCallbackConverter callback) { super(context, lazyDaemon, token, callback, 0 /* userId */, "Test", 0 /* cookie */, TEST_SENSOR_ID /* sensorId */, true /* shouldVibrate */, - mock(BiometricLogger.class), mock(BiometricContext.class)); + mock(BiometricLogger.class), mock(BiometricContext.class), + false /* isMandatoryBiometrics */); } @Override diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerOperationTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerOperationTest.java index ffc78110d496..4f07380dfb5a 100644 --- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerOperationTest.java +++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerOperationTest.java @@ -62,7 +62,8 @@ public class BiometricSchedulerOperationTest { extends HalClientMonitor<T> { public InterruptableMonitor() { super(null, null, null, null, 0, null, 0, 0, - mock(BiometricLogger.class), mock(BiometricContext.class)); + mock(BiometricLogger.class), mock(BiometricContext.class), + false /* isMandatoryBiometrics */); } } diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java index 36a7b3dff28d..90c07d489549 100644 --- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java +++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java @@ -1051,6 +1051,11 @@ public class BiometricSchedulerTest { public String getAttributionTag() { return null; } + + @Override + public boolean isMandatoryBiometrics() { + return false; + } } private static class TestAuthenticationClient @@ -1176,7 +1181,7 @@ public class BiometricSchedulerTest { @NonNull Supplier<Object> lazyDaemon, int cookie, int protoEnum) { super(context, lazyDaemon, token /* token */, null /* listener */, 0 /* userId */, TAG, cookie, TEST_SENSOR_ID, mock(BiometricLogger.class), - mock(BiometricContext.class)); + mock(BiometricContext.class), false /* isMandatoryBiometrics */); mProtoEnum = protoEnum; } diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java index b4cc3434e013..698bda335f83 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java @@ -60,6 +60,7 @@ import com.android.server.wm.ActivityTaskManagerInternal; import java.io.File; import java.io.IOException; +import java.util.List; import java.util.Map; /** @@ -325,6 +326,11 @@ public class DevicePolicyManagerServiceTestable extends DevicePolicyManagerServi } @Override + List<String> roleManagerGetRoleHoldersAsUser(String role, UserHandle userHandle) { + return services.roleManagerForMock.getRoleHoldersAsUser(role, userHandle); + } + + @Override PendingIntent pendingIntentGetActivityAsUser(Context context, int requestCode, Intent intent, int flags, Bundle options, UserHandle user) { return null; diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java index b7483d67563c..cb4269a205e4 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java @@ -109,6 +109,7 @@ import android.app.admin.PasswordMetrics; import android.app.admin.PreferentialNetworkServiceConfig; import android.app.admin.SystemUpdatePolicy; import android.app.admin.WifiSsidPolicy; +import android.app.role.RoleManager; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.Intent; @@ -2889,6 +2890,52 @@ public class DevicePolicyManagerTest extends DpmTestBase { } @Test + public void testSetMeteredDataDisabledPackagesExemptRoles() throws Exception { + // TODO(b/362545319): reference role name from role manager once it's exposed. + final String controllerRole = "android.app.role.SYSTEM_FINANCED_DEVICE_CONTROLLER"; + + setAsProfileOwner(admin1); + + assertThat(dpm.getMeteredDataDisabledPackages(admin1)).isEmpty(); + + // Setup + final ArrayList<String> pkgsToRestrict = new ArrayList<>(); + final ArrayList<String> pkgsExpectedAsNotRestricted = new ArrayList<>(); + final String packageWithControllerRole = "com.example.controller"; + final String packageWithKioskRole = "com.example.kiosk"; + final String packageWithNotExemptRole = "com.example.notexempt"; + + pkgsToRestrict.add(packageWithControllerRole); + pkgsToRestrict.add(packageWithKioskRole); + pkgsToRestrict.add(packageWithNotExemptRole); + + pkgsExpectedAsNotRestricted.add(packageWithControllerRole); + pkgsExpectedAsNotRestricted.add(packageWithKioskRole); + + setupPackageInPackageManager(packageWithControllerRole, CALLER_USER_HANDLE, 123, 0); + setupPackageInPackageManager(packageWithKioskRole, CALLER_USER_HANDLE, 456, 0); + setupPackageInPackageManager(packageWithNotExemptRole, CALLER_USER_HANDLE, 789, 0); + + when(getServices().roleManagerForMock.getRoleHoldersAsUser(controllerRole, + UserHandle.of(CALLER_USER_HANDLE))) + .thenReturn(new ArrayList<>(Arrays.asList(packageWithControllerRole))); + when(getServices().roleManagerForMock.getRoleHoldersAsUser( + RoleManager.ROLE_FINANCED_DEVICE_KIOSK, + UserHandle.of(CALLER_USER_HANDLE))) + .thenReturn(new ArrayList<>(Arrays.asList(packageWithKioskRole))); + + List<String> excludedPkgs = dpm.setMeteredDataDisabledPackages(admin1, pkgsToRestrict); + + // Verify + assertThat(excludedPkgs).containsExactlyElementsIn(pkgsExpectedAsNotRestricted); + assertThat(dpm.getMeteredDataDisabledPackages(admin1)) + .isEqualTo(Arrays.asList(packageWithNotExemptRole)); + verify(getServices().networkPolicyManagerInternal).setMeteredRestrictedPackages( + MockUtils.checkApps(packageWithNotExemptRole), + eq(CALLER_USER_HANDLE)); + } + + @Test public void testSetGetMeteredDataDisabledPackages_deviceAdmin() { mContext.callerPermissions.add(permission.MANAGE_DEVICE_ADMINS); dpm.setActiveAdmin(admin1, true); diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java b/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java index 76aa40c04e3d..2e200a9268f5 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java @@ -142,6 +142,7 @@ public class MockSystemServices { public final DevicePolicyManager devicePolicyManager; public final LocationManager locationManager; public final RoleManager roleManager; + public final RoleManagerForMock roleManagerForMock; public final SubscriptionManager subscriptionManager; /** Note this is a partial mock, not a real mock. */ public final PackageManager packageManager; @@ -200,6 +201,7 @@ public class MockSystemServices { devicePolicyManager = mock(DevicePolicyManager.class); locationManager = mock(LocationManager.class); roleManager = realContext.getSystemService(RoleManager.class); + roleManagerForMock = mock(RoleManagerForMock.class); subscriptionManager = mock(SubscriptionManager.class); // Package manager is huge, so we use a partial mock instead. @@ -495,6 +497,12 @@ public class MockSystemServices { } } + public static class RoleManagerForMock { + public List<String> getRoleHoldersAsUser(String role, UserHandle userHandle) { + return new ArrayList<>(); + } + } + public static class SettingsForMock { public int settingsSecureGetIntForUser(String name, int def, int userHandle) { return 0; diff --git a/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java b/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java index 17b499e112bc..d6f7e21a2069 100644 --- a/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java +++ b/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java @@ -625,25 +625,10 @@ public class RebootEscrowManagerTests { // pretend reboot happens here when(mInjected.getBootCount()).thenReturn(1); - ArgumentCaptor<Boolean> metricsSuccessCaptor = ArgumentCaptor.forClass(Boolean.class); - ArgumentCaptor<Integer> metricsErrorCodeCaptor = ArgumentCaptor.forClass(Integer.class); - doNothing() - .when(mInjected) - .reportMetric( - metricsSuccessCaptor.capture(), - metricsErrorCodeCaptor.capture(), - eq(2) /* Server based */, - eq(1) /* attempt count */, - anyInt(), - eq(0) /* vbmeta status */, - anyInt()); + mService.loadRebootEscrowDataIfAvailable(null); verify(mServiceConnection, never()).unwrap(any(), anyLong()); verify(mCallbacks, never()).onRebootEscrowRestored(anyByte(), any(), anyInt()); - assertFalse(metricsSuccessCaptor.getValue()); - assertEquals( - Integer.valueOf(RebootEscrowManager.ERROR_NO_REBOOT_ESCROW_DATA), - metricsErrorCodeCaptor.getValue()); } @Test diff --git a/services/tests/servicestests/src/com/android/server/pm/UserRestrictionsUtilsTest.java b/services/tests/servicestests/src/com/android/server/pm/UserRestrictionsUtilsTest.java index 55c48e07162b..f0a5f7583661 100644 --- a/services/tests/servicestests/src/com/android/server/pm/UserRestrictionsUtilsTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/UserRestrictionsUtilsTest.java @@ -36,7 +36,6 @@ import android.util.SparseArray; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; -import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; @@ -55,11 +54,6 @@ public class UserRestrictionsUtilsTest { @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); - @Before - public void setUp() { - mSetFlagsRule.enableFlags(android.app.admin.flags.Flags.FLAG_ESIM_MANAGEMENT_ENABLED); - } - @Test public void testNonNull() { Bundle out = UserRestrictionsUtils.nonNull(null); @@ -144,7 +138,6 @@ public class UserRestrictionsUtilsTest { @Test public void testCanProfileOwnerChange_restrictionRequiresOrgOwnedDevice_orgOwned() { - mSetFlagsRule.enableFlags(android.app.admin.flags.Flags.FLAG_ESIM_MANAGEMENT_ENABLED); assertTrue(UserRestrictionsUtils.canProfileOwnerChange( UserManager.DISALLOW_SIM_GLOBALLY, false, @@ -157,7 +150,6 @@ public class UserRestrictionsUtilsTest { @Test public void testCanProfileOwnerChange_restrictionRequiresOrgOwnedDevice_notOrgOwned() { - mSetFlagsRule.enableFlags(android.app.admin.flags.Flags.FLAG_ESIM_MANAGEMENT_ENABLED); assertFalse(UserRestrictionsUtils.canProfileOwnerChange( UserManager.DISALLOW_SIM_GLOBALLY, false, @@ -169,22 +161,7 @@ public class UserRestrictionsUtilsTest { } @Test - public void - testCanProfileOwnerChange_disabled_restrictionRequiresOrgOwnedDevice_notOrgOwned() { - mSetFlagsRule.disableFlags(android.app.admin.flags.Flags.FLAG_ESIM_MANAGEMENT_ENABLED); - assertTrue(UserRestrictionsUtils.canProfileOwnerChange( - UserManager.DISALLOW_SIM_GLOBALLY, - false, - false)); - assertTrue(UserRestrictionsUtils.canProfileOwnerChange( - UserManager.DISALLOW_SIM_GLOBALLY, - true, - false)); - } - - @Test public void testCanProfileOwnerChange_restrictionNotRequiresOrgOwnedDevice_orgOwned() { - mSetFlagsRule.enableFlags(android.app.admin.flags.Flags.FLAG_ESIM_MANAGEMENT_ENABLED); assertTrue(UserRestrictionsUtils.canProfileOwnerChange( UserManager.DISALLOW_ADJUST_VOLUME, false, @@ -197,7 +174,6 @@ public class UserRestrictionsUtilsTest { @Test public void testCanProfileOwnerChange_restrictionNotRequiresOrgOwnedDevice_notOrgOwned() { - mSetFlagsRule.enableFlags(android.app.admin.flags.Flags.FLAG_ESIM_MANAGEMENT_ENABLED); assertTrue(UserRestrictionsUtils.canProfileOwnerChange( UserManager.DISALLOW_ADJUST_VOLUME, false, diff --git a/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/TunerResourceManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/TunerResourceManagerServiceTest.java index 963b27e010fa..bf58443194e5 100644 --- a/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/TunerResourceManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/TunerResourceManagerServiceTest.java @@ -38,6 +38,7 @@ import android.media.tv.tunerresourcemanager.TunerFrontendInfo; import android.media.tv.tunerresourcemanager.TunerFrontendRequest; import android.media.tv.tunerresourcemanager.TunerLnbRequest; import android.media.tv.tunerresourcemanager.TunerResourceManager; +import android.os.RemoteException; import android.platform.test.annotations.Presubmit; import androidx.test.InstrumentationRegistry; @@ -69,6 +70,61 @@ public class TunerResourceManagerServiceTest { private TunerResourceManagerService mTunerResourceManagerService; private boolean mIsForeground; + private final class TunerClient extends IResourcesReclaimListener.Stub { + int[] mClientId; + ClientProfile mProfile; + boolean mReclaimed; + + TunerClient() { + mClientId = new int[1]; + mClientId[0] = TunerResourceManagerService.INVALID_CLIENT_ID; + } + + public void register(String sessionId, int useCase) { + ResourceClientProfile profile = new ResourceClientProfile(); + profile.tvInputSessionId = sessionId; + profile.useCase = useCase; + mTunerResourceManagerService.registerClientProfileInternal( + profile, this, mClientId); + assertThat(mClientId[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID); + mProfile = mTunerResourceManagerService.getClientProfile(mClientId[0]); + } + + public void register(String sessionId, int useCase, int priority, int niceValue) { + register(sessionId, useCase); + mTunerResourceManagerService.updateClientPriorityInternal( + mClientId[0], priority, niceValue); + } + + public void register(String sessionId, int useCase, int priority) { + register(sessionId, useCase, priority, 0); + } + + public void unregister() { + mTunerResourceManagerService.unregisterClientProfileInternal(mClientId[0]); + mClientId[0] = TunerResourceManagerService.INVALID_CLIENT_ID; + mReclaimed = false; + } + + public int getId() { + return mClientId[0]; + } + + public ClientProfile getProfile() { + return mProfile; + } + + @Override + public void onReclaimResources() { + mTunerResourceManagerService.clearAllResourcesAndClientMapping(mProfile); + mReclaimed = true; + } + + public boolean isReclaimed() { + return mReclaimed; + } + } + private static final class TestResourcesReclaimListener extends IResourcesReclaimListener.Stub { boolean mReclaimed; @@ -247,13 +303,11 @@ public class TunerResourceManagerServiceTest { } @Test - public void requestFrontendTest_NoFrontendWithGiveTypeAvailable() { - ResourceClientProfile profile = resourceClientProfile("0" /*sessionId*/, - TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK); - int[] clientId = new int[1]; - mTunerResourceManagerService.registerClientProfileInternal( - profile, null /*listener*/, clientId); - assertThat(clientId[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID); + public void requestFrontendTest_NoFrontendWithGiveTypeAvailable() throws RemoteException { + // Register clients + TunerClient client0 = new TunerClient(); + client0.register("0" /*sessionId*/, + TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK); // Init frontend resources. TunerFrontendInfo[] infos = new TunerFrontendInfo[1]; @@ -262,21 +316,20 @@ public class TunerResourceManagerServiceTest { mTunerResourceManagerService.setFrontendInfoListInternal(infos); TunerFrontendRequest request = - tunerFrontendRequest(clientId[0] /*clientId*/, FrontendSettings.TYPE_DVBT); + tunerFrontendRequest(client0.getId() /*clientId*/, FrontendSettings.TYPE_DVBT); int[] frontendHandle = new int[1]; assertThat(mTunerResourceManagerService .requestFrontendInternal(request, frontendHandle)).isFalse(); assertThat(frontendHandle[0]).isEqualTo(TunerResourceManager.INVALID_RESOURCE_HANDLE); + client0.unregister(); } @Test - public void requestFrontendTest_FrontendWithNoExclusiveGroupAvailable() { - ResourceClientProfile profile = resourceClientProfile("0" /*sessionId*/, - TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK); - int[] clientId = new int[1]; - mTunerResourceManagerService.registerClientProfileInternal( - profile, null /*listener*/, clientId); - assertThat(clientId[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID); + public void requestFrontendTest_FrontendWithNoExclusiveGroupAvailable() throws RemoteException { + // Register clients + TunerClient client0 = new TunerClient(); + client0.register("0" /*sessionId*/, + TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK); // Init frontend resources. TunerFrontendInfo[] infos = new TunerFrontendInfo[3]; @@ -295,27 +348,23 @@ public class TunerResourceManagerServiceTest { mTunerResourceManagerService.setFrontendInfoListInternal(infos); TunerFrontendRequest request = - tunerFrontendRequest(clientId[0] /*clientId*/, FrontendSettings.TYPE_DVBT); + tunerFrontendRequest(client0.getId() /*clientId*/, FrontendSettings.TYPE_DVBT); int[] frontendHandle = new int[1]; assertThat(mTunerResourceManagerService .requestFrontendInternal(request, frontendHandle)).isTrue(); assertThat(frontendHandle[0]).isEqualTo(0); + client0.unregister(); } @Test - public void requestFrontendTest_FrontendWithExclusiveGroupAvailable() { - ResourceClientProfile profile0 = resourceClientProfile("0" /*sessionId*/, - TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK); - ResourceClientProfile profile1 = resourceClientProfile("1" /*sessionId*/, - TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK); - int[] clientId0 = new int[1]; - int[] clientId1 = new int[1]; - mTunerResourceManagerService.registerClientProfileInternal( - profile0, null /*listener*/, clientId0); - mTunerResourceManagerService.registerClientProfileInternal( - profile1, null /*listener*/, clientId1); - assertThat(clientId0[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID); - assertThat(clientId1[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID); + public void requestFrontendTest_FrontendWithExclusiveGroupAvailable() throws RemoteException { + // Register clients + TunerClient client0 = new TunerClient(); + TunerClient client1 = new TunerClient(); + client0.register("0" /*sessionId*/, + TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK); + client1.register("1" /*sessionId*/, + TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK); // Init frontend resources. TunerFrontendInfo[] infos = new TunerFrontendInfo[3]; @@ -335,13 +384,13 @@ public class TunerResourceManagerServiceTest { int[] frontendHandle = new int[1]; TunerFrontendRequest request = - tunerFrontendRequest(clientId1[0] /*clientId*/, FrontendSettings.TYPE_DVBT); + tunerFrontendRequest(client1.getId() /*clientId*/, FrontendSettings.TYPE_DVBT); assertThat(mTunerResourceManagerService .requestFrontendInternal(request, frontendHandle)).isTrue(); assertThat(frontendHandle[0]).isEqualTo(infos[0].handle); request = - tunerFrontendRequest(clientId0[0] /*clientId*/, FrontendSettings.TYPE_DVBT); + tunerFrontendRequest(client0.getId() /*clientId*/, FrontendSettings.TYPE_DVBT); assertThat(mTunerResourceManagerService .requestFrontendInternal(request, frontendHandle)).isTrue(); assertThat(frontendHandle[0]).isEqualTo(infos[1].handle); @@ -349,31 +398,20 @@ public class TunerResourceManagerServiceTest { .isTrue(); assertThat(mTunerResourceManagerService.getFrontendResource(infos[2].handle).isInUse()) .isTrue(); + client0.unregister(); + client1.unregister(); } @Test - public void requestFrontendTest_NoFrontendAvailable_RequestWithLowerPriority() { + public void requestFrontendTest_NoFrontendAvailable_RequestWithLowerPriority() + throws RemoteException { // Register clients - ResourceClientProfile[] profiles = new ResourceClientProfile[2]; - profiles[0] = resourceClientProfile("0" /*sessionId*/, - TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK); - profiles[1] = resourceClientProfile("1" /*sessionId*/, - TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK); - int[] clientPriorities = {100, 50}; - int[] clientId0 = new int[1]; - int[] clientId1 = new int[1]; - TestResourcesReclaimListener listener = new TestResourcesReclaimListener(); - - mTunerResourceManagerService.registerClientProfileInternal( - profiles[0], listener, clientId0); - assertThat(clientId0[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID); - mTunerResourceManagerService.updateClientPriorityInternal( - clientId0[0], clientPriorities[0], 0/*niceValue*/); - mTunerResourceManagerService.registerClientProfileInternal( - profiles[1], new TestResourcesReclaimListener(), clientId1); - assertThat(clientId1[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID); - mTunerResourceManagerService.updateClientPriorityInternal( - clientId1[0], clientPriorities[1], 0/*niceValue*/); + TunerClient client0 = new TunerClient(); + TunerClient client1 = new TunerClient(); + client0.register("0" /*sessionId*/, + TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK, 100); + client1.register("1" /*sessionId*/, + TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK, 50); // Init frontend resources. TunerFrontendInfo[] infos = new TunerFrontendInfo[2]; @@ -384,46 +422,36 @@ public class TunerResourceManagerServiceTest { mTunerResourceManagerService.setFrontendInfoListInternal(infos); TunerFrontendRequest request = - tunerFrontendRequest(clientId0[0] /*clientId*/, FrontendSettings.TYPE_DVBT); + tunerFrontendRequest(client0.getId() /*clientId*/, FrontendSettings.TYPE_DVBT); int[] frontendHandle = new int[1]; assertThat(mTunerResourceManagerService .requestFrontendInternal(request, frontendHandle)).isTrue(); request = - tunerFrontendRequest(clientId1[0] /*clientId*/, FrontendSettings.TYPE_DVBT); + tunerFrontendRequest(client1.getId() /*clientId*/, FrontendSettings.TYPE_DVBT); assertThat(mTunerResourceManagerService .requestFrontendInternal(request, frontendHandle)).isFalse(); - assertThat(listener.isReclaimed()).isFalse(); + assertThat(client0.isReclaimed()).isFalse(); request = - tunerFrontendRequest(clientId1[0] /*clientId*/, FrontendSettings.TYPE_DVBS); + tunerFrontendRequest(client1.getId() /*clientId*/, FrontendSettings.TYPE_DVBS); assertThat(mTunerResourceManagerService .requestFrontendInternal(request, frontendHandle)).isFalse(); - assertThat(listener.isReclaimed()).isFalse(); + assertThat(client0.isReclaimed()).isFalse(); + client0.unregister(); + client1.unregister(); } @Test - public void requestFrontendTest_NoFrontendAvailable_RequestWithHigherPriority() { + public void requestFrontendTest_NoFrontendAvailable_RequestWithHigherPriority() + throws RemoteException { // Register clients - ResourceClientProfile[] profiles = new ResourceClientProfile[2]; - profiles[0] = resourceClientProfile("0" /*sessionId*/, - TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK); - profiles[1] = resourceClientProfile("1" /*sessionId*/, - TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK); - int[] clientPriorities = {100, 500}; - int[] clientId0 = new int[1]; - int[] clientId1 = new int[1]; - TestResourcesReclaimListener listener = new TestResourcesReclaimListener(); - mTunerResourceManagerService.registerClientProfileInternal( - profiles[0], listener, clientId0); - assertThat(clientId0[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID); - mTunerResourceManagerService.updateClientPriorityInternal( - clientId0[0], clientPriorities[0], 0/*niceValue*/); - mTunerResourceManagerService.registerClientProfileInternal( - profiles[1], new TestResourcesReclaimListener(), clientId1); - assertThat(clientId1[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID); - mTunerResourceManagerService.updateClientPriorityInternal( - clientId1[0], clientPriorities[1], 0/*niceValue*/); + TunerClient client0 = new TunerClient(); + TunerClient client1 = new TunerClient(); + client0.register("0" /*sessionId*/, + TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK, 100); + client1.register("1" /*sessionId*/, + TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK, 500); // Init frontend resources. TunerFrontendInfo[] infos = new TunerFrontendInfo[2]; @@ -434,17 +462,16 @@ public class TunerResourceManagerServiceTest { mTunerResourceManagerService.setFrontendInfoListInternal(infos); TunerFrontendRequest request = - tunerFrontendRequest(clientId0[0] /*clientId*/, FrontendSettings.TYPE_DVBT); + tunerFrontendRequest(client0.getId() /*clientId*/, FrontendSettings.TYPE_DVBT); int[] frontendHandle = new int[1]; assertThat(mTunerResourceManagerService .requestFrontendInternal(request, frontendHandle)).isTrue(); assertThat(frontendHandle[0]).isEqualTo(infos[0].handle); - assertThat(mTunerResourceManagerService.getClientProfile(clientId0[0]) - .getInUseFrontendHandles()).isEqualTo(new HashSet<Integer>(Arrays.asList( - infos[0].handle, infos[1].handle))); + assertThat(client0.getProfile().getInUseFrontendHandles()) + .isEqualTo(new HashSet<Integer>(Arrays.asList(infos[0].handle, infos[1].handle))); request = - tunerFrontendRequest(clientId1[0] /*clientId*/, FrontendSettings.TYPE_DVBS); + tunerFrontendRequest(client1.getId() /*clientId*/, FrontendSettings.TYPE_DVBS); assertThat(mTunerResourceManagerService .requestFrontendInternal(request, frontendHandle)).isTrue(); assertThat(frontendHandle[0]).isEqualTo(infos[1].handle); @@ -453,22 +480,20 @@ public class TunerResourceManagerServiceTest { assertThat(mTunerResourceManagerService.getFrontendResource(infos[1].handle) .isInUse()).isTrue(); assertThat(mTunerResourceManagerService.getFrontendResource(infos[0].handle) - .getOwnerClientId()).isEqualTo(clientId1[0]); + .getOwnerClientId()).isEqualTo(client1.getId()); assertThat(mTunerResourceManagerService.getFrontendResource(infos[1].handle) - .getOwnerClientId()).isEqualTo(clientId1[0]); - assertThat(listener.isReclaimed()).isTrue(); + .getOwnerClientId()).isEqualTo(client1.getId()); + assertThat(client0.isReclaimed()).isTrue(); + client0.unregister(); + client1.unregister(); } @Test - public void releaseFrontendTest_UnderTheSameExclusiveGroup() { + public void releaseFrontendTest_UnderTheSameExclusiveGroup() throws RemoteException { // Register clients - ResourceClientProfile[] profiles = new ResourceClientProfile[1]; - profiles[0] = resourceClientProfile("0" /*sessionId*/, - TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK); - int[] clientId = new int[1]; - TestResourcesReclaimListener listener = new TestResourcesReclaimListener(); - mTunerResourceManagerService.registerClientProfileInternal(profiles[0], listener, clientId); - assertThat(clientId[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID); + TunerClient client0 = new TunerClient(); + client0.register("0" /*sessionId*/, + TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK); // Init frontend resources. TunerFrontendInfo[] infos = new TunerFrontendInfo[2]; @@ -479,7 +504,7 @@ public class TunerResourceManagerServiceTest { mTunerResourceManagerService.setFrontendInfoListInternal(infos); TunerFrontendRequest request = - tunerFrontendRequest(clientId[0] /*clientId*/, FrontendSettings.TYPE_DVBT); + tunerFrontendRequest(client0.getId() /*clientId*/, FrontendSettings.TYPE_DVBT); int[] frontendHandle = new int[1]; assertThat(mTunerResourceManagerService .requestFrontendInternal(request, frontendHandle)).isTrue(); @@ -488,43 +513,29 @@ public class TunerResourceManagerServiceTest { .getFrontendResource(infos[1].handle).isInUse()).isTrue(); // Release frontend - mTunerResourceManagerService.releaseFrontendInternal(mTunerResourceManagerService - .getFrontendResource(frontendHandle[0]), clientId[0]); + mTunerResourceManagerService.releaseFrontendInternal(frontendHandle[0], client0.getId()); assertThat(mTunerResourceManagerService .getFrontendResource(frontendHandle[0]).isInUse()).isFalse(); assertThat(mTunerResourceManagerService .getFrontendResource(infos[1].handle).isInUse()).isFalse(); - assertThat(mTunerResourceManagerService - .getClientProfile(clientId[0]).getInUseFrontendHandles().size()).isEqualTo(0); + assertThat(client0.getProfile().getInUseFrontendHandles().size()).isEqualTo(0); + client0.unregister(); } @Test - public void requestCasTest_NoCasAvailable_RequestWithHigherPriority() { + public void requestCasTest_NoCasAvailable_RequestWithHigherPriority() throws RemoteException { // Register clients - ResourceClientProfile[] profiles = new ResourceClientProfile[2]; - profiles[0] = resourceClientProfile("0" /*sessionId*/, - TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK); - profiles[1] = resourceClientProfile("1" /*sessionId*/, - TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK); - int[] clientPriorities = {100, 500}; - int[] clientId0 = new int[1]; - int[] clientId1 = new int[1]; - TestResourcesReclaimListener listener = new TestResourcesReclaimListener(); - mTunerResourceManagerService.registerClientProfileInternal( - profiles[0], listener, clientId0); - assertThat(clientId0[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID); - mTunerResourceManagerService.updateClientPriorityInternal( - clientId0[0], clientPriorities[0], 0/*niceValue*/); - mTunerResourceManagerService.registerClientProfileInternal( - profiles[1], new TestResourcesReclaimListener(), clientId1); - assertThat(clientId1[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID); - mTunerResourceManagerService.updateClientPriorityInternal( - clientId1[0], clientPriorities[1], 0/*niceValue*/); + TunerClient client0 = new TunerClient(); + TunerClient client1 = new TunerClient(); + client0.register("0" /*sessionId*/, + TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK, 100); + client1.register("1" /*sessionId*/, + TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK, 500); // Init cas resources. mTunerResourceManagerService.updateCasInfoInternal(1 /*casSystemId*/, 2 /*maxSessionNum*/); - CasSessionRequest request = casSessionRequest(clientId0[0], 1 /*casSystemId*/); + CasSessionRequest request = casSessionRequest(client0.getId(), 1 /*casSystemId*/); int[] casSessionHandle = new int[1]; // Request for 2 cas sessions. assertThat(mTunerResourceManagerService @@ -533,54 +544,45 @@ public class TunerResourceManagerServiceTest { .requestCasSessionInternal(request, casSessionHandle)).isTrue(); assertThat(mTunerResourceManagerService.getResourceIdFromHandle(casSessionHandle[0])) .isEqualTo(1); - assertThat(mTunerResourceManagerService.getClientProfile(clientId0[0]) - .getInUseCasSystemId()).isEqualTo(1); + assertThat(client0.getProfile().getInUseCasSystemId()) + .isEqualTo(1); assertThat(mTunerResourceManagerService.getCasResource(1) - .getOwnerClientIds()).isEqualTo(new HashSet<Integer>(Arrays.asList(clientId0[0]))); + .getOwnerClientIds()).isEqualTo( + new HashSet<Integer>(Arrays.asList(client0.getId()))); assertThat(mTunerResourceManagerService.getCasResource(1).isFullyUsed()).isTrue(); - request = casSessionRequest(clientId1[0], 1); + request = casSessionRequest(client1.getId(), 1); assertThat(mTunerResourceManagerService .requestCasSessionInternal(request, casSessionHandle)).isTrue(); assertThat(mTunerResourceManagerService.getResourceIdFromHandle(casSessionHandle[0])) .isEqualTo(1); - assertThat(mTunerResourceManagerService.getClientProfile(clientId1[0]) - .getInUseCasSystemId()).isEqualTo(1); - assertThat(mTunerResourceManagerService.getClientProfile(clientId0[0]) - .getInUseCasSystemId()).isEqualTo(ClientProfile.INVALID_RESOURCE_ID); + assertThat(client1.getProfile().getInUseCasSystemId()).isEqualTo(1); + assertThat(client0.getProfile().getInUseCasSystemId()) + .isEqualTo(ClientProfile.INVALID_RESOURCE_ID); assertThat(mTunerResourceManagerService.getCasResource(1) - .getOwnerClientIds()).isEqualTo(new HashSet<Integer>(Arrays.asList(clientId1[0]))); + .getOwnerClientIds()).isEqualTo( + new HashSet<Integer>(Arrays.asList(client1.getId()))); assertThat(mTunerResourceManagerService.getCasResource(1).isFullyUsed()).isFalse(); - assertThat(listener.isReclaimed()).isTrue(); + assertThat(client0.isReclaimed()).isTrue(); + client0.unregister(); + client1.unregister(); } @Test - public void requestCiCamTest_NoCiCamAvailable_RequestWithHigherPriority() { + public void requestCiCamTest_NoCiCamAvailable_RequestWithHigherPriority() + throws RemoteException { // Register clients - ResourceClientProfile[] profiles = new ResourceClientProfile[2]; - profiles[0] = resourceClientProfile("0" /*sessionId*/, - TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK); - profiles[1] = resourceClientProfile("1" /*sessionId*/, - TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK); - int[] clientPriorities = {100, 500}; - int[] clientId0 = new int[1]; - int[] clientId1 = new int[1]; - TestResourcesReclaimListener listener = new TestResourcesReclaimListener(); - mTunerResourceManagerService.registerClientProfileInternal( - profiles[0], listener, clientId0); - assertThat(clientId0[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID); - mTunerResourceManagerService.updateClientPriorityInternal( - clientId0[0], clientPriorities[0], 0/*niceValue*/); - mTunerResourceManagerService.registerClientProfileInternal( - profiles[1], new TestResourcesReclaimListener(), clientId1); - assertThat(clientId1[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID); - mTunerResourceManagerService.updateClientPriorityInternal( - clientId1[0], clientPriorities[1], 0/*niceValue*/); + TunerClient client0 = new TunerClient(); + TunerClient client1 = new TunerClient(); + client0.register("0" /*sessionId*/, + TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK, 100); + client1.register("1" /*sessionId*/, + TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK, 500); // Init cicam/cas resources. mTunerResourceManagerService.updateCasInfoInternal(1 /*casSystemId*/, 2 /*maxSessionNum*/); - TunerCiCamRequest request = tunerCiCamRequest(clientId0[0], 1 /*ciCamId*/); + TunerCiCamRequest request = tunerCiCamRequest(client0.getId(), 1 /*ciCamId*/); int[] ciCamHandle = new int[1]; // Request for 2 ciCam sessions. assertThat(mTunerResourceManagerService @@ -589,139 +591,125 @@ public class TunerResourceManagerServiceTest { .requestCiCamInternal(request, ciCamHandle)).isTrue(); assertThat(mTunerResourceManagerService.getResourceIdFromHandle(ciCamHandle[0])) .isEqualTo(1); - assertThat(mTunerResourceManagerService.getClientProfile(clientId0[0]) - .getInUseCiCamId()).isEqualTo(1); + assertThat(client0.getProfile().getInUseCiCamId()).isEqualTo(1); assertThat(mTunerResourceManagerService.getCiCamResource(1) - .getOwnerClientIds()).isEqualTo(new HashSet<Integer>(Arrays.asList(clientId0[0]))); + .getOwnerClientIds()).isEqualTo( + new HashSet<Integer>(Arrays.asList(client0.getId()))); assertThat(mTunerResourceManagerService.getCiCamResource(1).isFullyUsed()).isTrue(); - request = tunerCiCamRequest(clientId1[0], 1); + request = tunerCiCamRequest(client1.getId(), 1); assertThat(mTunerResourceManagerService .requestCiCamInternal(request, ciCamHandle)).isTrue(); assertThat(mTunerResourceManagerService.getResourceIdFromHandle(ciCamHandle[0])) .isEqualTo(1); - assertThat(mTunerResourceManagerService.getClientProfile(clientId1[0]) - .getInUseCiCamId()).isEqualTo(1); - assertThat(mTunerResourceManagerService.getClientProfile(clientId0[0]) - .getInUseCiCamId()).isEqualTo(ClientProfile.INVALID_RESOURCE_ID); + assertThat(client1.getProfile().getInUseCiCamId()).isEqualTo(1); + assertThat(client0.getProfile().getInUseCiCamId()) + .isEqualTo(ClientProfile.INVALID_RESOURCE_ID); assertThat(mTunerResourceManagerService.getCiCamResource(1) - .getOwnerClientIds()).isEqualTo(new HashSet<Integer>(Arrays.asList(clientId1[0]))); - assertThat(mTunerResourceManagerService.getCiCamResource(1).isFullyUsed()).isFalse(); - assertThat(listener.isReclaimed()).isTrue(); + .getOwnerClientIds()).isEqualTo( + new HashSet<Integer>(Arrays.asList(client1.getId()))); + assertThat(mTunerResourceManagerService + .getCiCamResource(1).isFullyUsed()).isFalse(); + assertThat(client0.isReclaimed()).isTrue(); + client0.unregister(); + client1.unregister(); } @Test - public void releaseCasTest() { + public void releaseCasTest() throws RemoteException { // Register clients - ResourceClientProfile[] profiles = new ResourceClientProfile[1]; - profiles[0] = resourceClientProfile("0" /*sessionId*/, - TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK); - int[] clientId = new int[1]; - TestResourcesReclaimListener listener = new TestResourcesReclaimListener(); - mTunerResourceManagerService.registerClientProfileInternal(profiles[0], listener, clientId); - assertThat(clientId[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID); + TunerClient client0 = new TunerClient(); + client0.register("0" /*sessionId*/, + TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK); // Init cas resources. mTunerResourceManagerService.updateCasInfoInternal(1 /*casSystemId*/, 2 /*maxSessionNum*/); - CasSessionRequest request = casSessionRequest(clientId[0], 1 /*casSystemId*/); + CasSessionRequest request = casSessionRequest(client0.getId(), 1 /*casSystemId*/); int[] casSessionHandle = new int[1]; // Request for 1 cas sessions. assertThat(mTunerResourceManagerService .requestCasSessionInternal(request, casSessionHandle)).isTrue(); assertThat(mTunerResourceManagerService.getResourceIdFromHandle(casSessionHandle[0])) .isEqualTo(1); - assertThat(mTunerResourceManagerService.getClientProfile(clientId[0]) - .getInUseCasSystemId()).isEqualTo(1); + assertThat(client0.getProfile().getInUseCasSystemId()).isEqualTo(1); assertThat(mTunerResourceManagerService.getCasResource(1) - .getOwnerClientIds()).isEqualTo(new HashSet<Integer>(Arrays.asList(clientId[0]))); + .getOwnerClientIds()).isEqualTo( + new HashSet<Integer>(Arrays.asList(client0.getId()))); assertThat(mTunerResourceManagerService.getCasResource(1).isFullyUsed()).isFalse(); // Release cas mTunerResourceManagerService.releaseCasSessionInternal(mTunerResourceManagerService - .getCasResource(1), clientId[0]); - assertThat(mTunerResourceManagerService.getClientProfile(clientId[0]) - .getInUseCasSystemId()).isEqualTo(ClientProfile.INVALID_RESOURCE_ID); + .getCasResource(1), client0.getId()); + assertThat(client0.getProfile().getInUseCasSystemId()) + .isEqualTo(ClientProfile.INVALID_RESOURCE_ID); assertThat(mTunerResourceManagerService.getCasResource(1).isFullyUsed()).isFalse(); assertThat(mTunerResourceManagerService.getCasResource(1) .getOwnerClientIds()).isEmpty(); + client0.unregister(); } @Test - public void releaseCiCamTest() { + public void releaseCiCamTest() throws RemoteException { // Register clients - ResourceClientProfile[] profiles = new ResourceClientProfile[1]; - profiles[0] = resourceClientProfile("0" /*sessionId*/, - TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK); - int[] clientId = new int[1]; - TestResourcesReclaimListener listener = new TestResourcesReclaimListener(); - mTunerResourceManagerService.registerClientProfileInternal(profiles[0], listener, clientId); - assertThat(clientId[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID); + TunerClient client0 = new TunerClient(); + client0.register("0" /*sessionId*/, + TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK); // Init cas resources. mTunerResourceManagerService.updateCasInfoInternal(1 /*casSystemId*/, 2 /*maxSessionNum*/); - TunerCiCamRequest request = tunerCiCamRequest(clientId[0], 1 /*ciCamId*/); + TunerCiCamRequest request = tunerCiCamRequest(client0.getId(), 1 /*ciCamId*/); int[] ciCamHandle = new int[1]; // Request for 1 ciCam sessions. assertThat(mTunerResourceManagerService .requestCiCamInternal(request, ciCamHandle)).isTrue(); assertThat(mTunerResourceManagerService.getResourceIdFromHandle(ciCamHandle[0])) .isEqualTo(1); - assertThat(mTunerResourceManagerService.getClientProfile(clientId[0]) - .getInUseCiCamId()).isEqualTo(1); + assertThat(client0.getProfile().getInUseCiCamId()).isEqualTo(1); assertThat(mTunerResourceManagerService.getCiCamResource(1) - .getOwnerClientIds()).isEqualTo(new HashSet<Integer>(Arrays.asList(clientId[0]))); - assertThat(mTunerResourceManagerService.getCiCamResource(1).isFullyUsed()).isFalse(); + .getOwnerClientIds()).isEqualTo( + new HashSet<Integer>(Arrays.asList(client0.getId()))); + assertThat(mTunerResourceManagerService + .getCiCamResource(1).isFullyUsed()).isFalse(); // Release ciCam mTunerResourceManagerService.releaseCiCamInternal(mTunerResourceManagerService - .getCiCamResource(1), clientId[0]); - assertThat(mTunerResourceManagerService.getClientProfile(clientId[0]) - .getInUseCiCamId()).isEqualTo(ClientProfile.INVALID_RESOURCE_ID); - assertThat(mTunerResourceManagerService.getCiCamResource(1).isFullyUsed()).isFalse(); + .getCiCamResource(1), client0.getId()); + assertThat(client0.getProfile().getInUseCiCamId()) + .isEqualTo(ClientProfile.INVALID_RESOURCE_ID); + assertThat(mTunerResourceManagerService + .getCiCamResource(1).isFullyUsed()).isFalse(); assertThat(mTunerResourceManagerService.getCiCamResource(1) .getOwnerClientIds()).isEmpty(); + client0.unregister(); } @Test - public void requestLnbTest_NoLnbAvailable_RequestWithHigherPriority() { + public void requestLnbTest_NoLnbAvailable_RequestWithHigherPriority() throws RemoteException { // Register clients - ResourceClientProfile[] profiles = new ResourceClientProfile[2]; - profiles[0] = resourceClientProfile("0" /*sessionId*/, - TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK); - profiles[1] = resourceClientProfile("1" /*sessionId*/, - TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK); - int[] clientPriorities = {100, 500}; - int[] clientId0 = new int[1]; - int[] clientId1 = new int[1]; - TestResourcesReclaimListener listener = new TestResourcesReclaimListener(); - mTunerResourceManagerService.registerClientProfileInternal( - profiles[0], listener, clientId0); - assertThat(clientId0[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID); - mTunerResourceManagerService.updateClientPriorityInternal( - clientId0[0], clientPriorities[0], 0/*niceValue*/); - mTunerResourceManagerService.registerClientProfileInternal( - profiles[1], new TestResourcesReclaimListener(), clientId1); - assertThat(clientId1[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID); - mTunerResourceManagerService.updateClientPriorityInternal( - clientId1[0], clientPriorities[1], 0/*niceValue*/); + TunerClient client0 = new TunerClient(); + TunerClient client1 = new TunerClient(); + client0.register("0" /*sessionId*/, + TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK, 100); + client1.register("1" /*sessionId*/, + TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK, 500); // Init lnb resources. int[] lnbHandles = {1}; mTunerResourceManagerService.setLnbInfoListInternal(lnbHandles); TunerLnbRequest request = new TunerLnbRequest(); - request.clientId = clientId0[0]; + request.clientId = client0.getId(); int[] lnbHandle = new int[1]; assertThat(mTunerResourceManagerService .requestLnbInternal(request, lnbHandle)).isTrue(); assertThat(lnbHandle[0]).isEqualTo(lnbHandles[0]); - assertThat(mTunerResourceManagerService.getClientProfile(clientId0[0]).getInUseLnbHandles()) + assertThat(client0.getProfile().getInUseLnbHandles()) .isEqualTo(new HashSet<Integer>(Arrays.asList(lnbHandles[0]))); request = new TunerLnbRequest(); - request.clientId = clientId1[0]; + request.clientId = client1.getId(); assertThat(mTunerResourceManagerService .requestLnbInternal(request, lnbHandle)).isTrue(); @@ -729,29 +717,26 @@ public class TunerResourceManagerServiceTest { assertThat(mTunerResourceManagerService.getLnbResource(lnbHandles[0]) .isInUse()).isTrue(); assertThat(mTunerResourceManagerService.getLnbResource(lnbHandles[0]) - .getOwnerClientId()).isEqualTo(clientId1[0]); - assertThat(listener.isReclaimed()).isTrue(); - assertThat(mTunerResourceManagerService.getClientProfile(clientId0[0]) - .getInUseLnbHandles().size()).isEqualTo(0); + .getOwnerClientId()).isEqualTo(client1.getId()); + assertThat(client0.isReclaimed()).isTrue(); + assertThat(client0.getProfile().getInUseLnbHandles().size()).isEqualTo(0); + client0.unregister(); + client1.unregister(); } @Test - public void releaseLnbTest() { + public void releaseLnbTest() throws RemoteException { // Register clients - ResourceClientProfile[] profiles = new ResourceClientProfile[1]; - profiles[0] = resourceClientProfile("0" /*sessionId*/, - TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK); - int[] clientId = new int[1]; - TestResourcesReclaimListener listener = new TestResourcesReclaimListener(); - mTunerResourceManagerService.registerClientProfileInternal(profiles[0], listener, clientId); - assertThat(clientId[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID); + TunerClient client0 = new TunerClient(); + client0.register("0" /*sessionId*/, + TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK); // Init lnb resources. int[] lnbHandles = {0}; mTunerResourceManagerService.setLnbInfoListInternal(lnbHandles); TunerLnbRequest request = new TunerLnbRequest(); - request.clientId = clientId[0]; + request.clientId = client0.getId(); int[] lnbHandle = new int[1]; assertThat(mTunerResourceManagerService .requestLnbInternal(request, lnbHandle)).isTrue(); @@ -762,19 +747,16 @@ public class TunerResourceManagerServiceTest { .getLnbResource(lnbHandle[0])); assertThat(mTunerResourceManagerService .getLnbResource(lnbHandle[0]).isInUse()).isFalse(); - assertThat(mTunerResourceManagerService - .getClientProfile(clientId[0]).getInUseLnbHandles().size()).isEqualTo(0); + assertThat(client0.getProfile().getInUseLnbHandles().size()).isEqualTo(0); + client0.unregister(); } @Test - public void unregisterClientTest_usingFrontend() { - // Register client - ResourceClientProfile profile = resourceClientProfile("0" /*sessionId*/, - TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK); - int[] clientId = new int[1]; - mTunerResourceManagerService.registerClientProfileInternal( - profile, null /*listener*/, clientId); - assertThat(clientId[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID); + public void unregisterClientTest_usingFrontend() throws RemoteException { + // Register clients + TunerClient client0 = new TunerClient(); + client0.register("0" /*sessionId*/, + TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK); // Init frontend resources. TunerFrontendInfo[] infos = new TunerFrontendInfo[2]; @@ -785,7 +767,7 @@ public class TunerResourceManagerServiceTest { mTunerResourceManagerService.setFrontendInfoListInternal(infos); TunerFrontendRequest request = - tunerFrontendRequest(clientId[0] /*clientId*/, FrontendSettings.TYPE_DVBT); + tunerFrontendRequest(client0.getId() /*clientId*/, FrontendSettings.TYPE_DVBT); int[] frontendHandle = new int[1]; assertThat(mTunerResourceManagerService .requestFrontendInternal(request, frontendHandle)).isTrue(); @@ -796,26 +778,20 @@ public class TunerResourceManagerServiceTest { .isInUse()).isTrue(); // Unregister client when using frontend - mTunerResourceManagerService.unregisterClientProfileInternal(clientId[0]); + client0.unregister(); assertThat(mTunerResourceManagerService.getFrontendResource(infos[0].handle) .isInUse()).isFalse(); assertThat(mTunerResourceManagerService.getFrontendResource(infos[1].handle) .isInUse()).isFalse(); - assertThat(mTunerResourceManagerService.checkClientExists(clientId[0])).isFalse(); - + assertThat(mTunerResourceManagerService.checkClientExists(client0.getId())).isFalse(); } @Test - public void requestDemuxTest() { - // Register client - ResourceClientProfile profile0 = resourceClientProfile("0" /*sessionId*/, - TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK); - ResourceClientProfile profile1 = resourceClientProfile("1" /*sessionId*/, - TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK); - int[] clientId0 = new int[1]; - mTunerResourceManagerService.registerClientProfileInternal( - profile0, null /*listener*/, clientId0); - assertThat(clientId0[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID); + public void requestDemuxTest() throws RemoteException { + // Register clients + TunerClient client0 = new TunerClient(); + client0.register("0" /*sessionId*/, + TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK); TunerDemuxInfo[] infos = new TunerDemuxInfo[3]; infos[0] = tunerDemuxInfo(0 /* handle */, Filter.TYPE_TS | Filter.TYPE_IP); @@ -825,7 +801,7 @@ public class TunerResourceManagerServiceTest { int[] demuxHandle0 = new int[1]; // first with undefined type (should be the first one with least # of caps) - TunerDemuxRequest request = tunerDemuxRequest(clientId0[0], Filter.TYPE_UNDEFINED); + TunerDemuxRequest request = tunerDemuxRequest(client0.getId(), Filter.TYPE_UNDEFINED); assertThat(mTunerResourceManagerService.requestDemuxInternal(request, demuxHandle0)) .isTrue(); assertThat(demuxHandle0[0]).isEqualTo(1); @@ -846,16 +822,16 @@ public class TunerResourceManagerServiceTest { assertThat(demuxHandle0[0]).isEqualTo(2); // request for another TS - int[] clientId1 = new int[1]; - mTunerResourceManagerService.registerClientProfileInternal( - profile1, null /*listener*/, clientId1); - assertThat(clientId1[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID); + TunerClient client1 = new TunerClient(); + client1.register("1" /*sessionId*/, + TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK); + int[] demuxHandle1 = new int[1]; - TunerDemuxRequest request1 = tunerDemuxRequest(clientId1[0], Filter.TYPE_TS); + TunerDemuxRequest request1 = tunerDemuxRequest(client1.getId(), Filter.TYPE_TS); assertThat(mTunerResourceManagerService.requestDemuxInternal(request1, demuxHandle1)) .isTrue(); assertThat(demuxHandle1[0]).isEqualTo(0); - assertThat(mTunerResourceManagerService.getResourceIdFromHandle(demuxHandle1[0])) + assertThat(mTunerResourceManagerService.getResourceIdFromHandle(client1.getId())) .isEqualTo(0); // release demuxes @@ -863,33 +839,23 @@ public class TunerResourceManagerServiceTest { mTunerResourceManagerService.releaseDemuxInternal(dr); dr = mTunerResourceManagerService.getDemuxResource(demuxHandle1[0]); mTunerResourceManagerService.releaseDemuxInternal(dr); + + client0.unregister(); + client1.unregister(); } @Test - public void requestDemuxTest_ResourceReclaim() { + public void requestDemuxTest_ResourceReclaim() throws RemoteException { // Register clients - ResourceClientProfile profile0 = resourceClientProfile("0" /*sessionId*/, - TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK); - ResourceClientProfile profile1 = resourceClientProfile("1" /*sessionId*/, - TvInputService.PRIORITY_HINT_USE_CASE_TYPE_SCAN); - ResourceClientProfile profile2 = resourceClientProfile("2" /*sessionId*/, - TvInputService.PRIORITY_HINT_USE_CASE_TYPE_SCAN); - int[] clientId0 = new int[1]; - int[] clientId1 = new int[1]; - int[] clientId2 = new int[1]; - TestResourcesReclaimListener listener0 = new TestResourcesReclaimListener(); - TestResourcesReclaimListener listener1 = new TestResourcesReclaimListener(); - TestResourcesReclaimListener listener2 = new TestResourcesReclaimListener(); - - mTunerResourceManagerService.registerClientProfileInternal( - profile0, listener0, clientId0); - assertThat(clientId0[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID); - mTunerResourceManagerService.registerClientProfileInternal( - profile1, listener1, clientId1); - assertThat(clientId1[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID); - mTunerResourceManagerService.registerClientProfileInternal( - profile2, listener2, clientId1); - assertThat(clientId2[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID); + TunerClient client0 = new TunerClient(); + TunerClient client1 = new TunerClient(); + TunerClient client2 = new TunerClient(); + client0.register("0" /*sessionId*/, + TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK); + client1.register("1" /*sessionId*/, + TvInputService.PRIORITY_HINT_USE_CASE_TYPE_SCAN); + client2.register("2" /*sessionId*/, + TvInputService.PRIORITY_HINT_USE_CASE_TYPE_SCAN); // Init demux resources. TunerDemuxInfo[] infos = new TunerDemuxInfo[2]; @@ -897,66 +863,67 @@ public class TunerResourceManagerServiceTest { infos[1] = tunerDemuxInfo(1 /*handle*/, Filter.TYPE_TS); mTunerResourceManagerService.setDemuxInfoListInternal(infos); - // let clientId0(prio:100) request for IP - should succeed - TunerDemuxRequest request0 = tunerDemuxRequest(clientId0[0], Filter.TYPE_IP); + // let client0(prio:100) request for IP - should succeed + TunerDemuxRequest request0 = tunerDemuxRequest(client0.getId(), Filter.TYPE_IP); int[] demuxHandle0 = new int[1]; assertThat(mTunerResourceManagerService .requestDemuxInternal(request0, demuxHandle0)).isTrue(); assertThat(demuxHandle0[0]).isEqualTo(0); - // let clientId1(prio:50) request for IP - should fail - TunerDemuxRequest request1 = tunerDemuxRequest(clientId1[0], Filter.TYPE_IP); + // let client1(prio:50) request for IP - should fail + TunerDemuxRequest request1 = tunerDemuxRequest(client1.getId(), Filter.TYPE_IP); int[] demuxHandle1 = new int[1]; demuxHandle1[0] = -1; assertThat(mTunerResourceManagerService .requestDemuxInternal(request1, demuxHandle1)).isFalse(); - assertThat(listener0.isReclaimed()).isFalse(); + assertThat(client0.isReclaimed()).isFalse(); assertThat(demuxHandle1[0]).isEqualTo(-1); - // let clientId1(prio:50) request for TS - should succeed + // let client1(prio:50) request for TS - should succeed request1.desiredFilterTypes = Filter.TYPE_TS; assertThat(mTunerResourceManagerService .requestDemuxInternal(request1, demuxHandle1)).isTrue(); assertThat(demuxHandle1[0]).isEqualTo(1); - assertThat(listener0.isReclaimed()).isFalse(); + assertThat(client0.isReclaimed()).isFalse(); - // now release demux for the clientId0 (higher priority) and request demux + // now release demux for the client0 (higher priority) and request demux DemuxResource dr = mTunerResourceManagerService.getDemuxResource(demuxHandle0[0]); mTunerResourceManagerService.releaseDemuxInternal(dr); - // let clientId2(prio:50) request for TS - should succeed - TunerDemuxRequest request2 = tunerDemuxRequest(clientId2[0], Filter.TYPE_TS); + // let client2(prio:50) request for TS - should succeed + TunerDemuxRequest request2 = tunerDemuxRequest(client2.getId(), Filter.TYPE_TS); int[] demuxHandle2 = new int[1]; assertThat(mTunerResourceManagerService .requestDemuxInternal(request2, demuxHandle2)).isTrue(); assertThat(demuxHandle2[0]).isEqualTo(0); - assertThat(listener1.isReclaimed()).isFalse(); + assertThat(client1.isReclaimed()).isFalse(); - // let clientId0(prio:100) request for TS - should reclaim from clientId2 + // let client0(prio:100) request for TS - should reclaim from client1 // , who has the smaller caps request0.desiredFilterTypes = Filter.TYPE_TS; assertThat(mTunerResourceManagerService .requestDemuxInternal(request0, demuxHandle0)).isTrue(); - assertThat(listener1.isReclaimed()).isFalse(); - assertThat(listener2.isReclaimed()).isTrue(); + assertThat(client1.isReclaimed()).isTrue(); + assertThat(client2.isReclaimed()).isFalse(); + client0.unregister(); + client1.unregister(); + client2.unregister(); } @Test public void requestDescramblerTest() { - // Register client - ResourceClientProfile profile = resourceClientProfile("0" /*sessionId*/, - TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK); - int[] clientId = new int[1]; - mTunerResourceManagerService.registerClientProfileInternal( - profile, null /*listener*/, clientId); - assertThat(clientId[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID); + // Register clients + TunerClient client0 = new TunerClient(); + client0.register("0" /*sessionId*/, + TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK); int[] desHandle = new int[1]; TunerDescramblerRequest request = new TunerDescramblerRequest(); - request.clientId = clientId[0]; + request.clientId = client0.getId(); assertThat(mTunerResourceManagerService.requestDescramblerInternal(request, desHandle)) .isTrue(); assertThat(mTunerResourceManagerService.getResourceIdFromHandle(desHandle[0])).isEqualTo(0); + client0.unregister(); } @Test @@ -978,74 +945,26 @@ public class TunerResourceManagerServiceTest { } @Test - public void shareFrontendTest_FrontendWithExclusiveGroupReadyToShare() { + public void shareFrontendTest_FrontendWithExclusiveGroupReadyToShare() throws RemoteException { /**** Register Clients and Set Priority ****/ + TunerClient ownerClient0 = new TunerClient(); + TunerClient ownerClient1 = new TunerClient(); + TunerClient shareClient0 = new TunerClient(); + TunerClient shareClient1 = new TunerClient(); + ownerClient0.register("0" /*sessionId*/, + TvInputService.PRIORITY_HINT_USE_CASE_TYPE_LIVE, 100); + ownerClient1.register("1" /*sessionId*/, + TvInputService.PRIORITY_HINT_USE_CASE_TYPE_LIVE, 300); + shareClient0.register("2" /*sessionId*/, + TvInputService.PRIORITY_HINT_USE_CASE_TYPE_RECORD, 200); + shareClient1.register("3" /*sessionId*/, + TvInputService.PRIORITY_HINT_USE_CASE_TYPE_RECORD, 400); - // Int array to save the returned client ids - int[] ownerClientId0 = new int[1]; - int[] ownerClientId1 = new int[1]; - int[] shareClientId0 = new int[1]; - int[] shareClientId1 = new int[1]; - - // Predefined client profiles - ResourceClientProfile[] ownerProfiles = new ResourceClientProfile[2]; - ResourceClientProfile[] shareProfiles = new ResourceClientProfile[2]; - ownerProfiles[0] = resourceClientProfile( - "0" /*sessionId*/, - TvInputService.PRIORITY_HINT_USE_CASE_TYPE_LIVE); - ownerProfiles[1] = resourceClientProfile( - "1" /*sessionId*/, - TvInputService.PRIORITY_HINT_USE_CASE_TYPE_LIVE); - shareProfiles[0] = resourceClientProfile( - "2" /*sessionId*/, - TvInputService.PRIORITY_HINT_USE_CASE_TYPE_RECORD); - shareProfiles[1] = resourceClientProfile( - "3" /*sessionId*/, - TvInputService.PRIORITY_HINT_USE_CASE_TYPE_RECORD); - - // Predefined client reclaim listeners - TestResourcesReclaimListener ownerListener0 = new TestResourcesReclaimListener(); - TestResourcesReclaimListener shareListener0 = new TestResourcesReclaimListener(); - TestResourcesReclaimListener ownerListener1 = new TestResourcesReclaimListener(); - TestResourcesReclaimListener shareListener1 = new TestResourcesReclaimListener(); - // Register clients and validate the returned client ids - mTunerResourceManagerService - .registerClientProfileInternal(ownerProfiles[0], ownerListener0, ownerClientId0); - mTunerResourceManagerService - .registerClientProfileInternal(shareProfiles[0], shareListener0, shareClientId0); - mTunerResourceManagerService - .registerClientProfileInternal(ownerProfiles[1], ownerListener1, ownerClientId1); - mTunerResourceManagerService - .registerClientProfileInternal(shareProfiles[1], shareListener1, shareClientId1); - assertThat(ownerClientId0[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID); - assertThat(shareClientId0[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID); - assertThat(ownerClientId1[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID); - assertThat(shareClientId1[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID); - - mTunerResourceManagerService.updateClientPriorityInternal( - ownerClientId0[0], - 100/*priority*/, - 0/*niceValue*/); - mTunerResourceManagerService.updateClientPriorityInternal( - shareClientId0[0], - 200/*priority*/, - 0/*niceValue*/); - mTunerResourceManagerService.updateClientPriorityInternal( - ownerClientId1[0], - 300/*priority*/, - 0/*niceValue*/); - mTunerResourceManagerService.updateClientPriorityInternal( - shareClientId1[0], - 400/*priority*/, - 0/*niceValue*/); mTunerResourceManagerService.updateClientPriorityInternal( - shareClientId1[0], + shareClient1.getId(), -1/*invalid priority*/, 0/*niceValue*/); - assertThat(mTunerResourceManagerService - .getClientProfile(shareClientId1[0]) - .getPriority()) - .isEqualTo(400); + assertThat(shareClient1.getProfile().getPriority()).isEqualTo(400); /**** Init Frontend Resources ****/ @@ -1072,7 +991,7 @@ public class TunerResourceManagerServiceTest { // Predefined frontend request and array to save returned frontend handle int[] frontendHandle = new int[1]; TunerFrontendRequest request = tunerFrontendRequest( - ownerClientId0[0] /*clientId*/, + ownerClient0.getId() /*clientId*/, FrontendSettings.TYPE_DVBT); // Request call and validate granted resource and internal mapping @@ -1080,9 +999,7 @@ public class TunerResourceManagerServiceTest { .requestFrontendInternal(request, frontendHandle)) .isTrue(); assertThat(frontendHandle[0]).isEqualTo(infos[0].handle); - assertThat(mTunerResourceManagerService - .getClientProfile(ownerClientId0[0]) - .getInUseFrontendHandles()) + assertThat(ownerClient0.getProfile().getInUseFrontendHandles()) .isEqualTo(new HashSet<Integer>(Arrays.asList( infos[0].handle, infos[1].handle))); @@ -1091,11 +1008,11 @@ public class TunerResourceManagerServiceTest { // Share frontend call and validate the internal mapping mTunerResourceManagerService.shareFrontendInternal( - shareClientId0[0]/*selfClientId*/, - ownerClientId0[0]/*targetClientId*/); + shareClient0.getId()/*selfClientId*/, + ownerClient0.getId()/*targetClientId*/); mTunerResourceManagerService.shareFrontendInternal( - shareClientId1[0]/*selfClientId*/, - ownerClientId0[0]/*targetClientId*/); + shareClient1.getId()/*selfClientId*/, + ownerClient0.getId()/*targetClientId*/); // Verify fe in use status assertThat(mTunerResourceManagerService.getFrontendResource(infos[0].handle) .isInUse()).isTrue(); @@ -1103,31 +1020,24 @@ public class TunerResourceManagerServiceTest { .isInUse()).isTrue(); // Verify fe owner status assertThat(mTunerResourceManagerService.getFrontendResource(infos[0].handle) - .getOwnerClientId()).isEqualTo(ownerClientId0[0]); + .getOwnerClientId()).isEqualTo(ownerClient0.getId()); assertThat(mTunerResourceManagerService.getFrontendResource(infos[1].handle) - .getOwnerClientId()).isEqualTo(ownerClientId0[0]); + .getOwnerClientId()).isEqualTo(ownerClient0.getId()); // Verify share fe client status in the primary owner client - assertThat(mTunerResourceManagerService.getClientProfile(ownerClientId0[0]) - .getShareFeClientIds()) + assertThat(ownerClient0.getProfile().getShareFeClientIds()) .isEqualTo(new HashSet<Integer>(Arrays.asList( - shareClientId0[0], - shareClientId1[0]))); + shareClient0.getId(), + shareClient1.getId()))); // Verify in use frontend list in all the primary owner and share owner clients - assertThat(mTunerResourceManagerService - .getClientProfile(ownerClientId0[0]) - .getInUseFrontendHandles()) + assertThat(ownerClient0.getProfile().getInUseFrontendHandles()) .isEqualTo(new HashSet<Integer>(Arrays.asList( infos[0].handle, infos[1].handle))); - assertThat(mTunerResourceManagerService - .getClientProfile(shareClientId0[0]) - .getInUseFrontendHandles()) + assertThat(shareClient0.getProfile().getInUseFrontendHandles()) .isEqualTo(new HashSet<Integer>(Arrays.asList( infos[0].handle, infos[1].handle))); - assertThat(mTunerResourceManagerService - .getClientProfile(shareClientId1[0]) - .getInUseFrontendHandles()) + assertThat(shareClient1.getProfile().getInUseFrontendHandles()) .isEqualTo(new HashSet<Integer>(Arrays.asList( infos[0].handle, infos[1].handle))); @@ -1135,21 +1045,17 @@ public class TunerResourceManagerServiceTest { /**** Remove Frontend Share Owner ****/ // Unregister the second share fe client - mTunerResourceManagerService.unregisterClientProfileInternal(shareClientId1[0]); + shareClient1.unregister(); // Validate the internal mapping - assertThat(mTunerResourceManagerService.getClientProfile(ownerClientId0[0]) - .getShareFeClientIds()) + assertThat(ownerClient0.getProfile().getShareFeClientIds()) .isEqualTo(new HashSet<Integer>(Arrays.asList( - shareClientId0[0]))); - assertThat(mTunerResourceManagerService - .getClientProfile(ownerClientId0[0]) - .getInUseFrontendHandles()) + shareClient0.getId()))); + assertThat(ownerClient0.getProfile().getInUseFrontendHandles()) .isEqualTo(new HashSet<Integer>(Arrays.asList( infos[0].handle, infos[1].handle))); - assertThat(mTunerResourceManagerService - .getClientProfile(shareClientId0[0]) + assertThat(shareClient0.getProfile() .getInUseFrontendHandles()) .isEqualTo(new HashSet<Integer>(Arrays.asList( infos[0].handle, @@ -1159,7 +1065,7 @@ public class TunerResourceManagerServiceTest { // Predefined second frontend request request = tunerFrontendRequest( - ownerClientId1[0] /*clientId*/, + ownerClient1.getId() /*clientId*/, FrontendSettings.TYPE_DVBT); // Second request call @@ -1170,43 +1076,35 @@ public class TunerResourceManagerServiceTest { // Validate granted resource and internal mapping assertThat(frontendHandle[0]).isEqualTo(infos[0].handle); assertThat(mTunerResourceManagerService.getFrontendResource(infos[0].handle) - .getOwnerClientId()).isEqualTo(ownerClientId1[0]); + .getOwnerClientId()).isEqualTo(ownerClient1.getId()); assertThat(mTunerResourceManagerService.getFrontendResource(infos[1].handle) - .getOwnerClientId()).isEqualTo(ownerClientId1[0]); - assertThat(mTunerResourceManagerService - .getClientProfile(ownerClientId1[0]) - .getInUseFrontendHandles()) + .getOwnerClientId()).isEqualTo(ownerClient1.getId()); + assertThat(ownerClient1.getProfile().getInUseFrontendHandles()) .isEqualTo(new HashSet<Integer>(Arrays.asList( infos[0].handle, infos[1].handle))); - assertThat(mTunerResourceManagerService - .getClientProfile(ownerClientId0[0]) - .getInUseFrontendHandles() + assertThat(ownerClient0.getProfile().getInUseFrontendHandles() .isEmpty()) .isTrue(); - assertThat(mTunerResourceManagerService - .getClientProfile(shareClientId0[0]) - .getInUseFrontendHandles() + assertThat(shareClient0.getProfile().getInUseFrontendHandles() .isEmpty()) .isTrue(); - assertThat(mTunerResourceManagerService - .getClientProfile(ownerClientId0[0]) - .getShareFeClientIds() + assertThat(ownerClient0.getProfile().getShareFeClientIds() .isEmpty()) .isTrue(); - assertThat(ownerListener0.isReclaimed()).isTrue(); - assertThat(shareListener0.isReclaimed()).isTrue(); + assertThat(ownerClient0.isReclaimed()).isTrue(); + assertThat(shareClient0.isReclaimed()).isTrue(); /**** Release Frontend Resource From Primary Owner ****/ // Reshare the frontend mTunerResourceManagerService.shareFrontendInternal( - shareClientId0[0]/*selfClientId*/, - ownerClientId1[0]/*targetClientId*/); + shareClient0.getId()/*selfClientId*/, + ownerClient1.getId()/*targetClientId*/); // Release the frontend resource from the primary owner - mTunerResourceManagerService.releaseFrontendInternal(mTunerResourceManagerService - .getFrontendResource(infos[0].handle), ownerClientId1[0]); + mTunerResourceManagerService.releaseFrontendInternal(infos[0].handle, + ownerClient1.getId()); // Validate the internal mapping assertThat(mTunerResourceManagerService.getFrontendResource(infos[0].handle) @@ -1214,19 +1112,13 @@ public class TunerResourceManagerServiceTest { assertThat(mTunerResourceManagerService.getFrontendResource(infos[1].handle) .isInUse()).isFalse(); // Verify client status - assertThat(mTunerResourceManagerService - .getClientProfile(ownerClientId1[0]) - .getInUseFrontendHandles() + assertThat(ownerClient1.getProfile().getInUseFrontendHandles() .isEmpty()) .isTrue(); - assertThat(mTunerResourceManagerService - .getClientProfile(shareClientId0[0]) - .getInUseFrontendHandles() + assertThat(shareClient0.getProfile().getInUseFrontendHandles() .isEmpty()) .isTrue(); - assertThat(mTunerResourceManagerService - .getClientProfile(ownerClientId1[0]) - .getShareFeClientIds() + assertThat(ownerClient1.getProfile().getShareFeClientIds() .isEmpty()) .isTrue(); @@ -1234,7 +1126,7 @@ public class TunerResourceManagerServiceTest { // Predefined Lnb request and handle array TunerLnbRequest requestLnb = new TunerLnbRequest(); - requestLnb.clientId = shareClientId0[0]; + requestLnb.clientId = shareClient0.getId(); int[] lnbHandle = new int[1]; // Request for an Lnb @@ -1247,11 +1139,11 @@ public class TunerResourceManagerServiceTest { .requestFrontendInternal(request, frontendHandle)) .isTrue(); mTunerResourceManagerService.shareFrontendInternal( - shareClientId0[0]/*selfClientId*/, - ownerClientId1[0]/*targetClientId*/); + shareClient0.getId()/*selfClientId*/, + ownerClient1.getId()/*targetClientId*/); // Unregister the primary owner of the shared frontend - mTunerResourceManagerService.unregisterClientProfileInternal(ownerClientId1[0]); + ownerClient1.unregister(); // Validate the internal mapping assertThat(mTunerResourceManagerService.getFrontendResource(infos[0].handle) @@ -1259,16 +1151,15 @@ public class TunerResourceManagerServiceTest { assertThat(mTunerResourceManagerService.getFrontendResource(infos[1].handle) .isInUse()).isFalse(); // Verify client status - assertThat(mTunerResourceManagerService - .getClientProfile(shareClientId0[0]) - .getInUseFrontendHandles() + assertThat(shareClient0.getProfile().getInUseFrontendHandles() .isEmpty()) .isTrue(); - assertThat(mTunerResourceManagerService - .getClientProfile(shareClientId0[0]) - .getInUseLnbHandles()) + assertThat(shareClient0.getProfile().getInUseLnbHandles()) .isEqualTo(new HashSet<Integer>(Arrays.asList( lnbHandles[0]))); + + ownerClient0.unregister(); + shareClient0.unregister(); } private TunerFrontendInfo tunerFrontendInfo( diff --git a/services/tests/vibrator/src/com/android/server/vibrator/InputDeviceDelegateTest.java b/services/tests/vibrator/src/com/android/server/vibrator/InputDeviceDelegateTest.java index f3ecfcc933b4..66788b6d2444 100644 --- a/services/tests/vibrator/src/com/android/server/vibrator/InputDeviceDelegateTest.java +++ b/services/tests/vibrator/src/com/android/server/vibrator/InputDeviceDelegateTest.java @@ -45,6 +45,8 @@ import android.view.InputDevice; import androidx.test.InstrumentationRegistry; +import com.android.server.vibrator.VibrationSession.CallerInfo; + import org.junit.After; import org.junit.Before; import org.junit.Rule; @@ -262,7 +264,7 @@ public class InputDeviceDelegateTest { public void vibrateIfAvailable_withNoInputDevice_returnsFalse() { assertFalse(mInputDeviceDelegate.isAvailable()); assertFalse(mInputDeviceDelegate.vibrateIfAvailable( - new Vibration.CallerInfo(VIBRATION_ATTRIBUTES, UID, -1, PACKAGE_NAME, REASON), + new CallerInfo(VIBRATION_ATTRIBUTES, UID, -1, PACKAGE_NAME, REASON), SYNCED_EFFECT)); } @@ -277,7 +279,7 @@ public class InputDeviceDelegateTest { mInputDeviceDelegate.updateInputDeviceVibrators(/* vibrateInputDevices= */ true); assertTrue(mInputDeviceDelegate.vibrateIfAvailable( - new Vibration.CallerInfo(VIBRATION_ATTRIBUTES, UID, -1, PACKAGE_NAME, REASON), + new CallerInfo(VIBRATION_ATTRIBUTES, UID, -1, PACKAGE_NAME, REASON), SYNCED_EFFECT)); verify(mIInputManagerMock).vibrateCombined(eq(1), same(SYNCED_EFFECT), any()); verify(mIInputManagerMock).vibrateCombined(eq(2), same(SYNCED_EFFECT), any()); diff --git a/services/tests/vibrator/src/com/android/server/vibrator/VibrationTest.java b/services/tests/vibrator/src/com/android/server/vibrator/VibrationSessionTest.java index 84f8412fb4b5..f69d1c42eb14 100644 --- a/services/tests/vibrator/src/com/android/server/vibrator/VibrationTest.java +++ b/services/tests/vibrator/src/com/android/server/vibrator/VibrationSessionTest.java @@ -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. @@ -24,13 +24,13 @@ import org.junit.Test; import java.util.Arrays; -public class VibrationTest { +public class VibrationSessionTest { @Test public void status_hasUniqueProtoEnumValues() { assertThat( - Arrays.stream(Vibration.Status.values()) - .map(Vibration.Status::getProtoEnumValue) + Arrays.stream(VibrationSession.Status.values()) + .map(VibrationSession.Status::getProtoEnumValue) .collect(toList())) .containsNoDuplicates(); } diff --git a/services/tests/vibrator/src/com/android/server/vibrator/VibrationSettingsTest.java b/services/tests/vibrator/src/com/android/server/vibrator/VibrationSettingsTest.java index 38cd49daf99d..c7a136ad0f9f 100644 --- a/services/tests/vibrator/src/com/android/server/vibrator/VibrationSettingsTest.java +++ b/services/tests/vibrator/src/com/android/server/vibrator/VibrationSettingsTest.java @@ -81,6 +81,8 @@ import com.android.internal.util.test.FakeSettingsProvider; import com.android.internal.util.test.FakeSettingsProviderRule; import com.android.server.LocalServices; import com.android.server.companion.virtual.VirtualDeviceManagerInternal; +import com.android.server.vibrator.VibrationSession.CallerInfo; +import com.android.server.vibrator.VibrationSession.Status; import org.junit.After; import org.junit.Before; @@ -292,7 +294,7 @@ public class VibrationSettingsTest { if (expectedAllowedVibrations.contains(usage)) { assertVibrationNotIgnoredForUsage(usage); } else { - assertVibrationIgnoredForUsage(usage, Vibration.Status.IGNORED_BACKGROUND); + assertVibrationIgnoredForUsage(usage, Status.IGNORED_BACKGROUND); } } } @@ -350,7 +352,7 @@ public class VibrationSettingsTest { createSystemReadyVibrationSettings(); for (int usage : ALL_USAGES) { - assertVibrationIgnoredForUsage(usage, Vibration.Status.IGNORED_ON_WIRELESS_CHARGER); + assertVibrationIgnoredForUsage(usage, Status.IGNORED_ON_WIRELESS_CHARGER); } } @@ -365,7 +367,7 @@ public class VibrationSettingsTest { mRegisteredBatteryBroadcastReceiver.onReceive(mContextSpy, wirelessChargingIntent); for (int usage : ALL_USAGES) { - assertVibrationIgnoredForUsage(usage, Vibration.Status.IGNORED_ON_WIRELESS_CHARGER); + assertVibrationIgnoredForUsage(usage, Status.IGNORED_ON_WIRELESS_CHARGER); } } @@ -377,7 +379,7 @@ public class VibrationSettingsTest { createSystemReadyVibrationSettings(); // Check that initially, all usages are ignored due to the wireless charging. for (int usage : ALL_USAGES) { - assertVibrationIgnoredForUsage(usage, Vibration.Status.IGNORED_ON_WIRELESS_CHARGER); + assertVibrationIgnoredForUsage(usage, Status.IGNORED_ON_WIRELESS_CHARGER); } Intent nonWirelessChargingIntent = getBatteryChangedIntent(BATTERY_PLUGGED_USB); @@ -404,7 +406,7 @@ public class VibrationSettingsTest { if (expectedAllowedVibrations.contains(usage)) { assertVibrationNotIgnoredForUsage(usage); } else { - assertVibrationIgnoredForUsage(usage, Vibration.Status.IGNORED_FOR_POWER); + assertVibrationIgnoredForUsage(usage, Status.IGNORED_FOR_POWER); } } } @@ -426,7 +428,7 @@ public class VibrationSettingsTest { for (int usage : ALL_USAGES) { if (usage == USAGE_RINGTONE || usage == USAGE_NOTIFICATION) { - assertVibrationIgnoredForUsage(usage, Vibration.Status.IGNORED_FOR_RINGER_MODE); + assertVibrationIgnoredForUsage(usage, Status.IGNORED_FOR_RINGER_MODE); } else { assertVibrationNotIgnoredForUsage(usage); } @@ -470,7 +472,7 @@ public class VibrationSettingsTest { if (usage == USAGE_ACCESSIBILITY) { assertVibrationNotIgnoredForUsage(usage); } else { - assertVibrationIgnoredForUsage(usage, Vibration.Status.IGNORED_FOR_SETTINGS); + assertVibrationIgnoredForUsage(usage, Status.IGNORED_FOR_SETTINGS); } assertVibrationNotIgnoredForUsageAndFlags(usage, VibrationAttributes.FLAG_BYPASS_USER_VIBRATION_INTENSITY_OFF); @@ -512,7 +514,7 @@ public class VibrationSettingsTest { for (int usage : ALL_USAGES) { if (usage == USAGE_TOUCH) { - assertVibrationIgnoredForUsage(usage, Vibration.Status.IGNORED_FOR_SETTINGS); + assertVibrationIgnoredForUsage(usage, Status.IGNORED_FOR_SETTINGS); } else { assertVibrationNotIgnoredForUsage(usage); } @@ -527,7 +529,7 @@ public class VibrationSettingsTest { for (int usage : ALL_USAGES) { if (usage == USAGE_TOUCH || usage == USAGE_IME_FEEDBACK) { - assertVibrationIgnoredForUsage(usage, Vibration.Status.IGNORED_FOR_SETTINGS); + assertVibrationIgnoredForUsage(usage, Status.IGNORED_FOR_SETTINGS); } else { assertVibrationNotIgnoredForUsage(usage); } @@ -542,7 +544,7 @@ public class VibrationSettingsTest { for (int usage : ALL_USAGES) { if (usage == USAGE_HARDWARE_FEEDBACK || usage == USAGE_PHYSICAL_EMULATION) { - assertVibrationIgnoredForUsage(usage, Vibration.Status.IGNORED_FOR_SETTINGS); + assertVibrationIgnoredForUsage(usage, Status.IGNORED_FOR_SETTINGS); } else { assertVibrationNotIgnoredForUsage(usage); } @@ -557,7 +559,7 @@ public class VibrationSettingsTest { for (int usage : ALL_USAGES) { if (usage == USAGE_NOTIFICATION) { - assertVibrationIgnoredForUsage(usage, Vibration.Status.IGNORED_FOR_SETTINGS); + assertVibrationIgnoredForUsage(usage, Status.IGNORED_FOR_SETTINGS); } else { assertVibrationNotIgnoredForUsage(usage); } @@ -574,7 +576,7 @@ public class VibrationSettingsTest { for (int usage : ALL_USAGES) { if (usage == USAGE_RINGTONE) { - assertVibrationIgnoredForUsage(usage, Vibration.Status.IGNORED_FOR_SETTINGS); + assertVibrationIgnoredForUsage(usage, Status.IGNORED_FOR_SETTINGS); } else { assertVibrationNotIgnoredForUsage(usage); } @@ -597,7 +599,7 @@ public class VibrationSettingsTest { mVibrationSettings.mSettingChangeReceiver.onReceive(mContextSpy, new Intent(AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION)); - assertVibrationIgnoredForUsage(USAGE_RINGTONE, Vibration.Status.IGNORED_FOR_RINGER_MODE); + assertVibrationIgnoredForUsage(USAGE_RINGTONE, Status.IGNORED_FOR_RINGER_MODE); } @Test @@ -611,7 +613,7 @@ public class VibrationSettingsTest { new VibrationAttributes.Builder() .setUsage(USAGE_IME_FEEDBACK) .build(), - Vibration.Status.IGNORED_FOR_SETTINGS); + Status.IGNORED_FOR_SETTINGS); // General touch and keyboard touch with bypass flag not ignored. assertVibrationNotIgnoredForUsage(USAGE_TOUCH); @@ -629,7 +631,7 @@ public class VibrationSettingsTest { setUserSetting(Settings.System.KEYBOARD_VIBRATION_ENABLED, 1 /* ON */); // General touch ignored. - assertVibrationIgnoredForUsage(USAGE_TOUCH, Vibration.Status.IGNORED_FOR_SETTINGS); + assertVibrationIgnoredForUsage(USAGE_TOUCH, Status.IGNORED_FOR_SETTINGS); // Keyboard touch not ignored. assertVibrationNotIgnoredForAttributes( @@ -645,14 +647,14 @@ public class VibrationSettingsTest { setUserSetting(Settings.System.KEYBOARD_VIBRATION_ENABLED, 1 /* ON */); // General touch ignored. - assertVibrationIgnoredForUsage(USAGE_TOUCH, Vibration.Status.IGNORED_FOR_SETTINGS); + assertVibrationIgnoredForUsage(USAGE_TOUCH, Status.IGNORED_FOR_SETTINGS); // Keyboard touch ignored. assertVibrationIgnoredForAttributes( new VibrationAttributes.Builder() .setUsage(USAGE_IME_FEEDBACK) .build(), - Vibration.Status.IGNORED_FOR_SETTINGS); + Status.IGNORED_FOR_SETTINGS); } @Test @@ -668,7 +670,7 @@ public class VibrationSettingsTest { // Ignore the vibration when the coming device id represents a virtual device. for (int usage : ALL_USAGES) { assertVibrationIgnoredForUsageAndDevice(usage, VIRTUAL_DEVICE_ID, - Vibration.Status.IGNORED_FROM_VIRTUAL_DEVICE); + Status.IGNORED_FROM_VIRTUAL_DEVICE); } } @@ -911,22 +913,21 @@ public class VibrationSettingsTest { } private void assertVibrationIgnoredForUsage(@VibrationAttributes.Usage int usage, - Vibration.Status expectedStatus) { + Status expectedStatus) { assertVibrationIgnoredForUsageAndDevice(usage, Context.DEVICE_ID_DEFAULT, expectedStatus); } private void assertVibrationIgnoredForUsageAndDevice(@VibrationAttributes.Usage int usage, - int deviceId, Vibration.Status expectedStatus) { - Vibration.CallerInfo callerInfo = new Vibration.CallerInfo( + int deviceId, Status expectedStatus) { + CallerInfo callerInfo = new CallerInfo( VibrationAttributes.createForUsage(usage), UID, deviceId, null, null); assertEquals(errorMessageForUsage(usage), expectedStatus, mVibrationSettings.shouldIgnoreVibration(callerInfo)); } private void assertVibrationIgnoredForAttributes(VibrationAttributes attrs, - Vibration.Status expectedStatus) { - Vibration.CallerInfo callerInfo = new Vibration.CallerInfo(attrs, UID, - Context.DEVICE_ID_DEFAULT, null, null); + Status expectedStatus) { + CallerInfo callerInfo = new CallerInfo(attrs, UID, Context.DEVICE_ID_DEFAULT, null, null); assertEquals(errorMessageForAttributes(attrs), expectedStatus, mVibrationSettings.shouldIgnoreVibration(callerInfo)); } @@ -948,7 +949,7 @@ public class VibrationSettingsTest { private void assertVibrationNotIgnoredForUsageAndFlagsAndDevice( @VibrationAttributes.Usage int usage, int deviceId, @VibrationAttributes.Flag int flags) { - Vibration.CallerInfo callerInfo = new Vibration.CallerInfo( + CallerInfo callerInfo = new CallerInfo( new VibrationAttributes.Builder().setUsage(usage).setFlags(flags).build(), UID, deviceId, null, null); assertNull(errorMessageForUsage(usage), @@ -956,7 +957,7 @@ public class VibrationSettingsTest { } private void assertVibrationNotIgnoredForAttributes(VibrationAttributes attrs) { - Vibration.CallerInfo callerInfo = new Vibration.CallerInfo(attrs, UID, + CallerInfo callerInfo = new CallerInfo(attrs, UID, Context.DEVICE_ID_DEFAULT, null, null); assertNull(errorMessageForAttributes(attrs), mVibrationSettings.shouldIgnoreVibration(callerInfo)); @@ -1017,10 +1018,10 @@ public class VibrationSettingsTest { new PowerManager.SleepData(sleepTime, reason)); } - private Vibration.CallerInfo createCallerInfo(int uid, String opPkg, + private CallerInfo createCallerInfo(int uid, String opPkg, @VibrationAttributes.Usage int usage) { VibrationAttributes attrs = VibrationAttributes.createForUsage(usage); - return new Vibration.CallerInfo(attrs, uid, VIRTUAL_DEVICE_ID, opPkg, null); + return new CallerInfo(attrs, uid, VIRTUAL_DEVICE_ID, opPkg, null); } private void setBatteryReceiverRegistrationResult(Intent result) { 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 bfdaa785a669..31cc50f18299 100644 --- a/services/tests/vibrator/src/com/android/server/vibrator/VibrationThreadTest.java +++ b/services/tests/vibrator/src/com/android/server/vibrator/VibrationThreadTest.java @@ -78,6 +78,8 @@ import androidx.test.filters.LargeTest; import com.android.internal.util.test.FakeSettingsProvider; import com.android.internal.util.test.FakeSettingsProviderRule; import com.android.server.LocalServices; +import com.android.server.vibrator.VibrationSession.CallerInfo; +import com.android.server.vibrator.VibrationSession.Status; import org.junit.After; import org.junit.Before; @@ -189,7 +191,7 @@ public class VibrationThreadTest { waitForCompletion(); verify(mControllerCallbacks, never()).onComplete(anyInt(), eq(vibrationId)); - verifyCallbacksTriggered(vibrationId, Vibration.Status.IGNORED_UNSUPPORTED); + verifyCallbacksTriggered(vibrationId, Status.IGNORED_UNSUPPORTED); } @Test @@ -202,7 +204,7 @@ public class VibrationThreadTest { waitForCompletion(); verify(mControllerCallbacks, never()).onComplete(anyInt(), eq(vibrationId)); - verifyCallbacksTriggered(vibrationId, Vibration.Status.IGNORED_UNSUPPORTED); + verifyCallbacksTriggered(vibrationId, Status.IGNORED_UNSUPPORTED); } @Test @@ -216,7 +218,7 @@ public class VibrationThreadTest { verify(mManagerHooks).noteVibratorOn(eq(UID), eq(10L)); verify(mManagerHooks).noteVibratorOff(eq(UID)); verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibrationId)); - verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED); + verifyCallbacksTriggered(vibrationId, Status.FINISHED); assertFalse(mControllers.get(VIBRATOR_ID).isVibrating()); assertEquals(Arrays.asList(expectedOneShot(10)), @@ -233,7 +235,7 @@ public class VibrationThreadTest { verify(mManagerHooks).noteVibratorOn(eq(UID), eq(10L)); verify(mManagerHooks).noteVibratorOff(eq(UID)); verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibrationId)); - verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED); + verifyCallbacksTriggered(vibrationId, Status.FINISHED); assertFalse(mControllers.get(VIBRATOR_ID).isVibrating()); assertEquals(Arrays.asList(expectedOneShot(10)), @@ -253,7 +255,7 @@ public class VibrationThreadTest { verify(mManagerHooks).noteVibratorOn(eq(UID), eq(15L)); verify(mManagerHooks).noteVibratorOff(eq(UID)); verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibrationId)); - verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED); + verifyCallbacksTriggered(vibrationId, Status.FINISHED); assertFalse(mControllers.get(VIBRATOR_ID).isVibrating()); assertEquals(Arrays.asList(expectedOneShot(15)), @@ -326,13 +328,10 @@ public class VibrationThreadTest { assertTrue(mThread.isRunningVibrationId(vibrationId)); assertTrue(mControllers.get(VIBRATOR_ID).isVibrating()); - Vibration.EndInfo cancelVibrationInfo = new Vibration.EndInfo( - Vibration.Status.CANCELLED_SUPERSEDED, new Vibration.CallerInfo( - VibrationAttributes.createForUsage(VibrationAttributes.USAGE_ALARM), /* uid= */ - 1, /* deviceId= */ -1, /* opPkg= */ null, /* reason= */ null)); - mVibrationConductor.notifyCancelled( - cancelVibrationInfo, - /* immediate= */ false); + Vibration.EndInfo cancelVibrationInfo = new Vibration.EndInfo(Status.CANCELLED_SUPERSEDED, + new CallerInfo(VibrationAttributes.createForUsage(VibrationAttributes.USAGE_ALARM), + /* uid= */ 1, /* deviceId= */ -1, /* opPkg= */ null, /* reason= */ null)); + mVibrationConductor.notifyCancelled(cancelVibrationInfo, /* immediate= */ false); waitForCompletion(); assertFalse(mThread.isRunningVibrationId(vibrationId)); @@ -363,11 +362,10 @@ public class VibrationThreadTest { assertTrue(waitUntil(() -> !fakeVibrator.getAmplitudes().isEmpty(), TEST_TIMEOUT_MILLIS)); mVibrationConductor.notifyCancelled( - new Vibration.EndInfo(Vibration.Status.CANCELLED_BY_USER), - /* immediate= */ false); + new Vibration.EndInfo(Status.CANCELLED_BY_USER), /* immediate= */ false); waitForCompletion(); - verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED_BY_USER); + verifyCallbacksTriggered(vibrationId, Status.CANCELLED_BY_USER); assertFalse(mControllers.get(VIBRATOR_ID).isVibrating()); assertEquals(Arrays.asList(expectedOneShot(5000)), fakeVibrator.getEffectSegments(vibrationId)); @@ -385,7 +383,7 @@ public class VibrationThreadTest { verify(mManagerHooks).noteVibratorOn(eq(UID), eq(300L)); verify(mManagerHooks).noteVibratorOff(eq(UID)); - verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED); + verifyCallbacksTriggered(vibrationId, Status.FINISHED); assertThat(mControllers.get(VIBRATOR_ID).isVibrating()).isFalse(); assertThat(mVibratorProviders.get(VIBRATOR_ID).getEffectSegments(vibrationId)) @@ -406,7 +404,7 @@ public class VibrationThreadTest { verify(mManagerHooks).noteVibratorOn(eq(UID), eq(350L)); verify(mManagerHooks).noteVibratorOff(eq(UID)); - verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED); + verifyCallbacksTriggered(vibrationId, Status.FINISHED); assertThat(mControllers.get(VIBRATOR_ID).isVibrating()).isFalse(); assertThat(mVibratorProviders.get(VIBRATOR_ID).getEffectSegments(vibrationId)) @@ -433,11 +431,10 @@ public class VibrationThreadTest { assertTrue(waitUntil(() -> fakeVibrator.getEffectSegments(vibrationId).size() >= 5, 5000L + TEST_TIMEOUT_MILLIS)); mVibrationConductor.notifyCancelled( - new Vibration.EndInfo(Vibration.Status.CANCELLED_BY_USER), - /* immediate= */ false); + new Vibration.EndInfo(Status.CANCELLED_BY_USER), /* immediate= */ false); waitForCompletion(); - verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED_BY_USER); + verifyCallbacksTriggered(vibrationId, Status.CANCELLED_BY_USER); assertThat(mControllers.get(VIBRATOR_ID).isVibrating()).isFalse(); assertThat(mVibratorProviders.get(VIBRATOR_ID).getEffectSegments(vibrationId).subList(0, 5)) @@ -466,12 +463,11 @@ public class VibrationThreadTest { assertTrue(waitUntil(() -> !fakeVibrator.getEffectSegments(vibrationId).isEmpty(), TEST_TIMEOUT_MILLIS)); mVibrationConductor.notifyCancelled( - new Vibration.EndInfo(Vibration.Status.CANCELLED_BY_USER), - /* immediate= */ false); + new Vibration.EndInfo(Status.CANCELLED_BY_USER), /* immediate= */ false); waitForCompletion(); // PWLE size max was used to generate a single vibrate call with 10 segments. - verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED_BY_USER); + verifyCallbacksTriggered(vibrationId, Status.CANCELLED_BY_USER); assertFalse(mControllers.get(VIBRATOR_ID).isVibrating()); assertEquals(10, fakeVibrator.getEffectSegments(vibrationId).size()); } @@ -496,12 +492,11 @@ public class VibrationThreadTest { assertTrue(waitUntil(() -> !fakeVibrator.getEffectSegments(vibrationId).isEmpty(), TEST_TIMEOUT_MILLIS)); mVibrationConductor.notifyCancelled( - new Vibration.EndInfo(Vibration.Status.CANCELLED_BY_SCREEN_OFF), - /* immediate= */ false); + new Vibration.EndInfo(Status.CANCELLED_BY_SCREEN_OFF), /* immediate= */ false); waitForCompletion(); // Composition size max was used to generate a single vibrate call with 10 primitives. - verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED_BY_SCREEN_OFF); + verifyCallbacksTriggered(vibrationId, Status.CANCELLED_BY_SCREEN_OFF); assertFalse(mControllers.get(VIBRATOR_ID).isVibrating()); assertEquals(10, fakeVibrator.getEffectSegments(vibrationId).size()); } @@ -519,11 +514,10 @@ public class VibrationThreadTest { assertTrue(waitUntil(() -> !fakeVibrator.getAmplitudes().isEmpty(), TEST_TIMEOUT_MILLIS)); mVibrationConductor.notifyCancelled( - new Vibration.EndInfo(Vibration.Status.CANCELLED_BY_USER), - /* immediate= */ false); + new Vibration.EndInfo(Status.CANCELLED_BY_USER), /* immediate= */ false); waitForCompletion(); - verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED_BY_USER); + verifyCallbacksTriggered(vibrationId, Status.CANCELLED_BY_USER); assertFalse(mControllers.get(VIBRATOR_ID).isVibrating()); assertEquals(Arrays.asList(expectedOneShot(5550)), fakeVibrator.getEffectSegments(vibrationId)); @@ -545,11 +539,10 @@ public class VibrationThreadTest { assertTrue(waitUntil(() -> fakeVibrator.getEffectSegments(vibrationId).size() > 1, expectedOnDuration + TEST_TIMEOUT_MILLIS)); mVibrationConductor.notifyCancelled( - new Vibration.EndInfo(Vibration.Status.CANCELLED_BY_USER), - /* immediate= */ false); + new Vibration.EndInfo(Status.CANCELLED_BY_USER), /* immediate= */ false); waitForCompletion(); - verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED_BY_USER); + verifyCallbacksTriggered(vibrationId, Status.CANCELLED_BY_USER); assertFalse(mControllers.get(VIBRATOR_ID).isVibrating()); List<VibrationEffectSegment> effectSegments = fakeVibrator.getEffectSegments(vibrationId); // First time, turn vibrator ON for the expected fixed duration. @@ -584,14 +577,14 @@ public class VibrationThreadTest { // fail at waitForCompletion(vibrationThread) if the vibration not cancelled immediately. Thread cancellingThread = new Thread(() -> mVibrationConductor.notifyCancelled( - new Vibration.EndInfo(Vibration.Status.CANCELLED_BY_SETTINGS_UPDATE), + new Vibration.EndInfo(Status.CANCELLED_BY_SETTINGS_UPDATE), /* immediate= */ false)); cancellingThread.start(); waitForCompletion(/* timeout= */ 50); cancellingThread.join(); - verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED_BY_SETTINGS_UPDATE); + verifyCallbacksTriggered(vibrationId, Status.CANCELLED_BY_SETTINGS_UPDATE); assertFalse(mControllers.get(VIBRATOR_ID).isVibrating()); } @@ -614,14 +607,14 @@ public class VibrationThreadTest { // fail at waitForCompletion(vibrationThread) if the vibration not cancelled immediately. Thread cancellingThread = new Thread(() -> mVibrationConductor.notifyCancelled( - new Vibration.EndInfo(Vibration.Status.CANCELLED_BY_SETTINGS_UPDATE), + new Vibration.EndInfo(Status.CANCELLED_BY_SETTINGS_UPDATE), /* immediate= */ false)); cancellingThread.start(); waitForCompletion(/* timeout= */ 50); cancellingThread.join(); - verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED_BY_SETTINGS_UPDATE); + verifyCallbacksTriggered(vibrationId, Status.CANCELLED_BY_SETTINGS_UPDATE); assertFalse(mControllers.get(VIBRATOR_ID).isVibrating()); } @@ -641,14 +634,14 @@ public class VibrationThreadTest { // fail at waitForCompletion(vibrationThread) if the vibration not cancelled immediately. Thread cancellingThread = new Thread(() -> mVibrationConductor.notifyCancelled( - new Vibration.EndInfo(Vibration.Status.CANCELLED_BY_SCREEN_OFF), + new Vibration.EndInfo(Status.CANCELLED_BY_SCREEN_OFF), /* immediate= */ false)); cancellingThread.start(); waitForCompletion(/* timeout= */ 50); cancellingThread.join(); - verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED_BY_SCREEN_OFF); + verifyCallbacksTriggered(vibrationId, Status.CANCELLED_BY_SCREEN_OFF); assertFalse(mControllers.get(VIBRATOR_ID).isVibrating()); } @@ -663,7 +656,7 @@ public class VibrationThreadTest { verify(mManagerHooks).noteVibratorOn(eq(UID), eq(20L)); verify(mManagerHooks).noteVibratorOff(eq(UID)); verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibrationId)); - verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED); + verifyCallbacksTriggered(vibrationId, Status.FINISHED); assertFalse(mControllers.get(VIBRATOR_ID).isVibrating()); assertEquals(Arrays.asList(expectedPrebaked(VibrationEffect.EFFECT_THUD)), @@ -684,7 +677,7 @@ public class VibrationThreadTest { verify(mManagerHooks).noteVibratorOn(eq(UID), eq(10L)); verify(mManagerHooks).noteVibratorOff(eq(UID)); verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibrationId)); - verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED); + verifyCallbacksTriggered(vibrationId, Status.FINISHED); assertFalse(mControllers.get(VIBRATOR_ID).isVibrating()); assertEquals(Arrays.asList(expectedOneShot(10)), @@ -701,7 +694,7 @@ public class VibrationThreadTest { verify(mManagerHooks).noteVibratorOn(eq(UID), eq(0L)); verify(mManagerHooks, never()).noteVibratorOff(eq(UID)); verify(mControllerCallbacks, never()).onComplete(eq(VIBRATOR_ID), eq(vibrationId)); - verifyCallbacksTriggered(vibrationId, Vibration.Status.IGNORED_UNSUPPORTED); + verifyCallbacksTriggered(vibrationId, Status.IGNORED_UNSUPPORTED); assertTrue(mVibratorProviders.get(VIBRATOR_ID).getEffectSegments(vibrationId).isEmpty()); } @@ -718,7 +711,7 @@ public class VibrationThreadTest { eq(PerformVendorEffectVibratorStep.VENDOR_EFFECT_MAX_DURATION_MS)); verify(mManagerHooks).noteVibratorOff(eq(UID)); verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibrationId)); - verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED); + verifyCallbacksTriggered(vibrationId, Status.FINISHED); assertThat(mControllers.get(VIBRATOR_ID).isVibrating()).isFalse(); assertThat(mVibratorProviders.get(VIBRATOR_ID).getVendorEffects(vibrationId)) @@ -743,7 +736,7 @@ public class VibrationThreadTest { verify(mManagerHooks).noteVibratorOn(eq(UID), eq(40L)); verify(mManagerHooks).noteVibratorOff(eq(UID)); verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibrationId)); - verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED); + verifyCallbacksTriggered(vibrationId, Status.FINISHED); assertFalse(mControllers.get(VIBRATOR_ID).isVibrating()); assertEquals(Arrays.asList( expectedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1, 0), @@ -762,7 +755,7 @@ public class VibrationThreadTest { verify(mManagerHooks).noteVibratorOn(eq(UID), eq(0L)); verify(mManagerHooks, never()).noteVibratorOff(eq(UID)); verify(mControllerCallbacks, never()).onComplete(eq(VIBRATOR_ID), eq(vibrationId)); - verifyCallbacksTriggered(vibrationId, Vibration.Status.IGNORED_UNSUPPORTED); + verifyCallbacksTriggered(vibrationId, Status.IGNORED_UNSUPPORTED); assertTrue(mVibratorProviders.get(VIBRATOR_ID).getEffectSegments(vibrationId).isEmpty()); } @@ -784,7 +777,7 @@ public class VibrationThreadTest { long vibrationId = startThreadAndDispatcher(effect); waitForCompletion(); - verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED); + verifyCallbacksTriggered(vibrationId, Status.FINISHED); // Vibrator compose called twice. verify(mControllerCallbacks, times(2)).onComplete(eq(VIBRATOR_ID), eq(vibrationId)); assertEquals(3, fakeVibrator.getEffectSegments(vibrationId).size()); @@ -824,7 +817,7 @@ public class VibrationThreadTest { verify(mManagerHooks).noteVibratorOn(eq(UID), eq(10L)); verify(mManagerHooks).noteVibratorOff(eq(UID)); verify(mControllerCallbacks, times(5)).onComplete(eq(VIBRATOR_ID), eq(vibrationId)); - verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED); + verifyCallbacksTriggered(vibrationId, Status.FINISHED); assertFalse(mControllers.get(VIBRATOR_ID).isVibrating()); assertEquals(Arrays.asList( expectedOneShot(10), @@ -865,7 +858,7 @@ public class VibrationThreadTest { verify(mManagerHooks).noteVibratorOn(eq(UID), anyLong()); verify(mManagerHooks).noteVibratorOff(eq(UID)); verify(mControllerCallbacks, times(4)).onComplete(eq(VIBRATOR_ID), eq(vibrationId)); - verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED); + verifyCallbacksTriggered(vibrationId, Status.FINISHED); assertFalse(mControllers.get(VIBRATOR_ID).isVibrating()); List<VibrationEffectSegment> segments = @@ -904,7 +897,7 @@ public class VibrationThreadTest { verify(mManagerHooks).noteVibratorOn(eq(UID), eq(100L)); verify(mManagerHooks).noteVibratorOff(eq(UID)); verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibrationId)); - verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED); + verifyCallbacksTriggered(vibrationId, Status.FINISHED); assertFalse(mControllers.get(VIBRATOR_ID).isVibrating()); assertEquals(Arrays.asList( expectedRamp(/* amplitude= */ 1, /* frequencyHz= */ 150, /* duration= */ 10), @@ -942,7 +935,7 @@ public class VibrationThreadTest { long vibrationId = startThreadAndDispatcher(effect); waitForCompletion(); - verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED); + verifyCallbacksTriggered(vibrationId, Status.FINISHED); // Vibrator compose called 3 times with 2 segments instead of 2 times with 3 segments. // Using best split points instead of max-packing PWLEs. @@ -967,7 +960,7 @@ public class VibrationThreadTest { waitForCompletion(); assertFalse(mControllers.get(VIBRATOR_ID).isVibrating()); - verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED_BINDER_DIED); + verifyCallbacksTriggered(vibrationId, Status.CANCELLED_BINDER_DIED); } @Test @@ -977,7 +970,7 @@ public class VibrationThreadTest { long vibrationId = startThreadAndDispatcher(VibrationEffect.createOneShot(10, 100)); waitForCompletion(); - verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED); + verifyCallbacksTriggered(vibrationId, Status.FINISHED); verify(mManagerHooks, never()).prepareSyncedVibration(anyLong(), any()); verify(mManagerHooks, never()).triggerSyncedVibration(anyLong()); verify(mManagerHooks, never()).cancelSyncedVibration(); @@ -998,7 +991,7 @@ public class VibrationThreadTest { verify(mManagerHooks).noteVibratorOff(eq(UID)); verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibrationId)); verify(mControllerCallbacks, never()).onComplete(eq(2), eq(vibrationId)); - verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED); + verifyCallbacksTriggered(vibrationId, Status.FINISHED); assertFalse(mControllers.get(VIBRATOR_ID).isVibrating()); assertEquals(Arrays.asList(expectedPrebaked(VibrationEffect.EFFECT_TICK)), @@ -1022,7 +1015,7 @@ public class VibrationThreadTest { verify(mControllerCallbacks).onComplete(eq(1), eq(vibrationId)); verify(mControllerCallbacks).onComplete(eq(2), eq(vibrationId)); verify(mControllerCallbacks).onComplete(eq(3), eq(vibrationId)); - verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED); + verifyCallbacksTriggered(vibrationId, Status.FINISHED); assertFalse(mControllers.get(1).isVibrating()); assertFalse(mControllers.get(2).isVibrating()); assertFalse(mControllers.get(3).isVibrating()); @@ -1065,7 +1058,7 @@ public class VibrationThreadTest { verify(mControllerCallbacks).onComplete(eq(2), eq(vibrationId)); verify(mControllerCallbacks).onComplete(eq(3), eq(vibrationId)); verify(mControllerCallbacks).onComplete(eq(4), eq(vibrationId)); - verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED); + verifyCallbacksTriggered(vibrationId, Status.FINISHED); assertFalse(mControllers.get(1).isVibrating()); assertFalse(mControllers.get(2).isVibrating()); assertFalse(mControllers.get(3).isVibrating()); @@ -1117,7 +1110,7 @@ public class VibrationThreadTest { batteryVerifier.verify(mManagerHooks).noteVibratorOn(eq(UID), eq(20L)); batteryVerifier.verify(mManagerHooks).noteVibratorOff(eq(UID)); - verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED); + verifyCallbacksTriggered(vibrationId, Status.FINISHED); assertFalse(mControllers.get(1).isVibrating()); assertFalse(mControllers.get(2).isVibrating()); assertFalse(mControllers.get(3).isVibrating()); @@ -1166,7 +1159,7 @@ public class VibrationThreadTest { verify(mManagerHooks).prepareSyncedVibration(eq(expectedCap), eq(vibratorIds)); verify(mManagerHooks).triggerSyncedVibration(eq(vibrationId)); verify(mManagerHooks, never()).cancelSyncedVibration(); - verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED); + verifyCallbacksTriggered(vibrationId, Status.FINISHED); VibrationEffectSegment expected = expectedPrimitive( VibrationEffect.Composition.PRIMITIVE_CLICK, 1, 100); @@ -1213,7 +1206,7 @@ public class VibrationThreadTest { verify(mManagerHooks).prepareSyncedVibration(eq(expectedCap), eq(vibratorIds)); verify(mManagerHooks).triggerSyncedVibration(eq(vibrationId)); verify(mManagerHooks, never()).cancelSyncedVibration(); - verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED); + verifyCallbacksTriggered(vibrationId, Status.FINISHED); } @Test @@ -1305,7 +1298,7 @@ public class VibrationThreadTest { verify(mControllerCallbacks).onComplete(eq(1), eq(vibrationId)); verify(mControllerCallbacks).onComplete(eq(2), eq(vibrationId)); verify(mControllerCallbacks).onComplete(eq(3), eq(vibrationId)); - verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED); + verifyCallbacksTriggered(vibrationId, Status.FINISHED); assertFalse(mControllers.get(1).isVibrating()); assertFalse(mControllers.get(2).isVibrating()); assertFalse(mControllers.get(3).isVibrating()); @@ -1478,8 +1471,7 @@ public class VibrationThreadTest { // fail at waitForCompletion(cancellingThread). Thread cancellingThread = new Thread( () -> mVibrationConductor.notifyCancelled( - new Vibration.EndInfo(Vibration.Status.CANCELLED_BY_USER), - /* immediate= */ false)); + new Vibration.EndInfo(Status.CANCELLED_BY_USER), /* immediate= */ false)); cancellingThread.start(); // Cancelling the vibration should be fast and return right away, even if the thread is @@ -1488,7 +1480,7 @@ public class VibrationThreadTest { // After the vibrator call ends the vibration is cancelled and the vibrator is turned off. waitForCompletion(/* timeout= */ latency + TEST_TIMEOUT_MILLIS); - verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED_BY_USER); + verifyCallbacksTriggered(vibrationId, Status.CANCELLED_BY_USER); assertFalse(mControllers.get(VIBRATOR_ID).isVibrating()); } @@ -1517,14 +1509,14 @@ public class VibrationThreadTest { // fail at waitForCompletion(vibrationThread) if the vibration not cancelled immediately. Thread cancellingThread = new Thread( () -> mVibrationConductor.notifyCancelled( - new Vibration.EndInfo(Vibration.Status.CANCELLED_BY_SCREEN_OFF), + new Vibration.EndInfo(Status.CANCELLED_BY_SCREEN_OFF), /* immediate= */ false)); cancellingThread.start(); waitForCompletion(/* timeout= */ 50); cancellingThread.join(); - verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED_BY_SCREEN_OFF); + verifyCallbacksTriggered(vibrationId, Status.CANCELLED_BY_SCREEN_OFF); assertFalse(mControllers.get(1).isVibrating()); assertFalse(mControllers.get(2).isVibrating()); } @@ -1551,14 +1543,14 @@ public class VibrationThreadTest { // fail at waitForCompletion(vibrationThread) if the vibration not cancelled immediately. Thread cancellingThread = new Thread( () -> mVibrationConductor.notifyCancelled( - new Vibration.EndInfo(Vibration.Status.CANCELLED_BY_SCREEN_OFF), + new Vibration.EndInfo(Status.CANCELLED_BY_SCREEN_OFF), /* immediate= */ false)); cancellingThread.start(); waitForCompletion(/* timeout= */ 50); cancellingThread.join(); - verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED_BY_SCREEN_OFF); + verifyCallbacksTriggered(vibrationId, Status.CANCELLED_BY_SCREEN_OFF); assertFalse(mControllers.get(1).isVibrating()); assertFalse(mControllers.get(2).isVibrating()); } @@ -1585,15 +1577,14 @@ public class VibrationThreadTest { // fail at waitForCompletion(vibrationThread) if the vibration not cancelled immediately. Thread cancellingThread = new Thread( () -> mVibrationConductor.notifyCancelled( - new Vibration.EndInfo( - Vibration.Status.CANCELLED_BY_SCREEN_OFF), + new Vibration.EndInfo(Status.CANCELLED_BY_SCREEN_OFF), /* immediate= */ false)); cancellingThread.start(); waitForCompletion(/* timeout= */ 50); cancellingThread.join(); - verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED_BY_SCREEN_OFF); + verifyCallbacksTriggered(vibrationId, Status.CANCELLED_BY_SCREEN_OFF); assertFalse(mControllers.get(1).isVibrating()); assertFalse(mControllers.get(2).isVibrating()); } @@ -1612,7 +1603,7 @@ public class VibrationThreadTest { verify(mVibrationToken).linkToDeath(same(mVibrationConductor), eq(0)); verify(mVibrationToken).unlinkToDeath(same(mVibrationConductor), eq(0)); - verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED_BINDER_DIED); + verifyCallbacksTriggered(vibrationId, Status.CANCELLED_BINDER_DIED); assertFalse(mVibratorProviders.get(VIBRATOR_ID).getEffectSegments(vibrationId).isEmpty()); assertFalse(mControllers.get(VIBRATOR_ID).isVibrating()); } @@ -1628,7 +1619,7 @@ public class VibrationThreadTest { waitForCompletion(); verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibrationId)); - verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED); + verifyCallbacksTriggered(vibrationId, Status.FINISHED); // Duration extended for 5 + 5 + 5 + 15. assertEquals(Arrays.asList(expectedOneShot(30)), @@ -1651,7 +1642,7 @@ public class VibrationThreadTest { // Vibration completed but vibrator not yet released. verify(mManagerHooks, timeout(TEST_TIMEOUT_MILLIS)).onVibrationCompleted(eq(vibrationId), - eq(new Vibration.EndInfo(Vibration.Status.FINISHED))); + eq(new Vibration.EndInfo(Status.FINISHED))); verify(mManagerHooks, never()).onVibrationThreadReleased(anyLong()); // Thread still running ramp down. @@ -1663,13 +1654,12 @@ public class VibrationThreadTest { // Will stop the ramp down right away. mVibrationConductor.notifyCancelled( - new Vibration.EndInfo(Vibration.Status.CANCELLED_BY_SETTINGS_UPDATE), - /* immediate= */ true); + new Vibration.EndInfo(Status.CANCELLED_BY_SETTINGS_UPDATE), /* immediate= */ true); waitForCompletion(); // Does not cancel already finished vibration, but releases vibrator. verify(mManagerHooks, never()).onVibrationCompleted(eq(vibrationId), - eq(new Vibration.EndInfo(Vibration.Status.CANCELLED_BY_SETTINGS_UPDATE))); + eq(new Vibration.EndInfo(Status.CANCELLED_BY_SETTINGS_UPDATE))); verify(mManagerHooks).onVibrationThreadReleased(vibrationId); } @@ -1684,11 +1674,10 @@ public class VibrationThreadTest { assertTrue(waitUntil(() -> mControllers.get(VIBRATOR_ID).isVibrating(), TEST_TIMEOUT_MILLIS)); mVibrationConductor.notifyCancelled( - new Vibration.EndInfo(Vibration.Status.CANCELLED_BY_USER), - /* immediate= */ false); + new Vibration.EndInfo(Status.CANCELLED_BY_USER), /* immediate= */ false); waitForCompletion(); - verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED_BY_USER); + verifyCallbacksTriggered(vibrationId, Status.CANCELLED_BY_USER); // Duration extended for 10000 + 15. assertEquals(Arrays.asList(expectedOneShot(10_015)), @@ -1711,7 +1700,7 @@ public class VibrationThreadTest { waitForCompletion(); verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibrationId)); - verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED); + verifyCallbacksTriggered(vibrationId, Status.FINISHED); assertEquals(Arrays.asList(expectedPrebaked(VibrationEffect.EFFECT_CLICK)), mVibratorProviders.get(VIBRATOR_ID).getEffectSegments(vibrationId)); @@ -1729,7 +1718,7 @@ public class VibrationThreadTest { waitForCompletion(); verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibrationId)); - verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED); + verifyCallbacksTriggered(vibrationId, Status.FINISHED); assertThat(mVibratorProviders.get(VIBRATOR_ID).getVendorEffects(vibrationId)) .containsExactly(effect) @@ -1752,7 +1741,7 @@ public class VibrationThreadTest { waitForCompletion(); verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibrationId)); - verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED); + verifyCallbacksTriggered(vibrationId, Status.FINISHED); assertEquals( Arrays.asList(expectedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1, 0)), @@ -1779,7 +1768,7 @@ public class VibrationThreadTest { waitForCompletion(); verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibrationId)); - verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED); + verifyCallbacksTriggered(vibrationId, Status.FINISHED); assertEquals(Arrays.asList(expectedRamp(0, 1, 150, 150, 1)), fakeVibrator.getEffectSegments(vibrationId)); @@ -1810,14 +1799,13 @@ public class VibrationThreadTest { long vibrationId1 = startThreadAndDispatcher(effect1); waitForCompletion(); verify(mControllerCallbacks).onComplete(VIBRATOR_ID, vibrationId1); - verifyCallbacksTriggered(vibrationId1, Vibration.Status.FINISHED); + verifyCallbacksTriggered(vibrationId1, Status.FINISHED); long vibrationId2 = startThreadAndDispatcher(effect2); // Effect2 won't complete on its own. Cancel it after a couple of repeats. Thread.sleep(150); // More than two TICKs. mVibrationConductor.notifyCancelled( - new Vibration.EndInfo(Vibration.Status.CANCELLED_BY_USER), - /* immediate= */ false); + new Vibration.EndInfo(Status.CANCELLED_BY_USER), /* immediate= */ false); waitForCompletion(); long vibrationId3 = startThreadAndDispatcher(effect3); @@ -1827,8 +1815,7 @@ public class VibrationThreadTest { long start4 = System.currentTimeMillis(); long vibrationId4 = startThreadAndDispatcher(effect4); mVibrationConductor.notifyCancelled( - new Vibration.EndInfo(Vibration.Status.CANCELLED_BY_SCREEN_OFF), - /* immediate= */ true); + new Vibration.EndInfo(Status.CANCELLED_BY_SCREEN_OFF), /* immediate= */ true); waitForCompletion(); long duration4 = System.currentTimeMillis() - start4; @@ -1841,14 +1828,14 @@ public class VibrationThreadTest { // Effect1 verify(mControllerCallbacks).onComplete(VIBRATOR_ID, vibrationId1); - verifyCallbacksTriggered(vibrationId1, Vibration.Status.FINISHED); + verifyCallbacksTriggered(vibrationId1, Status.FINISHED); assertEquals(Arrays.asList(expectedPrebaked(VibrationEffect.EFFECT_CLICK)), fakeVibrator.getEffectSegments(vibrationId1)); // Effect2: repeating, cancelled. verify(mControllerCallbacks, atLeast(2)).onComplete(VIBRATOR_ID, vibrationId2); - verifyCallbacksTriggered(vibrationId2, Vibration.Status.CANCELLED_BY_USER); + verifyCallbacksTriggered(vibrationId2, Status.CANCELLED_BY_USER); // The exact count of segments might vary, so just check that there's more than 2 and // all elements are the same segment. @@ -1860,13 +1847,13 @@ public class VibrationThreadTest { // Effect3 verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibrationId3)); - verifyCallbacksTriggered(vibrationId3, Vibration.Status.FINISHED); + verifyCallbacksTriggered(vibrationId3, Status.FINISHED); assertEquals(Arrays.asList( expectedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1, 0)), fakeVibrator.getEffectSegments(vibrationId3)); // Effect4: cancelled quickly. - verifyCallbacksTriggered(vibrationId4, Vibration.Status.CANCELLED_BY_SCREEN_OFF); + verifyCallbacksTriggered(vibrationId4, Status.CANCELLED_BY_SCREEN_OFF); assertTrue("Tested duration=" + duration4, duration4 < 2000); // Effect5: played normally after effect4, which may or may not have played. @@ -1907,7 +1894,7 @@ public class VibrationThreadTest { .build(); HalVibration vib = new HalVibration(mVibrationToken, CombinedVibration.createParallel(effect), - new Vibration.CallerInfo(attrs, UID, DEVICE_ID, PACKAGE_NAME, "reason")); + new CallerInfo(attrs, UID, DEVICE_ID, PACKAGE_NAME, "reason")); return startThreadAndDispatcher(vib, requestVibrationParamsFuture); } @@ -1944,7 +1931,7 @@ public class VibrationThreadTest { private HalVibration createVibration(CombinedVibration effect) { return new HalVibration(mVibrationToken, effect, - new Vibration.CallerInfo(ATTRS, UID, DEVICE_ID, PACKAGE_NAME, "reason")); + new CallerInfo(ATTRS, UID, DEVICE_ID, PACKAGE_NAME, "reason")); } private SparseArray<VibratorController> createVibratorControllers() { @@ -2007,7 +1994,7 @@ public class VibrationThreadTest { .collect(Collectors.toList()); } - private void verifyCallbacksTriggered(long vibrationId, Vibration.Status expectedStatus) { + private void verifyCallbacksTriggered(long vibrationId, Status expectedStatus) { verifyCallbacksTriggered(vibrationId, new Vibration.EndInfo(expectedStatus)); } diff --git a/services/tests/vibrator/src/com/android/server/vibrator/VibratorFrameworkStatsLoggerTest.java b/services/tests/vibrator/src/com/android/server/vibrator/VibratorFrameworkStatsLoggerTest.java index 3466bbb26fc8..cd057b619000 100644 --- a/services/tests/vibrator/src/com/android/server/vibrator/VibratorFrameworkStatsLoggerTest.java +++ b/services/tests/vibrator/src/com/android/server/vibrator/VibratorFrameworkStatsLoggerTest.java @@ -22,6 +22,8 @@ import static org.junit.Assert.assertTrue; import android.os.Handler; import android.os.test.TestLooper; +import com.android.server.vibrator.VibrationSession.Status; + import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -113,7 +115,6 @@ public class VibratorFrameworkStatsLoggerTest { } private static VibrationStats.StatsInfo newEmptyStatsInfo() { - return new VibrationStats.StatsInfo( - 0, 0, 0, Vibration.Status.FINISHED, new VibrationStats(), 0L); + return new VibrationStats.StatsInfo(0, 0, 0, Status.FINISHED, new VibrationStats(), 0L); } } diff --git a/services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java b/services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java index 4afb56265563..4012575cda88 100644 --- a/services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java +++ b/services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java @@ -110,6 +110,7 @@ import com.android.internal.util.test.FakeSettingsProviderRule; import com.android.server.LocalServices; import com.android.server.companion.virtual.VirtualDeviceManagerInternal; import com.android.server.pm.BackgroundUserSoundNotifier; +import com.android.server.vibrator.VibrationSession.Status; import org.junit.After; import org.junit.Before; @@ -803,8 +804,8 @@ public class VibratorManagerServiceTest { mockVibrators(1); VibratorManagerService service = createSystemReadyService(); - var vib = vibrate(service, - VibrationEffect.createWaveform(new long[]{100, 100, 100, 100}, 0), RINGTONE_ATTRS); + HalVibration vib = vibrate(service, VibrationEffect.createWaveform( + new long[]{0, TEST_TIMEOUT_MILLIS, TEST_TIMEOUT_MILLIS}, 0), RINGTONE_ATTRS); assertTrue(waitUntil(s -> s.isVibrating(1), service, TEST_TIMEOUT_MILLIS)); @@ -815,14 +816,80 @@ public class VibratorManagerServiceTest { service.mAppOpsChangeListener.onOpChanged(AppOpsManager.OP_VIBRATE, null); assertTrue(waitUntil(s -> vib.hasEnded(), service, TEST_TIMEOUT_MILLIS)); + assertThat(vib.getStatus()).isEqualTo(Status.CANCELLED_BY_APP_OPS); + } - var statsInfoCaptor = ArgumentCaptor.forClass(VibrationStats.StatsInfo.class); - verify(mVibratorFrameworkStatsLoggerMock, timeout(TEST_TIMEOUT_MILLIS)) - .writeVibrationReportedAsync(statsInfoCaptor.capture()); + @Test + public void vibrate_thenPowerModeChanges_getsCancelled() throws Exception { + mockVibrators(1, 2); + VibratorManagerService service = createSystemReadyService(); - VibrationStats.StatsInfo touchMetrics = statsInfoCaptor.getAllValues().get(0); - assertEquals(Vibration.Status.CANCELLED_BY_APP_OPS.getProtoEnumValue(), - touchMetrics.status); + HalVibration vib = vibrate(service, + CombinedVibration.startParallel() + .addVibrator(1, VibrationEffect.createOneShot(2 * TEST_TIMEOUT_MILLIS, 100)) + .combine(), + HAPTIC_FEEDBACK_ATTRS); + + // VibrationThread will start this vibration async, so wait until vibration is triggered. + assertTrue(waitUntil(s -> s.isVibrating(1), service, TEST_TIMEOUT_MILLIS)); + + mRegisteredPowerModeListener.onLowPowerModeChanged(LOW_POWER_STATE); + + assertTrue(waitUntil(s -> vib.hasEnded(), service, TEST_TIMEOUT_MILLIS)); + assertThat(vib.getStatus()).isEqualTo(Status.CANCELLED_BY_SETTINGS_UPDATE); + } + + @Test + public void vibrate_thenSettingsRefreshedWithoutChange_doNotCancelVibration() throws Exception { + mockVibrators(1); + VibratorManagerService service = createSystemReadyService(); + + vibrate(service, VibrationEffect.createOneShot(2 * TEST_TIMEOUT_MILLIS, 100), + HAPTIC_FEEDBACK_ATTRS); + + // VibrationThread will start this vibration async, so wait until vibration is triggered. + assertTrue(waitUntil(s -> s.isVibrating(1), service, TEST_TIMEOUT_MILLIS)); + + service.updateServiceState(); + + // Vibration is not stopped nearly after updating service. + assertFalse(waitUntil(s -> !s.isVibrating(1), service, 50)); + } + + @Test + public void vibrate_thenSettingsChange_getsCancelled() throws Exception { + mockVibrators(1); + VibratorManagerService service = createSystemReadyService(); + + HalVibration vib = vibrate(service, + VibrationEffect.createOneShot(2 * TEST_TIMEOUT_MILLIS, 100), + HAPTIC_FEEDBACK_ATTRS); + + // VibrationThread will start this vibration async, so wait until vibration is triggered. + assertTrue(waitUntil(s -> s.isVibrating(1), service, TEST_TIMEOUT_MILLIS)); + + setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, Vibrator.VIBRATION_INTENSITY_OFF); + service.mVibrationSettings.mSettingObserver.onChange(false); + service.updateServiceState(); + + assertTrue(waitUntil(s -> vib.hasEnded(), service, TEST_TIMEOUT_MILLIS)); + assertThat(vib.getStatus()).isEqualTo(Status.CANCELLED_BY_SETTINGS_UPDATE); + } + + @Test + public void vibrate_thenScreenTurnsOff_getsCancelled() throws Throwable { + mockVibrators(1); + VibratorManagerService service = createSystemReadyService(); + + HalVibration vib = vibrate(service, VibrationEffect.createWaveform( + new long[]{0, TEST_TIMEOUT_MILLIS, TEST_TIMEOUT_MILLIS}, 0), ALARM_ATTRS); + + assertTrue(waitUntil(s -> s.isVibrating(1), service, TEST_TIMEOUT_MILLIS)); + + service.mIntentReceiver.onReceive(mContextSpy, new Intent(Intent.ACTION_SCREEN_OFF)); + + assertTrue(waitUntil(s -> vib.hasEnded(), service, TEST_TIMEOUT_MILLIS)); + assertThat(vib.getStatus()).isEqualTo(Status.CANCELLED_BY_SCREEN_OFF); } @Test @@ -831,8 +898,8 @@ public class VibratorManagerServiceTest { mockVibrators(1); VibratorManagerService service = createSystemReadyService(); - var vib = vibrate(service, - VibrationEffect.createWaveform(new long[]{100, 100, 100, 100}, 0), ALARM_ATTRS); + HalVibration vib = vibrate(service, VibrationEffect.createWaveform( + new long[]{0, TEST_TIMEOUT_MILLIS, TEST_TIMEOUT_MILLIS}, 0), ALARM_ATTRS); assertTrue(waitUntil(s -> s.isVibrating(1), service, TEST_TIMEOUT_MILLIS)); @@ -841,14 +908,7 @@ public class VibratorManagerServiceTest { BackgroundUserSoundNotifier.ACTION_MUTE_SOUND)); assertTrue(waitUntil(s -> vib.hasEnded(), service, TEST_TIMEOUT_MILLIS)); - - var statsInfoCaptor = ArgumentCaptor.forClass(VibrationStats.StatsInfo.class); - verify(mVibratorFrameworkStatsLoggerMock, timeout(TEST_TIMEOUT_MILLIS)) - .writeVibrationReportedAsync(statsInfoCaptor.capture()); - - VibrationStats.StatsInfo touchMetrics = statsInfoCaptor.getAllValues().get(0); - assertEquals(Vibration.Status.CANCELLED_BY_FOREGROUND_USER.getProtoEnumValue(), - touchMetrics.status); + assertThat(vib.getStatus()).isEqualTo(Status.CANCELLED_BY_FOREGROUND_USER); } @Test @@ -1314,7 +1374,7 @@ public class VibratorManagerServiceTest { } @Test - public void vibrate_withriggerCallback_finishesVibration() throws Exception { + public void vibrate_withTriggerCallback_finishesVibration() throws Exception { mockCapabilities(IVibratorManager.CAP_SYNC, IVibratorManager.CAP_PREPARE_COMPOSE); mockVibrators(1, 2); mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS); @@ -1947,41 +2007,6 @@ public class VibratorManagerServiceTest { } @Test - public void vibrate_withPowerModeChange_cancelVibrationIfNotAllowed() throws Exception { - mockVibrators(1, 2); - VibratorManagerService service = createSystemReadyService(); - vibrate(service, - CombinedVibration.startParallel() - .addVibrator(1, VibrationEffect.createOneShot(1000, 100)) - .combine(), - HAPTIC_FEEDBACK_ATTRS); - - // VibrationThread will start this vibration async, so wait until vibration is triggered. - assertTrue(waitUntil(s -> s.isVibrating(1), service, TEST_TIMEOUT_MILLIS)); - - mRegisteredPowerModeListener.onLowPowerModeChanged(LOW_POWER_STATE); - - // Haptic feedback cancelled on low power mode. - assertTrue(waitUntil(s -> !s.isVibrating(1), service, TEST_TIMEOUT_MILLIS)); - } - - @Test - public void vibrate_withSettingsChange_doNotCancelVibration() throws Exception { - mockVibrators(1); - VibratorManagerService service = createSystemReadyService(); - - vibrate(service, VibrationEffect.createOneShot(1000, 100), HAPTIC_FEEDBACK_ATTRS); - - // VibrationThread will start this vibration async, so wait until vibration is triggered. - assertTrue(waitUntil(s -> s.isVibrating(1), service, TEST_TIMEOUT_MILLIS)); - - service.updateServiceState(); - - // Vibration is not stopped nearly after updating service. - assertFalse(waitUntil(s -> !s.isVibrating(1), service, 50)); - } - - @Test public void vibrate_ignoreVibrationFromVirtualDevice() throws Exception { mockVibrators(1); VibratorManagerService service = createSystemReadyService(); @@ -2475,6 +2500,113 @@ public class VibratorManagerServiceTest { } @Test + public void onExternalVibration_thenDeniedAppOps_doNotCancelVibration() throws Throwable { + mockVibrators(1); + mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_EXTERNAL_CONTROL); + VibratorManagerService service = createSystemReadyService(); + + IExternalVibrationController externalVibrationControllerMock = + mock(IExternalVibrationController.class); + ExternalVibration externalVibration = new ExternalVibration(UID, PACKAGE_NAME, + AUDIO_ALARM_ATTRS, externalVibrationControllerMock, mock(IBinder.class)); + ExternalVibrationScale scale = mExternalVibratorService.onExternalVibrationStart( + externalVibration); + + assertThat(scale.scaleLevel).isNotEqualTo(ExternalVibrationScale.ScaleLevel.SCALE_MUTE); + + when(mAppOpsManagerMock.checkAudioOpNoThrow(eq(AppOpsManager.OP_VIBRATE), + eq(AudioAttributes.USAGE_ALARM), anyInt(), anyString())) + .thenReturn(AppOpsManager.MODE_IGNORED); + service.mAppOpsChangeListener.onOpChanged(AppOpsManager.OP_VIBRATE, null); + + verify(externalVibrationControllerMock, never()).mute(); + } + + @Test + public void onExternalVibration_thenPowerModeChanges_doNotCancelVibration() throws Exception { + mockVibrators(1); + mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_EXTERNAL_CONTROL); + createSystemReadyService(); + + IExternalVibrationController externalVibrationControllerMock = + mock(IExternalVibrationController.class); + ExternalVibration externalVibration = new ExternalVibration(UID, PACKAGE_NAME, + AUDIO_ALARM_ATTRS, externalVibrationControllerMock, mock(IBinder.class)); + ExternalVibrationScale scale = mExternalVibratorService.onExternalVibrationStart( + externalVibration); + + assertThat(scale.scaleLevel).isNotEqualTo(ExternalVibrationScale.ScaleLevel.SCALE_MUTE); + + mRegisteredPowerModeListener.onLowPowerModeChanged(LOW_POWER_STATE); + + verify(externalVibrationControllerMock, never()).mute(); + } + + @Test + public void onExternalVibration_thenSettingsChange_doNotCancelVibration() throws Exception { + mockVibrators(1); + mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_EXTERNAL_CONTROL); + VibratorManagerService service = createSystemReadyService(); + + IExternalVibrationController externalVibrationControllerMock = + mock(IExternalVibrationController.class); + ExternalVibration externalVibration = new ExternalVibration(UID, PACKAGE_NAME, + AUDIO_ALARM_ATTRS, externalVibrationControllerMock, mock(IBinder.class)); + ExternalVibrationScale scale = mExternalVibratorService.onExternalVibrationStart( + externalVibration); + + assertThat(scale.scaleLevel).isNotEqualTo(ExternalVibrationScale.ScaleLevel.SCALE_MUTE); + + setUserSetting(Settings.System.ALARM_VIBRATION_INTENSITY, Vibrator.VIBRATION_INTENSITY_OFF); + service.mVibrationSettings.mSettingObserver.onChange(false); + service.updateServiceState(); + + verify(externalVibrationControllerMock, never()).mute(); + } + + @Test + public void onExternalVibration_thenScreenTurnsOff_doNotCancelVibration() throws Throwable { + mockVibrators(1); + mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_EXTERNAL_CONTROL); + VibratorManagerService service = createSystemReadyService(); + + IExternalVibrationController externalVibrationControllerMock = + mock(IExternalVibrationController.class); + ExternalVibration externalVibration = new ExternalVibration(UID, PACKAGE_NAME, + AUDIO_ALARM_ATTRS, externalVibrationControllerMock, mock(IBinder.class)); + ExternalVibrationScale scale = mExternalVibratorService.onExternalVibrationStart( + externalVibration); + + assertThat(scale.scaleLevel).isNotEqualTo(ExternalVibrationScale.ScaleLevel.SCALE_MUTE); + + service.mIntentReceiver.onReceive(mContextSpy, new Intent(Intent.ACTION_SCREEN_OFF)); + + verify(externalVibrationControllerMock, never()).mute(); + } + + @Test + @RequiresFlagsEnabled(android.multiuser.Flags.FLAG_ADD_UI_FOR_SOUNDS_FROM_BACKGROUND_USERS) + public void onExternalVibration_thenFgUserRequestsMute_doNotCancelVibration() throws Throwable { + mockVibrators(1); + mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_EXTERNAL_CONTROL); + VibratorManagerService service = createSystemReadyService(); + + IExternalVibrationController externalVibrationControllerMock = + mock(IExternalVibrationController.class); + ExternalVibration externalVibration = new ExternalVibration(UID, PACKAGE_NAME, + AUDIO_ALARM_ATTRS, externalVibrationControllerMock, mock(IBinder.class)); + ExternalVibrationScale scale = mExternalVibratorService.onExternalVibrationStart( + externalVibration); + + assertThat(scale.scaleLevel).isNotEqualTo(ExternalVibrationScale.ScaleLevel.SCALE_MUTE); + + service.mIntentReceiver.onReceive(mContextSpy, new Intent( + BackgroundUserSoundNotifier.ACTION_MUTE_SOUND)); + + verify(externalVibrationControllerMock, never()).mute(); + } + + @Test public void frameworkStats_externalVibration_reportsAllMetrics() throws Exception { mockVibrators(1); mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_EXTERNAL_CONTROL); @@ -2501,7 +2633,7 @@ public class VibratorManagerServiceTest { assertEquals(FrameworkStatsLog.VIBRATION_REPORTED__VIBRATION_TYPE__EXTERNAL, statsInfo.vibrationType); assertEquals(VibrationAttributes.USAGE_ALARM, statsInfo.usage); - assertEquals(Vibration.Status.FINISHED.getProtoEnumValue(), statsInfo.status); + assertEquals(Status.FINISHED.getProtoEnumValue(), statsInfo.status); assertTrue(statsInfo.totalDurationMillis > 0); assertTrue( "Expected vibrator ON for at least 10ms, got " + statsInfo.vibratorOnMillis + "ms", @@ -2533,7 +2665,7 @@ public class VibratorManagerServiceTest { assertEquals(FrameworkStatsLog.VIBRATION_REPORTED__VIBRATION_TYPE__SINGLE, metrics.vibrationType); assertEquals(VibrationAttributes.USAGE_RINGTONE, metrics.usage); - assertEquals(Vibration.Status.FINISHED.getProtoEnumValue(), metrics.status); + assertEquals(Status.FINISHED.getProtoEnumValue(), metrics.status); assertTrue("Total duration was too low, " + metrics.totalDurationMillis + "ms", metrics.totalDurationMillis >= 20); assertTrue("Vibrator ON duration was too low, " + metrics.vibratorOnMillis + "ms", @@ -2586,7 +2718,7 @@ public class VibratorManagerServiceTest { assertEquals(FrameworkStatsLog.VIBRATION_REPORTED__VIBRATION_TYPE__REPEATED, metrics.vibrationType); assertEquals(VibrationAttributes.USAGE_RINGTONE, metrics.usage); - assertEquals(Vibration.Status.CANCELLED_BY_USER.getProtoEnumValue(), metrics.status); + assertEquals(Status.CANCELLED_BY_USER.getProtoEnumValue(), metrics.status); assertTrue("Total duration was too low, " + metrics.totalDurationMillis + "ms", metrics.totalDurationMillis >= 100); assertTrue("Vibrator ON duration was too low, " + metrics.vibratorOnMillis + "ms", @@ -2647,7 +2779,7 @@ public class VibratorManagerServiceTest { assertEquals(FrameworkStatsLog.VIBRATION_REPORTED__VIBRATION_TYPE__SINGLE, metrics.vibrationType); assertEquals(VibrationAttributes.USAGE_ALARM, metrics.usage); - assertEquals(Vibration.Status.FINISHED.getProtoEnumValue(), metrics.status); + assertEquals(Status.FINISHED.getProtoEnumValue(), metrics.status); // At least 4 effect/primitive played, 20ms each, plus configured fallback. assertTrue("Total duration was too low, " + metrics.totalDurationMillis + "ms", @@ -2703,7 +2835,7 @@ public class VibratorManagerServiceTest { VibrationStats.StatsInfo touchMetrics = argumentCaptor.getAllValues().get(0); assertEquals(UID, touchMetrics.uid); assertEquals(VibrationAttributes.USAGE_TOUCH, touchMetrics.usage); - assertEquals(Vibration.Status.CANCELLED_SUPERSEDED.getProtoEnumValue(), + assertEquals(Status.CANCELLED_SUPERSEDED.getProtoEnumValue(), touchMetrics.status); assertTrue(touchMetrics.endedBySameUid); assertEquals(VibrationAttributes.USAGE_ALARM, touchMetrics.endedByUsage); @@ -2712,7 +2844,7 @@ public class VibratorManagerServiceTest { VibrationStats.StatsInfo alarmMetrics = argumentCaptor.getAllValues().get(1); assertEquals(UID, alarmMetrics.uid); assertEquals(VibrationAttributes.USAGE_ALARM, alarmMetrics.usage); - assertEquals(Vibration.Status.FINISHED.getProtoEnumValue(), alarmMetrics.status); + assertEquals(Status.FINISHED.getProtoEnumValue(), alarmMetrics.status); assertFalse(alarmMetrics.endedBySameUid); assertEquals(-1, alarmMetrics.endedByUsage); assertEquals(VibrationAttributes.USAGE_TOUCH, alarmMetrics.interruptedUsage); @@ -2742,12 +2874,12 @@ public class VibratorManagerServiceTest { VibrationStats.StatsInfo touchMetrics = argumentCaptor.getAllValues().get(0); assertEquals(UID, touchMetrics.uid); assertEquals(VibrationAttributes.USAGE_TOUCH, touchMetrics.usage); - assertEquals(Vibration.Status.IGNORED_FOR_POWER.getProtoEnumValue(), touchMetrics.status); + assertEquals(Status.IGNORED_FOR_POWER.getProtoEnumValue(), touchMetrics.status); VibrationStats.StatsInfo ringtoneMetrics = argumentCaptor.getAllValues().get(1); assertEquals(UID, ringtoneMetrics.uid); assertEquals(VibrationAttributes.USAGE_RINGTONE, ringtoneMetrics.usage); - assertEquals(Vibration.Status.IGNORED_FOR_SETTINGS.getProtoEnumValue(), + assertEquals(Status.IGNORED_FOR_SETTINGS.getProtoEnumValue(), ringtoneMetrics.status); for (VibrationStats.StatsInfo metrics : argumentCaptor.getAllValues()) { @@ -2814,7 +2946,7 @@ public class VibratorManagerServiceTest { assertEquals(FrameworkStatsLog.VIBRATION_REPORTED__VIBRATION_TYPE__SINGLE, metrics.vibrationType); assertEquals(VibrationAttributes.USAGE_NOTIFICATION, metrics.usage); - assertEquals(Vibration.Status.FINISHED.getProtoEnumValue(), metrics.status); + assertEquals(Status.FINISHED.getProtoEnumValue(), metrics.status); assertTrue(metrics.totalDurationMillis >= 20); // vibratorOnMillis accumulates both vibrators, it's 20 for each constant. diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java index 0d8b7208bcac..1e035dab3c5e 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java @@ -1723,10 +1723,12 @@ public class ActivityRecordTests extends WindowTestsBase { @Test public void testDestroyImmediately_hadApp_notFinishing() { final ActivityRecord activity = createActivityWithTask(); + activity.idle = true; activity.finishing = false; activity.destroyImmediately("test"); assertEquals(DESTROYED, activity.getState()); + assertFalse(activity.idle); } /** 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 6adf0fe15ba8..7cb62c5a6769 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RootTaskTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/RootTaskTests.java @@ -1420,20 +1420,4 @@ public class RootTaskTests extends WindowTestsBase { verify(mSupervisor).startSpecificActivity(any(), eq(false) /* andResume */, anyBoolean()); } - - private void verifyShouldSleepActivities(boolean focusedRootTask, - boolean keyguardGoingAway, boolean displaySleeping, boolean isDefaultDisplay, - boolean expected) { - final Task task = new TaskBuilder(mSupervisor).build(); - final DisplayContent display = mock(DisplayContent.class); - final KeyguardController keyguardController = mSupervisor.getKeyguardController(); - display.isDefaultDisplay = isDefaultDisplay; - - task.mDisplayContent = display; - doReturn(keyguardGoingAway).when(display).isKeyguardGoingAway(); - doReturn(displaySleeping).when(display).isSleeping(); - doReturn(focusedRootTask).when(task).isFocusedRootTaskOnDisplay(); - - assertEquals(expected, task.shouldSleepActivities()); - } } 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 f1db7130c1b9..957b5e04fef6 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java @@ -371,8 +371,7 @@ public class RootWindowContainerTests extends WindowTestsBase { ensureTaskPlacement(fullscreenTask, firstActivity, secondActivity); // Move first activity to pinned root task. - mRootWindowContainer.moveActivityToPinnedRootTask(firstActivity, - null /* launchIntoPipHostActivity */, "initialMove"); + mRootWindowContainer.moveActivityToPinnedRootTask(firstActivity, "initialMove"); final TaskDisplayArea taskDisplayArea = fullscreenTask.getDisplayArea(); Task pinnedRootTask = taskDisplayArea.getRootPinnedTask(); @@ -381,8 +380,7 @@ public class RootWindowContainerTests extends WindowTestsBase { ensureTaskPlacement(fullscreenTask, secondActivity); // Move second activity to pinned root task. - mRootWindowContainer.moveActivityToPinnedRootTask(secondActivity, - null /* launchIntoPipHostActivity */, "secondMove"); + mRootWindowContainer.moveActivityToPinnedRootTask(secondActivity, "secondMove"); // Need to get root tasks again as a new instance might have been created. pinnedRootTask = taskDisplayArea.getRootPinnedTask(); @@ -413,8 +411,7 @@ public class RootWindowContainerTests extends WindowTestsBase { // Move first activity to pinned root task. - mRootWindowContainer.moveActivityToPinnedRootTask(secondActivity, - null /* launchIntoPipHostActivity */, "initialMove"); + mRootWindowContainer.moveActivityToPinnedRootTask(secondActivity, "initialMove"); assertTrue(firstActivity.mRequestForceTransition); } @@ -434,8 +431,7 @@ public class RootWindowContainerTests extends WindowTestsBase { transientActivity.setState(RESUMED, "test"); transientActivity.getTask().moveToFront("test"); - mRootWindowContainer.moveActivityToPinnedRootTask(activity2, - null /* launchIntoPipHostActivity */, "test"); + mRootWindowContainer.moveActivityToPinnedRootTask(activity2, "test"); assertEquals("Created PiP task must not change focus", transientActivity.getTask(), mRootWindowContainer.getTopDisplayFocusedRootTask()); final Task newPipTask = activity2.getTask(); @@ -460,8 +456,7 @@ public class RootWindowContainerTests extends WindowTestsBase { final Task task = activity.getTask(); // Move activity to pinned root task. - mRootWindowContainer.moveActivityToPinnedRootTask(activity, - null /* launchIntoPipHostActivity */, "test"); + mRootWindowContainer.moveActivityToPinnedRootTask(activity, "test"); // Ensure a task has moved over. ensureTaskPlacement(task, activity); @@ -499,8 +494,7 @@ public class RootWindowContainerTests extends WindowTestsBase { final Task task = activity.getTask(); // Move activity to pinned root task. - mRootWindowContainer.moveActivityToPinnedRootTask(activity, - null /* launchIntoPipHostActivity */, "test"); + mRootWindowContainer.moveActivityToPinnedRootTask(activity, "test"); // Ensure a task has moved over. ensureTaskPlacement(task, activity); @@ -524,8 +518,7 @@ public class RootWindowContainerTests extends WindowTestsBase { final ActivityRecord secondActivity = taskFragment.getBottomMostActivity(); // Move first activity to pinned root task. - mRootWindowContainer.moveActivityToPinnedRootTask(firstActivity, - null /* launchIntoPipHostActivity */, "test"); + mRootWindowContainer.moveActivityToPinnedRootTask(firstActivity, "test"); final TaskDisplayArea taskDisplayArea = fullscreenTask.getDisplayArea(); final Task pinnedRootTask = taskDisplayArea.getRootPinnedTask(); @@ -556,8 +549,7 @@ public class RootWindowContainerTests extends WindowTestsBase { final ActivityRecord topActivity = taskFragment.getTopMostActivity(); // Move the top activity to pinned root task. - mRootWindowContainer.moveActivityToPinnedRootTask(topActivity, - null /* launchIntoPipHostActivity */, "test"); + mRootWindowContainer.moveActivityToPinnedRootTask(topActivity, "test"); final Task pinnedRootTask = task.getDisplayArea().getRootPinnedTask(); 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 6be1af2c143f..cc1805aa933c 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java @@ -368,8 +368,7 @@ public class TaskFragmentTest extends WindowTestsBase { assertNotEquals(TaskFragmentAnimationParams.DEFAULT, taskFragment0.getAnimationParams()); // Move activity to pinned root task. - mRootWindowContainer.moveActivityToPinnedRootTask(activity, - null /* launchIntoPipHostActivity */, "test"); + mRootWindowContainer.moveActivityToPinnedRootTask(activity, "test"); // Ensure taskFragment requested config is reset. assertEquals(taskFragment0, activity.getOrganizedTaskFragment()); @@ -399,8 +398,7 @@ public class TaskFragmentTest extends WindowTestsBase { spyOn(mAtm.mTaskFragmentOrganizerController); // Move activity to pinned. - mRootWindowContainer.moveActivityToPinnedRootTask(activity0, - null /* launchIntoPipHostActivity */, "test"); + mRootWindowContainer.moveActivityToPinnedRootTask(activity0, "test"); // Ensure taskFragment requested config is reset. assertTrue(taskFragment0.mClearedTaskFragmentForPip); @@ -434,8 +432,7 @@ public class TaskFragmentTest extends WindowTestsBase { .createActivityCount(1) .build(); final ActivityRecord activity = taskFragment.getTopMostActivity(); - mRootWindowContainer.moveActivityToPinnedRootTask(activity, - null /* launchIntoPipHostActivity */, "test"); + mRootWindowContainer.moveActivityToPinnedRootTask(activity, "test"); spyOn(mAtm.mTaskFragmentOrganizerController); assertEquals(mIOrganizer, activity.mLastTaskFragmentOrganizerBeforePip); 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 55f74e9de192..45082d280587 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java @@ -230,8 +230,7 @@ public class TaskTests extends WindowTestsBase { final Task originalTask = activityMain.getTask(); final ActivityRecord activityPip = new ActivityBuilder(mAtm).setTask(originalTask).build(); activityPip.setState(RESUMED, "test"); - mAtm.mRootWindowContainer.moveActivityToPinnedRootTask(activityPip, - null /* launchIntoPipHostActivity */, "test"); + mAtm.mRootWindowContainer.moveActivityToPinnedRootTask(activityPip, "test"); final Task pinnedActivityTask = activityPip.getTask(); // Simulate pinnedActivityTask unintentionally added to recent during top activity resume. diff --git a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java index 56fca31afa37..7320c0bd4666 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java @@ -1251,7 +1251,7 @@ public class TransitionTests extends WindowTestsBase { final Transition transition = app.mTransitionController.createTransition(TRANSIT_OPEN); app.mTransitionController.requestStartTransition(transition, app.getTask(), null /* remoteTransition */, null /* displayChange */); - app.mTransitionController.collectExistenceChange(app.getTask()); + transition.collectExistenceChange(app.getTask()); mDisplayContent.setFixedRotationLaunchingAppUnchecked(app); final AsyncRotationController asyncRotationController = mDisplayContent.getAsyncRotationController(); @@ -1416,7 +1416,8 @@ public class TransitionTests extends WindowTestsBase { activity1.setVisibleRequested(false); activity2.setVisibleRequested(true); - openTransition.finishTransition(); + final ActionChain chain = ActionChain.testFinish(null); + openTransition.finishTransition(chain); // We finished the openTransition. Even though activity1 is visibleRequested=false, since // the closeTransition animation hasn't played yet, make sure that we didn't commit @@ -1429,7 +1430,7 @@ public class TransitionTests extends WindowTestsBase { // normally. mWm.mSyncEngine.abort(closeTransition.getSyncId()); - closeTransition.finishTransition(); + closeTransition.finishTransition(chain); assertFalse(activity1.isVisible()); assertTrue(activity2.isVisible()); @@ -1449,7 +1450,7 @@ public class TransitionTests extends WindowTestsBase { activity1.setState(ActivityRecord.State.INITIALIZING, "test"); activity1.mLaunchTaskBehind = true; mWm.mSyncEngine.abort(noChangeTransition.getSyncId()); - noChangeTransition.finishTransition(); + noChangeTransition.finishTransition(chain); assertTrue(activity1.mLaunchTaskBehind); } @@ -1468,7 +1469,7 @@ public class TransitionTests extends WindowTestsBase { // We didn't call abort on the transition itself, so it will still run onTransactionReady // normally. mWm.mSyncEngine.abort(transition1.getSyncId()); - transition1.finishTransition(); + transition1.finishTransition(ActionChain.testFinish(transition1)); verify(transitionEndedListener).run(); @@ -1530,7 +1531,7 @@ public class TransitionTests extends WindowTestsBase { verify(taskSnapshotController, times(1)).recordSnapshot(eq(task2)); - controller.finishTransition(openTransition); + controller.finishTransition(ActionChain.testFinish(openTransition)); // We are now going to simulate closing task1 to return back to (open) task2. final Transition closeTransition = createTestTransition(TRANSIT_CLOSE, controller); @@ -1595,7 +1596,7 @@ public class TransitionTests extends WindowTestsBase { doReturn(true).when(task1).isTranslucentForTransition(); assertFalse(controller.canApplyDim(task1)); - controller.finishTransition(closeTransition); + controller.finishTransition(ActionChain.testFinish(closeTransition)); assertTrue(wasInFinishingTransition[0]); assertFalse(calledListenerOnOtherDisplay[0]); assertNull(controller.mFinishingTransition); @@ -1651,7 +1652,7 @@ public class TransitionTests extends WindowTestsBase { // to avoid the latency to resume the current top, i.e. appB. assertTrue(controller.isTransientVisible(taskRecent)); // The recent is paused after the transient transition is finished. - controller.finishTransition(transition); + controller.finishTransition(ActionChain.testFinish(transition)); assertFalse(controller.isTransientVisible(taskRecent)); } @@ -2004,10 +2005,10 @@ public class TransitionTests extends WindowTestsBase { @DisableFlags(Flags.FLAG_MOVE_ANIMATION_OPTIONS_TO_CHANGE) @Test public void testOverrideAnimationOptionsToInfoIfNecessary_disableAnimOptionsPerChange() { - initializeOverrideAnimationOptionsTest(); + ActivityRecord r = initializeOverrideAnimationOptionsTest(); TransitionInfo.AnimationOptions options = TransitionInfo.AnimationOptions .makeCommonAnimOptions("testPackage"); - mTransition.setOverrideAnimation(options, null /* startCallback */, + mTransition.setOverrideAnimation(options, r, null /* startCallback */, null /* finishCallback */); mTransition.overrideAnimationOptionsToInfoIfNecessary(mInfo); @@ -2018,10 +2019,10 @@ public class TransitionTests extends WindowTestsBase { @EnableFlags(Flags.FLAG_MOVE_ANIMATION_OPTIONS_TO_CHANGE) @Test public void testOverrideAnimationOptionsToInfoIfNecessary_fromStyleAnimOptions() { - initializeOverrideAnimationOptionsTest(); + ActivityRecord r = initializeOverrideAnimationOptionsTest(); TransitionInfo.AnimationOptions options = TransitionInfo.AnimationOptions .makeCommonAnimOptions("testPackage"); - mTransition.setOverrideAnimation(options, null /* startCallback */, + mTransition.setOverrideAnimation(options, r, null /* startCallback */, null /* finishCallback */); mTransition.overrideAnimationOptionsToInfoIfNecessary(mInfo); @@ -2044,10 +2045,10 @@ public class TransitionTests extends WindowTestsBase { @EnableFlags(Flags.FLAG_MOVE_ANIMATION_OPTIONS_TO_CHANGE) @Test public void testOverrideAnimationOptionsToInfoIfNecessary_sceneAnimOptions() { - initializeOverrideAnimationOptionsTest(); + ActivityRecord r = initializeOverrideAnimationOptionsTest(); TransitionInfo.AnimationOptions options = TransitionInfo.AnimationOptions .makeSceneTransitionAnimOptions(); - mTransition.setOverrideAnimation(options, null /* startCallback */, + mTransition.setOverrideAnimation(options, r, null /* startCallback */, null /* finishCallback */); mTransition.overrideAnimationOptionsToInfoIfNecessary(mInfo); @@ -2070,10 +2071,10 @@ public class TransitionTests extends WindowTestsBase { @EnableFlags(Flags.FLAG_MOVE_ANIMATION_OPTIONS_TO_CHANGE) @Test public void testOverrideAnimationOptionsToInfoIfNecessary_crossProfileAnimOptions() { - initializeOverrideAnimationOptionsTest(); + ActivityRecord r = initializeOverrideAnimationOptionsTest(); TransitionInfo.AnimationOptions options = TransitionInfo.AnimationOptions .makeCrossProfileAnimOptions(); - mTransition.setOverrideAnimation(options, null /* startCallback */, + mTransition.setOverrideAnimation(options, r, null /* startCallback */, null /* finishCallback */); final TransitionInfo.Change displayChange = mInfo.getChanges().get(0); @@ -2098,13 +2099,13 @@ public class TransitionTests extends WindowTestsBase { @EnableFlags(Flags.FLAG_MOVE_ANIMATION_OPTIONS_TO_CHANGE) @Test public void testOverrideAnimationOptionsToInfoIfNecessary_customAnimOptions() { - initializeOverrideAnimationOptionsTest(); + ActivityRecord r = initializeOverrideAnimationOptionsTest(); TransitionInfo.AnimationOptions options = TransitionInfo.AnimationOptions .makeCustomAnimOptions("testPackage", Resources.ID_NULL, TransitionInfo.AnimationOptions.DEFAULT_ANIMATION_RESOURCES_ID, TransitionInfo.AnimationOptions.DEFAULT_ANIMATION_RESOURCES_ID, Color.GREEN, false /* overrideTaskTransition */); - mTransition.setOverrideAnimation(options, null /* startCallback */, + mTransition.setOverrideAnimation(options, r, null /* startCallback */, null /* finishCallback */); mTransition.overrideAnimationOptionsToInfoIfNecessary(mInfo); @@ -2131,7 +2132,7 @@ public class TransitionTests extends WindowTestsBase { @EnableFlags(Flags.FLAG_MOVE_ANIMATION_OPTIONS_TO_CHANGE) @Test public void testOverrideAnimationOptionsToInfoIfNecessary_haveTaskFragmentAnimParams() { - initializeOverrideAnimationOptionsTest(); + ActivityRecord r = initializeOverrideAnimationOptionsTest(); final TaskFragment embeddedTf = mTransition.mTargets.get(2).mContainer.asTaskFragment(); embeddedTf.setAnimationParams(new TaskFragmentAnimationParams.Builder() @@ -2144,7 +2145,7 @@ public class TransitionTests extends WindowTestsBase { TransitionInfo.AnimationOptions.DEFAULT_ANIMATION_RESOURCES_ID, TransitionInfo.AnimationOptions.DEFAULT_ANIMATION_RESOURCES_ID, Color.GREEN, false /* overrideTaskTransition */); - mTransition.setOverrideAnimation(options, null /* startCallback */, + mTransition.setOverrideAnimation(options, r, null /* startCallback */, null /* finishCallback */); final TransitionInfo.Change displayChange = mInfo.getChanges().get(0); @@ -2180,13 +2181,13 @@ public class TransitionTests extends WindowTestsBase { @EnableFlags(Flags.FLAG_MOVE_ANIMATION_OPTIONS_TO_CHANGE) @Test public void testOverrideAnimationOptionsToInfoIfNecessary_customAnimOptionsWithTaskOverride() { - initializeOverrideAnimationOptionsTest(); + ActivityRecord r = initializeOverrideAnimationOptionsTest(); TransitionInfo.AnimationOptions options = TransitionInfo.AnimationOptions .makeCustomAnimOptions("testPackage", Resources.ID_NULL, TransitionInfo.AnimationOptions.DEFAULT_ANIMATION_RESOURCES_ID, TransitionInfo.AnimationOptions.DEFAULT_ANIMATION_RESOURCES_ID, Color.GREEN, true /* overrideTaskTransition */); - mTransition.setOverrideAnimation(options, null /* startCallback */, + mTransition.setOverrideAnimation(options, r, null /* startCallback */, null /* finishCallback */); mTransition.overrideAnimationOptionsToInfoIfNecessary(mInfo); @@ -2212,7 +2213,7 @@ public class TransitionTests extends WindowTestsBase { options.getBackgroundColor(), activityChange.getBackgroundColor()); } - private void initializeOverrideAnimationOptionsTest() { + private ActivityRecord initializeOverrideAnimationOptionsTest() { mTransition = createTestTransition(TRANSIT_OPEN); // Test set AnimationOptions for Activity and Task. @@ -2240,6 +2241,7 @@ public class TransitionTests extends WindowTestsBase { embeddedTf.getAnimationLeash())); mInfo.addChange(new TransitionInfo.Change(null /* container */, nonEmbeddedActivity.getAnimationLeash())); + return nonEmbeddedActivity; } @Test diff --git a/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java index 7652861449de..9602ae29604f 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java @@ -407,7 +407,7 @@ public class WallpaperControllerTests extends WindowTestsBase { final SurfaceControl.Transaction t = mock(SurfaceControl.Transaction.class); token.finishSync(t, token.getSyncGroup(), false /* cancel */); transit.onTransactionReady(transit.getSyncId(), t); - dc.mTransitionController.finishTransition(transit); + dc.mTransitionController.finishTransition(ActionChain.testFinish(transit)); assertFalse(wallpaperWindow.isVisible()); assertFalse(token.isVisible()); } diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java index 39276a191fb9..5a54af10888f 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java @@ -310,10 +310,8 @@ public class WindowStateTests extends WindowTestsBase { // Simulate the window is in split screen root task. final Task rootTask = createTask(mDisplayContent, WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD); - spyOn(appWindow); - spyOn(rootTask); rootTask.setFocusable(false); - doReturn(rootTask).when(appWindow).getRootTask(); + appWindow.mActivityRecord.reparent(rootTask, 0 /* position */, "test"); // Make sure canBeImeTarget is false; assertFalse(appWindow.canBeImeTarget()); @@ -1035,7 +1033,7 @@ public class WindowStateTests extends WindowTestsBase { mDisplayContent, "SystemDialog", true); mDisplayContent.setImeLayeringTarget(mAppWindow); - mAppWindow.getRootTask().setWindowingMode(WINDOWING_MODE_MULTI_WINDOW); + mAppWindow.getTask().setWindowingMode(WINDOWING_MODE_MULTI_WINDOW); makeWindowVisible(mImeWindow); systemDialogWindow.mAttrs.flags |= FLAG_ALT_FOCUSABLE_IM; assertTrue(systemDialogWindow.needsRelativeLayeringToIme()); 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 bcf4ebcd9740..a215c0a80b46 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java @@ -2131,7 +2131,7 @@ public class WindowTestsBase extends SystemServiceTestsBase { } public void finish() { - mController.finishTransition(mLastTransit); + mController.finishTransition(ActionChain.testFinish(mLastTransit)); } } } diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTracingPerfettoTest.java b/services/tests/wmtests/src/com/android/server/wm/WindowTracingPerfettoTest.java index 1d567b1169bd..c45b99d9dc23 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowTracingPerfettoTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowTracingPerfettoTest.java @@ -39,8 +39,7 @@ import androidx.test.filters.SmallTest; import org.junit.After; import org.junit.Before; import org.junit.Test; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; +import org.mockito.Mockito; import perfetto.protos.PerfettoConfig.WindowManagerConfig.LogFrequency; @@ -50,9 +49,7 @@ import perfetto.protos.PerfettoConfig.WindowManagerConfig.LogFrequency; @SmallTest @Presubmit public class WindowTracingPerfettoTest { - @Mock private WindowManagerService mWmMock; - @Mock private Choreographer mChoreographer; private WindowTracing mWindowTracing; private PerfettoTraceMonitor mTraceMonitor; @@ -60,7 +57,10 @@ public class WindowTracingPerfettoTest { @Before public void setUp() throws Exception { - MockitoAnnotations.initMocks(this); + mWmMock = Mockito.mock(WindowManagerService.class); + Mockito.doNothing().when(mWmMock).dumpDebugLocked(Mockito.any(), Mockito.anyInt()); + + mChoreographer = Mockito.mock(Choreographer.class); mWindowTracing = new WindowTracingPerfetto(mWmMock, mChoreographer, new WindowManagerGlobalLock()); diff --git a/services/tests/wmtests/src/com/android/server/wm/utils/DesktopModeFlagsUtilTest.java b/services/tests/wmtests/src/com/android/server/wm/utils/DesktopModeFlagsUtilTest.java index 381e9e46af92..46b8e3a430c8 100644 --- a/services/tests/wmtests/src/com/android/server/wm/utils/DesktopModeFlagsUtilTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/utils/DesktopModeFlagsUtilTest.java @@ -234,11 +234,11 @@ public class DesktopModeFlagsUtilTest extends WindowTestsBase { FLAG_ENABLE_DESKTOP_WINDOWING_MODE, FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS }) - public void isEnabled_dwFlagOn_overrideOff_featureFlagOn_returnsFalse() { + public void isEnabled_dwFlagOn_overrideOff_featureFlagOn_returnsTrue() { setOverride(OVERRIDE_OFF.getSetting()); // Follow override if they exist, and is not equal to default toggle state (dw flag) - assertThat(DesktopModeFlagsUtil.DYNAMIC_INITIAL_BOUNDS.isEnabled(mContext)).isFalse(); + assertThat(DesktopModeFlagsUtil.DYNAMIC_INITIAL_BOUNDS.isEnabled(mContext)).isTrue(); } @Test @@ -296,11 +296,11 @@ public class DesktopModeFlagsUtilTest extends WindowTestsBase { FLAG_ENABLE_DESKTOP_WINDOWING_MODE, FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS }) - public void isEnabled_dwFlagOff_overrideOn_featureFlagOff_returnTrue() { + public void isEnabled_dwFlagOff_overrideOn_featureFlagOff_returnFalse() { setOverride(OVERRIDE_ON.getSetting()); // Follow override if they exist, and is not equal to default toggle state (dw flag) - assertThat(DesktopModeFlagsUtil.DYNAMIC_INITIAL_BOUNDS.isEnabled(mContext)).isTrue(); + assertThat(DesktopModeFlagsUtil.DYNAMIC_INITIAL_BOUNDS.isEnabled(mContext)).isFalse(); } @Test diff --git a/telecomm/java/android/telecom/CallControl.java b/telecomm/java/android/telecom/CallControl.java index 808a57589b47..1e1abf283af8 100644 --- a/telecomm/java/android/telecom/CallControl.java +++ b/telecomm/java/android/telecom/CallControl.java @@ -39,7 +39,10 @@ import java.util.concurrent.Executor; /** * CallControl provides client side control of a call. Each Call will get an individual CallControl - * instance in which the client can alter the state of the associated call. + * instance in which the client can alter the state of the associated call. Outgoing and incoming + * calls should move to active (via {@link CallControl#setActive(Executor, OutcomeReceiver)} or + * answered (via {@link CallControl#answer(int, Executor, OutcomeReceiver)} before 60 seconds. If + * the new call is not moved to active or answered before 60 seconds, the call will be disconnected. * * <p> * Each method is Transactional meaning that it can succeed or fail. If a transaction succeeds, diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java index 1df6cf78047c..ad7d9870ca98 100644 --- a/telecomm/java/android/telecom/Connection.java +++ b/telecomm/java/android/telecom/Connection.java @@ -2621,7 +2621,9 @@ public abstract class Connection extends Conferenceable { } /** - * Sets state to ringing (e.g., an inbound ringing connection). + * Sets state to ringing (e.g., an inbound ringing connection). The Connection should not be + * in STATE_RINGING for more than 60 seconds. After 60 seconds, the Connection will + * be disconnected. */ public final void setRinging() { checkImmutable(); @@ -2645,7 +2647,9 @@ public abstract class Connection extends Conferenceable { } /** - * Sets state to dialing (e.g., dialing an outbound connection). + * Sets state to dialing (e.g., dialing an outbound connection). The Connection should not be + * in STATE_DIALING for more than 60 seconds. After 60 seconds, the Connection will + * be disconnected. */ public final void setDialing() { checkImmutable(); diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java index 1ba496df5005..13bd5eb67e44 100644 --- a/telephony/java/android/telephony/CarrierConfigManager.java +++ b/telephony/java/android/telephony/CarrierConfigManager.java @@ -9994,6 +9994,17 @@ public class CarrierConfigManager { public static final String KEY_SATELLITE_ESOS_SUPPORTED_BOOL = "satellite_esos_supported_bool"; /** + * Indicate whether carrier roaming to satellite is using P2P SMS. + * + * This will need agreement with carriers before enabling this flag. + * + * The default value is false. + */ + @FlaggedApi(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN) + public static final String KEY_SATELLITE_ROAMING_P2P_SMS_SUPPORTED_BOOL = + "satellite_roaming_p2p_sms_supported_bool"; + + /** * Defines the NIDD (Non-IP Data Delivery) APN to be used for carrier roaming to satellite * attachment. For more on NIDD, see 3GPP TS 29.542. * Note this config is the only source of truth regarding the definition of the APN. @@ -10003,6 +10014,19 @@ public class CarrierConfigManager { public static final String KEY_SATELLITE_NIDD_APN_NAME_STRING = "satellite_nidd_apn_name_string"; + /** + * Default value {@code true}, meaning when an emergency call request comes in, if the device is + * in emergency satellite mode but hasn't sent the first satellite datagram, then exits + * satellite mode to allow the emergency call to go through. + * + * If {@code false}, the emergency call is always blocked if device is in emergency satellite + * mode. Note if device is NOT in emergency satellite mode, emergency call is always allowed. + * + * @hide + */ + public static final String KEY_SATELLITE_ROAMING_TURN_OFF_SESSION_FOR_EMERGENCY_CALL_BOOL = + "satellite_roaming_turn_off_session_for_emergency_call_bool"; + /** @hide */ @IntDef({ CARRIER_ROAMING_NTN_CONNECT_AUTOMATIC, @@ -10075,7 +10099,35 @@ public class CarrierConfigManager { */ @FlaggedApi(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN) public static final String KEY_SATELLITE_SCREEN_OFF_INACTIVITY_TIMEOUT_SEC_INT = - "satellite_screen_off_inactivity_timeout_duration_sec_int"; + "satellite_screen_off_inactivity_timeout_sec_int"; + + /** + * An integer key holds the timeout duration in seconds used to determine whether to exit P2P + * SMS mode and start TN scanning. + * + * The timer is started when the device is not connected, user is not pointing to the + * satellite and no data transfer is happening. + * When the timer expires, the device will move to IDLE mode upon which TN scanning will start. + * + * The default value is 180 seconds. + */ + @FlaggedApi(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN) + public static final String KEY_SATELLITE_P2P_SMS_INACTIVITY_TIMEOUT_SEC_INT = + "satellite_p2p_sms_inactivity_timeout_sec_int"; + + /** + * An integer key holds the timeout duration in seconds used to determine whether to exit ESOS + * mode and start TN scanning. + * + * The timer is started when the device is not connected, user is not pointing to the + * satellite and no data transfer is happening. + * When the timer expires, the device will move to IDLE mode upon which TN scanning will start. + * + * The default value is 600 seconds. + */ + @FlaggedApi(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN) + public static final String KEY_SATELLITE_ESOS_INACTIVITY_TIMEOUT_SEC_INT = + "satellite_esos_inactivity_timeout_sec_int"; /** * Indicating whether DUN APN should be disabled when the device is roaming. In that case, @@ -11235,12 +11287,16 @@ public class CarrierConfigManager { sDefaults.putInt(KEY_EMERGENCY_CALL_TO_SATELLITE_T911_HANDOVER_TIMEOUT_MILLIS_INT, (int) TimeUnit.SECONDS.toMillis(30)); sDefaults.putBoolean(KEY_SATELLITE_ESOS_SUPPORTED_BOOL, false); + sDefaults.putBoolean(KEY_SATELLITE_ROAMING_P2P_SMS_SUPPORTED_BOOL, false); sDefaults.putString(KEY_SATELLITE_NIDD_APN_NAME_STRING, ""); + sDefaults.putBoolean(KEY_SATELLITE_ROAMING_TURN_OFF_SESSION_FOR_EMERGENCY_CALL_BOOL, true); sDefaults.putInt(KEY_CARRIER_ROAMING_NTN_CONNECT_TYPE_INT, 0); sDefaults.putInt(KEY_CARRIER_ROAMING_NTN_EMERGENCY_CALL_TO_SATELLITE_HANDOVER_TYPE_INT, SatelliteManager.EMERGENCY_CALL_TO_SATELLITE_HANDOVER_TYPE_T911); sDefaults.putInt(KEY_CARRIER_SUPPORTED_SATELLITE_NOTIFICATION_HYSTERESIS_SEC_INT, 180); sDefaults.putInt(KEY_SATELLITE_SCREEN_OFF_INACTIVITY_TIMEOUT_SEC_INT, 30); + sDefaults.putInt(KEY_SATELLITE_P2P_SMS_INACTIVITY_TIMEOUT_SEC_INT, 180); + sDefaults.putInt(KEY_SATELLITE_ESOS_INACTIVITY_TIMEOUT_SEC_INT, 600); sDefaults.putString(KEY_DEFAULT_PREFERRED_APN_NAME_STRING, ""); sDefaults.putBoolean(KEY_SUPPORTS_CALL_COMPOSER_BOOL, false); sDefaults.putBoolean(KEY_SUPPORTS_BUSINESS_CALL_COMPOSER_BOOL, false); diff --git a/telephony/java/android/telephony/satellite/SatelliteManager.java b/telephony/java/android/telephony/satellite/SatelliteManager.java index 6ef953c505cb..c5934a70610f 100644 --- a/telephony/java/android/telephony/satellite/SatelliteManager.java +++ b/telephony/java/android/telephony/satellite/SatelliteManager.java @@ -546,6 +546,16 @@ public final class SatelliteManager { public static final int EMERGENCY_CALL_TO_SATELLITE_HANDOVER_TYPE_T911 = 2; /** + * This intent will be broadcasted if there are any change to list of subscriber informations. + * This intent will be sent only to the app with component defined in + * config_satellite_carrier_roaming_esos_provisioned_class and package defined in + * config_satellite_gateway_service_package + * @hide + */ + public static final String ACTION_SATELLITE_SUBSCRIBER_ID_LIST_CHANGED = + "android.telephony.action.ACTION_SATELLITE_SUBSCRIBER_ID_LIST_CHANGED"; + + /** * Request to enable or disable the satellite modem and demo mode. * If satellite modem and cellular modem cannot work concurrently, * then this will disable the cellular modem if satellite modem is enabled, diff --git a/tests/Input/assets/testPointerFillStyle.png b/tests/Input/assets/testPointerFillStyle.png Binary files differindex b2354f8f4799..297244f9d6d1 100644 --- a/tests/Input/assets/testPointerFillStyle.png +++ b/tests/Input/assets/testPointerFillStyle.png diff --git a/tools/aapt2/ResourceTable.cpp b/tools/aapt2/ResourceTable.cpp index 7a4f40e471d2..97514599c0b1 100644 --- a/tools/aapt2/ResourceTable.cpp +++ b/tools/aapt2/ResourceTable.cpp @@ -50,21 +50,21 @@ bool less_than_type(const std::unique_ptr<ResourceTableType>& lhs, template <typename T> bool less_than_struct_with_name(const std::unique_ptr<T>& lhs, StringPiece rhs) { - return lhs->name.compare(0, lhs->name.size(), rhs.data(), rhs.size()) < 0; + return lhs->name < rhs; } template <typename T> bool greater_than_struct_with_name(StringPiece lhs, const std::unique_ptr<T>& rhs) { - return rhs->name.compare(0, rhs->name.size(), lhs.data(), lhs.size()) > 0; + return rhs->name > lhs; } template <typename T> struct NameEqualRange { bool operator()(const std::unique_ptr<T>& lhs, StringPiece rhs) const { - return less_than_struct_with_name<T>(lhs, rhs); + return less_than_struct_with_name(lhs, rhs); } bool operator()(StringPiece lhs, const std::unique_ptr<T>& rhs) const { - return greater_than_struct_with_name<T>(lhs, rhs); + return greater_than_struct_with_name(lhs, rhs); } }; @@ -74,7 +74,7 @@ bool less_than_struct_with_name_and_id(const T& lhs, if (lhs.id != rhs.second) { return lhs.id < rhs.second; } - return lhs.name.compare(0, lhs.name.size(), rhs.first.data(), rhs.first.size()) < 0; + return lhs.name < rhs.first; } template <typename T, typename Func, typename Elements> @@ -90,14 +90,16 @@ struct ConfigKey { StringPiece product; }; -template <typename T> -bool lt_config_key_ref(const T& lhs, const ConfigKey& rhs) { - int cmp = lhs->config.compare(*rhs.config); - if (cmp == 0) { - cmp = StringPiece(lhs->product).compare(rhs.product); +struct lt_config_key_ref { + template <typename T> + bool operator()(const T& lhs, const ConfigKey& rhs) const noexcept { + int cmp = lhs->config.compare(*rhs.config); + if (cmp == 0) { + cmp = lhs->product.compare(rhs.product); + } + return cmp < 0; } - return cmp < 0; -} +}; } // namespace @@ -159,10 +161,10 @@ ResourceEntry* ResourceTableType::FindOrCreateEntry(android::StringPiece name) { ResourceConfigValue* ResourceEntry::FindValue(const ConfigDescription& config, android::StringPiece product) { auto iter = std::lower_bound(values.begin(), values.end(), ConfigKey{&config, product}, - lt_config_key_ref<std::unique_ptr<ResourceConfigValue>>); + lt_config_key_ref()); if (iter != values.end()) { ResourceConfigValue* value = iter->get(); - if (value->config == config && StringPiece(value->product) == product) { + if (value->config == config && value->product == product) { return value; } } @@ -172,10 +174,10 @@ ResourceConfigValue* ResourceEntry::FindValue(const ConfigDescription& config, const ResourceConfigValue* ResourceEntry::FindValue(const android::ConfigDescription& config, android::StringPiece product) const { auto iter = std::lower_bound(values.begin(), values.end(), ConfigKey{&config, product}, - lt_config_key_ref<std::unique_ptr<ResourceConfigValue>>); + lt_config_key_ref()); if (iter != values.end()) { ResourceConfigValue* value = iter->get(); - if (value->config == config && StringPiece(value->product) == product) { + if (value->config == config && value->product == product) { return value; } } @@ -185,10 +187,10 @@ const ResourceConfigValue* ResourceEntry::FindValue(const android::ConfigDescrip ResourceConfigValue* ResourceEntry::FindOrCreateValue(const ConfigDescription& config, StringPiece product) { auto iter = std::lower_bound(values.begin(), values.end(), ConfigKey{&config, product}, - lt_config_key_ref<std::unique_ptr<ResourceConfigValue>>); + lt_config_key_ref()); if (iter != values.end()) { ResourceConfigValue* value = iter->get(); - if (value->config == config && StringPiece(value->product) == product) { + if (value->config == config && value->product == product) { return value; } } @@ -199,36 +201,21 @@ ResourceConfigValue* ResourceEntry::FindOrCreateValue(const ConfigDescription& c std::vector<ResourceConfigValue*> ResourceEntry::FindAllValues(const ConfigDescription& config) { std::vector<ResourceConfigValue*> results; - - auto iter = values.begin(); + auto iter = + std::lower_bound(values.begin(), values.end(), ConfigKey{&config, ""}, lt_config_key_ref()); for (; iter != values.end(); ++iter) { ResourceConfigValue* value = iter->get(); - if (value->config == config) { - results.push_back(value); - ++iter; + if (value->config != config) { break; } - } - - for (; iter != values.end(); ++iter) { - ResourceConfigValue* value = iter->get(); - if (value->config == config) { - results.push_back(value); - } + results.push_back(value); } return results; } bool ResourceEntry::HasDefaultValue() const { - const ConfigDescription& default_config = ConfigDescription::DefaultConfig(); - // The default config should be at the top of the list, since the list is sorted. - for (auto& config_value : values) { - if (config_value->config == default_config) { - return true; - } - } - return false; + return !values.empty() && values.front()->config == ConfigDescription::DefaultConfig(); } ResourceTable::CollisionResult ResourceTable::ResolveFlagCollision(FlagStatus existing, @@ -364,14 +351,14 @@ struct SortedVectorInserter : public Comparer { if (found) { return &*it; } - return &*el.insert(it, std::forward<T>(value)); + return &*el.insert(it, std::move(value)); } }; struct PackageViewComparer { bool operator()(const ResourceTablePackageView& lhs, const ResourceTablePackageView& rhs) { return less_than_struct_with_name_and_id<ResourceTablePackageView, uint8_t>( - lhs, std::make_pair(rhs.name, rhs.id)); + lhs, std::tie(rhs.name, rhs.id)); } }; @@ -384,7 +371,7 @@ struct TypeViewComparer { struct EntryViewComparer { bool operator()(const ResourceTableEntryView& lhs, const ResourceTableEntryView& rhs) { return less_than_struct_with_name_and_id<ResourceTableEntryView, uint16_t>( - lhs, std::make_pair(rhs.name, rhs.id)); + lhs, std::tie(rhs.name, rhs.id)); } }; @@ -429,10 +416,10 @@ void InsertEntryIntoTableView(ResourceTableView& table, const ResourceTablePacka const ResourceConfigValue* ResourceTableEntryView::FindValue(const ConfigDescription& config, android::StringPiece product) const { auto iter = std::lower_bound(values.begin(), values.end(), ConfigKey{&config, product}, - lt_config_key_ref<const ResourceConfigValue*>); + lt_config_key_ref()); if (iter != values.end()) { const ResourceConfigValue* value = *iter; - if (value->config == config && StringPiece(value->product) == product) { + if (value->config == config && value->product == product) { return value; } } @@ -615,11 +602,15 @@ bool ResourceTable::AddResource(NewResource&& res, android::IDiagnostics* diag) result = ResolveValueCollision(config_value->value.get(), res.value.get()); } switch (result) { - case CollisionResult::kKeepBoth: + case CollisionResult::kKeepBoth: { // Insert the value ignoring for duplicate configurations - entry->values.push_back(util::make_unique<ResourceConfigValue>(res.config, res.product)); - entry->values.back()->value = std::move(res.value); + auto it = entry->values.insert( + std::lower_bound(entry->values.begin(), entry->values.end(), + ConfigKey{&res.config, res.product}, lt_config_key_ref()), + util::make_unique<ResourceConfigValue>(res.config, res.product)); + (*it)->value = std::move(res.value); break; + } case CollisionResult::kTakeNew: // Take the incoming value. diff --git a/tools/hoststubgen/hoststubgen/invoketest/hoststubgen-invoke-test.sh b/tools/hoststubgen/hoststubgen/invoketest/hoststubgen-invoke-test.sh index d97dd7c18045..5c5421a9151a 100755 --- a/tools/hoststubgen/hoststubgen/invoketest/hoststubgen-invoke-test.sh +++ b/tools/hoststubgen/hoststubgen/invoketest/hoststubgen-invoke-test.sh @@ -213,9 +213,9 @@ com.unsupported.* " run_hoststubgen_for_failure "One specific class disallowed" \ - "TinyFrameworkClassAnnotations is not allowed to have Ravenwood annotations" \ + "TinyFrameworkAnnotations is not allowed to have Ravenwood annotations" \ " -!com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnotations +!com.android.hoststubgen.test.tinyframework.TinyFrameworkAnnotations * # All other classes allowed " diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/asm/AsmUtils.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/asm/AsmUtils.kt index 6cf214300b43..7dd4fdd078a2 100644 --- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/asm/AsmUtils.kt +++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/asm/AsmUtils.kt @@ -202,18 +202,6 @@ fun isAnonymousInnerClass(cn: ClassNode): Boolean { } /** - * Take a class name. If it's a nested class, then return the name of its direct outer class name. - * Otherwise, return null. - */ -fun getDirectOuterClassName(className: String): String? { - val pos = className.lastIndexOf('$') - if (pos < 0) { - return null - } - return className.substring(0, pos) -} - -/** * Write bytecode to push all the method arguments to the stack. * The number of arguments and their type are taken from [methodDescriptor]. */ diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/ClassWidePolicyPropagatingFilter.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/ClassWidePolicyPropagatingFilter.kt index 47790b10782a..37048d9c7c60 100644 --- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/ClassWidePolicyPropagatingFilter.kt +++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/ClassWidePolicyPropagatingFilter.kt @@ -16,7 +16,6 @@ package com.android.hoststubgen.filters import com.android.hoststubgen.asm.ClassNodes -import com.android.hoststubgen.asm.getDirectOuterClassName /** * This is used as the second last fallback filter. This filter propagates the class-wide policy @@ -24,72 +23,69 @@ import com.android.hoststubgen.asm.getDirectOuterClassName */ class ClassWidePolicyPropagatingFilter( private val classes: ClassNodes, - fallback: OutputFilter, - ) : DelegatingFilter(fallback) { + fallback: OutputFilter +) : DelegatingFilter(fallback) { - private fun getClassWidePolicy(className: String, resolve: Boolean): FilterPolicyWithReason? { + /** + * We don't use ClassNode.outerClass, because it gives as the top-level + * outer class (A$B$C -> A), not the direct outer class (A$B$C -> A$B). + * + * Sometimes a class name includes `$`, but is not as a nested class name separator + * (e.g. a class name like `MyClass$$`). In this case, `MyClass$` is not actually a class. + * + * So before getting the class policy on a nonexistent class, which may cause an + * incorrect result, we make sure the class actually exists. + */ + private fun getDirectOuterClass(className: String): String? { var currentClass = className - - - // If the class name is `a.b.c.A$B$C`, then we try to get the class wide policy - // from a.b.c.A$B$C, then a.b.c.A$B, and then a.b.c.A. while (true) { - // Sometimes a class name has a `$` in it but not as a nest class name separator -- - // e.g. class name like "MyClass$$". In this case, `MyClass$` may not actually be - // a class name. - // So before getting the class policy on a nonexistent class, which may cause an - // incorrect result, we make sure if className actually exists. - if (classes.hasClass(className)) { - outermostFilter.getPolicyForClass(className).let { policy -> - if (policy.policy.isClassWidePolicy) { - val p = if (resolve) { - policy.policy.resolveClassWidePolicy() - } else { - policy.policy - } - - return p.withReason(policy.reason) - .wrapReason("class-wide in $currentClass") - } - // If the class's policy is remove, then remove it. - if (policy.policy == FilterPolicy.Remove) { - return FilterPolicy.Remove.withReason("class-wide in $currentClass") - } - } - } - - // Next, look at the outer class... - val outer = getDirectOuterClassName(currentClass) - if (outer == null) { + val pos = currentClass.lastIndexOf('$') + if (pos < 0) { return null } - currentClass = outer + currentClass = currentClass.substring(0, pos) + if (classes.hasClass(currentClass)) { + return currentClass + } } } - override fun getPolicyForClass(className: String): FilterPolicyWithReason { - // If it's a nested class, use the outer class's policy. - getDirectOuterClassName(className)?.let { outerName -> - getClassWidePolicy(outerName, resolve = false)?.let { policy -> - return policy + private fun getClassWidePolicy(className: String, resolve: Boolean): FilterPolicyWithReason? { + outermostFilter.getPolicyForClass(className).let { policy -> + if (policy.policy.isClassWidePolicy) { + val p = if (resolve) { + policy.policy.resolveClassWidePolicy() + } else { + policy.policy + } + + return p.withReason(policy.reason) + .wrapReason("class-wide in $className") + } + // If the class's policy is remove, then remove it. + if (policy.policy == FilterPolicy.Remove) { + return FilterPolicy.Remove.withReason("class-wide in $className") } } + return null + } - return super.getPolicyForClass(className) + override fun getPolicyForClass(className: String): FilterPolicyWithReason { + // If the class name is `a.b.c.A$B$C`, then we try to get the class wide policy + // from a.b.c.A$B$C, then a.b.c.A$B, and then a.b.c.A, recursively + return getDirectOuterClass(className)?.let { getClassWidePolicy(it, resolve = false) } + ?: super.getPolicyForClass(className) } - override fun getPolicyForField( - className: String, - fieldName: String - ): FilterPolicyWithReason { + override fun getPolicyForField(className: String, fieldName: String): FilterPolicyWithReason { return getClassWidePolicy(className, resolve = true) ?: super.getPolicyForField(className, fieldName) } override fun getPolicyForMethod( - className: String, - methodName: String, - descriptor: String + className: String, + methodName: String, + descriptor: String ): FilterPolicyWithReason { return getClassWidePolicy(className, resolve = true) ?: super.getPolicyForMethod(className, methodName, descriptor) diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/annotation-allowed-classes-tiny-framework.txt b/tools/hoststubgen/hoststubgen/test-tiny-framework/annotation-allowed-classes-tiny-framework.txt index bd9e85e17890..de4cb0c536c1 100644 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/annotation-allowed-classes-tiny-framework.txt +++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/annotation-allowed-classes-tiny-framework.txt @@ -6,10 +6,10 @@ # To allow a specific class to use annotations: -# com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnotations +# com.android.hoststubgen.test.tinyframework.TinyFrameworkAnnotations # To disallow a specific class to use annotations: -# !com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnotations +# !com.android.hoststubgen.test.tinyframework.TinyFrameworkAnnotations # To allow a specific package to use annotations: # com.android.hoststubgen.test.* diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/01-hoststubgen-test-tiny-framework-orig-dump.txt b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/01-hoststubgen-test-tiny-framework-orig-dump.txt index c2f593cf5ae3..845e1d08ce92 100644 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/01-hoststubgen-test-tiny-framework-orig-dump.txt +++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/01-hoststubgen-test-tiny-framework-orig-dump.txt @@ -394,111 +394,13 @@ NestMembers: com/android/hoststubgen/test/tinyframework/R$Nested InnerClasses: public static #x= #x of #x; // Nested=class com/android/hoststubgen/test/tinyframework/R$Nested of class com/android/hoststubgen/test/tinyframework/R -## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl.class - Compiled from "TinyFrameworkCallerCheck.java" -class com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck$Impl - minor version: 0 - major version: 61 - flags: (0x0020) ACC_SUPER - this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl - super_class: #x // java/lang/Object - interfaces: 0, fields: 0, methods: 3, attributes: 3 - private com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck$Impl(); - descriptor: ()V - flags: (0x0002) ACC_PRIVATE - Code: - stack=1, locals=1, args_size=1 - x: aload_0 - x: invokespecial #x // Method java/lang/Object."<init>":()V - x: return - LineNumberTable: - LocalVariableTable: - Start Length Slot Name Signature - 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl; - - public static int getOneKeep(); - descriptor: ()I - flags: (0x0009) ACC_PUBLIC, ACC_STATIC - Code: - stack=1, locals=0, args_size=0 - x: iconst_1 - x: ireturn - LineNumberTable: - RuntimeInvisibleAnnotations: - x: #x() - android.hosttest.annotation.HostSideTestKeep - - public static int getOneStub(); - descriptor: ()I - flags: (0x0009) ACC_PUBLIC, ACC_STATIC - Code: - stack=1, locals=0, args_size=0 - x: iconst_1 - x: ireturn - LineNumberTable: - RuntimeInvisibleAnnotations: - x: #x() - android.hosttest.annotation.HostSideTestStub -} -SourceFile: "TinyFrameworkCallerCheck.java" -NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck -InnerClasses: - private static #x= #x of #x; // Impl=class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl of class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck -## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck.class - Compiled from "TinyFrameworkCallerCheck.java" -public class com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck - minor version: 0 - major version: 61 - flags: (0x0021) ACC_PUBLIC, ACC_SUPER - this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck - super_class: #x // java/lang/Object - interfaces: 0, fields: 0, methods: 3, attributes: 4 - public com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck(); - descriptor: ()V - flags: (0x0001) ACC_PUBLIC - Code: - stack=1, locals=1, args_size=1 - x: aload_0 - x: invokespecial #x // Method java/lang/Object."<init>":()V - x: return - LineNumberTable: - LocalVariableTable: - Start Length Slot Name Signature - 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck; - - public static int getOne_withCheck(); - descriptor: ()I - flags: (0x0009) ACC_PUBLIC, ACC_STATIC - Code: - stack=1, locals=0, args_size=0 - x: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl.getOneKeep:()I - x: ireturn - LineNumberTable: - - public static int getOne_noCheck(); - descriptor: ()I - flags: (0x0009) ACC_PUBLIC, ACC_STATIC - Code: - stack=1, locals=0, args_size=0 - x: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl.getOneStub:()I - x: ireturn - LineNumberTable: -} -SourceFile: "TinyFrameworkCallerCheck.java" -RuntimeInvisibleAnnotations: - x: #x() - android.hosttest.annotation.HostSideTestWholeClassStub -NestMembers: - com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl -InnerClasses: - private static #x= #x of #x; // Impl=class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl of class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck -## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations.class - Compiled from "TinyFrameworkClassAnnotations.java" -public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnotations +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations.class + Compiled from "TinyFrameworkAnnotations.java" +public class com.android.hoststubgen.test.tinyframework.TinyFrameworkAnnotations minor version: 0 major version: 61 flags: (0x0021) ACC_PUBLIC, ACC_SUPER - this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations super_class: #x // java/lang/Object interfaces: 0, fields: 3, methods: 10, attributes: 2 public int stub; @@ -519,7 +421,7 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnota descriptor: I flags: (0x0001) ACC_PUBLIC - public com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnotations(); + public com.android.hoststubgen.test.tinyframework.TinyFrameworkAnnotations(); descriptor: ()V flags: (0x0001) ACC_PUBLIC Code: @@ -536,7 +438,7 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnota LineNumberTable: LocalVariableTable: Start Length Slot Name Signature - 0 15 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations; + 0 15 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations; RuntimeInvisibleAnnotations: x: #x() android.hosttest.annotation.HostSideTestStub @@ -553,7 +455,7 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnota LineNumberTable: LocalVariableTable: Start Length Slot Name Signature - 0 6 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations; + 0 6 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations; 0 6 1 value I RuntimeInvisibleAnnotations: x: #x() @@ -571,7 +473,7 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnota LineNumberTable: LocalVariableTable: Start Length Slot Name Signature - 0 4 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations; + 0 4 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations; 0 4 1 value I RuntimeInvisibleAnnotations: x: #x() @@ -589,7 +491,7 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnota LineNumberTable: LocalVariableTable: Start Length Slot Name Signature - 0 8 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations; + 0 8 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations; 0 8 1 foo Ljava/lang/String; RuntimeInvisibleAnnotations: x: #x() @@ -608,7 +510,7 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnota LineNumberTable: LocalVariableTable: Start Length Slot Name Signature - 0 10 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations; + 0 10 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations; 0 10 1 value I RuntimeInvisibleAnnotations: x: #x() @@ -630,7 +532,7 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnota LineNumberTable: LocalVariableTable: Start Length Slot Name Signature - 0 4 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations; + 0 4 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations; 0 4 1 value I public static native int nativeAddThree(int); @@ -668,7 +570,7 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnota LineNumberTable: LocalVariableTable: Start Length Slot Name Signature - 0 3 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations; + 0 3 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations; RuntimeInvisibleAnnotations: x: #x() android.hosttest.annotation.HostSideTestThrow @@ -684,12 +586,12 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnota LineNumberTable: LocalVariableTable: Start Length Slot Name Signature - 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations; + 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations; RuntimeInvisibleAnnotations: x: #x() android.hosttest.annotation.HostSideTestStub } -SourceFile: "TinyFrameworkClassAnnotations.java" +SourceFile: "TinyFrameworkAnnotations.java" RuntimeInvisibleAnnotations: x: #x() android.hosttest.annotation.HostSideTestStub @@ -697,183 +599,104 @@ RuntimeInvisibleAnnotations: android.hosttest.annotation.HostSideTestClassLoadHook( value="com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded" ) -## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations.class - Compiled from "TinyFrameworkClassClassWideAnnotations.java" -public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassClassWideAnnotations +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl.class + Compiled from "TinyFrameworkCallerCheck.java" +class com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck$Impl minor version: 0 major version: 61 - flags: (0x0021) ACC_PUBLIC, ACC_SUPER - this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations + flags: (0x0020) ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl super_class: #x // java/lang/Object - interfaces: 0, fields: 3, methods: 10, attributes: 2 - public int stub; - descriptor: I - flags: (0x0001) ACC_PUBLIC - - public int keep; - descriptor: I - flags: (0x0001) ACC_PUBLIC - - public int remove; - descriptor: I - flags: (0x0001) ACC_PUBLIC - - public com.android.hoststubgen.test.tinyframework.TinyFrameworkClassClassWideAnnotations(); + interfaces: 0, fields: 0, methods: 3, attributes: 3 + private com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck$Impl(); descriptor: ()V - flags: (0x0001) ACC_PUBLIC + flags: (0x0002) ACC_PRIVATE Code: - stack=2, locals=1, args_size=1 + stack=1, locals=1, args_size=1 x: aload_0 x: invokespecial #x // Method java/lang/Object."<init>":()V - x: aload_0 - x: iconst_1 - x: putfield #x // Field stub:I - x: aload_0 - x: iconst_2 - x: putfield #x // Field keep:I - x: return + x: return LineNumberTable: LocalVariableTable: Start Length Slot Name Signature - 0 15 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations; + 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl; - public int addOne(int); - descriptor: (I)I - flags: (0x0001) ACC_PUBLIC + public static int getOneKeep(); + descriptor: ()I + flags: (0x0009) ACC_PUBLIC, ACC_STATIC Code: - stack=2, locals=2, args_size=2 - x: aload_0 - x: iload_1 - x: invokevirtual #x // Method addOneInner:(I)I + stack=1, locals=0, args_size=0 + x: iconst_1 x: ireturn LineNumberTable: - LocalVariableTable: - Start Length Slot Name Signature - 0 6 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations; - 0 6 1 value I + RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestKeep - public int addOneInner(int); - descriptor: (I)I - flags: (0x0001) ACC_PUBLIC + public static int getOneStub(); + descriptor: ()I + flags: (0x0009) ACC_PUBLIC, ACC_STATIC Code: - stack=2, locals=2, args_size=2 - x: iload_1 + stack=1, locals=0, args_size=0 x: iconst_1 - x: iadd x: ireturn LineNumberTable: - LocalVariableTable: - Start Length Slot Name Signature - 0 4 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations; - 0 4 1 value I - - public void toBeRemoved(java.lang.String); - descriptor: (Ljava/lang/String;)V - flags: (0x0001) ACC_PUBLIC - Code: - stack=2, locals=2, args_size=2 - x: new #x // class java/lang/RuntimeException - x: dup - x: invokespecial #x // Method java/lang/RuntimeException."<init>":()V - x: athrow - LineNumberTable: - LocalVariableTable: - Start Length Slot Name Signature - 0 8 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations; - 0 8 1 foo Ljava/lang/String; - - public int addTwo(int); - descriptor: (I)I - flags: (0x0001) ACC_PUBLIC - Code: - stack=3, locals=2, args_size=2 - x: new #x // class java/lang/RuntimeException - x: dup - x: ldc #x // String not supported on host side - x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V - x: athrow - LineNumberTable: - LocalVariableTable: - Start Length Slot Name Signature - 0 10 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations; - 0 10 1 value I RuntimeInvisibleAnnotations: x: #x() android.hosttest.annotation.HostSideTestStub - x: #x(#x=s#x) - android.hosttest.annotation.HostSideTestSubstitute( - suffix="_host" - ) - - public int addTwo_host(int); - descriptor: (I)I +} +SourceFile: "TinyFrameworkCallerCheck.java" +NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck +InnerClasses: + private static #x= #x of #x; // Impl=class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl of class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck.class + Compiled from "TinyFrameworkCallerCheck.java" +public class com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck + minor version: 0 + major version: 61 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck + super_class: #x // java/lang/Object + interfaces: 0, fields: 0, methods: 3, attributes: 4 + public com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck(); + descriptor: ()V flags: (0x0001) ACC_PUBLIC Code: - stack=2, locals=2, args_size=2 - x: iload_1 - x: iconst_2 - x: iadd - x: ireturn + stack=1, locals=1, args_size=1 + x: aload_0 + x: invokespecial #x // Method java/lang/Object."<init>":()V + x: return LineNumberTable: LocalVariableTable: Start Length Slot Name Signature - 0 4 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations; - 0 4 1 value I - - public static native int nativeAddThree(int); - descriptor: (I)I - flags: (0x0109) ACC_PUBLIC, ACC_STATIC, ACC_NATIVE - RuntimeInvisibleAnnotations: - x: #x() - android.hosttest.annotation.HostSideTestStub - x: #x(#x=s#x) - android.hosttest.annotation.HostSideTestSubstitute( - suffix="_host" - ) + 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck; - public static int nativeAddThree_host(int); - descriptor: (I)I + public static int getOne_withCheck(); + descriptor: ()I flags: (0x0009) ACC_PUBLIC, ACC_STATIC Code: - stack=2, locals=1, args_size=1 - x: iload_0 - x: iconst_3 - x: iadd + stack=1, locals=0, args_size=0 + x: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl.getOneKeep:()I x: ireturn LineNumberTable: - LocalVariableTable: - Start Length Slot Name Signature - 0 4 0 value I - - public java.lang.String unsupportedMethod(); - descriptor: ()Ljava/lang/String; - flags: (0x0001) ACC_PUBLIC - Code: - stack=1, locals=1, args_size=1 - x: ldc #x // String This value shouldn\'t be seen on the host side. - x: areturn - LineNumberTable: - LocalVariableTable: - Start Length Slot Name Signature - 0 3 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations; - public java.lang.String visibleButUsesUnsupportedMethod(); - descriptor: ()Ljava/lang/String; - flags: (0x0001) ACC_PUBLIC + public static int getOne_noCheck(); + descriptor: ()I + flags: (0x0009) ACC_PUBLIC, ACC_STATIC Code: - stack=1, locals=1, args_size=1 - x: aload_0 - x: invokevirtual #x // Method unsupportedMethod:()Ljava/lang/String; - x: areturn + stack=1, locals=0, args_size=0 + x: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl.getOneStub:()I + x: ireturn LineNumberTable: - LocalVariableTable: - Start Length Slot Name Signature - 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations; } -SourceFile: "TinyFrameworkClassClassWideAnnotations.java" +SourceFile: "TinyFrameworkCallerCheck.java" RuntimeInvisibleAnnotations: x: #x() android.hosttest.annotation.HostSideTestWholeClassStub +NestMembers: + com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl +InnerClasses: + private static #x= #x of #x; // Impl=class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl of class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck ## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassLoadHook.class Compiled from "TinyFrameworkClassLoadHook.java" public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook @@ -936,6 +759,118 @@ SourceFile: "TinyFrameworkClassLoadHook.java" RuntimeInvisibleAnnotations: x: #x() android.hosttest.annotation.HostSideTestWholeClassStub +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotations.class + Compiled from "TinyFrameworkClassWideAnnotations.java" +public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassWideAnnotations + minor version: 0 + major version: 61 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotations + super_class: #x // java/lang/Object + interfaces: 0, fields: 1, methods: 6, attributes: 2 + public int stub; + descriptor: I + flags: (0x0001) ACC_PUBLIC + + public com.android.hoststubgen.test.tinyframework.TinyFrameworkClassWideAnnotations(); + descriptor: ()V + flags: (0x0001) ACC_PUBLIC + Code: + stack=2, locals=1, args_size=1 + x: aload_0 + x: invokespecial #x // Method java/lang/Object."<init>":()V + x: aload_0 + x: iconst_1 + x: putfield #x // Field stub:I + x: return + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 10 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotations; + + public int addOne(int); + descriptor: (I)I + flags: (0x0001) ACC_PUBLIC + Code: + stack=2, locals=2, args_size=2 + x: iload_1 + x: iconst_1 + x: iadd + x: ireturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 4 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotations; + 0 4 1 value I + + public int addTwo(int); + descriptor: (I)I + flags: (0x0001) ACC_PUBLIC + Code: + stack=3, locals=2, args_size=2 + x: new #x // class java/lang/RuntimeException + x: dup + x: ldc #x // String not supported on host side + x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V + x: athrow + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 10 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotations; + 0 10 1 value I + RuntimeInvisibleAnnotations: + x: #x(#x=s#x) + android.hosttest.annotation.HostSideTestSubstitute( + suffix="_host" + ) + + public int addTwo_host(int); + descriptor: (I)I + flags: (0x0001) ACC_PUBLIC + Code: + stack=2, locals=2, args_size=2 + x: iload_1 + x: iconst_2 + x: iadd + x: ireturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 4 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotations; + 0 4 1 value I + + public java.lang.String unsupportedMethod(); + descriptor: ()Ljava/lang/String; + flags: (0x0001) ACC_PUBLIC + Code: + stack=1, locals=1, args_size=1 + x: ldc #x // String This value shouldn\'t be seen on the host side. + x: areturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 3 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotations; + RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestThrow + + public java.lang.String visibleButUsesUnsupportedMethod(); + descriptor: ()Ljava/lang/String; + flags: (0x0001) ACC_PUBLIC + Code: + stack=1, locals=1, args_size=1 + x: aload_0 + x: invokevirtual #x // Method unsupportedMethod:()Ljava/lang/String; + x: areturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotations; +} +SourceFile: "TinyFrameworkClassWideAnnotations.java" +RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestWholeClassStub ## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializerDefault.class Compiled from "TinyFrameworkClassWithInitializerDefault.java" public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassWithInitializerDefault @@ -2684,7 +2619,7 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClass flags: (0x0021) ACC_PUBLIC, ACC_SUPER this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass super_class: #x // java/lang/Object - interfaces: 0, fields: 2, methods: 1, attributes: 4 + interfaces: 0, fields: 2, methods: 1, attributes: 3 public int value; descriptor: I flags: (0x0001) ACC_PUBLIC @@ -2717,9 +2652,6 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClass <no name> final mandated } SourceFile: "TinyFrameworkNestedClasses.java" -RuntimeInvisibleAnnotations: - x: #x() - android.hosttest.annotation.HostSideTestWholeClassStub NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses InnerClasses: public #x= #x of #x; // InnerClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses @@ -2778,6 +2710,40 @@ NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedCl InnerClasses: public static #x= #x of #x; // StaticNestedClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses #x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1 +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$Double$NestedClass.class + Compiled from "TinyFrameworkNestedClasses.java" +public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$StaticNestedClass$Double$NestedClass + minor version: 0 + major version: 61 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$Double$NestedClass + super_class: #x // java/lang/Object + interfaces: 0, fields: 1, methods: 1, attributes: 3 + public int value; + descriptor: I + flags: (0x0001) ACC_PUBLIC + + public com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$StaticNestedClass$Double$NestedClass(); + descriptor: ()V + flags: (0x0001) ACC_PUBLIC + Code: + stack=2, locals=1, args_size=1 + x: aload_0 + x: invokespecial #x // Method java/lang/Object."<init>":()V + x: aload_0 + x: bipush 8 + x: putfield #x // Field value:I + x: return + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 11 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$Double$NestedClass; +} +SourceFile: "TinyFrameworkNestedClasses.java" +NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses +InnerClasses: + public static #x= #x of #x; // StaticNestedClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses + public static #x= #x of #x; // Double$NestedClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$Double$NestedClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass ## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass.class Compiled from "TinyFrameworkNestedClasses.java" public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$StaticNestedClass @@ -2786,7 +2752,7 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClass flags: (0x0021) ACC_PUBLIC, ACC_SUPER this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass super_class: #x // java/lang/Object - interfaces: 0, fields: 1, methods: 2, attributes: 4 + interfaces: 0, fields: 1, methods: 2, attributes: 3 public int value; descriptor: I flags: (0x0001) ACC_PUBLIC @@ -2820,13 +2786,11 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClass Signature: #x // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>; } SourceFile: "TinyFrameworkNestedClasses.java" -RuntimeInvisibleAnnotations: - x: #x() - android.hosttest.annotation.HostSideTestWholeClassStub NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses InnerClasses: public static #x= #x of #x; // StaticNestedClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses #x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1 + public static #x= #x of #x; // Double$NestedClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$Double$NestedClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass ## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass.class Compiled from "TinyFrameworkNestedClasses.java" public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$SubClass extends com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$BaseClass @@ -2942,6 +2906,7 @@ NestMembers: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass + com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$Double$NestedClass com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1 com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4 @@ -2957,6 +2922,7 @@ InnerClasses: public static #x= #x of #x; // BaseClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses public static #x= #x of #x; // StaticNestedClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses public #x= #x of #x; // InnerClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses + public static #x= #x of #x; // Double$NestedClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$Double$NestedClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass #x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1 ## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkPackageRedirect.class Compiled from "TinyFrameworkPackageRedirect.java" diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/02-hoststubgen-test-tiny-framework-host-stub-dump.txt b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/02-hoststubgen-test-tiny-framework-host-stub-dump.txt index 1b83d244c55c..86a9c65f59b4 100644 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/02-hoststubgen-test-tiny-framework-host-stub-dump.txt +++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/02-hoststubgen-test-tiny-framework-host-stub-dump.txt @@ -216,136 +216,13 @@ RuntimeVisibleAnnotations: com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl NestMembers: com/android/hoststubgen/test/tinyframework/R$Nested -## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl.class - Compiled from "TinyFrameworkCallerCheck.java" -class com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck$Impl - minor version: 0 - major version: 61 - flags: (0x0020) ACC_SUPER - this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl - super_class: #x // java/lang/Object - interfaces: 0, fields: 0, methods: 2, attributes: 4 - private com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck$Impl(); - descriptor: ()V - flags: (0x0002) ACC_PRIVATE - Code: - stack=3, locals=1, args_size=1 - x: new #x // class java/lang/RuntimeException - x: dup - x: ldc #x // String Stub! - x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V - x: athrow - RuntimeVisibleAnnotations: - x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInStub - x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl - - public static int getOneStub(); - descriptor: ()I - flags: (0x0009) ACC_PUBLIC, ACC_STATIC - Code: - stack=3, locals=0, args_size=0 - x: new #x // class java/lang/RuntimeException - x: dup - x: ldc #x // String Stub! - x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V - x: athrow - RuntimeVisibleAnnotations: - x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInStub - x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl - RuntimeInvisibleAnnotations: - x: #x() - android.hosttest.annotation.HostSideTestStub -} -InnerClasses: - private static #x= #x of #x; // Impl=class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl of class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck -SourceFile: "TinyFrameworkCallerCheck.java" -RuntimeVisibleAnnotations: - x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInStub - x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl -NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck -## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck.class - Compiled from "TinyFrameworkCallerCheck.java" -public class com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations.class + Compiled from "TinyFrameworkAnnotations.java" +public class com.android.hoststubgen.test.tinyframework.TinyFrameworkAnnotations minor version: 0 major version: 61 flags: (0x0021) ACC_PUBLIC, ACC_SUPER - this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck - super_class: #x // java/lang/Object - interfaces: 0, fields: 0, methods: 3, attributes: 5 - public com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck(); - descriptor: ()V - flags: (0x0001) ACC_PUBLIC - Code: - stack=3, locals=1, args_size=1 - x: new #x // class java/lang/RuntimeException - x: dup - x: ldc #x // String Stub! - x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V - x: athrow - RuntimeVisibleAnnotations: - x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInStub - x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl - - public static int getOne_withCheck(); - descriptor: ()I - flags: (0x0009) ACC_PUBLIC, ACC_STATIC - Code: - stack=3, locals=0, args_size=0 - x: new #x // class java/lang/RuntimeException - x: dup - x: ldc #x // String Stub! - x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V - x: athrow - RuntimeVisibleAnnotations: - x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInStub - x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl - - public static int getOne_noCheck(); - descriptor: ()I - flags: (0x0009) ACC_PUBLIC, ACC_STATIC - Code: - stack=3, locals=0, args_size=0 - x: new #x // class java/lang/RuntimeException - x: dup - x: ldc #x // String Stub! - x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V - x: athrow - RuntimeVisibleAnnotations: - x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInStub - x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl -} -InnerClasses: - private static #x= #x of #x; // Impl=class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl of class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck -SourceFile: "TinyFrameworkCallerCheck.java" -RuntimeVisibleAnnotations: - x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInStub - x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl -RuntimeInvisibleAnnotations: - x: #x() - android.hosttest.annotation.HostSideTestWholeClassStub -NestMembers: - com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl -## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations.class - Compiled from "TinyFrameworkClassAnnotations.java" -public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnotations - minor version: 0 - major version: 61 - flags: (0x0021) ACC_PUBLIC, ACC_SUPER - this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations super_class: #x // java/lang/Object interfaces: 0, fields: 1, methods: 5, attributes: 3 public int stub; @@ -360,7 +237,7 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnota x: #x() android.hosttest.annotation.HostSideTestStub - public com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnotations(); + public com.android.hoststubgen.test.tinyframework.TinyFrameworkAnnotations(); descriptor: ()V flags: (0x0001) ACC_PUBLIC Code: @@ -449,7 +326,7 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnota x: #x() android.hosttest.annotation.HostSideTestStub } -SourceFile: "TinyFrameworkClassAnnotations.java" +SourceFile: "TinyFrameworkAnnotations.java" RuntimeVisibleAnnotations: x: #x() com.android.hoststubgen.hosthelper.HostStubGenKeptInStub @@ -462,45 +339,18 @@ RuntimeInvisibleAnnotations: android.hosttest.annotation.HostSideTestClassLoadHook( value="com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded" ) -## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations.class - Compiled from "TinyFrameworkClassClassWideAnnotations.java" -public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassClassWideAnnotations +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl.class + Compiled from "TinyFrameworkCallerCheck.java" +class com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck$Impl minor version: 0 major version: 61 - flags: (0x0021) ACC_PUBLIC, ACC_SUPER - this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations + flags: (0x0020) ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl super_class: #x // java/lang/Object - interfaces: 0, fields: 3, methods: 8, attributes: 3 - public int stub; - descriptor: I - flags: (0x0001) ACC_PUBLIC - RuntimeVisibleAnnotations: - x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInStub - x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl - - public int keep; - descriptor: I - flags: (0x0001) ACC_PUBLIC - RuntimeVisibleAnnotations: - x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInStub - x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl - - public int remove; - descriptor: I - flags: (0x0001) ACC_PUBLIC - RuntimeVisibleAnnotations: - x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInStub - x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl - - public com.android.hoststubgen.test.tinyframework.TinyFrameworkClassClassWideAnnotations(); + interfaces: 0, fields: 0, methods: 2, attributes: 4 + private com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck$Impl(); descriptor: ()V - flags: (0x0001) ACC_PUBLIC + flags: (0x0002) ACC_PRIVATE Code: stack=3, locals=1, args_size=1 x: new #x // class java/lang/RuntimeException @@ -514,11 +364,11 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassClassW x: #x() com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl - public int addOne(int); - descriptor: (I)I - flags: (0x0001) ACC_PUBLIC + public static int getOneStub(); + descriptor: ()I + flags: (0x0009) ACC_PUBLIC, ACC_STATIC Code: - stack=3, locals=2, args_size=2 + stack=3, locals=0, args_size=0 x: new #x // class java/lang/RuntimeException x: dup x: ldc #x // String Stub! @@ -529,12 +379,33 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassClassW com.android.hoststubgen.hosthelper.HostStubGenKeptInStub x: #x() com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl - - public int addOneInner(int); - descriptor: (I)I + RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestStub +} +InnerClasses: + private static #x= #x of #x; // Impl=class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl of class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck +SourceFile: "TinyFrameworkCallerCheck.java" +RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInStub + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl +NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck.class + Compiled from "TinyFrameworkCallerCheck.java" +public class com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck + minor version: 0 + major version: 61 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck + super_class: #x // java/lang/Object + interfaces: 0, fields: 0, methods: 3, attributes: 5 + public com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck(); + descriptor: ()V flags: (0x0001) ACC_PUBLIC Code: - stack=3, locals=2, args_size=2 + stack=3, locals=1, args_size=1 x: new #x // class java/lang/RuntimeException x: dup x: ldc #x // String Stub! @@ -546,11 +417,11 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassClassW x: #x() com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl - public void toBeRemoved(java.lang.String); - descriptor: (Ljava/lang/String;)V - flags: (0x0001) ACC_PUBLIC + public static int getOne_withCheck(); + descriptor: ()I + flags: (0x0009) ACC_PUBLIC, ACC_STATIC Code: - stack=3, locals=2, args_size=2 + stack=3, locals=0, args_size=0 x: new #x // class java/lang/RuntimeException x: dup x: ldc #x // String Stub! @@ -562,11 +433,11 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassClassW x: #x() com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl - public int addTwo(int); - descriptor: (I)I - flags: (0x0001) ACC_PUBLIC + public static int getOne_noCheck(); + descriptor: ()I + flags: (0x0009) ACC_PUBLIC, ACC_STATIC Code: - stack=3, locals=2, args_size=2 + stack=3, locals=0, args_size=0 x: new #x // class java/lang/RuntimeException x: dup x: ldc #x // String Stub! @@ -577,10 +448,42 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassClassW com.android.hoststubgen.hosthelper.HostStubGenKeptInStub x: #x() com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl +} +InnerClasses: + private static #x= #x of #x; // Impl=class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl of class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck +SourceFile: "TinyFrameworkCallerCheck.java" +RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInStub + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl +RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestWholeClassStub +NestMembers: + com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassLoadHook.class + Compiled from "TinyFrameworkClassLoadHook.java" +public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook + minor version: 0 + major version: 61 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkClassLoadHook + super_class: #x // java/lang/Object + interfaces: 0, fields: 1, methods: 3, attributes: 3 + public static final java.util.Set<java.lang.Class<?>> sLoadedClasses; + descriptor: Ljava/util/Set; + flags: (0x0019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL + Signature: #x // Ljava/util/Set<Ljava/lang/Class<*>;>; + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInStub + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl - public static int nativeAddThree(int); - descriptor: (I)I - flags: (0x0009) ACC_PUBLIC, ACC_STATIC + private com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook(); + descriptor: ()V + flags: (0x0002) ACC_PRIVATE Code: stack=3, locals=1, args_size=1 x: new #x // class java/lang/RuntimeException @@ -594,9 +497,9 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassClassW x: #x() com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl - public java.lang.String unsupportedMethod(); - descriptor: ()Ljava/lang/String; - flags: (0x0001) ACC_PUBLIC + public static void onClassLoaded(java.lang.Class<?>); + descriptor: (Ljava/lang/Class;)V + flags: (0x0009) ACC_PUBLIC, ACC_STATIC Code: stack=3, locals=1, args_size=1 x: new #x // class java/lang/RuntimeException @@ -604,17 +507,18 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassClassW x: ldc #x // String Stub! x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V x: athrow + Signature: #x // (Ljava/lang/Class<*>;)V RuntimeVisibleAnnotations: x: #x() com.android.hoststubgen.hosthelper.HostStubGenKeptInStub x: #x() com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl - public java.lang.String visibleButUsesUnsupportedMethod(); - descriptor: ()Ljava/lang/String; - flags: (0x0001) ACC_PUBLIC + static {}; + descriptor: ()V + flags: (0x0008) ACC_STATIC Code: - stack=3, locals=1, args_size=1 + stack=3, locals=0, args_size=0 x: new #x // class java/lang/RuntimeException x: dup x: ldc #x // String Stub! @@ -626,7 +530,7 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassClassW x: #x() com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl } -SourceFile: "TinyFrameworkClassClassWideAnnotations.java" +SourceFile: "TinyFrameworkClassLoadHook.java" RuntimeVisibleAnnotations: x: #x() com.android.hoststubgen.hosthelper.HostStubGenKeptInStub @@ -635,28 +539,27 @@ RuntimeVisibleAnnotations: RuntimeInvisibleAnnotations: x: #x() android.hosttest.annotation.HostSideTestWholeClassStub -## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassLoadHook.class - Compiled from "TinyFrameworkClassLoadHook.java" -public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotations.class + Compiled from "TinyFrameworkClassWideAnnotations.java" +public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassWideAnnotations minor version: 0 major version: 61 flags: (0x0021) ACC_PUBLIC, ACC_SUPER - this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkClassLoadHook + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotations super_class: #x // java/lang/Object - interfaces: 0, fields: 1, methods: 3, attributes: 3 - public static final java.util.Set<java.lang.Class<?>> sLoadedClasses; - descriptor: Ljava/util/Set; - flags: (0x0019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL - Signature: #x // Ljava/util/Set<Ljava/lang/Class<*>;>; + interfaces: 0, fields: 1, methods: 4, attributes: 3 + public int stub; + descriptor: I + flags: (0x0001) ACC_PUBLIC RuntimeVisibleAnnotations: x: #x() com.android.hoststubgen.hosthelper.HostStubGenKeptInStub x: #x() com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl - private com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook(); + public com.android.hoststubgen.test.tinyframework.TinyFrameworkClassWideAnnotations(); descriptor: ()V - flags: (0x0002) ACC_PRIVATE + flags: (0x0001) ACC_PUBLIC Code: stack=3, locals=1, args_size=1 x: new #x // class java/lang/RuntimeException @@ -670,28 +573,43 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHo x: #x() com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl - public static void onClassLoaded(java.lang.Class<?>); - descriptor: (Ljava/lang/Class;)V - flags: (0x0009) ACC_PUBLIC, ACC_STATIC + public int addOne(int); + descriptor: (I)I + flags: (0x0001) ACC_PUBLIC Code: - stack=3, locals=1, args_size=1 + stack=3, locals=2, args_size=2 x: new #x // class java/lang/RuntimeException x: dup x: ldc #x // String Stub! x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V x: athrow - Signature: #x // (Ljava/lang/Class<*>;)V RuntimeVisibleAnnotations: x: #x() com.android.hoststubgen.hosthelper.HostStubGenKeptInStub x: #x() com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl - static {}; - descriptor: ()V - flags: (0x0008) ACC_STATIC + public int addTwo(int); + descriptor: (I)I + flags: (0x0001) ACC_PUBLIC Code: - stack=3, locals=0, args_size=0 + stack=3, locals=2, args_size=2 + x: new #x // class java/lang/RuntimeException + x: dup + x: ldc #x // String Stub! + x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V + x: athrow + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInStub + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl + + public java.lang.String visibleButUsesUnsupportedMethod(); + descriptor: ()Ljava/lang/String; + flags: (0x0001) ACC_PUBLIC + Code: + stack=3, locals=1, args_size=1 x: new #x // class java/lang/RuntimeException x: dup x: ldc #x // String Stub! @@ -703,7 +621,7 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHo x: #x() com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl } -SourceFile: "TinyFrameworkClassLoadHook.java" +SourceFile: "TinyFrameworkClassWideAnnotations.java" RuntimeVisibleAnnotations: x: #x() com.android.hoststubgen.hosthelper.HostStubGenKeptInStub @@ -2153,7 +2071,7 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClass flags: (0x0021) ACC_PUBLIC, ACC_SUPER this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass super_class: #x // java/lang/Object - interfaces: 0, fields: 2, methods: 1, attributes: 5 + interfaces: 0, fields: 2, methods: 1, attributes: 4 public int value; descriptor: I flags: (0x0001) ACC_PUBLIC @@ -2199,9 +2117,50 @@ RuntimeVisibleAnnotations: com.android.hoststubgen.hosthelper.HostStubGenKeptInStub x: #x() com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl -RuntimeInvisibleAnnotations: +NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$Double$NestedClass.class + Compiled from "TinyFrameworkNestedClasses.java" +public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$StaticNestedClass$Double$NestedClass + minor version: 0 + major version: 61 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$Double$NestedClass + super_class: #x // java/lang/Object + interfaces: 0, fields: 1, methods: 1, attributes: 4 + public int value; + descriptor: I + flags: (0x0001) ACC_PUBLIC + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInStub + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl + + public com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$StaticNestedClass$Double$NestedClass(); + descriptor: ()V + flags: (0x0001) ACC_PUBLIC + Code: + stack=3, locals=1, args_size=1 + x: new #x // class java/lang/RuntimeException + x: dup + x: ldc #x // String Stub! + x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V + x: athrow + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInStub + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl +} +InnerClasses: + public static #x= #x of #x; // StaticNestedClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses + public static #x= #x of #x; // Double$NestedClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$Double$NestedClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass +SourceFile: "TinyFrameworkNestedClasses.java" +RuntimeVisibleAnnotations: x: #x() - android.hosttest.annotation.HostSideTestWholeClassStub + com.android.hoststubgen.hosthelper.HostStubGenKeptInStub + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses ## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass.class Compiled from "TinyFrameworkNestedClasses.java" @@ -2211,7 +2170,7 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClass flags: (0x0021) ACC_PUBLIC, ACC_SUPER this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass super_class: #x // java/lang/Object - interfaces: 0, fields: 1, methods: 2, attributes: 5 + interfaces: 0, fields: 1, methods: 2, attributes: 4 public int value; descriptor: I flags: (0x0001) ACC_PUBLIC @@ -2257,15 +2216,13 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClass InnerClasses: public static #x= #x of #x; // StaticNestedClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses #x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1 + public static #x= #x of #x; // Double$NestedClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$Double$NestedClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass SourceFile: "TinyFrameworkNestedClasses.java" RuntimeVisibleAnnotations: x: #x() com.android.hoststubgen.hosthelper.HostStubGenKeptInStub x: #x() com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl -RuntimeInvisibleAnnotations: - x: #x() - android.hosttest.annotation.HostSideTestWholeClassStub NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses ## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass.class Compiled from "TinyFrameworkNestedClasses.java" @@ -2406,6 +2363,7 @@ InnerClasses: public static #x= #x of #x; // BaseClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses public static #x= #x of #x; // StaticNestedClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses public #x= #x of #x; // InnerClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses + public static #x= #x of #x; // Double$NestedClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$Double$NestedClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass #x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1 SourceFile: "TinyFrameworkNestedClasses.java" RuntimeVisibleAnnotations: @@ -2420,6 +2378,7 @@ NestMembers: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass + com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$Double$NestedClass com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1 com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4 diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/03-hoststubgen-test-tiny-framework-host-impl-dump.txt b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/03-hoststubgen-test-tiny-framework-host-impl-dump.txt index d23b450c4076..c6b9c7a9e4f1 100644 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/03-hoststubgen-test-tiny-framework-host-impl-dump.txt +++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/03-hoststubgen-test-tiny-framework-host-impl-dump.txt @@ -450,155 +450,13 @@ RuntimeVisibleAnnotations: com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl NestMembers: com/android/hoststubgen/test/tinyframework/R$Nested -## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl.class - Compiled from "TinyFrameworkCallerCheck.java" -class com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck$Impl - minor version: 0 - major version: 61 - flags: (0x0020) ACC_SUPER - this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl - super_class: #x // java/lang/Object - interfaces: 0, fields: 0, methods: 3, attributes: 4 - private com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck$Impl(); - descriptor: ()V - flags: (0x0002) ACC_PRIVATE - Code: - stack=1, locals=1, args_size=1 - x: aload_0 - x: invokespecial #x // Method java/lang/Object."<init>":()V - x: return - LineNumberTable: - LocalVariableTable: - Start Length Slot Name Signature - 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl; - RuntimeVisibleAnnotations: - x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInStub - x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl - - public static int getOneKeep(); - descriptor: ()I - flags: (0x0009) ACC_PUBLIC, ACC_STATIC - Code: - stack=4, locals=0, args_size=0 - x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl - x: ldc #x // String getOneKeep - x: ldc #x // String ()I - x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker; - x: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class; - x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V - x: iconst_1 - x: ireturn - LineNumberTable: - RuntimeVisibleAnnotations: - x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl - RuntimeInvisibleAnnotations: - x: #x() - android.hosttest.annotation.HostSideTestKeep - - public static int getOneStub(); - descriptor: ()I - flags: (0x0009) ACC_PUBLIC, ACC_STATIC - Code: - stack=1, locals=0, args_size=0 - x: iconst_1 - x: ireturn - LineNumberTable: - RuntimeVisibleAnnotations: - x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInStub - x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl - RuntimeInvisibleAnnotations: - x: #x() - android.hosttest.annotation.HostSideTestStub -} -InnerClasses: - private static #x= #x of #x; // Impl=class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl of class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck -SourceFile: "TinyFrameworkCallerCheck.java" -RuntimeVisibleAnnotations: - x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInStub - x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl -NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck -## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck.class - Compiled from "TinyFrameworkCallerCheck.java" -public class com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations.class + Compiled from "TinyFrameworkAnnotations.java" +public class com.android.hoststubgen.test.tinyframework.TinyFrameworkAnnotations minor version: 0 major version: 61 flags: (0x0021) ACC_PUBLIC, ACC_SUPER - this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck - super_class: #x // java/lang/Object - interfaces: 0, fields: 0, methods: 3, attributes: 5 - public com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck(); - descriptor: ()V - flags: (0x0001) ACC_PUBLIC - Code: - stack=1, locals=1, args_size=1 - x: aload_0 - x: invokespecial #x // Method java/lang/Object."<init>":()V - x: return - LineNumberTable: - LocalVariableTable: - Start Length Slot Name Signature - 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck; - RuntimeVisibleAnnotations: - x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInStub - x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl - - public static int getOne_withCheck(); - descriptor: ()I - flags: (0x0009) ACC_PUBLIC, ACC_STATIC - Code: - stack=1, locals=0, args_size=0 - x: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl.getOneKeep:()I - x: ireturn - LineNumberTable: - RuntimeVisibleAnnotations: - x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInStub - x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl - - public static int getOne_noCheck(); - descriptor: ()I - flags: (0x0009) ACC_PUBLIC, ACC_STATIC - Code: - stack=1, locals=0, args_size=0 - x: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl.getOneStub:()I - x: ireturn - LineNumberTable: - RuntimeVisibleAnnotations: - x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInStub - x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl -} -InnerClasses: - private static #x= #x of #x; // Impl=class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl of class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck -SourceFile: "TinyFrameworkCallerCheck.java" -RuntimeVisibleAnnotations: - x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInStub - x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl -RuntimeInvisibleAnnotations: - x: #x() - android.hosttest.annotation.HostSideTestWholeClassStub -NestMembers: - com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl -## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations.class - Compiled from "TinyFrameworkClassAnnotations.java" -public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnotations - minor version: 0 - major version: 61 - flags: (0x0021) ACC_PUBLIC, ACC_SUPER - this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations super_class: #x // java/lang/Object interfaces: 0, fields: 2, methods: 8, attributes: 3 public int stub; @@ -628,12 +486,12 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnota flags: (0x000a) ACC_PRIVATE, ACC_STATIC Code: stack=2, locals=0, args_size=0 - x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations + x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations x: ldc #x // String com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V x: return - public com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnotations(); + public com.android.hoststubgen.test.tinyframework.TinyFrameworkAnnotations(); descriptor: ()V flags: (0x0001) ACC_PUBLIC Code: @@ -650,7 +508,7 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnota LineNumberTable: LocalVariableTable: Start Length Slot Name Signature - 0 15 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations; + 0 15 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations; RuntimeVisibleAnnotations: x: #x() com.android.hoststubgen.hosthelper.HostStubGenKeptInStub @@ -672,7 +530,7 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnota LineNumberTable: LocalVariableTable: Start Length Slot Name Signature - 0 6 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations; + 0 6 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations; 0 6 1 value I RuntimeVisibleAnnotations: x: #x() @@ -688,7 +546,7 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnota flags: (0x0001) ACC_PUBLIC Code: stack=4, locals=2, args_size=2 - x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations + x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations x: ldc #x // String addOneInner x: ldc #x // String (I)I x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker; @@ -701,7 +559,7 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnota LineNumberTable: LocalVariableTable: Start Length Slot Name Signature - 15 4 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations; + 15 4 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations; 15 4 1 value I RuntimeVisibleAnnotations: x: #x() @@ -722,7 +580,7 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnota LineNumberTable: LocalVariableTable: Start Length Slot Name Signature - 0 4 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations; + 0 4 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations; 0 4 1 value I RuntimeVisibleAnnotations: x: #x() @@ -758,7 +616,7 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnota flags: (0x0001) ACC_PUBLIC Code: stack=4, locals=1, args_size=1 - x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations + x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations x: ldc #x // String unsupportedMethod x: ldc #x // String ()Ljava/lang/String; x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker; @@ -790,7 +648,7 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnota LineNumberTable: LocalVariableTable: Start Length Slot Name Signature - 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations; + 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations; RuntimeVisibleAnnotations: x: #x() com.android.hoststubgen.hosthelper.HostStubGenKeptInStub @@ -800,7 +658,7 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnota x: #x() android.hosttest.annotation.HostSideTestStub } -SourceFile: "TinyFrameworkClassAnnotations.java" +SourceFile: "TinyFrameworkAnnotations.java" RuntimeVisibleAnnotations: x: #x() com.android.hoststubgen.hosthelper.HostStubGenKeptInStub @@ -813,205 +671,138 @@ RuntimeInvisibleAnnotations: android.hosttest.annotation.HostSideTestClassLoadHook( value="com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded" ) -## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations.class - Compiled from "TinyFrameworkClassClassWideAnnotations.java" -public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassClassWideAnnotations +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl.class + Compiled from "TinyFrameworkCallerCheck.java" +class com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck$Impl minor version: 0 major version: 61 - flags: (0x0021) ACC_PUBLIC, ACC_SUPER - this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations + flags: (0x0020) ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl super_class: #x // java/lang/Object - interfaces: 0, fields: 3, methods: 8, attributes: 3 - public int stub; - descriptor: I - flags: (0x0001) ACC_PUBLIC - RuntimeVisibleAnnotations: - x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInStub - x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl - - public int keep; - descriptor: I - flags: (0x0001) ACC_PUBLIC - RuntimeVisibleAnnotations: - x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInStub - x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl - - public int remove; - descriptor: I - flags: (0x0001) ACC_PUBLIC - RuntimeVisibleAnnotations: - x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInStub - x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl - - public com.android.hoststubgen.test.tinyframework.TinyFrameworkClassClassWideAnnotations(); + interfaces: 0, fields: 0, methods: 3, attributes: 4 + private com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck$Impl(); descriptor: ()V - flags: (0x0001) ACC_PUBLIC + flags: (0x0002) ACC_PRIVATE Code: - stack=2, locals=1, args_size=1 + stack=1, locals=1, args_size=1 x: aload_0 x: invokespecial #x // Method java/lang/Object."<init>":()V - x: aload_0 - x: iconst_1 - x: putfield #x // Field stub:I - x: aload_0 - x: iconst_2 - x: putfield #x // Field keep:I - x: return + x: return LineNumberTable: LocalVariableTable: Start Length Slot Name Signature - 0 15 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations; + 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl; RuntimeVisibleAnnotations: x: #x() com.android.hoststubgen.hosthelper.HostStubGenKeptInStub x: #x() com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl - public int addOne(int); - descriptor: (I)I - flags: (0x0001) ACC_PUBLIC + public static int getOneKeep(); + descriptor: ()I + flags: (0x0009) ACC_PUBLIC, ACC_STATIC Code: - stack=2, locals=2, args_size=2 - x: aload_0 - x: iload_1 - x: invokevirtual #x // Method addOneInner:(I)I - x: ireturn + stack=4, locals=0, args_size=0 + x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl + x: ldc #x // String getOneKeep + x: ldc #x // String ()I + x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker; + x: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class; + x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V + x: iconst_1 + x: ireturn LineNumberTable: - LocalVariableTable: - Start Length Slot Name Signature - 0 6 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations; - 0 6 1 value I RuntimeVisibleAnnotations: x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInStub - x: #x() com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl + RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestKeep - public int addOneInner(int); - descriptor: (I)I - flags: (0x0001) ACC_PUBLIC + public static int getOneStub(); + descriptor: ()I + flags: (0x0009) ACC_PUBLIC, ACC_STATIC Code: - stack=2, locals=2, args_size=2 - x: iload_1 + stack=1, locals=0, args_size=0 x: iconst_1 - x: iadd x: ireturn LineNumberTable: - LocalVariableTable: - Start Length Slot Name Signature - 0 4 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations; - 0 4 1 value I RuntimeVisibleAnnotations: x: #x() com.android.hoststubgen.hosthelper.HostStubGenKeptInStub x: #x() com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl - - public void toBeRemoved(java.lang.String); - descriptor: (Ljava/lang/String;)V - flags: (0x0001) ACC_PUBLIC - Code: - stack=2, locals=2, args_size=2 - x: new #x // class java/lang/RuntimeException - x: dup - x: invokespecial #x // Method java/lang/RuntimeException."<init>":()V - x: athrow - LineNumberTable: - LocalVariableTable: - Start Length Slot Name Signature - 0 8 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations; - 0 8 1 foo Ljava/lang/String; - RuntimeVisibleAnnotations: - x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInStub + RuntimeInvisibleAnnotations: x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl - - public int addTwo(int); - descriptor: (I)I + android.hosttest.annotation.HostSideTestStub +} +InnerClasses: + private static #x= #x of #x; // Impl=class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl of class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck +SourceFile: "TinyFrameworkCallerCheck.java" +RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInStub + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl +NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck.class + Compiled from "TinyFrameworkCallerCheck.java" +public class com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck + minor version: 0 + major version: 61 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck + super_class: #x // java/lang/Object + interfaces: 0, fields: 0, methods: 3, attributes: 5 + public com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck(); + descriptor: ()V flags: (0x0001) ACC_PUBLIC Code: - stack=2, locals=2, args_size=2 - x: iload_1 - x: iconst_2 - x: iadd - x: ireturn + stack=1, locals=1, args_size=1 + x: aload_0 + x: invokespecial #x // Method java/lang/Object."<init>":()V + x: return LineNumberTable: LocalVariableTable: Start Length Slot Name Signature - 0 4 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations; - 0 4 1 value I + 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck; RuntimeVisibleAnnotations: x: #x() - com.android.hoststubgen.hosthelper.HostStubGenProcessedAsSubstitute - x: #x() com.android.hoststubgen.hosthelper.HostStubGenKeptInStub x: #x() com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl - public static int nativeAddThree(int); - descriptor: (I)I + public static int getOne_withCheck(); + descriptor: ()I flags: (0x0009) ACC_PUBLIC, ACC_STATIC Code: - stack=2, locals=1, args_size=1 - x: iload_0 - x: iconst_3 - x: iadd + stack=1, locals=0, args_size=0 + x: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl.getOneKeep:()I x: ireturn LineNumberTable: - LocalVariableTable: - Start Length Slot Name Signature - 0 4 0 value I - RuntimeVisibleAnnotations: - x: #x() - com.android.hoststubgen.hosthelper.HostStubGenProcessedAsSubstitute - x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInStub - x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl - - public java.lang.String unsupportedMethod(); - descriptor: ()Ljava/lang/String; - flags: (0x0001) ACC_PUBLIC - Code: - stack=1, locals=1, args_size=1 - x: ldc #x // String This value shouldn\'t be seen on the host side. - x: areturn - LineNumberTable: - LocalVariableTable: - Start Length Slot Name Signature - 0 3 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations; RuntimeVisibleAnnotations: x: #x() com.android.hoststubgen.hosthelper.HostStubGenKeptInStub x: #x() com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl - public java.lang.String visibleButUsesUnsupportedMethod(); - descriptor: ()Ljava/lang/String; - flags: (0x0001) ACC_PUBLIC + public static int getOne_noCheck(); + descriptor: ()I + flags: (0x0009) ACC_PUBLIC, ACC_STATIC Code: - stack=1, locals=1, args_size=1 - x: aload_0 - x: invokevirtual #x // Method unsupportedMethod:()Ljava/lang/String; - x: areturn + stack=1, locals=0, args_size=0 + x: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl.getOneStub:()I + x: ireturn LineNumberTable: - LocalVariableTable: - Start Length Slot Name Signature - 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations; RuntimeVisibleAnnotations: x: #x() com.android.hoststubgen.hosthelper.HostStubGenKeptInStub x: #x() com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl } -SourceFile: "TinyFrameworkClassClassWideAnnotations.java" +InnerClasses: + private static #x= #x of #x; // Impl=class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl of class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck +SourceFile: "TinyFrameworkCallerCheck.java" RuntimeVisibleAnnotations: x: #x() com.android.hoststubgen.hosthelper.HostStubGenKeptInStub @@ -1020,6 +811,8 @@ RuntimeVisibleAnnotations: RuntimeInvisibleAnnotations: x: #x() android.hosttest.annotation.HostSideTestWholeClassStub +NestMembers: + com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl ## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassLoadHook.class Compiled from "TinyFrameworkClassLoadHook.java" public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook @@ -1107,6 +900,140 @@ RuntimeVisibleAnnotations: RuntimeInvisibleAnnotations: x: #x() android.hosttest.annotation.HostSideTestWholeClassStub +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotations.class + Compiled from "TinyFrameworkClassWideAnnotations.java" +public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassWideAnnotations + minor version: 0 + major version: 61 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotations + super_class: #x // java/lang/Object + interfaces: 0, fields: 1, methods: 5, attributes: 3 + public int stub; + descriptor: I + flags: (0x0001) ACC_PUBLIC + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInStub + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl + + public com.android.hoststubgen.test.tinyframework.TinyFrameworkClassWideAnnotations(); + descriptor: ()V + flags: (0x0001) ACC_PUBLIC + Code: + stack=2, locals=1, args_size=1 + x: aload_0 + x: invokespecial #x // Method java/lang/Object."<init>":()V + x: aload_0 + x: iconst_1 + x: putfield #x // Field stub:I + x: return + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 10 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotations; + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInStub + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl + + public int addOne(int); + descriptor: (I)I + flags: (0x0001) ACC_PUBLIC + Code: + stack=2, locals=2, args_size=2 + x: iload_1 + x: iconst_1 + x: iadd + x: ireturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 4 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotations; + 0 4 1 value I + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInStub + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl + + public int addTwo(int); + descriptor: (I)I + flags: (0x0001) ACC_PUBLIC + Code: + stack=2, locals=2, args_size=2 + x: iload_1 + x: iconst_2 + x: iadd + x: ireturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 4 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotations; + 0 4 1 value I + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsSubstitute + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInStub + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl + + public java.lang.String unsupportedMethod(); + descriptor: ()Ljava/lang/String; + flags: (0x0001) ACC_PUBLIC + Code: + stack=4, locals=1, args_size=1 + x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotations + x: ldc #x // String unsupportedMethod + x: ldc #x // String ()Ljava/lang/String; + x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker; + x: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class; + x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V + x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onThrowMethodCalled:()V + x: new #x // class java/lang/RuntimeException + x: dup + x: ldc #x // String Unreachable + x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V + x: athrow + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsThrow + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl + RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestThrow + + public java.lang.String visibleButUsesUnsupportedMethod(); + descriptor: ()Ljava/lang/String; + flags: (0x0001) ACC_PUBLIC + Code: + stack=1, locals=1, args_size=1 + x: aload_0 + x: invokevirtual #x // Method unsupportedMethod:()Ljava/lang/String; + x: areturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotations; + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInStub + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl +} +SourceFile: "TinyFrameworkClassWideAnnotations.java" +RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInStub + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl +RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestWholeClassStub ## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializerDefault.class Compiled from "TinyFrameworkClassWithInitializerDefault.java" public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassWithInitializerDefault @@ -3430,7 +3357,7 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClass flags: (0x0021) ACC_PUBLIC, ACC_SUPER this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass super_class: #x // java/lang/Object - interfaces: 0, fields: 2, methods: 1, attributes: 5 + interfaces: 0, fields: 2, methods: 1, attributes: 4 public int value; descriptor: I flags: (0x0001) ACC_PUBLIC @@ -3485,9 +3412,6 @@ RuntimeVisibleAnnotations: com.android.hoststubgen.hosthelper.HostStubGenKeptInStub x: #x() com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl -RuntimeInvisibleAnnotations: - x: #x() - android.hosttest.annotation.HostSideTestWholeClassStub NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses ## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1.class Compiled from "TinyFrameworkNestedClasses.java" @@ -3568,6 +3492,55 @@ RuntimeVisibleAnnotations: x: #x() com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$Double$NestedClass.class + Compiled from "TinyFrameworkNestedClasses.java" +public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$StaticNestedClass$Double$NestedClass + minor version: 0 + major version: 61 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$Double$NestedClass + super_class: #x // java/lang/Object + interfaces: 0, fields: 1, methods: 1, attributes: 4 + public int value; + descriptor: I + flags: (0x0001) ACC_PUBLIC + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInStub + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl + + public com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$StaticNestedClass$Double$NestedClass(); + descriptor: ()V + flags: (0x0001) ACC_PUBLIC + Code: + stack=2, locals=1, args_size=1 + x: aload_0 + x: invokespecial #x // Method java/lang/Object."<init>":()V + x: aload_0 + x: bipush 8 + x: putfield #x // Field value:I + x: return + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 11 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$Double$NestedClass; + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInStub + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl +} +InnerClasses: + public static #x= #x of #x; // StaticNestedClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses + public static #x= #x of #x; // Double$NestedClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$Double$NestedClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass +SourceFile: "TinyFrameworkNestedClasses.java" +RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInStub + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl +NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses ## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass.class Compiled from "TinyFrameworkNestedClasses.java" public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$StaticNestedClass @@ -3576,7 +3549,7 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClass flags: (0x0021) ACC_PUBLIC, ACC_SUPER this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass super_class: #x // java/lang/Object - interfaces: 0, fields: 1, methods: 2, attributes: 5 + interfaces: 0, fields: 1, methods: 2, attributes: 4 public int value; descriptor: I flags: (0x0001) ACC_PUBLIC @@ -3627,15 +3600,13 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClass InnerClasses: public static #x= #x of #x; // StaticNestedClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses #x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1 + public static #x= #x of #x; // Double$NestedClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$Double$NestedClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass SourceFile: "TinyFrameworkNestedClasses.java" RuntimeVisibleAnnotations: x: #x() com.android.hoststubgen.hosthelper.HostStubGenKeptInStub x: #x() com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl -RuntimeInvisibleAnnotations: - x: #x() - android.hosttest.annotation.HostSideTestWholeClassStub NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses ## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass.class Compiled from "TinyFrameworkNestedClasses.java" @@ -3793,6 +3764,7 @@ InnerClasses: public static #x= #x of #x; // BaseClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses public static #x= #x of #x; // StaticNestedClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses public #x= #x of #x; // InnerClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses + public static #x= #x of #x; // Double$NestedClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$Double$NestedClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass #x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1 SourceFile: "TinyFrameworkNestedClasses.java" RuntimeVisibleAnnotations: @@ -3807,6 +3779,7 @@ NestMembers: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass + com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$Double$NestedClass com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1 com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4 diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/12-hoststubgen-test-tiny-framework-host-ext-stub-dump.txt b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/12-hoststubgen-test-tiny-framework-host-ext-stub-dump.txt index 1b83d244c55c..86a9c65f59b4 100644 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/12-hoststubgen-test-tiny-framework-host-ext-stub-dump.txt +++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/12-hoststubgen-test-tiny-framework-host-ext-stub-dump.txt @@ -216,136 +216,13 @@ RuntimeVisibleAnnotations: com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl NestMembers: com/android/hoststubgen/test/tinyframework/R$Nested -## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl.class - Compiled from "TinyFrameworkCallerCheck.java" -class com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck$Impl - minor version: 0 - major version: 61 - flags: (0x0020) ACC_SUPER - this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl - super_class: #x // java/lang/Object - interfaces: 0, fields: 0, methods: 2, attributes: 4 - private com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck$Impl(); - descriptor: ()V - flags: (0x0002) ACC_PRIVATE - Code: - stack=3, locals=1, args_size=1 - x: new #x // class java/lang/RuntimeException - x: dup - x: ldc #x // String Stub! - x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V - x: athrow - RuntimeVisibleAnnotations: - x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInStub - x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl - - public static int getOneStub(); - descriptor: ()I - flags: (0x0009) ACC_PUBLIC, ACC_STATIC - Code: - stack=3, locals=0, args_size=0 - x: new #x // class java/lang/RuntimeException - x: dup - x: ldc #x // String Stub! - x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V - x: athrow - RuntimeVisibleAnnotations: - x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInStub - x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl - RuntimeInvisibleAnnotations: - x: #x() - android.hosttest.annotation.HostSideTestStub -} -InnerClasses: - private static #x= #x of #x; // Impl=class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl of class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck -SourceFile: "TinyFrameworkCallerCheck.java" -RuntimeVisibleAnnotations: - x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInStub - x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl -NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck -## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck.class - Compiled from "TinyFrameworkCallerCheck.java" -public class com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations.class + Compiled from "TinyFrameworkAnnotations.java" +public class com.android.hoststubgen.test.tinyframework.TinyFrameworkAnnotations minor version: 0 major version: 61 flags: (0x0021) ACC_PUBLIC, ACC_SUPER - this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck - super_class: #x // java/lang/Object - interfaces: 0, fields: 0, methods: 3, attributes: 5 - public com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck(); - descriptor: ()V - flags: (0x0001) ACC_PUBLIC - Code: - stack=3, locals=1, args_size=1 - x: new #x // class java/lang/RuntimeException - x: dup - x: ldc #x // String Stub! - x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V - x: athrow - RuntimeVisibleAnnotations: - x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInStub - x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl - - public static int getOne_withCheck(); - descriptor: ()I - flags: (0x0009) ACC_PUBLIC, ACC_STATIC - Code: - stack=3, locals=0, args_size=0 - x: new #x // class java/lang/RuntimeException - x: dup - x: ldc #x // String Stub! - x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V - x: athrow - RuntimeVisibleAnnotations: - x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInStub - x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl - - public static int getOne_noCheck(); - descriptor: ()I - flags: (0x0009) ACC_PUBLIC, ACC_STATIC - Code: - stack=3, locals=0, args_size=0 - x: new #x // class java/lang/RuntimeException - x: dup - x: ldc #x // String Stub! - x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V - x: athrow - RuntimeVisibleAnnotations: - x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInStub - x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl -} -InnerClasses: - private static #x= #x of #x; // Impl=class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl of class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck -SourceFile: "TinyFrameworkCallerCheck.java" -RuntimeVisibleAnnotations: - x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInStub - x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl -RuntimeInvisibleAnnotations: - x: #x() - android.hosttest.annotation.HostSideTestWholeClassStub -NestMembers: - com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl -## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations.class - Compiled from "TinyFrameworkClassAnnotations.java" -public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnotations - minor version: 0 - major version: 61 - flags: (0x0021) ACC_PUBLIC, ACC_SUPER - this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations super_class: #x // java/lang/Object interfaces: 0, fields: 1, methods: 5, attributes: 3 public int stub; @@ -360,7 +237,7 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnota x: #x() android.hosttest.annotation.HostSideTestStub - public com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnotations(); + public com.android.hoststubgen.test.tinyframework.TinyFrameworkAnnotations(); descriptor: ()V flags: (0x0001) ACC_PUBLIC Code: @@ -449,7 +326,7 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnota x: #x() android.hosttest.annotation.HostSideTestStub } -SourceFile: "TinyFrameworkClassAnnotations.java" +SourceFile: "TinyFrameworkAnnotations.java" RuntimeVisibleAnnotations: x: #x() com.android.hoststubgen.hosthelper.HostStubGenKeptInStub @@ -462,45 +339,18 @@ RuntimeInvisibleAnnotations: android.hosttest.annotation.HostSideTestClassLoadHook( value="com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded" ) -## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations.class - Compiled from "TinyFrameworkClassClassWideAnnotations.java" -public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassClassWideAnnotations +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl.class + Compiled from "TinyFrameworkCallerCheck.java" +class com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck$Impl minor version: 0 major version: 61 - flags: (0x0021) ACC_PUBLIC, ACC_SUPER - this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations + flags: (0x0020) ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl super_class: #x // java/lang/Object - interfaces: 0, fields: 3, methods: 8, attributes: 3 - public int stub; - descriptor: I - flags: (0x0001) ACC_PUBLIC - RuntimeVisibleAnnotations: - x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInStub - x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl - - public int keep; - descriptor: I - flags: (0x0001) ACC_PUBLIC - RuntimeVisibleAnnotations: - x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInStub - x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl - - public int remove; - descriptor: I - flags: (0x0001) ACC_PUBLIC - RuntimeVisibleAnnotations: - x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInStub - x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl - - public com.android.hoststubgen.test.tinyframework.TinyFrameworkClassClassWideAnnotations(); + interfaces: 0, fields: 0, methods: 2, attributes: 4 + private com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck$Impl(); descriptor: ()V - flags: (0x0001) ACC_PUBLIC + flags: (0x0002) ACC_PRIVATE Code: stack=3, locals=1, args_size=1 x: new #x // class java/lang/RuntimeException @@ -514,11 +364,11 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassClassW x: #x() com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl - public int addOne(int); - descriptor: (I)I - flags: (0x0001) ACC_PUBLIC + public static int getOneStub(); + descriptor: ()I + flags: (0x0009) ACC_PUBLIC, ACC_STATIC Code: - stack=3, locals=2, args_size=2 + stack=3, locals=0, args_size=0 x: new #x // class java/lang/RuntimeException x: dup x: ldc #x // String Stub! @@ -529,12 +379,33 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassClassW com.android.hoststubgen.hosthelper.HostStubGenKeptInStub x: #x() com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl - - public int addOneInner(int); - descriptor: (I)I + RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestStub +} +InnerClasses: + private static #x= #x of #x; // Impl=class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl of class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck +SourceFile: "TinyFrameworkCallerCheck.java" +RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInStub + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl +NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck.class + Compiled from "TinyFrameworkCallerCheck.java" +public class com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck + minor version: 0 + major version: 61 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck + super_class: #x // java/lang/Object + interfaces: 0, fields: 0, methods: 3, attributes: 5 + public com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck(); + descriptor: ()V flags: (0x0001) ACC_PUBLIC Code: - stack=3, locals=2, args_size=2 + stack=3, locals=1, args_size=1 x: new #x // class java/lang/RuntimeException x: dup x: ldc #x // String Stub! @@ -546,11 +417,11 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassClassW x: #x() com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl - public void toBeRemoved(java.lang.String); - descriptor: (Ljava/lang/String;)V - flags: (0x0001) ACC_PUBLIC + public static int getOne_withCheck(); + descriptor: ()I + flags: (0x0009) ACC_PUBLIC, ACC_STATIC Code: - stack=3, locals=2, args_size=2 + stack=3, locals=0, args_size=0 x: new #x // class java/lang/RuntimeException x: dup x: ldc #x // String Stub! @@ -562,11 +433,11 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassClassW x: #x() com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl - public int addTwo(int); - descriptor: (I)I - flags: (0x0001) ACC_PUBLIC + public static int getOne_noCheck(); + descriptor: ()I + flags: (0x0009) ACC_PUBLIC, ACC_STATIC Code: - stack=3, locals=2, args_size=2 + stack=3, locals=0, args_size=0 x: new #x // class java/lang/RuntimeException x: dup x: ldc #x // String Stub! @@ -577,10 +448,42 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassClassW com.android.hoststubgen.hosthelper.HostStubGenKeptInStub x: #x() com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl +} +InnerClasses: + private static #x= #x of #x; // Impl=class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl of class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck +SourceFile: "TinyFrameworkCallerCheck.java" +RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInStub + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl +RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestWholeClassStub +NestMembers: + com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassLoadHook.class + Compiled from "TinyFrameworkClassLoadHook.java" +public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook + minor version: 0 + major version: 61 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkClassLoadHook + super_class: #x // java/lang/Object + interfaces: 0, fields: 1, methods: 3, attributes: 3 + public static final java.util.Set<java.lang.Class<?>> sLoadedClasses; + descriptor: Ljava/util/Set; + flags: (0x0019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL + Signature: #x // Ljava/util/Set<Ljava/lang/Class<*>;>; + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInStub + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl - public static int nativeAddThree(int); - descriptor: (I)I - flags: (0x0009) ACC_PUBLIC, ACC_STATIC + private com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook(); + descriptor: ()V + flags: (0x0002) ACC_PRIVATE Code: stack=3, locals=1, args_size=1 x: new #x // class java/lang/RuntimeException @@ -594,9 +497,9 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassClassW x: #x() com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl - public java.lang.String unsupportedMethod(); - descriptor: ()Ljava/lang/String; - flags: (0x0001) ACC_PUBLIC + public static void onClassLoaded(java.lang.Class<?>); + descriptor: (Ljava/lang/Class;)V + flags: (0x0009) ACC_PUBLIC, ACC_STATIC Code: stack=3, locals=1, args_size=1 x: new #x // class java/lang/RuntimeException @@ -604,17 +507,18 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassClassW x: ldc #x // String Stub! x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V x: athrow + Signature: #x // (Ljava/lang/Class<*>;)V RuntimeVisibleAnnotations: x: #x() com.android.hoststubgen.hosthelper.HostStubGenKeptInStub x: #x() com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl - public java.lang.String visibleButUsesUnsupportedMethod(); - descriptor: ()Ljava/lang/String; - flags: (0x0001) ACC_PUBLIC + static {}; + descriptor: ()V + flags: (0x0008) ACC_STATIC Code: - stack=3, locals=1, args_size=1 + stack=3, locals=0, args_size=0 x: new #x // class java/lang/RuntimeException x: dup x: ldc #x // String Stub! @@ -626,7 +530,7 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassClassW x: #x() com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl } -SourceFile: "TinyFrameworkClassClassWideAnnotations.java" +SourceFile: "TinyFrameworkClassLoadHook.java" RuntimeVisibleAnnotations: x: #x() com.android.hoststubgen.hosthelper.HostStubGenKeptInStub @@ -635,28 +539,27 @@ RuntimeVisibleAnnotations: RuntimeInvisibleAnnotations: x: #x() android.hosttest.annotation.HostSideTestWholeClassStub -## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassLoadHook.class - Compiled from "TinyFrameworkClassLoadHook.java" -public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotations.class + Compiled from "TinyFrameworkClassWideAnnotations.java" +public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassWideAnnotations minor version: 0 major version: 61 flags: (0x0021) ACC_PUBLIC, ACC_SUPER - this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkClassLoadHook + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotations super_class: #x // java/lang/Object - interfaces: 0, fields: 1, methods: 3, attributes: 3 - public static final java.util.Set<java.lang.Class<?>> sLoadedClasses; - descriptor: Ljava/util/Set; - flags: (0x0019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL - Signature: #x // Ljava/util/Set<Ljava/lang/Class<*>;>; + interfaces: 0, fields: 1, methods: 4, attributes: 3 + public int stub; + descriptor: I + flags: (0x0001) ACC_PUBLIC RuntimeVisibleAnnotations: x: #x() com.android.hoststubgen.hosthelper.HostStubGenKeptInStub x: #x() com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl - private com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook(); + public com.android.hoststubgen.test.tinyframework.TinyFrameworkClassWideAnnotations(); descriptor: ()V - flags: (0x0002) ACC_PRIVATE + flags: (0x0001) ACC_PUBLIC Code: stack=3, locals=1, args_size=1 x: new #x // class java/lang/RuntimeException @@ -670,28 +573,43 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHo x: #x() com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl - public static void onClassLoaded(java.lang.Class<?>); - descriptor: (Ljava/lang/Class;)V - flags: (0x0009) ACC_PUBLIC, ACC_STATIC + public int addOne(int); + descriptor: (I)I + flags: (0x0001) ACC_PUBLIC Code: - stack=3, locals=1, args_size=1 + stack=3, locals=2, args_size=2 x: new #x // class java/lang/RuntimeException x: dup x: ldc #x // String Stub! x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V x: athrow - Signature: #x // (Ljava/lang/Class<*>;)V RuntimeVisibleAnnotations: x: #x() com.android.hoststubgen.hosthelper.HostStubGenKeptInStub x: #x() com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl - static {}; - descriptor: ()V - flags: (0x0008) ACC_STATIC + public int addTwo(int); + descriptor: (I)I + flags: (0x0001) ACC_PUBLIC Code: - stack=3, locals=0, args_size=0 + stack=3, locals=2, args_size=2 + x: new #x // class java/lang/RuntimeException + x: dup + x: ldc #x // String Stub! + x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V + x: athrow + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInStub + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl + + public java.lang.String visibleButUsesUnsupportedMethod(); + descriptor: ()Ljava/lang/String; + flags: (0x0001) ACC_PUBLIC + Code: + stack=3, locals=1, args_size=1 x: new #x // class java/lang/RuntimeException x: dup x: ldc #x // String Stub! @@ -703,7 +621,7 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHo x: #x() com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl } -SourceFile: "TinyFrameworkClassLoadHook.java" +SourceFile: "TinyFrameworkClassWideAnnotations.java" RuntimeVisibleAnnotations: x: #x() com.android.hoststubgen.hosthelper.HostStubGenKeptInStub @@ -2153,7 +2071,7 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClass flags: (0x0021) ACC_PUBLIC, ACC_SUPER this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass super_class: #x // java/lang/Object - interfaces: 0, fields: 2, methods: 1, attributes: 5 + interfaces: 0, fields: 2, methods: 1, attributes: 4 public int value; descriptor: I flags: (0x0001) ACC_PUBLIC @@ -2199,9 +2117,50 @@ RuntimeVisibleAnnotations: com.android.hoststubgen.hosthelper.HostStubGenKeptInStub x: #x() com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl -RuntimeInvisibleAnnotations: +NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$Double$NestedClass.class + Compiled from "TinyFrameworkNestedClasses.java" +public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$StaticNestedClass$Double$NestedClass + minor version: 0 + major version: 61 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$Double$NestedClass + super_class: #x // java/lang/Object + interfaces: 0, fields: 1, methods: 1, attributes: 4 + public int value; + descriptor: I + flags: (0x0001) ACC_PUBLIC + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInStub + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl + + public com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$StaticNestedClass$Double$NestedClass(); + descriptor: ()V + flags: (0x0001) ACC_PUBLIC + Code: + stack=3, locals=1, args_size=1 + x: new #x // class java/lang/RuntimeException + x: dup + x: ldc #x // String Stub! + x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V + x: athrow + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInStub + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl +} +InnerClasses: + public static #x= #x of #x; // StaticNestedClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses + public static #x= #x of #x; // Double$NestedClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$Double$NestedClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass +SourceFile: "TinyFrameworkNestedClasses.java" +RuntimeVisibleAnnotations: x: #x() - android.hosttest.annotation.HostSideTestWholeClassStub + com.android.hoststubgen.hosthelper.HostStubGenKeptInStub + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses ## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass.class Compiled from "TinyFrameworkNestedClasses.java" @@ -2211,7 +2170,7 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClass flags: (0x0021) ACC_PUBLIC, ACC_SUPER this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass super_class: #x // java/lang/Object - interfaces: 0, fields: 1, methods: 2, attributes: 5 + interfaces: 0, fields: 1, methods: 2, attributes: 4 public int value; descriptor: I flags: (0x0001) ACC_PUBLIC @@ -2257,15 +2216,13 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClass InnerClasses: public static #x= #x of #x; // StaticNestedClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses #x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1 + public static #x= #x of #x; // Double$NestedClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$Double$NestedClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass SourceFile: "TinyFrameworkNestedClasses.java" RuntimeVisibleAnnotations: x: #x() com.android.hoststubgen.hosthelper.HostStubGenKeptInStub x: #x() com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl -RuntimeInvisibleAnnotations: - x: #x() - android.hosttest.annotation.HostSideTestWholeClassStub NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses ## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass.class Compiled from "TinyFrameworkNestedClasses.java" @@ -2406,6 +2363,7 @@ InnerClasses: public static #x= #x of #x; // BaseClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses public static #x= #x of #x; // StaticNestedClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses public #x= #x of #x; // InnerClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses + public static #x= #x of #x; // Double$NestedClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$Double$NestedClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass #x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1 SourceFile: "TinyFrameworkNestedClasses.java" RuntimeVisibleAnnotations: @@ -2420,6 +2378,7 @@ NestMembers: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass + com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$Double$NestedClass com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1 com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4 diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/13-hoststubgen-test-tiny-framework-host-ext-impl-dump.txt b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/13-hoststubgen-test-tiny-framework-host-ext-impl-dump.txt index d12a23d3d68f..da434a615c81 100644 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/13-hoststubgen-test-tiny-framework-host-ext-impl-dump.txt +++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/13-hoststubgen-test-tiny-framework-host-ext-impl-dump.txt @@ -611,205 +611,13 @@ RuntimeVisibleAnnotations: com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl NestMembers: com/android/hoststubgen/test/tinyframework/R$Nested -## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl.class - Compiled from "TinyFrameworkCallerCheck.java" -class com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck$Impl - minor version: 0 - major version: 61 - flags: (0x0020) ACC_SUPER - this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl - super_class: #x // java/lang/Object - interfaces: 0, fields: 0, methods: 4, attributes: 4 - private static {}; - descriptor: ()V - flags: (0x000a) ACC_PRIVATE, ACC_STATIC - Code: - stack=2, locals=0, args_size=0 - x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl - x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded - x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V - x: return - - private com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck$Impl(); - descriptor: ()V - flags: (0x0002) ACC_PRIVATE - Code: - stack=4, locals=1, args_size=1 - x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl - x: ldc #x // String <init> - x: ldc #x // String ()V - x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall - x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V - x: aload_0 - x: invokespecial #x // Method java/lang/Object."<init>":()V - x: return - LineNumberTable: - LocalVariableTable: - Start Length Slot Name Signature - 11 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl; - RuntimeVisibleAnnotations: - x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInStub - x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl - - public static int getOneKeep(); - descriptor: ()I - flags: (0x0009) ACC_PUBLIC, ACC_STATIC - Code: - stack=4, locals=0, args_size=0 - x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl - x: ldc #x // String getOneKeep - x: ldc #x // String ()I - x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall - x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V - x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl - x: ldc #x // String getOneKeep - x: ldc #x // String ()I - x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker; - x: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class; - x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V - x: iconst_1 - x: ireturn - LineNumberTable: - RuntimeVisibleAnnotations: - x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl - RuntimeInvisibleAnnotations: - x: #x() - android.hosttest.annotation.HostSideTestKeep - - public static int getOneStub(); - descriptor: ()I - flags: (0x0009) ACC_PUBLIC, ACC_STATIC - Code: - stack=4, locals=0, args_size=0 - x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl - x: ldc #x // String getOneStub - x: ldc #x // String ()I - x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall - x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V - x: iconst_1 - x: ireturn - LineNumberTable: - RuntimeVisibleAnnotations: - x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInStub - x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl - RuntimeInvisibleAnnotations: - x: #x() - android.hosttest.annotation.HostSideTestStub -} -InnerClasses: - private static #x= #x of #x; // Impl=class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl of class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck -SourceFile: "TinyFrameworkCallerCheck.java" -RuntimeVisibleAnnotations: - x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInStub - x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl -NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck -## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck.class - Compiled from "TinyFrameworkCallerCheck.java" -public class com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck - minor version: 0 - major version: 61 - flags: (0x0021) ACC_PUBLIC, ACC_SUPER - this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck - super_class: #x // java/lang/Object - interfaces: 0, fields: 0, methods: 4, attributes: 5 - private static {}; - descriptor: ()V - flags: (0x000a) ACC_PRIVATE, ACC_STATIC - Code: - stack=2, locals=0, args_size=0 - x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck - x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded - x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V - x: return - - public com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck(); - descriptor: ()V - flags: (0x0001) ACC_PUBLIC - Code: - stack=4, locals=1, args_size=1 - x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck - x: ldc #x // String <init> - x: ldc #x // String ()V - x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall - x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V - x: aload_0 - x: invokespecial #x // Method java/lang/Object."<init>":()V - x: return - LineNumberTable: - LocalVariableTable: - Start Length Slot Name Signature - 11 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck; - RuntimeVisibleAnnotations: - x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInStub - x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl - - public static int getOne_withCheck(); - descriptor: ()I - flags: (0x0009) ACC_PUBLIC, ACC_STATIC - Code: - stack=4, locals=0, args_size=0 - x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck - x: ldc #x // String getOne_withCheck - x: ldc #x // String ()I - x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall - x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V - x: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl.getOneKeep:()I - x: ireturn - LineNumberTable: - RuntimeVisibleAnnotations: - x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInStub - x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl - - public static int getOne_noCheck(); - descriptor: ()I - flags: (0x0009) ACC_PUBLIC, ACC_STATIC - Code: - stack=4, locals=0, args_size=0 - x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck - x: ldc #x // String getOne_noCheck - x: ldc #x // String ()I - x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall - x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V - x: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl.getOneStub:()I - x: ireturn - LineNumberTable: - RuntimeVisibleAnnotations: - x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInStub - x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl -} -InnerClasses: - private static #x= #x of #x; // Impl=class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl of class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck -SourceFile: "TinyFrameworkCallerCheck.java" -RuntimeVisibleAnnotations: - x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInStub - x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl -RuntimeInvisibleAnnotations: - x: #x() - android.hosttest.annotation.HostSideTestWholeClassStub -NestMembers: - com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl -## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations.class - Compiled from "TinyFrameworkClassAnnotations.java" -public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnotations +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations.class + Compiled from "TinyFrameworkAnnotations.java" +public class com.android.hoststubgen.test.tinyframework.TinyFrameworkAnnotations minor version: 0 major version: 61 flags: (0x0021) ACC_PUBLIC, ACC_SUPER - this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations super_class: #x // java/lang/Object interfaces: 0, fields: 2, methods: 8, attributes: 3 public int stub; @@ -839,20 +647,20 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnota flags: (0x000a) ACC_PRIVATE, ACC_STATIC Code: stack=2, locals=0, args_size=0 - x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations + x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V - x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations + x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations x: ldc #x // String com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V x: return - public com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnotations(); + public com.android.hoststubgen.test.tinyframework.TinyFrameworkAnnotations(); descriptor: ()V flags: (0x0001) ACC_PUBLIC Code: stack=4, locals=1, args_size=1 - x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations + x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations x: ldc #x // String <init> x: ldc #x // String ()V x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall @@ -869,7 +677,7 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnota LineNumberTable: LocalVariableTable: Start Length Slot Name Signature - 11 15 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations; + 11 15 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations; RuntimeVisibleAnnotations: x: #x() com.android.hoststubgen.hosthelper.HostStubGenKeptInStub @@ -884,7 +692,7 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnota flags: (0x0001) ACC_PUBLIC Code: stack=4, locals=2, args_size=2 - x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations + x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations x: ldc #x // String addOne x: ldc #x // String (I)I x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall @@ -896,7 +704,7 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnota LineNumberTable: LocalVariableTable: Start Length Slot Name Signature - 11 6 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations; + 11 6 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations; 11 6 1 value I RuntimeVisibleAnnotations: x: #x() @@ -912,12 +720,12 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnota flags: (0x0001) ACC_PUBLIC Code: stack=4, locals=2, args_size=2 - x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations + x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations x: ldc #x // String addOneInner x: ldc #x // String (I)I x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V - x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations + x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations x: ldc #x // String addOneInner x: ldc #x // String (I)I x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker; @@ -930,7 +738,7 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnota LineNumberTable: LocalVariableTable: Start Length Slot Name Signature - 26 4 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations; + 26 4 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations; 26 4 1 value I RuntimeVisibleAnnotations: x: #x() @@ -944,7 +752,7 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnota flags: (0x0001) ACC_PUBLIC Code: stack=4, locals=2, args_size=2 - x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations + x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations x: ldc #x // String addTwo x: ldc #x // String (I)I x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall @@ -956,7 +764,7 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnota LineNumberTable: LocalVariableTable: Start Length Slot Name Signature - 11 4 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations; + 11 4 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations; 11 4 1 value I RuntimeVisibleAnnotations: x: #x() @@ -971,7 +779,7 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnota flags: (0x0009) ACC_PUBLIC, ACC_STATIC Code: stack=4, locals=1, args_size=1 - x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations + x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations x: ldc #x // String nativeAddThree x: ldc #x // String (I)I x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall @@ -997,12 +805,12 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnota flags: (0x0001) ACC_PUBLIC Code: stack=4, locals=1, args_size=1 - x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations + x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations x: ldc #x // String unsupportedMethod x: ldc #x // String ()Ljava/lang/String; x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V - x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations + x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations x: ldc #x // String unsupportedMethod x: ldc #x // String ()Ljava/lang/String; x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker; @@ -1028,7 +836,7 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnota flags: (0x0001) ACC_PUBLIC Code: stack=4, locals=1, args_size=1 - x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations + x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations x: ldc #x // String visibleButUsesUnsupportedMethod x: ldc #x // String ()Ljava/lang/String; x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall @@ -1039,7 +847,7 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnota LineNumberTable: LocalVariableTable: Start Length Slot Name Signature - 11 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations; + 11 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations; RuntimeVisibleAnnotations: x: #x() com.android.hoststubgen.hosthelper.HostStubGenKeptInStub @@ -1049,7 +857,7 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnota x: #x() android.hosttest.annotation.HostSideTestStub } -SourceFile: "TinyFrameworkClassAnnotations.java" +SourceFile: "TinyFrameworkAnnotations.java" RuntimeVisibleAnnotations: x: #x() com.android.hoststubgen.hosthelper.HostStubGenKeptInStub @@ -1062,255 +870,188 @@ RuntimeInvisibleAnnotations: android.hosttest.annotation.HostSideTestClassLoadHook( value="com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded" ) -## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations.class - Compiled from "TinyFrameworkClassClassWideAnnotations.java" -public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassClassWideAnnotations +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl.class + Compiled from "TinyFrameworkCallerCheck.java" +class com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck$Impl minor version: 0 major version: 61 - flags: (0x0021) ACC_PUBLIC, ACC_SUPER - this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations + flags: (0x0020) ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl super_class: #x // java/lang/Object - interfaces: 0, fields: 3, methods: 9, attributes: 3 - public int stub; - descriptor: I - flags: (0x0001) ACC_PUBLIC - RuntimeVisibleAnnotations: - x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInStub - x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl - - public int keep; - descriptor: I - flags: (0x0001) ACC_PUBLIC - RuntimeVisibleAnnotations: - x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInStub - x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl - - public int remove; - descriptor: I - flags: (0x0001) ACC_PUBLIC - RuntimeVisibleAnnotations: - x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInStub - x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl - + interfaces: 0, fields: 0, methods: 4, attributes: 4 private static {}; descriptor: ()V flags: (0x000a) ACC_PRIVATE, ACC_STATIC Code: stack=2, locals=0, args_size=0 - x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations + x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V x: return - public com.android.hoststubgen.test.tinyframework.TinyFrameworkClassClassWideAnnotations(); + private com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck$Impl(); descriptor: ()V - flags: (0x0001) ACC_PUBLIC + flags: (0x0002) ACC_PRIVATE Code: stack=4, locals=1, args_size=1 - x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations + x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl x: ldc #x // String <init> x: ldc #x // String ()V x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V x: aload_0 x: invokespecial #x // Method java/lang/Object."<init>":()V - x: aload_0 - x: iconst_1 - x: putfield #x // Field stub:I - x: aload_0 - x: iconst_2 - x: putfield #x // Field keep:I x: return LineNumberTable: LocalVariableTable: Start Length Slot Name Signature - 11 15 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations; + 11 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl; RuntimeVisibleAnnotations: x: #x() com.android.hoststubgen.hosthelper.HostStubGenKeptInStub x: #x() com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl - public int addOne(int); - descriptor: (I)I - flags: (0x0001) ACC_PUBLIC + public static int getOneKeep(); + descriptor: ()I + flags: (0x0009) ACC_PUBLIC, ACC_STATIC Code: - stack=4, locals=2, args_size=2 - x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations - x: ldc #x // String addOne - x: ldc #x // String (I)I + stack=4, locals=0, args_size=0 + x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl + x: ldc #x // String getOneKeep + x: ldc #x // String ()I x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V - x: aload_0 - x: iload_1 - x: invokevirtual #x // Method addOneInner:(I)I + x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl + x: ldc #x // String getOneKeep + x: ldc #x // String ()I + x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker; + x: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class; + x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V + x: iconst_1 x: ireturn LineNumberTable: - LocalVariableTable: - Start Length Slot Name Signature - 11 6 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations; - 11 6 1 value I RuntimeVisibleAnnotations: x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInStub - x: #x() com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl + RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestKeep - public int addOneInner(int); - descriptor: (I)I - flags: (0x0001) ACC_PUBLIC + public static int getOneStub(); + descriptor: ()I + flags: (0x0009) ACC_PUBLIC, ACC_STATIC Code: - stack=4, locals=2, args_size=2 - x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations - x: ldc #x // String addOneInner - x: ldc #x // String (I)I + stack=4, locals=0, args_size=0 + x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl + x: ldc #x // String getOneStub + x: ldc #x // String ()I x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V - x: iload_1 x: iconst_1 - x: iadd x: ireturn LineNumberTable: - LocalVariableTable: - Start Length Slot Name Signature - 11 4 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations; - 11 4 1 value I RuntimeVisibleAnnotations: x: #x() com.android.hoststubgen.hosthelper.HostStubGenKeptInStub x: #x() com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl - - public void toBeRemoved(java.lang.String); - descriptor: (Ljava/lang/String;)V - flags: (0x0001) ACC_PUBLIC - Code: - stack=4, locals=2, args_size=2 - x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations - x: ldc #x // String toBeRemoved - x: ldc #x // String (Ljava/lang/String;)V - x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall - x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V - x: new #x // class java/lang/RuntimeException - x: dup - x: invokespecial #x // Method java/lang/RuntimeException."<init>":()V - x: athrow - LineNumberTable: - LocalVariableTable: - Start Length Slot Name Signature - 11 8 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations; - 11 8 1 foo Ljava/lang/String; - RuntimeVisibleAnnotations: - x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInStub + RuntimeInvisibleAnnotations: x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl + android.hosttest.annotation.HostSideTestStub +} +InnerClasses: + private static #x= #x of #x; // Impl=class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl of class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck +SourceFile: "TinyFrameworkCallerCheck.java" +RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInStub + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl +NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck.class + Compiled from "TinyFrameworkCallerCheck.java" +public class com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck + minor version: 0 + major version: 61 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck + super_class: #x // java/lang/Object + interfaces: 0, fields: 0, methods: 4, attributes: 5 + private static {}; + descriptor: ()V + flags: (0x000a) ACC_PRIVATE, ACC_STATIC + Code: + stack=2, locals=0, args_size=0 + x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck + x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded + x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V + x: return - public int addTwo(int); - descriptor: (I)I + public com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck(); + descriptor: ()V flags: (0x0001) ACC_PUBLIC Code: - stack=4, locals=2, args_size=2 - x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations - x: ldc #x // String addTwo - x: ldc #x // String (I)I + stack=4, locals=1, args_size=1 + x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck + x: ldc #x // String <init> + x: ldc #x // String ()V x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V - x: iload_1 - x: iconst_2 - x: iadd - x: ireturn + x: aload_0 + x: invokespecial #x // Method java/lang/Object."<init>":()V + x: return LineNumberTable: LocalVariableTable: Start Length Slot Name Signature - 11 4 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations; - 11 4 1 value I + 11 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck; RuntimeVisibleAnnotations: x: #x() - com.android.hoststubgen.hosthelper.HostStubGenProcessedAsSubstitute - x: #x() com.android.hoststubgen.hosthelper.HostStubGenKeptInStub x: #x() com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl - public static int nativeAddThree(int); - descriptor: (I)I + public static int getOne_withCheck(); + descriptor: ()I flags: (0x0009) ACC_PUBLIC, ACC_STATIC Code: - stack=4, locals=1, args_size=1 - x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations - x: ldc #x // String nativeAddThree - x: ldc #x // String (I)I + stack=4, locals=0, args_size=0 + x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck + x: ldc #x // String getOne_withCheck + x: ldc #x // String ()I x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V - x: iload_0 - x: iconst_3 - x: iadd + x: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl.getOneKeep:()I x: ireturn LineNumberTable: - LocalVariableTable: - Start Length Slot Name Signature - 11 4 0 value I RuntimeVisibleAnnotations: x: #x() - com.android.hoststubgen.hosthelper.HostStubGenProcessedAsSubstitute - x: #x() com.android.hoststubgen.hosthelper.HostStubGenKeptInStub x: #x() com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl - public java.lang.String unsupportedMethod(); - descriptor: ()Ljava/lang/String; - flags: (0x0001) ACC_PUBLIC - Code: - stack=4, locals=1, args_size=1 - x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations - x: ldc #x // String unsupportedMethod - x: ldc #x // String ()Ljava/lang/String; - x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall - x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V - x: ldc #x // String This value shouldn\'t be seen on the host side. - x: areturn - LineNumberTable: - LocalVariableTable: - Start Length Slot Name Signature - 11 3 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations; - RuntimeVisibleAnnotations: - x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInStub - x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl - - public java.lang.String visibleButUsesUnsupportedMethod(); - descriptor: ()Ljava/lang/String; - flags: (0x0001) ACC_PUBLIC + public static int getOne_noCheck(); + descriptor: ()I + flags: (0x0009) ACC_PUBLIC, ACC_STATIC Code: - stack=4, locals=1, args_size=1 - x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations - x: ldc #x // String visibleButUsesUnsupportedMethod - x: ldc #x // String ()Ljava/lang/String; + stack=4, locals=0, args_size=0 + x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck + x: ldc #x // String getOne_noCheck + x: ldc #x // String ()I x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V - x: aload_0 - x: invokevirtual #x // Method unsupportedMethod:()Ljava/lang/String; - x: areturn + x: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl.getOneStub:()I + x: ireturn LineNumberTable: - LocalVariableTable: - Start Length Slot Name Signature - 11 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations; RuntimeVisibleAnnotations: x: #x() com.android.hoststubgen.hosthelper.HostStubGenKeptInStub x: #x() com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl } -SourceFile: "TinyFrameworkClassClassWideAnnotations.java" +InnerClasses: + private static #x= #x of #x; // Impl=class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl of class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck +SourceFile: "TinyFrameworkCallerCheck.java" RuntimeVisibleAnnotations: x: #x() com.android.hoststubgen.hosthelper.HostStubGenKeptInStub @@ -1319,6 +1060,8 @@ RuntimeVisibleAnnotations: RuntimeInvisibleAnnotations: x: #x() android.hosttest.annotation.HostSideTestWholeClassStub +NestMembers: + com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl ## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassLoadHook.class Compiled from "TinyFrameworkClassLoadHook.java" public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook @@ -1424,6 +1167,175 @@ RuntimeVisibleAnnotations: RuntimeInvisibleAnnotations: x: #x() android.hosttest.annotation.HostSideTestWholeClassStub +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotations.class + Compiled from "TinyFrameworkClassWideAnnotations.java" +public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassWideAnnotations + minor version: 0 + major version: 61 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotations + super_class: #x // java/lang/Object + interfaces: 0, fields: 1, methods: 6, attributes: 3 + public int stub; + descriptor: I + flags: (0x0001) ACC_PUBLIC + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInStub + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl + + private static {}; + descriptor: ()V + flags: (0x000a) ACC_PRIVATE, ACC_STATIC + Code: + stack=2, locals=0, args_size=0 + x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotations + x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded + x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V + x: return + + public com.android.hoststubgen.test.tinyframework.TinyFrameworkClassWideAnnotations(); + descriptor: ()V + flags: (0x0001) ACC_PUBLIC + Code: + stack=4, locals=1, args_size=1 + x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotations + x: ldc #x // String <init> + x: ldc #x // String ()V + x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall + x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V + x: aload_0 + x: invokespecial #x // Method java/lang/Object."<init>":()V + x: aload_0 + x: iconst_1 + x: putfield #x // Field stub:I + x: return + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 11 10 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotations; + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInStub + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl + + public int addOne(int); + descriptor: (I)I + flags: (0x0001) ACC_PUBLIC + Code: + stack=4, locals=2, args_size=2 + x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotations + x: ldc #x // String addOne + x: ldc #x // String (I)I + x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall + x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V + x: iload_1 + x: iconst_1 + x: iadd + x: ireturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 11 4 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotations; + 11 4 1 value I + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInStub + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl + + public int addTwo(int); + descriptor: (I)I + flags: (0x0001) ACC_PUBLIC + Code: + stack=4, locals=2, args_size=2 + x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotations + x: ldc #x // String addTwo + x: ldc #x // String (I)I + x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall + x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V + x: iload_1 + x: iconst_2 + x: iadd + x: ireturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 11 4 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotations; + 11 4 1 value I + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsSubstitute + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInStub + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl + + public java.lang.String unsupportedMethod(); + descriptor: ()Ljava/lang/String; + flags: (0x0001) ACC_PUBLIC + Code: + stack=4, locals=1, args_size=1 + x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotations + x: ldc #x // String unsupportedMethod + x: ldc #x // String ()Ljava/lang/String; + x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall + x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V + x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotations + x: ldc #x // String unsupportedMethod + x: ldc #x // String ()Ljava/lang/String; + x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker; + x: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class; + x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V + x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onThrowMethodCalled:()V + x: new #x // class java/lang/RuntimeException + x: dup + x: ldc #x // String Unreachable + x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V + x: athrow + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsThrow + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl + RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestThrow + + public java.lang.String visibleButUsesUnsupportedMethod(); + descriptor: ()Ljava/lang/String; + flags: (0x0001) ACC_PUBLIC + Code: + stack=4, locals=1, args_size=1 + x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotations + x: ldc #x // String visibleButUsesUnsupportedMethod + x: ldc #x // String ()Ljava/lang/String; + x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall + x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V + x: aload_0 + x: invokevirtual #x // Method unsupportedMethod:()Ljava/lang/String; + x: areturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 11 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotations; + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInStub + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl +} +SourceFile: "TinyFrameworkClassWideAnnotations.java" +RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInStub + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl +RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestWholeClassStub ## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializerDefault.class Compiled from "TinyFrameworkClassWithInitializerDefault.java" public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassWithInitializerDefault @@ -4280,7 +4192,7 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClass flags: (0x0021) ACC_PUBLIC, ACC_SUPER this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass super_class: #x // java/lang/Object - interfaces: 0, fields: 2, methods: 2, attributes: 5 + interfaces: 0, fields: 2, methods: 2, attributes: 4 public int value; descriptor: I flags: (0x0001) ACC_PUBLIC @@ -4350,9 +4262,6 @@ RuntimeVisibleAnnotations: com.android.hoststubgen.hosthelper.HostStubGenKeptInStub x: #x() com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl -RuntimeInvisibleAnnotations: - x: #x() - android.hosttest.annotation.HostSideTestWholeClassStub NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses ## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1.class Compiled from "TinyFrameworkNestedClasses.java" @@ -4458,6 +4367,70 @@ RuntimeVisibleAnnotations: x: #x() com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$Double$NestedClass.class + Compiled from "TinyFrameworkNestedClasses.java" +public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$StaticNestedClass$Double$NestedClass + minor version: 0 + major version: 61 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$Double$NestedClass + super_class: #x // java/lang/Object + interfaces: 0, fields: 1, methods: 2, attributes: 4 + public int value; + descriptor: I + flags: (0x0001) ACC_PUBLIC + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInStub + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl + + private static {}; + descriptor: ()V + flags: (0x000a) ACC_PRIVATE, ACC_STATIC + Code: + stack=2, locals=0, args_size=0 + x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$Double$NestedClass + x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded + x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V + x: return + + public com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$StaticNestedClass$Double$NestedClass(); + descriptor: ()V + flags: (0x0001) ACC_PUBLIC + Code: + stack=4, locals=1, args_size=1 + x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$Double$NestedClass + x: ldc #x // String <init> + x: ldc #x // String ()V + x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall + x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V + x: aload_0 + x: invokespecial #x // Method java/lang/Object."<init>":()V + x: aload_0 + x: bipush 8 + x: putfield #x // Field value:I + x: return + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 11 11 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$Double$NestedClass; + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInStub + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl +} +InnerClasses: + public static #x= #x of #x; // StaticNestedClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses + public static #x= #x of #x; // Double$NestedClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$Double$NestedClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass +SourceFile: "TinyFrameworkNestedClasses.java" +RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInStub + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl +NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses ## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass.class Compiled from "TinyFrameworkNestedClasses.java" public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$StaticNestedClass @@ -4466,7 +4439,7 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClass flags: (0x0021) ACC_PUBLIC, ACC_SUPER this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass super_class: #x // java/lang/Object - interfaces: 0, fields: 1, methods: 3, attributes: 5 + interfaces: 0, fields: 1, methods: 3, attributes: 4 public int value; descriptor: I flags: (0x0001) ACC_PUBLIC @@ -4537,15 +4510,13 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClass InnerClasses: public static #x= #x of #x; // StaticNestedClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses #x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1 + public static #x= #x of #x; // Double$NestedClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$Double$NestedClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass SourceFile: "TinyFrameworkNestedClasses.java" RuntimeVisibleAnnotations: x: #x() com.android.hoststubgen.hosthelper.HostStubGenKeptInStub x: #x() com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl -RuntimeInvisibleAnnotations: - x: #x() - android.hosttest.annotation.HostSideTestWholeClassStub NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses ## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass.class Compiled from "TinyFrameworkNestedClasses.java" @@ -4741,6 +4712,7 @@ InnerClasses: public static #x= #x of #x; // BaseClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses public static #x= #x of #x; // StaticNestedClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses public #x= #x of #x; // InnerClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses + public static #x= #x of #x; // Double$NestedClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$Double$NestedClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass #x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1 SourceFile: "TinyFrameworkNestedClasses.java" RuntimeVisibleAnnotations: @@ -4755,6 +4727,7 @@ NestMembers: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass + com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$Double$NestedClass com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1 com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4 diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations.java index 6d8a48a37fea..30dfc80fc60b 100644 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations.java +++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations.java @@ -28,9 +28,9 @@ import android.hosttest.annotation.HostSideTestThrow; @HostSideTestStub @HostSideTestClassLoadHook( "com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded") -public class TinyFrameworkClassAnnotations { +public class TinyFrameworkAnnotations { @HostSideTestStub - public TinyFrameworkClassAnnotations() { + public TinyFrameworkAnnotations() { } @HostSideTestStub diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotations.java index 145b65a98d7b..a626bc943018 100644 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations.java +++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotations.java @@ -15,38 +15,21 @@ */ package com.android.hoststubgen.test.tinyframework; -import android.hosttest.annotation.HostSideTestStub; import android.hosttest.annotation.HostSideTestSubstitute; +import android.hosttest.annotation.HostSideTestThrow; import android.hosttest.annotation.HostSideTestWholeClassStub; @HostSideTestWholeClassStub -public class TinyFrameworkClassClassWideAnnotations { - public TinyFrameworkClassClassWideAnnotations() { +public class TinyFrameworkClassWideAnnotations { + public TinyFrameworkClassWideAnnotations() { } public int stub = 1; - public int keep = 2; - - // Cannot have an initial value, because otherwise .ctor will fail to set it at runtime. - public int remove; - - // @Stub public int addOne(int value) { - return addOneInner(value); - } - - // @Keep - public int addOneInner(int value) { return value + 1; } - // @Remove - public void toBeRemoved(String foo) { - throw new RuntimeException(); - } - - @HostSideTestStub @HostSideTestSubstitute(suffix = "_host") public int addTwo(int value) { throw new RuntimeException("not supported on host side"); @@ -56,14 +39,7 @@ public class TinyFrameworkClassClassWideAnnotations { return value + 2; } - @HostSideTestStub - @HostSideTestSubstitute(suffix = "_host") - public static native int nativeAddThree(int value); - - public static int nativeAddThree_host(int value) { - return value + 3; - } - + @HostSideTestThrow public String unsupportedMethod() { return "This value shouldn't be seen on the host side."; } diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses.java index e1c48bb85563..fec307a3db25 100644 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses.java +++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses.java @@ -34,6 +34,7 @@ public class TinyFrameworkNestedClasses { return 2; } }; + public Supplier<Integer> getSupplier() { return new Supplier<Integer>() { @Override @@ -52,12 +53,10 @@ public class TinyFrameworkNestedClasses { }; } - @HostSideTestWholeClassStub public class InnerClass { public int value = 5; } - @HostSideTestWholeClassStub public static class StaticNestedClass { public int value = 6; @@ -70,6 +69,10 @@ public class TinyFrameworkNestedClasses { } }; } + + public static class Double$NestedClass { + public int value = 8; + } } public static class BaseClass { diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithAnnotTest.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotationsTest.java index 288c7162aa58..181902a41ec9 100644 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithAnnotTest.java +++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotationsTest.java @@ -21,20 +21,20 @@ import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; -public class TinyFrameworkClassWithAnnotTest { +public class TinyFrameworkAnnotationsTest { @Rule public ExpectedException thrown = ExpectedException.none(); @Test public void testSimple() { - TinyFrameworkClassAnnotations tfc = new TinyFrameworkClassAnnotations(); + TinyFrameworkAnnotations tfc = new TinyFrameworkAnnotations(); assertThat(tfc.addOne(1)).isEqualTo(2); assertThat(tfc.stub).isEqualTo(1); } // @Test // public void testDoesntCompile() { -// TinyFrameworkClassWithAnnot tfc = new TinyFrameworkClassWithAnnot(); +// TinyFrameworkAnnotations tfc = new TinyFrameworkAnnotations(); // // tfc.addOneInner(1); // Shouldn't compile. // tfc.toBeRemoved("abc"); // Shouldn't compile. @@ -45,19 +45,19 @@ public class TinyFrameworkClassWithAnnotTest { @Test public void testSubstitute() { - TinyFrameworkClassAnnotations tfc = new TinyFrameworkClassAnnotations(); + TinyFrameworkAnnotations tfc = new TinyFrameworkAnnotations(); assertThat(tfc.addTwo(1)).isEqualTo(3); } @Test public void testSubstituteNative() { - TinyFrameworkClassAnnotations tfc = new TinyFrameworkClassAnnotations(); + TinyFrameworkAnnotations tfc = new TinyFrameworkAnnotations(); assertThat(tfc.nativeAddThree(1)).isEqualTo(4); } @Test public void testVisibleButUsesUnsupportedMethod() { - TinyFrameworkClassAnnotations tfc = new TinyFrameworkClassAnnotations(); + TinyFrameworkAnnotations tfc = new TinyFrameworkAnnotations(); thrown.expect(RuntimeException.class); thrown.expectMessage("not yet supported"); diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassTest.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassTest.java index 1692c6e89e8c..dda5a0529278 100644 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassTest.java +++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassTest.java @@ -20,7 +20,6 @@ import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.fail; import com.android.hoststubgen.test.tinyframework.R.Nested; -import com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses.SubClass; import org.junit.Rule; import org.junit.Test; @@ -88,42 +87,6 @@ public class TinyFrameworkClassTest { } @Test - public void testNestedClass1() { - assertThat(new TinyFrameworkNestedClasses().mSupplier.get()).isEqualTo(1); - } - - @Test - public void testNestedClass2() { - assertThat(TinyFrameworkNestedClasses.sSupplier.get()).isEqualTo(2); - } - - @Test - public void testNestedClass3() { - assertThat(new TinyFrameworkNestedClasses().getSupplier().get()).isEqualTo(3); - } - - @Test - public void testNestedClass4() { - assertThat(TinyFrameworkNestedClasses.getSupplier_static().get()).isEqualTo(4); - } - - @Test - public void testNestedClass5() { - assertThat((new TinyFrameworkNestedClasses()).new InnerClass().value).isEqualTo(5); - } - - @Test - public void testNestedClass6() { - assertThat(new TinyFrameworkNestedClasses.StaticNestedClass().value).isEqualTo(6); - } - - @Test - public void testNestedClass7() { - assertThat(TinyFrameworkNestedClasses.StaticNestedClass.getSupplier_static().get()) - .isEqualTo(7); - } - - @Test public void testLambda1() { assertThat(new TinyFrameworkLambdas().mSupplier.get()).isEqualTo(1); } @@ -220,18 +183,13 @@ public class TinyFrameworkClassTest { } @Test - public void testMethodCallBeforeSuperCall() { - assertThat(new SubClass(3).value).isEqualTo(3); - } - - @Test public void testClassLoadHook() { assertThat(TinyFrameworkClassWithInitializerStub.sInitialized).isTrue(); // Having this line before assertThat() will ensure these class are already loaded. var classes = new Class[]{ TinyFrameworkClassWithInitializerStub.class, - TinyFrameworkClassAnnotations.class, + TinyFrameworkAnnotations.class, TinyFrameworkForTextPolicy.class, }; diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotationsTest.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotationsTest.java new file mode 100644 index 000000000000..83753b5b1fb2 --- /dev/null +++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotationsTest.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.hoststubgen.test.tinyframework; + +import static com.google.common.truth.Truth.assertThat; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; + +public class TinyFrameworkClassWideAnnotationsTest { + @Rule + public ExpectedException thrown = ExpectedException.none(); + + @Test + public void testSimple() { + var tfc = new TinyFrameworkClassWideAnnotations(); + assertThat(tfc.addOne(1)).isEqualTo(2); + assertThat(tfc.stub).isEqualTo(1); + } + + @Test + public void testSubstitute() { + var tfc = new TinyFrameworkClassWideAnnotations(); + assertThat(tfc.addTwo(1)).isEqualTo(3); + } + + @Test + public void testVisibleButUsesUnsupportedMethod() { + var tfc = new TinyFrameworkClassWideAnnotations(); + + thrown.expect(RuntimeException.class); + thrown.expectMessage("not yet supported"); + tfc.visibleButUsesUnsupportedMethod(); + } + + @Test + public void testMethodCallBeforeSuperCall() { + assertThat(new TinyFrameworkNestedClasses.SubClass(3).value).isEqualTo(3); + } + + @Test + public void testNestedClass1() { + assertThat(new TinyFrameworkNestedClasses().mSupplier.get()).isEqualTo(1); + } + + @Test + public void testNestedClass2() { + assertThat(TinyFrameworkNestedClasses.sSupplier.get()).isEqualTo(2); + } + + @Test + public void testNestedClass3() { + assertThat(new TinyFrameworkNestedClasses().getSupplier().get()).isEqualTo(3); + } + + @Test + public void testNestedClass4() { + assertThat(TinyFrameworkNestedClasses.getSupplier_static().get()).isEqualTo(4); + } + + @Test + public void testNestedClass5() { + assertThat((new TinyFrameworkNestedClasses()).new InnerClass().value).isEqualTo(5); + } + + @Test + public void testNestedClass6() { + assertThat(new TinyFrameworkNestedClasses.StaticNestedClass().value).isEqualTo(6); + } + + @Test + public void testNestedClass7() { + assertThat(TinyFrameworkNestedClasses.StaticNestedClass.getSupplier_static().get()) + .isEqualTo(7); + } + + @Test + public void testNestedClass8() { + assertThat(new TinyFrameworkNestedClasses.StaticNestedClass.Double$NestedClass().value) + .isEqualTo(8); + } +} diff --git a/tools/hoststubgen/hoststubgen/test/com/android/hoststubgen/asm/AsmUtilsTest.kt b/tools/hoststubgen/hoststubgen/test/com/android/hoststubgen/asm/AsmUtilsTest.kt index 6b46c84bd0bb..5b2795c4cff2 100644 --- a/tools/hoststubgen/hoststubgen/test/com/android/hoststubgen/asm/AsmUtilsTest.kt +++ b/tools/hoststubgen/hoststubgen/test/com/android/hoststubgen/asm/AsmUtilsTest.kt @@ -23,18 +23,6 @@ import org.objectweb.asm.Opcodes.ACC_PUBLIC import org.objectweb.asm.Opcodes.ACC_STATIC class AsmUtilsTest { - private fun checkGetDirectOuterClassName(input: String, expected: String?) { - assertThat(getDirectOuterClassName(input)).isEqualTo(expected) - } - - @Test - fun testGetDirectOuterClassName() { - checkGetDirectOuterClassName("a", null) - checkGetDirectOuterClassName("a\$x", "a") - checkGetDirectOuterClassName("a.b.c\$x", "a.b.c") - checkGetDirectOuterClassName("a.b.c\$x\$y", "a.b.c\$x") - } - @Test fun testVisibility() { fun test(access: Int, expected: Visibility) { |